{"id":11635,"date":"2026-11-05T17:00:37","date_gmt":"2026-11-05T16:00:37","guid":{"rendered":"https:\/\/retrofixer.it\/?page_id=11635"},"modified":"2026-11-05T17:00:57","modified_gmt":"2026-11-05T16:00:57","slug":"kim-1","status":"publish","type":"page","link":"https:\/\/retrofixer.it\/en\/kim-1\/","title":{"rendered":"KIM-1"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"11635\" class=\"elementor elementor-11635\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-7c1a5e2 e-con-full e-flex e-con e-parent\" data-id=\"7c1a5e2\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-9b4d3f1 elementor-widget elementor-widget-html\" data-id=\"9b4d3f1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@400;600;700;800&family=Press+Start+2P&family=VT323&display=swap\" rel=\"stylesheet\">\n\n<style>\n.rfkim{--bg:#0f0c24;--bg2:#171338;--panel:#1d1846;--line:#342c74;--text:#eceaff;--muted:#aaa5d8;--c64l:#7c70da;--mag:#e0398a;--yel:#ffd23f;--grn:#5ad15f;--cya:#3fd8ff;--red:#f94144;font-family:'Inter',system-ui,-apple-system,Segoe UI,Roboto,sans-serif;color:var(--text);background:var(--bg);line-height:1.6;padding:36px 0 28px}\n.rfkim *,.rfkim *::before,.rfkim *::after{box-sizing:border-box}\n.rfkim h2,.rfkim h3,.rfkim h4,.rfkim p,.rfkim ul,.rfkim ol,.rfkim figure{margin:0;padding:0}\n.rfkim h2,.rfkim h3,.rfkim h4{font-family:'Inter',system-ui,sans-serif!important;letter-spacing:0;text-transform:none;font-weight:700;color:var(--text)}\n.rfkim a{color:var(--cya)}\n.rfkim button,.rfkim input,.rfkim select,.rfkim textarea{font:inherit;color:inherit}\n.rfkim button{cursor:pointer}\n.rfkim [hidden]{display:none!important}\n.rfkim code{font-family:ui-monospace,Consolas,monospace;font-size:.92em;background:var(--bg2);border:1px solid var(--line);border-radius:4px;padding:0 4px}\n.rfkim .wrap{max-width:1400px;margin:0 auto;padding:0 16px}\n.post-views.post-11635{background:#0f0c24;color:#aaa5d8;margin:0;padding:0 16px 8px;font-size:13px}\n\n\/* intestazione *\/\n.rfkim .head{margin-bottom:34px}\n.rfkim .kicker{font-family:'VT323',monospace;font-size:21px;color:var(--c64l);margin-bottom:6px}\n.rfkim .head h2{font-family:'Press Start 2P',monospace!important;font-size:clamp(24px,4.6vw,44px);line-height:1.3;color:var(--yel);margin-bottom:16px;text-shadow:0 0 18px rgba(255,210,63,.25)}\n.rfkim .lead{color:var(--muted);max-width:920px;font-size:17px}\n.rfkim .lead b{color:var(--text)}\n.rfkim .toc{display:flex;flex-wrap:wrap;gap:8px;margin-top:18px}\n.rfkim .toc a{text-decoration:none;border:1px solid var(--line);background:var(--bg2);border-radius:999px;padding:6px 14px;font-size:14px;color:var(--text)}\n.rfkim .toc a:hover{background:var(--line)}\n\n\/* sezioni *\/\n.rfkim .sec{margin:0 0 46px;scroll-margin-top:90px}\n.rfkim .sec>h3,.rfkim .txt>h3{font-family:'Press Start 2P',monospace!important;font-size:clamp(14px,2vw,18px);line-height:1.5;color:var(--cya);margin-bottom:14px}\n.rfkim .sec h4{font-size:17px;margin:26px 0 10px;color:var(--yel)}\n.rfkim .intro{color:var(--muted);max-width:960px;margin-bottom:18px}\n.rfkim .prose p{margin-bottom:12px}\n.rfkim .prose p:last-child{margin-bottom:0}\n.rfkim .prose b{color:#fff}\n.rfkim .prose ul{margin:0 0 12px 20px}\n.rfkim .box{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:18px 20px;margin-top:22px}\n.rfkim .two{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);gap:28px;align-items:start;margin-bottom:26px}\n.rfkim .two.rev{grid-template-columns:minmax(0,1fr) minmax(0,1.15fr)}\n.rfkim .two.rev .photo{order:-1}\n.rfkim .photo{background:var(--panel);border:1px solid var(--line);border-radius:12px;overflow:hidden}\n.rfkim .photo img{display:block;width:100%;height:auto;background:#0a0820}\n.rfkim .photo figcaption{padding:10px 14px;font-size:13.5px;color:var(--muted)}\n.rfkim .photo .cred{display:block;font-size:12px;opacity:.85;margin-top:2px}\n.rfkim .specs,.rfkim .ref{width:100%;border-collapse:collapse;font-size:15px}\n.rfkim .specs th,.rfkim .specs td,.rfkim .ref th,.rfkim .ref td{text-align:left;vertical-align:top;padding:7px 10px;border-bottom:1px solid var(--line)}\n.rfkim .specs th,.rfkim .ref th{color:var(--muted);font-weight:600;white-space:nowrap;width:1%}\n.rfkim .ref td:first-child{font-family:'VT323',monospace;font-size:19px;color:var(--yel);white-space:nowrap;width:1%}\n\n\/* progetti *\/\n.rfkim .cgroup{margin-bottom:22px}\n.rfkim .cgroup h4{margin-top:0}\n.rfkim .clist{display:grid;grid-template-columns:repeat(auto-fill,minmax(290px,1fr));gap:12px}\n.rfkim .card{display:flex;flex-direction:column;gap:4px;text-decoration:none;color:var(--text);background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:14px 16px;transition:border-color .15s,transform .15s}\n.rfkim .card:hover{border-color:var(--cya);transform:translateY(-2px)}\n.rfkim .card b{font-size:16px}\n.rfkim .ctag{font-family:'VT323',monospace;font-size:17px;color:var(--grn);line-height:1.1}\n.rfkim .cdesc{font-size:14px;color:var(--muted)}\n.rfkim .curl{font-size:12.5px;color:var(--cya);margin-top:auto;padding-top:4px;word-break:break-all}\n\n\/* barra comandi *\/\n.rfkim .bar{display:flex;flex-wrap:wrap;align-items:flex-end;gap:10px;margin:10px 0}\n.rfkim .bar .sp{flex:1}\n.rfkim .fld{display:flex;flex-direction:column;gap:3px;font-size:12.5px;color:var(--muted)}\n.rfkim select,.rfkim input[type=text]{background:var(--bg2);border:1px solid var(--line);border-radius:7px;padding:7px 10px;font-size:14px;outline:none;color:var(--text)}\n.rfkim select:focus,.rfkim input[type=text]:focus{border-color:var(--c64l)}\n.rfkim select option{background:var(--bg2)}\n.rfkim .hex4{width:78px;font-family:ui-monospace,Consolas,monospace;text-transform:uppercase}\n.rfkim .btn{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--line);background:var(--bg2);border-radius:8px;padding:7px 12px;font-size:14px;line-height:1.3;white-space:nowrap;color:var(--text)}\n.rfkim .btn:hover{background:var(--line);color:#fff}\n.rfkim .btn.pri{background:var(--grn);border-color:var(--grn);color:#0f0c24;font-weight:700}\n.rfkim .btn.pri:hover{background:#7be27f;color:#0f0c24}\n.rfkim .btn.sm{padding:3px 9px;font-size:13px;border-radius:6px}\n.rfkim .btn .i{font-size:13px;line-height:1}\n.rfkim label.ck{display:inline-flex;align-items:center;gap:6px;font-size:13.5px;color:var(--muted);cursor:pointer}\n.rfkim label.ck input{accent-color:var(--grn);width:15px;height:15px}\n.rfkim .pdesc{min-height:26px;color:var(--muted);font-size:15px;margin:2px 0 14px;max-width:1100px}\n.rfkim .pdesc b{color:var(--yel)}\n.rfkim .hint{font-size:13.5px;color:var(--muted);margin:8px 0 18px}\n.rfkim .hint b{color:var(--text)}\n\n\/* terminale *\/\n.rfkim .row2{display:grid;grid-template-columns:minmax(0,1fr) 330px;gap:18px;align-items:start}\n.rfkim .termbox{background:#26222e;border-radius:18px;padding:18px 18px 10px;box-shadow:0 14px 40px rgba(0,0,0,.5),inset 0 2px 0 #3c3746}\n.rfkim .crt{position:relative;border-radius:14px;overflow:hidden;background:#04130a;box-shadow:inset 0 0 40px rgba(0,0,0,.85),0 0 0 3px #111;cursor:text}\n.rfkim .crt canvas{display:block;width:100%;height:auto;filter:drop-shadow(0 0 3px rgba(93,255,138,.55))}\n.rfkim .crt::after{content:'';position:absolute;inset:0;pointer-events:none;background:repeating-linear-gradient(to bottom,rgba(0,0,0,0) 0,rgba(0,0,0,0) 2px,rgba(0,0,0,.22) 3px),radial-gradient(ellipse at center,rgba(0,0,0,0) 60%,rgba(0,0,0,.45) 100%)}\n.rfkim .crt.focus{box-shadow:inset 0 0 40px rgba(0,0,0,.85),0 0 0 3px #3a7a4c}\n.rfkim .kbd{position:absolute;left:0;top:0;width:100%;height:100%;opacity:0;resize:none;border:0;padding:0;margin:0;font-size:16px;cursor:text;z-index:2;color:transparent;background:transparent;caret-color:transparent}\n.rfkim .crtmsg{position:absolute;right:12px;bottom:10px;font-family:'VT323',monospace;font-size:17px;color:#5dff8a;background:rgba(0,0,0,.55);border:1px solid #1d5a30;border-radius:6px;padding:2px 9px;pointer-events:none;z-index:3}\n.rfkim .termbar{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:9px}\n.rfkim .termbar .tname{font-family:'VT323',monospace;font-size:18px;color:#b9b2c9;flex:1;letter-spacing:.04em}\n.rfkim .termbar .btn{background:#34303d;border-color:#47414f}\n\n\/* registri *\/\n.rfkim .dbg{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px 16px;font-family:'VT323',monospace;font-size:19px}\n.rfkim .dbg h4{font-family:'Inter',sans-serif!important;font-size:14px;margin:0 0 8px;color:var(--muted)}\n.rfkim .regs{display:grid;grid-template-columns:repeat(3,1fr);gap:4px 10px}\n.rfkim .regs span{color:var(--text)}\n.rfkim .regs i{font-style:normal;color:var(--c64l);margin-right:6px}\n.rfkim .flags{display:flex;gap:6px;margin:10px 0;flex-wrap:wrap}\n.rfkim .flags span{border:1px solid var(--line);border-radius:5px;padding:0 6px;color:#5d578f}\n.rfkim .flags span.on{color:#0f0c24;background:var(--yel);border-color:var(--yel)}\n.rfkim .flags .st{margin-left:auto;color:var(--grn);border-color:transparent}\n.rfkim .dis{list-style:none;border-top:1px solid var(--line);padding-top:6px}\n.rfkim .dis li{display:grid;grid-template-columns:48px 78px 1fr;gap:6px;line-height:1.2;color:var(--muted);white-space:nowrap}\n.rfkim .dis li.pc{color:#0f0c24;background:var(--grn);border-radius:3px}\n.rfkim .dis .ad{opacity:.8}\n.rfkim .cyc{font-size:16px;color:#6f69a6;margin-top:8px}\n\n\/* strumenti *\/\n.rfkim .tools{margin-top:22px;display:grid;gap:12px}\n.rfkim .tool{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:12px 16px}\n.rfkim .tool summary{cursor:pointer;font-weight:700;font-size:16px;color:var(--yel)}\n.rfkim .tool .note{font-size:14px;color:var(--muted);margin:8px 0}\n.rfkim .src{width:100%;background:#0a0820;border:1px solid var(--line);border-radius:8px;padding:10px 12px;font-family:ui-monospace,Consolas,monospace;font-size:13.5px;line-height:1.45;color:#d9f7e0;tab-size:8;resize:vertical;outline:none;white-space:pre;overflow:auto}\n.rfkim .src:focus{border-color:var(--c64l)}\n.rfkim .asmout{background:#0a0820;border:1px solid var(--line);border-radius:8px;padding:10px 12px;font-size:14px}\n.rfkim .asmout.err{border-color:var(--red);color:#ffb3b3}\n.rfkim .asmout b{display:block;margin-bottom:6px}\n.rfkim .asmout pre{margin:0;max-height:340px;overflow:auto;font-family:ui-monospace,Consolas,monospace;font-size:12.5px;line-height:1.4;color:#c8c3ef}\n.rfkim .msg{font-size:14px;color:var(--grn);min-height:20px}\n.rfkim .msg.err{color:var(--red)}\n\n\/* guida *\/\n.rfkim .steps{margin:14px 0 18px 22px;display:grid;gap:8px;max-width:1000px}\n.rfkim .steps li{padding-left:4px}\n.rfkim .steps li b{color:#fff}\n.rfkim .steps .setsw{margin-left:4px;font-family:'VT323',monospace;font-size:16px;padding:0 8px;vertical-align:1px}\n.rfkim .fonti ul{margin-left:20px;color:var(--muted);font-size:14.5px;display:grid;gap:4px}\n\n@media (max-width:1050px){.rfkim .row2{grid-template-columns:minmax(0,1fr)}}\n@media (max-width:820px){.rfkim .two,.rfkim .two.rev{grid-template-columns:minmax(0,1fr)}.rfkim .two.rev .photo{order:0}}\n@media (max-width:600px){.rfkim{padding-top:22px}.rfkim .lead{font-size:16px}.rfkim .termbox{padding:10px 10px 8px;border-radius:12px}.rfkim .dis li{grid-template-columns:44px 70px 1fr}}\n\n\/* contro gli stili del tema (Betheme): select chiari, tabelle a righe, campi con ombre *\/\n.rfkim select,.rfkim select:hover,.rfkim select:focus{-webkit-appearance:none;appearance:none;background:var(--bg2) url(\"data:image\/svg+xml,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23aaa5d8' stroke-width='2'\/%3E%3C\/svg%3E\") no-repeat right 10px center!important;background-color:var(--bg2)!important;color:var(--text)!important;padding:7px 30px 7px 10px!important;box-shadow:none!important;border:1px solid var(--line)!important;border-radius:7px!important;height:auto!important;margin:0!important;font-size:14px!important;line-height:1.3!important}\n.rfkim select option{background:#171338!important;color:#eceaff!important}\n.rfkim input[type=text],.rfkim input[type=text]:focus{background-color:var(--bg2)!important;color:var(--text)!important;border:1px solid var(--line)!important;box-shadow:none!important;margin:0!important;padding:7px 10px!important;border-radius:7px!important;font-size:14px!important;height:auto!important}\n.rfkim textarea.src,.rfkim textarea.src:focus{background:#0a0820!important;color:#d9f7e0!important;border:1px solid var(--line)!important;box-shadow:none!important;margin:0!important;padding:10px 12px!important;font-family:ui-monospace,Consolas,monospace!important;font-size:13.5px!important;line-height:1.45!important;border-radius:8px!important}\n.rfkim textarea.kbd,.rfkim textarea.kbd:focus{background:transparent!important;border:0!important;box-shadow:none!important;color:transparent!important;padding:0!important;margin:0!important}\n.rfkim table,.rfkim table tr,.rfkim table td,.rfkim table th{background:transparent!important;box-shadow:none!important}\n.rfkim table{border:0!important;margin:0!important}\n.rfkim .sec>h3,.rfkim .txt>h3{font-family:'Press Start 2P',monospace!important;font-size:clamp(14px,2vw,18px)!important;line-height:1.5!important;color:var(--cya)!important;margin:0 0 14px!important}\n.rfkim .sec h4{font-size:17px!important;line-height:1.4!important;color:var(--yel)!important;margin:26px 0 10px!important}\n.rfkim .cgroup h4{margin-top:0!important}\n.rfkim .dbg h4{font-family:'Inter',sans-serif!important;font-size:14px!important;margin:0 0 8px!important;color:var(--muted)!important}\n.rfkim .head h2{font-size:clamp(24px,4.6vw,44px)!important;line-height:1.3!important;color:var(--yel)!important;margin:0 0 16px!important}\n.rfkim .btn,.rfkim .btn:hover{box-shadow:none!important;text-decoration:none!important;margin:0!important}\n.rfkim .card,.rfkim .card:hover,.rfkim .toc a{text-decoration:none!important}\n.rfkim img{border:0!important;box-shadow:none!important;max-width:100%}\n.rfkim ol.dis,.rfkim ol.dis li{list-style:none!important;margin:0!important}\n.rfkim pre,.rfkim .asmout pre{background:transparent!important;background-image:none!important;border:0!important;padding:0!important;margin:0!important;box-shadow:none!important;color:#c8c3ef!important;font-family:ui-monospace,Consolas,monospace!important;font-size:12.5px!important;line-height:1.4!important;white-space:pre!important}\n.rfkim .bar select{width:auto!important;max-width:100%}\n\n\/* ---- KIM-1: scheda, display e tastiera ---- *\/\n.rfkim .row1{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);gap:18px;align-items:start;margin-bottom:18px}\n.rfkim .kimpcb{background:linear-gradient(160deg,#1f5a3a,#174a2f 60%,#123d27);border-radius:14px;padding:16px 18px 6px;box-shadow:0 14px 40px rgba(0,0,0,.5),inset 0 0 0 2px #2c7350;position:relative;overflow:hidden}\n.rfkim .kimpcb::before{content:'';position:absolute;inset:0;pointer-events:none;background:repeating-linear-gradient(90deg,rgba(255,255,255,.025) 0 2px,transparent 2px 26px),repeating-linear-gradient(0deg,rgba(255,255,255,.02) 0 2px,transparent 2px 30px)}\n.rfkim .pcbtop{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px;margin-bottom:12px;position:relative}\n.rfkim .pcbtop .logo{font-family:'Press Start 2P',monospace;font-size:12px;color:#e9e4c6;border:2px solid #e9e4c6;padding:3px 5px;border-radius:3px}\n.rfkim .pcbtop .pname{white-space:nowrap;font-family:'Press Start 2P',monospace;font-size:18px;color:#f3efd6;letter-spacing:.06em}\n.rfkim .pcbtop .pcbsub{font-family:'VT323',monospace;font-size:18px;color:#bfe3c9;margin-left:auto}\n.rfkim .kimboard{display:grid;grid-template-columns:minmax(0,1fr);gap:14px;position:relative;justify-items:center}\n.rfkim .kdisp{width:100%;max-width:560px;background:#1a0606;border-radius:8px;padding:12px 14px;box-shadow:inset 0 0 0 3px #0b0202,inset 0 0 26px rgba(0,0,0,.9),0 2px 0 rgba(255,255,255,.08)}\n.rfkim .kdsvg{display:block;width:100%;height:auto}\n.rfkim .kseg{fill:#3a0d0d}\n.rfkim .kseg.lit{fill:#ff2a1a;filter:drop-shadow(0 0 3px rgba(255,60,30,.85))}\n.rfkim .kvis{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}\n.rfkim .kpad{display:grid;grid-template-columns:repeat(4,64px);gap:10px;padding:14px 16px;background:#0f2c1c;border-radius:10px;outline:none;box-shadow:inset 0 0 0 2px #0a2015}\n.rfkim .kpad:focus-visible,.rfkim .kpad.focus{box-shadow:inset 0 0 0 2px #0a2015,0 0 0 3px var(--yel)}\n.rfkim .kkey{height:52px;border-radius:8px;border:0;background:linear-gradient(#f4f0e1,#d9d3bf);color:#1d1b16;font-family:'Inter',sans-serif;font-weight:800;font-size:19px;box-shadow:0 4px 0 #8f8a76,0 6px 10px rgba(0,0,0,.35);touch-action:manipulation;user-select:none;-webkit-user-select:none;padding:0;line-height:1}\n.rfkim .kkey.kfn{font-size:15px;background:linear-gradient(#e9e2c9,#cbc2a2)}\n.rfkim .kkey.kred{color:#a3231a}\n.rfkim .kkey:hover{filter:brightness(1.04)}\n.rfkim .kkey.down{transform:translateY(3px);box-shadow:0 1px 0 #8f8a76,0 2px 4px rgba(0,0,0,.35);filter:brightness(.95)}\n.rfkim .kkey:focus-visible{outline:3px solid var(--yel);outline-offset:2px}\n.rfkim .ksst{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;background:linear-gradient(#3b4a40,#2a362f);color:#e9e4c6;font-size:11px;box-shadow:0 3px 0 #18201b}\n.rfkim .ksst .ksl{font-weight:800;font-size:12px}\n.rfkim .ksst .ktog{width:34px;height:14px;border-radius:7px;background:#121814;position:relative;display:block}\n.rfkim .ksst .ktog i{position:absolute;left:2px;top:2px;width:14px;height:10px;border-radius:5px;background:#cfc9b2;transition:left .12s}\n.rfkim .ksst.on .ktog i{left:18px;background:var(--yel)}\n.rfkim .ksst .kon{font-size:9px;opacity:.5}\n.rfkim .ksst.on .kon{opacity:1;color:var(--yel)}\n.rfkim .kimpcb .hint{color:#bfe3c9;position:relative;text-align:center}\n.rfkim .kimpcb .hint b{color:#fff}\n.rfkim kbd{font-family:'Inter',sans-serif;font-size:12.5px;font-weight:800;background:#f1ecda;color:#1d1b16;border-radius:4px;padding:1px 6px;box-shadow:0 2px 0 #8f8a76;white-space:nowrap}\n\n\/* porta utente *\/\n.rfkim .ioport{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px 16px}\n.rfkim .ioport h4{margin:0 0 6px!important;font-size:15px!important}\n.rfkim .ioport .note,.rfkim .tape .note{font-size:13.5px;color:var(--muted);margin:4px 0 10px}\n.rfkim .leds{display:grid;grid-template-columns:44px repeat(8,minmax(0,1fr));gap:4px 5px;align-items:center;margin-bottom:12px}\n.rfkim .leds .pl{font-family:'VT323',monospace;font-size:20px;color:var(--yel)}\n.rfkim .lbit{display:flex;flex-direction:column;align-items:center;gap:3px;font-family:'VT323',monospace;font-size:15px;color:var(--muted)}\n.rfkim .led{display:block;width:20px;height:20px;border-radius:50%;background:#3b0f12;box-shadow:inset 0 -2px 3px rgba(0,0,0,.6);border:1px solid #170405}\n.rfkim .lbit.in .led{background:#1b1838;border-color:#2a2560}\n.rfkim .lsw{width:24px;height:30px;border-radius:5px;border:1px solid var(--line);background:#0c0a22;position:relative;padding:0;cursor:pointer}\n.rfkim .lsw::after{content:'';position:absolute;left:4px;right:4px;height:11px;border-radius:3px;background:#8f89c6;top:15px;transition:top .1s}\n.rfkim .lsw.hi::after{top:3px;background:var(--cya)}\n.rfkim .lsw:disabled{opacity:.25;cursor:default}\n.rfkim .ldir{font-size:13px;line-height:1;color:#6f69a6}\n.rfkim .ldir.out{color:var(--grn)}\n.rfkim .lbit.na{opacity:.3}\n.rfkim .opts{display:flex;flex-direction:column;gap:6px;border-top:1px solid var(--line);padding-top:10px}\n.rfkim .fld.inl{flex-direction:row;align-items:center;gap:8px}\n.rfkim .tape{border-top:1px solid var(--line);margin-top:12px;padding-top:10px}\n.rfkim .tdeck{display:flex;align-items:center;gap:14px;background:#0c0a22;border:1px solid var(--line);border-radius:10px;padding:8px 12px}\n.rfkim .reels{display:flex;gap:10px}\n.rfkim .reel{width:30px;height:30px;border-radius:50%;border:3px solid #8f89c6;position:relative;display:block}\n.rfkim .reel::before,.rfkim .reel::after{content:'';position:absolute;left:50%;top:50%;width:4px;height:22px;margin:-11px 0 0 -2px;background:#8f89c6;border-radius:2px}\n.rfkim .reel::after{transform:rotate(90deg)}\n.rfkim .tape.run .reel{animation:rfkspin 1.2s linear infinite}\n.rfkim .tape.rec .reel{border-color:var(--red)}\n@keyframes rfkspin{to{transform:rotate(360deg)}}\n.rfkim .tctr{font-family:'VT323',monospace;font-size:26px;color:var(--yel);display:flex;flex-direction:column;line-height:1}\n.rfkim .tctr small{font-size:15px;color:var(--muted)}\n.rfkim .bar.sm{gap:6px;margin:8px 0}\n.rfkim .i.rec{color:var(--red)}\n\n\/* telescrivente *\/\n.rfkim .termbox{background:#2b2a26;border-radius:16px;padding:16px 16px 10px;box-shadow:0 14px 40px rgba(0,0,0,.5),inset 0 2px 0 #44423b}\n.rfkim .tty{position:relative;border-radius:6px;overflow:hidden;background:#efe8d0;cursor:text;box-shadow:inset 0 10px 18px rgba(0,0,0,.18),0 0 0 3px #171612}\n.rfkim .tty canvas{display:block;width:100%;height:auto}\n.rfkim .tty.focus{box-shadow:inset 0 10px 18px rgba(0,0,0,.18),0 0 0 3px #c9a227}\n.rfkim .ttymsg{position:absolute;right:10px;bottom:8px;font-family:'VT323',monospace;font-size:17px;color:#3a3628;background:rgba(239,232,208,.9);border:1px solid #b9ae8a;border-radius:6px;padding:2px 9px;pointer-events:none;z-index:3}\n.rfkim .termbar .btn{background:#3d3b34;border-color:#55524a}\n.rfkim .mem{font-family:'VT323',monospace!important;font-size:17px!important;line-height:1.15!important;color:var(--muted)!important;white-space:pre!important;overflow-x:auto}\n.rfkim .dbg .bar.sm .btn,.rfkim .dbg .fld.inl{font-family:'Inter',sans-serif}\n.rfkim .dbg .hex4{width:70px}\n.rfkim .dbg h4.m2{margin-top:14px!important}\n\n@media (max-width:1050px){.rfkim .row1{grid-template-columns:minmax(0,1fr)}}\n@media (max-width:520px){.rfkim .kpad{grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;padding:10px;width:100%}.rfkim .kkey{height:48px;font-size:17px}.rfkim .kkey.kfn{font-size:13px}.rfkim .leds{grid-template-columns:30px repeat(8,minmax(0,1fr))}.rfkim .lsw{width:20px}}\n<\/style>\n\n<div class=\"rfkim\" data-no-translation>\n <div class=\"wrap\">\n\n  <header class=\"head\">\n   <p class=\"kicker\" data-t=\"kicker\"><\/p>\n   <h2>KIM-1<\/h2>\n   <p class=\"lead\" data-th=\"lead\"><\/p>\n   <nav class=\"toc\" aria-label=\"Indice\">\n    <a href=\"#rfkStoria\" data-t=\"navStoria\"><\/a>\n    <a href=\"#rfkEmu\" data-t=\"navEmu\"><\/a>\n    <a href=\"#rfkGuida\" data-t=\"navGuida\"><\/a>\n    <a href=\"#rfkOggi\" data-t=\"navOggi\"><\/a>\n   <\/nav>\n  <\/header>\n\n  <section class=\"sec\" id=\"rfkStoria\">\n   <div class=\"two\">\n    <div class=\"txt\">\n     <h3 data-t=\"storiaT\"><\/h3>\n     <div class=\"prose\" data-th=\"storia\"><\/div>\n    <\/div>\n    <figure class=\"photo\">\n     <img id=\"rfkFoto1\" alt=\"\" loading=\"lazy\" decoding=\"async\">\n     <figcaption id=\"rfkFoto1c\"><\/figcaption>\n    <\/figure>\n   <\/div>\n   <div class=\"two rev\">\n    <div class=\"txt\">\n     <h3 data-t=\"schedaT\"><\/h3>\n     <table class=\"specs\"><tbody data-th=\"scheda\"><\/tbody><\/table>\n    <\/div>\n    <figure class=\"photo\">\n     <img id=\"rfkFoto2\" alt=\"\" loading=\"lazy\" decoding=\"async\">\n     <figcaption id=\"rfkFoto2c\"><\/figcaption>\n    <\/figure>\n   <\/div>\n   <div class=\"prose box\" data-th=\"comeFunziona\"><\/div>\n  <\/section>\n\n  <section class=\"sec emu\" id=\"rfkEmu\">\n   <h3 data-t=\"emuT\"><\/h3>\n   <p class=\"intro\" data-th=\"emuIntro\"><\/p>\n\n   <div class=\"bar\">\n    <label class=\"fld\"><span data-t=\"prog\"><\/span><select id=\"rfkProg\"><\/select><\/label>\n    <button class=\"btn pri\" id=\"rfkLoadRun\" type=\"button\"><span class=\"i\">\u25b6<\/span><span data-t=\"loadRun\"><\/span><\/button>\n    <button class=\"btn\" id=\"rfkLoadOnly\" type=\"button\" data-t=\"loadOnly\"><\/button>\n    <span class=\"sp\"><\/span>\n    <label class=\"fld\"><span data-t=\"speed\"><\/span><select id=\"rfkSpeed\">\n     <option value=\"1\" data-t=\"sp1\"><\/option><option value=\"4\" data-t=\"sp4\"><\/option><option value=\"0\" data-t=\"sp0\"><\/option><\/select><\/label>\n    <label class=\"fld\"><span data-t=\"mode\"><\/span><select id=\"rfkMode\">\n     <option value=\"kb\" data-t=\"modeKb\"><\/option><option value=\"tty\" data-t=\"modeTty\"><\/option><\/select><\/label>\n    <label class=\"fld\"><span data-t=\"baud\"><\/span><select id=\"rfkBaud\">\n     <option value=\"110\">110<\/option><option value=\"300\">300<\/option><option value=\"1200\">1200<\/option><option value=\"2400\" selected>2400<\/option><option value=\"4800\">4800<\/option><option value=\"9600\">9600<\/option><\/select><\/label>\n    <button class=\"btn\" id=\"rfkPower\" type=\"button\" data-t=\"power\"><\/button>\n   <\/div>\n   <p class=\"pdesc\" id=\"rfkPDesc\"><\/p>\n\n   <div class=\"row1\">\n    <div class=\"kimpcb\">\n     <div class=\"pcbtop\"><span class=\"logo\">MOS<\/span><span class=\"pname\">KIM-1<\/span><span class=\"pcbsub\" data-t=\"pcbSub\"><\/span><\/div>\n     <div id=\"rfkBoard\"><\/div>\n     <p class=\"hint\" data-th=\"boardHint\"><\/p>\n    <\/div>\n\n    <div class=\"ioport\">\n     <h4 data-t=\"portT\"><\/h4>\n     <p class=\"note\" data-th=\"portNote\"><\/p>\n     <div class=\"leds\" id=\"rfkLedsA\"><\/div>\n     <div class=\"leds\" id=\"rfkLedsB\"><\/div>\n     <div class=\"opts\">\n      <label class=\"ck\"><input type=\"checkbox\" id=\"rfkAudio\"><span data-t=\"audio\"><\/span><\/label>\n      <label class=\"fld inl\"><span data-t=\"spkLine\"><\/span><select id=\"rfkSpk\"><\/select><\/label>\n      <label class=\"ck\"><input type=\"checkbox\" id=\"rfkIrq\"><span data-t=\"irqWire\"><\/span><\/label>\n      <label class=\"ck\"><input type=\"checkbox\" id=\"rfkExp\"><span data-t=\"expRam\"><\/span><\/label>\n     <\/div>\n     <div class=\"tape\">\n      <h4 data-t=\"tapeT\"><\/h4>\n      <div class=\"tdeck\">\n       <div class=\"reels\" aria-hidden=\"true\"><i class=\"reel\"><\/i><i class=\"reel\"><\/i><\/div>\n       <div class=\"tctr\"><span id=\"rfkTapeCtr\">00:00<\/span><small id=\"rfkTapeLen\"><\/small><\/div>\n      <\/div>\n      <div class=\"bar sm\">\n       <button class=\"btn sm\" id=\"rfkTapeRec\" type=\"button\"><span class=\"i rec\">\u25cf<\/span><span data-t=\"tRec\"><\/span><\/button>\n       <button class=\"btn sm\" id=\"rfkTapePlay\" type=\"button\"><span class=\"i\">\u25b6<\/span><span data-t=\"tPlay\"><\/span><\/button>\n       <button class=\"btn sm\" id=\"rfkTapeStop\" type=\"button\"><span class=\"i\">\u25a0<\/span><span data-t=\"tStop\"><\/span><\/button>\n       <button class=\"btn sm\" id=\"rfkTapeRew\" type=\"button\"><span class=\"i\">\u23ee<\/span><span data-t=\"tRew\"><\/span><\/button>\n      <\/div>\n      <div class=\"bar sm\">\n       <button class=\"btn sm\" id=\"rfkTapeWav\" type=\"button\"><span class=\"i\">\u2b07<\/span><span data-t=\"tWav\"><\/span><\/button>\n       <button class=\"btn sm\" id=\"rfkTapeOpen\" type=\"button\"><span class=\"i\">\ud83d\udcfc<\/span><span data-t=\"tOpen\"><\/span><\/button>\n       <input type=\"file\" id=\"rfkTapeFile\" accept=\".wav,audio\/wav\" hidden>\n      <\/div>\n      <p class=\"note\" data-th=\"tapeNote\"><\/p>\n     <\/div>\n    <\/div>\n   <\/div>\n\n   <div class=\"row2\">\n    <div class=\"termbox\">\n     <div class=\"tty\">\n      <canvas id=\"rfkTerm\" aria-label=\"Telescrivente\"><\/canvas>\n      <textarea id=\"rfkKbd\" class=\"kbd\" autocapitalize=\"off\" autocomplete=\"off\" autocorrect=\"off\" spellcheck=\"false\" aria-label=\"Tastiera della telescrivente\"><\/textarea>\n      <div class=\"ttymsg\" id=\"rfkTermMsg\"><\/div>\n     <\/div>\n     <div class=\"termbar\">\n      <span class=\"tname\" data-t=\"termName\"><\/span>\n      <button class=\"btn sm\" id=\"rfkRubout\" type=\"button\" data-t=\"rubout\"><\/button>\n      <button class=\"btn sm\" id=\"rfkTermClr\" type=\"button\" data-t=\"termClr\"><\/button>\n      <button class=\"btn sm\" id=\"rfkTermCopy\" type=\"button\" data-t=\"termCopy\"><\/button>\n     <\/div>\n    <\/div>\n\n    <div class=\"dbg\">\n     <h4 data-t=\"cpuT\"><\/h4>\n     <div class=\"regs\" id=\"rfkRegs\"><\/div>\n     <div class=\"flags\" id=\"rfkFlags\"><\/div>\n     <ol class=\"dis\" id=\"rfkDis\"><\/ol>\n     <div class=\"bar sm\">\n      <button class=\"btn sm\" id=\"rfkPause\" type=\"button\"><\/button>\n      <button class=\"btn sm\" id=\"rfkStep\" type=\"button\" data-t=\"step\"><\/button>\n      <label class=\"fld inl\"><span data-t=\"bpLbl\"><\/span><input type=\"text\" id=\"rfkBp\" maxlength=\"4\" class=\"hex4\" placeholder=\"----\"><\/label>\n     <\/div>\n     <p class=\"cyc\" id=\"rfkCyc\"><\/p>\n     <h4 class=\"m2\" data-t=\"memT\"><\/h4>\n     <div class=\"bar sm\"><label class=\"fld inl\"><span data-t=\"memAddr\"><\/span><input type=\"text\" id=\"rfkMemAt\" value=\"0200\" maxlength=\"4\" class=\"hex4\"><\/label><\/div>\n     <pre class=\"mem\" id=\"rfkMem\"><\/pre>\n    <\/div>\n   <\/div>\n\n   <div class=\"tools\">\n    <details class=\"tool\" open>\n     <summary data-t=\"asmT\"><\/summary>\n     <p class=\"note\" data-th=\"asmNote\"><\/p>\n     <textarea id=\"rfkSrc\" class=\"src\" spellcheck=\"false\" autocapitalize=\"off\" autocomplete=\"off\" rows=\"18\"><\/textarea>\n     <div class=\"bar\">\n      <button class=\"btn pri\" id=\"rfkAsmRun\" type=\"button\"><span class=\"i\">\u25b6<\/span><span data-t=\"asmRun\"><\/span><\/button>\n      <button class=\"btn\" id=\"rfkAsm\" type=\"button\" data-t=\"asmLoad\"><\/button>\n      <button class=\"btn\" id=\"rfkAsmSave\" type=\"button\"><span class=\"i\">\u2b07<\/span><span data-t=\"asmSave\"><\/span><\/button>\n     <\/div>\n     <div class=\"asmout\" id=\"rfkAsmOut\" hidden><\/div>\n    <\/details>\n\n    <details class=\"tool\">\n     <summary data-t=\"hexT\"><\/summary>\n     <p class=\"note\" data-th=\"hexNote\"><\/p>\n     <textarea id=\"rfkHex\" class=\"src\" spellcheck=\"false\" autocapitalize=\"off\" autocomplete=\"off\" rows=\"8\"><\/textarea>\n     <div class=\"bar\">\n      <button class=\"btn pri\" id=\"rfkHexRun\" type=\"button\"><span class=\"i\">\u25b6<\/span><span data-t=\"hexRun\"><\/span><\/button>\n      <button class=\"btn\" id=\"rfkHexLoad\" type=\"button\" data-t=\"hexLoad\"><\/button>\n      <button class=\"btn\" id=\"rfkHexTty\" type=\"button\" data-t=\"hexTty\"><\/button>\n     <\/div>\n     <p class=\"msg\" id=\"rfkHexMsg\"><\/p>\n    <\/details>\n\n    <details class=\"tool\">\n     <summary data-t=\"fileT\"><\/summary>\n     <p class=\"note\" data-th=\"fileNote\"><\/p>\n     <div class=\"bar\">\n      <label class=\"fld\"><span data-t=\"from\"><\/span><input type=\"text\" id=\"rfkFrom\" value=\"0200\" maxlength=\"4\" class=\"hex4\"><\/label>\n      <label class=\"fld\"><span data-t=\"to\"><\/span><input type=\"text\" id=\"rfkTo\" value=\"02FF\" maxlength=\"4\" class=\"hex4\"><\/label>\n      <button class=\"btn\" id=\"rfkSavePtp\" type=\"button\"><span class=\"i\">\u2b07<\/span><span data-t=\"savePtp\"><\/span><\/button>\n      <button class=\"btn\" id=\"rfkSaveBin\" type=\"button\"><span class=\"i\">\u2b07<\/span><span data-t=\"saveBin\"><\/span><\/button>\n     <\/div>\n     <div class=\"bar\">\n      <button class=\"btn\" id=\"rfkOpen\" type=\"button\"><span class=\"i\">\ud83d\udcc2<\/span><span data-t=\"openFile\"><\/span><\/button>\n      <input type=\"file\" id=\"rfkFile\" accept=\".ptp,.pap,.hex,.ihx,.bin,.asm,.s,.a65,.txt\" hidden>\n     <\/div>\n     <p class=\"msg\" id=\"rfkFileMsg\"><\/p>\n    <\/details>\n   <\/div>\n  <\/section>\n\n  <section class=\"sec\" id=\"rfkGuida\">\n   <h3 data-t=\"guidaT\"><\/h3>\n   <div class=\"prose\" data-th=\"guidaIntro\"><\/div>\n   <ol class=\"steps\" id=\"rfkSteps\"><\/ol>\n   <div class=\"prose\" data-th=\"guidaDopo\"><\/div>\n   <h4 data-t=\"tastiT\"><\/h4>\n   <table class=\"ref\"><tbody data-th=\"tasti\"><\/tbody><\/table>\n   <h4 data-t=\"ttyT\"><\/h4>\n   <table class=\"ref\"><tbody data-th=\"ttyCmd\"><\/tbody><\/table>\n   <h4 data-t=\"romT\"><\/h4>\n   <table class=\"ref\"><tbody data-th=\"rom\"><\/tbody><\/table>\n   <h4 data-t=\"mappaT\"><\/h4>\n   <table class=\"ref\"><tbody data-th=\"mappa\"><\/tbody><\/table>\n  <\/section>\n\n  <section class=\"sec\" id=\"rfkOggi\">\n   <h3 data-t=\"oggiT\"><\/h3>\n   <p class=\"intro\" data-th=\"oggiIntro\"><\/p>\n   <div class=\"cards\" id=\"rfkCards\"><\/div>\n  <\/section>\n\n  <section class=\"sec fonti\">\n   <h3 data-t=\"fontiT\"><\/h3>\n   <ul data-th=\"fonti\"><\/ul>\n   <p class=\"note\" data-th=\"romNote\"><\/p>\n  <\/section>\n <\/div>\n<\/div>\n\n<script>\n\/* RetroFixer \u00b7 KIM-1 \u00b7 ROM del monitor originale (2 KB)\n * $1800-$1BFF: 6530-003 (cassetta)  \u00b7  $1C00-$1FFF: 6530-002 (monitor, tastiera, display, TTY)\n * Copyright MOS Technology, Inc. 1975 (6530-002 rev. E del 13\/10\/1975, 6530-003 rev. D del 18\/10\/1975).\n * Ricostruita dal listato pubblicato (file kim.hex\/kim.lst della raccolta zimmers.net, controllato con i checksum\n * dei record): le zone non usate ($1A96-$1BF9 e $1FF7-$1FF9) qui valgono 00.\n *\/\n(function (RF) {\n'use strict';\nvar H = '' +\n  'A9AD8DEC17203219A9278D4217A9BF8D4317A264A916207A19CAD0F8A92A207A19ADF917206119ADF517205E19ADF617205E19ADED17CDF717ADEE17EDF81790' +\n  '24A92F207A19ADE717206119ADE817206119A202A904207A19CAD0F8A90085FA85FB4C4F1C20EC17205E1920EA194C33180F19A98D8DEC17203219A94C8DEF17' +\n  'AD71188DF017AD72188DF117A9078D4217A9FF8DE91720411A4EE9170DE9178DE917ADE917C916D0EDA20A20241AC916D0DFCAD0F620241AC92AF006C916D0D1' +\n  'F0F320F319CDF917F00DADF917C900F006C9FFF017D09C20F319204C198DED1720F319204C198DEE174CF81820F319204C1920F319204C19A20220241AC92FF0' +\n  '1420001AD023CAD0F1204C194CEC1720EA194CF81820F319CDE717D00C20F319CDE817D004A900F002A9FF85FA85FB4C4F1CADF5178DED17ADF6178DEE17A960' +\n  '8DEF17A9008DE7178DE81760A8186DE7178DE717ADE81769008DE8179860204C19A84A4A4A4A206F1998206F199860290FC90A183002690769308EE9178CEA17' +\n  'A008209E194AB006209E194C911920C41920C41988D0EBAEE917ACEA1760A209482C471710FBA97E8D4417A9A78D42172C471710FBA97E8D4417A9278D4217CA' +\n  'D0DF6860A206482C471710FBA9C38D4417A9A78D42172C471710FBA9C38D4417A9278D4217CAD0DF6860EEED17D003EEEE176020241A20001A20241A20001A60' +\n  'C930301EC947101AC94030031869092A2A2A2AA0042A2EE91788D0F9ADE917A00060C8608EEB17A20820411A4EEA170DEA178DEA17CAD0F1ADEA172A4AAEEB17' +\n  '602C421710FBAD4617A0FF8C4617A01488D0FD2C421730FB38ED4617A0FF8C4617A00788D0FD49FF298060A9278D4217A9BF8D43172C471710FBA99A8D4417A9' +\n  'A78D42172C471710FBA99A8D4417A9278D42174C751A000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +\n  '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +\n  '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +\n  '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +\n  '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +\n  '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006B1A6B1A6B1A' +\n  '85F36885F16885EF85FA6885F085FB84F486F5BA86F220881E4C4F1C6CFA176CFE17A2FF9A86F220881EA9FF8DF317A9012C4017D01930F9A9FC1869019003EE' +\n  'F317AC401710F38DF217A208206A1E208C1EA9012C4017D01E202F1EA20A20311E4CAF1DA90085F885F9205A1EC901F00620AC1F4CDB1D20191FD0D3A9012C40' +\n  '17F0CC20191FF0F420191FF0EF206A1FC91510BBC914F044C910F02CC911F02CC912F02FC913F0310A0A0A0A85FCA204A4FFD00AB1FA06FC2A91FA4CC31C0A26' +\n  'FA26FBCAD0EAF008A901D002A90085FF4C4F1C20631F4C4F1C4CC81DA5EF85FAA5F085FB4C4F1C205A1EC93BD0F9A90085F785F6209D1FAA20911F209D1F85FB' +\n  '20911F209D1F85FA20911F8AF00F209D1F91FA20911F20631FCAD0F2E8209D1FC5F6D017209D1FC5F7D0138AD0B9A20CA9278D421720311E4C4F1C209D1FA211' +\n  'D0EEA90085F885F9A90085F685F7202F1EA93B20A01EA5FACDF717A5FBEDF8179018A900203B1E20CC1F201E1EA5F6203B1EA5F7203B1E4C641CA918AA203B1E' +\n  '20911F201E1EA000B1FA203B1E20911F20631FCAD0F0A5F6203B1EA5F7203B1EE6F8D002E6F94C481D20CC1F202F1E201E1E209E1EA000B1FA203B1E209E1E4C' +\n  '641C20631F4CAC1DA6F29AA5FB48A5FA48A5F148A6F5A4F4A5F340C920F0CAC97FF01BC90DF0DBC90AF01CC92EF026C947F0D5C951F00AC94CF0094C6A1C4C4F' +\n  '1C4C421D4CE71C38A5FAE90185FAB002C6FB4CAC1DA000A5F891FA4CC21DA5FB203B1E20911FA5FA203B1E20911F60A207BDD51F20A01ECA10F76085FC4A4A4A' +\n  '4A204C1EA5FC204C1EA5FC60290FC90A183002690769304CA01E86FDA208A9012C4017D02230F920D41E20EB1EAD4017298046FE05FE85FE20D41ECAD0EF20EB' +\n  '1EA6FDA5FE2A4A60A20186FFA2008E4117A23F8E4317A2078E4217D87860A92085FE86FD20D41EAD421729FE8D421720D41EA208AD421729FE46FE69008D4217' +\n  '20D41ECAD0EEAD421709018D421720D41EA6FD60ADF3178DF417ADF21738E901B003CEF417ACF41710F360ADF3178DF417ADF2174A4EF41790E30980B0E0A003' +\n  'A201A9FF8E4217E8E82D401788D0F5A0078C4217098049FF60A000B1FA85F9A97F8D4117A209A003B9F8004A4A4A4A20481FB9F800290F20481F88D0EB8E4217' +\n  'A9008D41174CFE1E84FCA8B9E71FA0008C40178E42178D4017A07F88D0FDE8E8A4FC60E6FAD002E6FB60A221A00120021FD007E027D0F5A91560A0FF0AB003C8' +\n  '10FA8A290F4AAA981003186907CAD0FA601865F785F7A5F6690085F660205A1E20AC1F205A1E20AC1FA5F860C930301BC9471017C94030031869092A2A2A2AA0' +\n  '042A26F826F988D0F8A90060A5F885FAA5F985FB600000000000000A0D4D494B20135252452013BF86DBCFE6EDFD87FFEFF7FCB9DEF9F10000001C1C221C1F1C';\nvar rom = new Uint8Array(2048);\nfor (var i = 0; i < 2048; i++) rom[ i] = parseInt(H.substr(i * 2, 2), 16);\nRF.ROM = rom;\n})(globalThis.RFKim = globalThis.RFKim || {});\n\n\/* RetroFixer \u00b7 KIM-1 \u00b7 CPU MOS 6502\n * Tutti gli opcode documentati, con i cicli (anche il ciclo in pi\u00f9 quando l'indirizzo cambia pagina),\n * la modalit\u00e0 decimale come sul 6502 NMOS, le interruzioni NMI, IRQ e RESET.\n * Ogni accesso alla memoria passa dal bus (bus.rd \/ bus.wr), cos\u00ec i 6530 e i loro registri si comportano come\n * sull'hardware. Gli opcode non documentati fermano la CPU (sul KIM-1 il monitor non li usa).\n * La stessa tabella serve all'assembler e al disassembler.\n *\/\n(function (RF) {\n'use strict';\n\nvar LIST = (\n  '69 ADC imm 2|65 ADC zp 3|75 ADC zpx 4|6D ADC abs 4|7D ADC abx 4 p|79 ADC aby 4 p|61 ADC izx 6|71 ADC izy 5 p|' +\n  '29 AND imm 2|25 AND zp 3|35 AND zpx 4|2D AND abs 4|3D AND abx 4 p|39 AND aby 4 p|21 AND izx 6|31 AND izy 5 p|' +\n  '0A ASL acc 2|06 ASL zp 5|16 ASL zpx 6|0E ASL abs 6|1E ASL abx 7|' +\n  '90 BCC rel 2|B0 BCS rel 2|F0 BEQ rel 2|30 BMI rel 2|D0 BNE rel 2|10 BPL rel 2|50 BVC rel 2|70 BVS rel 2|' +\n  '24 BIT zp 3|2C BIT abs 4|00 BRK imp 7|18 CLC imp 2|D8 CLD imp 2|58 CLI imp 2|B8 CLV imp 2|' +\n  'C9 CMP imm 2|C5 CMP zp 3|D5 CMP zpx 4|CD CMP abs 4|DD CMP abx 4 p|D9 CMP aby 4 p|C1 CMP izx 6|D1 CMP izy 5 p|' +\n  'E0 CPX imm 2|E4 CPX zp 3|EC CPX abs 4|C0 CPY imm 2|C4 CPY zp 3|CC CPY abs 4|' +\n  'C6 DEC zp 5|D6 DEC zpx 6|CE DEC abs 6|DE DEC abx 7|CA DEX imp 2|88 DEY imp 2|' +\n  '49 EOR imm 2|45 EOR zp 3|55 EOR zpx 4|4D EOR abs 4|5D EOR abx 4 p|59 EOR aby 4 p|41 EOR izx 6|51 EOR izy 5 p|' +\n  'E6 INC zp 5|F6 INC zpx 6|EE INC abs 6|FE INC abx 7|E8 INX imp 2|C8 INY imp 2|' +\n  '4C JMP abs 3|6C JMP ind 5|20 JSR abs 6|' +\n  'A9 LDA imm 2|A5 LDA zp 3|B5 LDA zpx 4|AD LDA abs 4|BD LDA abx 4 p|B9 LDA aby 4 p|A1 LDA izx 6|B1 LDA izy 5 p|' +\n  'A2 LDX imm 2|A6 LDX zp 3|B6 LDX zpy 4|AE LDX abs 4|BE LDX aby 4 p|' +\n  'A0 LDY imm 2|A4 LDY zp 3|B4 LDY zpx 4|AC LDY abs 4|BC LDY abx 4 p|' +\n  '4A LSR acc 2|46 LSR zp 5|56 LSR zpx 6|4E LSR abs 6|5E LSR abx 7|EA NOP imp 2|' +\n  '09 ORA imm 2|05 ORA zp 3|15 ORA zpx 4|0D ORA abs 4|1D ORA abx 4 p|19 ORA aby 4 p|01 ORA izx 6|11 ORA izy 5 p|' +\n  '48 PHA imp 3|08 PHP imp 3|68 PLA imp 4|28 PLP imp 4|' +\n  '2A ROL acc 2|26 ROL zp 5|36 ROL zpx 6|2E ROL abs 6|3E ROL abx 7|' +\n  '6A ROR acc 2|66 ROR zp 5|76 ROR zpx 6|6E ROR abs 6|7E ROR abx 7|40 RTI imp 6|60 RTS imp 6|' +\n  'E9 SBC imm 2|E5 SBC zp 3|F5 SBC zpx 4|ED SBC abs 4|FD SBC abx 4 p|F9 SBC aby 4 p|E1 SBC izx 6|F1 SBC izy 5 p|' +\n  '38 SEC imp 2|F8 SED imp 2|78 SEI imp 2|' +\n  '85 STA zp 3|95 STA zpx 4|8D STA abs 4|9D STA abx 5|99 STA aby 5|81 STA izx 6|91 STA izy 6|' +\n  '86 STX zp 3|96 STX zpy 4|8E STX abs 4|84 STY zp 3|94 STY zpx 4|8C STY abs 4|' +\n  'AA TAX imp 2|A8 TAY imp 2|BA TSX imp 2|8A TXA imp 2|9A TXS imp 2|98 TYA imp 2'\n).split('|');\n\nvar OPS = new Array(256), BYNAME = {};\nLIST.forEach(function (s) {\n  var f = s.split(' '), op = parseInt(f[0], 16);\n  OPS[ op] = { op: op, n: f[1], m: f[2], c: +f[3], p: f[4] === 'p' };\n  (BYNAME[f[1]] = BYNAME[f[1]] || {})[f[2]] = op;\n});\nRF.OPS = OPS;\nRF.OPS_BYNAME = BYNAME;\nRF.OPLEN = { imp: 1, acc: 1, imm: 2, zp: 2, zpx: 2, zpy: 2, rel: 2, izx: 2, izy: 2, abs: 3, abx: 3, aby: 3, ind: 3 };\n\nfunction CPU(bus) {\n  this.bus = bus;\n  this.a = 0; this.x = 0; this.y = 0; this.sp = 0xFF; this.pc = 0;\n  this.c = 0; this.z = 0; this.i = 1; this.d = 0; this.v = 0; this.n = 0;\n  this.cycles = 0;      \/\/ cicli totali (1 ciclo = 1 \u00b5s a 1 MHz)\n  this.halted = false;  \/\/ opcode non valido: la CPU si ferma finch\u00e9 non arriva un RESET\n  this.badOp = -1;\n  this.nmiPending = false;\n  this.irqLine = false;\n}\nCPU.prototype.rd = function (a) { return this.bus.rd(a & 0xFFFF); };\nCPU.prototype.wr = function (a, v) { this.bus.wr(a & 0xFFFF, v & 0xFF); };\nCPU.prototype.rdw = function (a) { return this.rd(a) | (this.rd((a + 1) & 0xFFFF) << 8); };\nCPU.prototype.getP = function (b) {\n  return (this.n << 7) | (this.v << 6) | 0x20 | (b ? 0x10 : 0) | (this.d << 3) | (this.i << 2) | (this.z ? 2 : 0) | this.c;\n};\nCPU.prototype.setP = function (p) {\n  this.n = (p >> 7) & 1; this.v = (p >> 6) & 1; this.d = (p >> 3) & 1; this.i = (p >> 2) & 1; this.z = (p >> 1) & 1; this.c = p & 1;\n};\nCPU.prototype.push = function (v) { this.wr(0x100 | this.sp, v); this.sp = (this.sp - 1) & 0xFF; };\nCPU.prototype.pull = function () { this.sp = (this.sp + 1) & 0xFF; return this.rd(0x100 | this.sp); };\nCPU.prototype.nz = function (v) { this.z = v === 0 ? 1 : 0; this.n = (v >> 7) & 1; return v; };\n\nCPU.prototype.reset = function () {\n  this.sp = (this.sp - 3) & 0xFF;   \/\/ il 6502 fa tre letture finte dello stack\n  this.i = 1; this.d = 0;           \/\/ D resta casuale sul 6502 NMOS; qui lo azzeriamo\n  this.pc = this.rdw(0xFFFC);\n  this.halted = false; this.badOp = -1; this.nmiPending = false;\n  this.cycles += 7;\n};\nCPU.prototype.interrupt = function (vec) {\n  this.push(this.pc >> 8); this.push(this.pc); this.push(this.getP(false));\n  this.i = 1;\n  this.pc = this.rdw(vec);\n  this.cycles += 7;\n  return 7;\n};\nCPU.prototype.nmi = function () { this.nmiPending = true; };\n\nCPU.prototype.adc = function (v) {\n  var a = this.a, c = this.c, t;\n  if (!this.d) {\n    t = a + v + c;\n    this.v = ((~(a ^ v) & (a ^ t)) & 0x80) ? 1 : 0;\n    this.c = t > 0xFF ? 1 : 0;\n    this.a = this.nz(t & 0xFF);\n    return;\n  }\n  \/\/ decimale, come sul 6502 NMOS: Z dal risultato binario, N e V dal risultato intermedio\n  var lo = (a & 0x0F) + (v & 0x0F) + c;\n  if (lo > 9) lo += 6;\n  var hi = (a >> 4) + (v >> 4) + (lo > 0x0F ? 1 : 0);\n  this.z = ((a + v + c) & 0xFF) === 0 ? 1 : 0;\n  this.n = (hi & 8) ? 1 : 0;\n  this.v = ((~(a ^ v) & (a ^ (hi << 4))) & 0x80) ? 1 : 0;\n  if (hi > 9) hi += 6;\n  this.c = hi > 0x0F ? 1 : 0;\n  this.a = ((hi << 4) | (lo & 0x0F)) & 0xFF;\n};\nCPU.prototype.sbc = function (v) {\n  if (!this.d) { this.adc(v ^ 0xFF); return; }\n  var a = this.a, b = 1 - this.c, t = a - v - b;\n  this.v = (((a ^ v) & (a ^ t)) & 0x80) ? 1 : 0;\n  this.nz(t & 0xFF);\n  var lo = (a & 0x0F) - (v & 0x0F) - b, hi = (a >> 4) - (v >> 4);\n  if (lo < 0) { lo -= 6; hi--; }\n  if (hi < 0) hi -= 6;\n  this.c = t >= 0 ? 1 : 0;\n  this.a = ((hi << 4) | (lo & 0x0F)) & 0xFF;\n};\nCPU.prototype.cmp = function (r, v) { var t = r - v; this.c = t >= 0 ? 1 : 0; this.nz(t & 0xFF); };\n\n\/\/ Esegue un'istruzione (o serve un'interruzione) e restituisce i cicli usati.\nCPU.prototype.step = function () {\n  if (this.nmiPending) { this.nmiPending = false; this.halted = false; return this.interrupt(0xFFFA); }\n  if (this.halted) { this.cycles += 1; return 1; }\n  if (this.irqLine && !this.i) return this.interrupt(0xFFFE);\n  var pc = this.pc, opc = this.rd(pc), e = OPS[ opc];\n  if (!e) { this.halted = true; this.badOp = opc; this.cycles += 2; return 2; }\n  pc = (pc + 1) & 0xFFFF;\n  var ad = 0, b, z, cyc = e.c;\n  switch (e.m) {\n    case 'imm': ad = pc; pc++; break;\n    case 'zp': ad = this.rd(pc); pc++; break;\n    case 'zpx': ad = (this.rd(pc) + this.x) & 0xFF; pc++; break;\n    case 'zpy': ad = (this.rd(pc) + this.y) & 0xFF; pc++; break;\n    case 'abs': ad = this.rdw(pc); pc += 2; break;\n    case 'abx': b = this.rdw(pc); ad = (b + this.x) & 0xFFFF; if (e.p && ((b ^ ad) & 0xFF00)) cyc++; pc += 2; break;\n    case 'aby': b = this.rdw(pc); ad = (b + this.y) & 0xFFFF; if (e.p && ((b ^ ad) & 0xFF00)) cyc++; pc += 2; break;\n    case 'ind': b = this.rdw(pc); ad = this.rd(b) | (this.rd((b & 0xFF00) | ((b + 1) & 0xFF)) << 8); pc += 2; break;\n    case 'izx': z = (this.rd(pc) + this.x) & 0xFF; ad = this.rd(z) | (this.rd((z + 1) & 0xFF) << 8); pc++; break;\n    case 'izy': z = this.rd(pc); b = this.rd(z) | (this.rd((z + 1) & 0xFF) << 8); ad = (b + this.y) & 0xFFFF; if (e.p && ((b ^ ad) & 0xFF00)) cyc++; pc++; break;\n    case 'rel': b = this.rd(pc); pc++; ad = (pc + (b < 128 ? b : b - 256)) & 0xFFFF; break;\n  }\n  pc &= 0xFFFF;\n  this.pc = pc;\n  var v, t;\n  switch (e.n) {\n    case 'LDA': this.a = this.nz(this.rd(ad)); break;\n    case 'LDX': this.x = this.nz(this.rd(ad)); break;\n    case 'LDY': this.y = this.nz(this.rd(ad)); break;\n    case 'STA': this.wr(ad, this.a); break;\n    case 'STX': this.wr(ad, this.x); break;\n    case 'STY': this.wr(ad, this.y); break;\n    case 'ADC': this.adc(this.rd(ad)); break;\n    case 'SBC': this.sbc(this.rd(ad)); break;\n    case 'AND': this.a = this.nz(this.a & this.rd(ad)); break;\n    case 'ORA': this.a = this.nz(this.a | this.rd(ad)); break;\n    case 'EOR': this.a = this.nz(this.a ^ this.rd(ad)); break;\n    case 'CMP': this.cmp(this.a, this.rd(ad)); break;\n    case 'CPX': this.cmp(this.x, this.rd(ad)); break;\n    case 'CPY': this.cmp(this.y, this.rd(ad)); break;\n    case 'BIT': v = this.rd(ad); this.z = (this.a & v) ? 0 : 1; this.n = (v >> 7) & 1; this.v = (v >> 6) & 1; break;\n    case 'ASL': case 'LSR': case 'ROL': case 'ROR':\n      v = e.m === 'acc' ? this.a : this.rd(ad);\n      if (e.n === 'ASL') { this.c = (v >> 7) & 1; v = (v << 1) & 0xFF; }\n      else if (e.n === 'LSR') { this.c = v & 1; v >>= 1; }\n      else if (e.n === 'ROL') { t = this.c; this.c = (v >> 7) & 1; v = ((v << 1) | t) & 0xFF; }\n      else { t = this.c; this.c = v & 1; v = (v >> 1) | (t << 7); }\n      this.nz(v);\n      if (e.m === 'acc') this.a = v; else this.wr(ad, v);\n      break;\n    case 'INC': this.wr(ad, this.nz((this.rd(ad) + 1) & 0xFF)); break;\n    case 'DEC': this.wr(ad, this.nz((this.rd(ad) - 1) & 0xFF)); break;\n    case 'INX': this.x = this.nz((this.x + 1) & 0xFF); break;\n    case 'INY': this.y = this.nz((this.y + 1) & 0xFF); break;\n    case 'DEX': this.x = this.nz((this.x - 1) & 0xFF); break;\n    case 'DEY': this.y = this.nz((this.y - 1) & 0xFF); break;\n    case 'TAX': this.x = this.nz(this.a); break;\n    case 'TAY': this.y = this.nz(this.a); break;\n    case 'TXA': this.a = this.nz(this.x); break;\n    case 'TYA': this.a = this.nz(this.y); break;\n    case 'TSX': this.x = this.nz(this.sp); break;\n    case 'TXS': this.sp = this.x; break;\n    case 'PHA': this.push(this.a); break;\n    case 'PHP': this.push(this.getP(true)); break;\n    case 'PLA': this.a = this.nz(this.pull()); break;\n    case 'PLP': this.setP(this.pull()); break;\n    case 'CLC': this.c = 0; break;\n    case 'SEC': this.c = 1; break;\n    case 'CLI': this.i = 0; break;\n    case 'SEI': this.i = 1; break;\n    case 'CLD': this.d = 0; break;\n    case 'SED': this.d = 1; break;\n    case 'CLV': this.v = 0; break;\n    case 'NOP': break;\n    case 'JMP': this.pc = ad; break;\n    case 'JSR': t = (pc - 1) & 0xFFFF; this.push(t >> 8); this.push(t); this.pc = ad; break;\n    case 'RTS': t = this.pull(); t |= this.pull() << 8; this.pc = (t + 1) & 0xFFFF; break;\n    case 'RTI': this.setP(this.pull()); t = this.pull(); t |= this.pull() << 8; this.pc = t; break;\n    case 'BRK':\n      t = (pc + 1) & 0xFFFF; this.push(t >> 8); this.push(t); this.push(this.getP(true)); this.i = 1;\n      this.pc = this.rdw(0xFFFE); break;\n    default: \/\/ salti condizionati\n      var take;\n      switch (e.n) {\n        case 'BCC': take = !this.c; break; case 'BCS': take = this.c; break;\n        case 'BEQ': take = this.z; break; case 'BNE': take = !this.z; break;\n        case 'BMI': take = this.n; break; case 'BPL': take = !this.n; break;\n        case 'BVC': take = !this.v; break; case 'BVS': take = this.v; break;\n      }\n      if (take) { cyc++; if ((pc ^ ad) & 0xFF00) cyc++; this.pc = ad; }\n  }\n  this.cycles += cyc;\n  return cyc;\n};\n\n\/\/ Disassembla un'istruzione: { len, text } (peek = funzione che legge senza effetti collaterali)\nRF.disasm = function (peek, pc) {\n  var op = peek(pc), e = OPS[ op];\n  function h2(v) { return ('0' + v.toString(16).toUpperCase()).slice(-2); }\n  function h4(v) { return ('000' + v.toString(16).toUpperCase()).slice(-4); }\n  if (!e) return { len: 1, text: '???', bytes: [ op] };\n  var len = RF.OPLEN[e.m], b1 = peek((pc + 1) & 0xFFFF), b2 = peek((pc + 2) & 0xFFFF), w = b1 | (b2 << 8), s;\n  switch (e.m) {\n    case 'imp': s = ''; break;\n    case 'acc': s = 'A'; break;\n    case 'imm': s = '#$' + h2(b1); break;\n    case 'zp': s = '$' + h2(b1); break;\n    case 'zpx': s = '$' + h2(b1) + ',X'; break;\n    case 'zpy': s = '$' + h2(b1) + ',Y'; break;\n    case 'izx': s = '($' + h2(b1) + ',X)'; break;\n    case 'izy': s = '($' + h2(b1) + '),Y'; break;\n    case 'abs': s = '$' + h4(w); break;\n    case 'abx': s = '$' + h4(w) + ',X'; break;\n    case 'aby': s = '$' + h4(w) + ',Y'; break;\n    case 'ind': s = '($' + h4(w) + ')'; break;\n    case 'rel': s = '$' + h4((pc + 2 + (b1 < 128 ? b1 : b1 - 256)) & 0xFFFF); break;\n  }\n  var bytes = [ op]; if (len > 1) bytes.push(b1); if (len > 2) bytes.push(b2);\n  return { len: len, text: e.n + (s ? ' ' + s : ''), bytes: bytes };\n};\n\nRF.CPU6502 = CPU;\n})(globalThis.RFKim = globalThis.RFKim || {});\n\n\/* RetroFixer \u00b7 KIM-1 \u00b7 MOS 6530 RRIOT (ROM, RAM, I\/O, Timer)\n * Ogni 6530 ha 1 KB di ROM, 64 byte di RAM, due porte da 8 bit (A e B) con registro di direzione e un timer\n * a 8 bit con prescaler 1, 8, 64 o 1024 cicli.\n * Registri I\/O (offset dall'indirizzo base):\n *   0 PAD  dati porta A        1 PADD direzione porta A (1 = uscita)\n *   2 PBD  dati porta B        3 PBDD direzione porta B\n *   scrittura 4-7: carica il timer (divisore 1, 8, 64, 1024); con A3 = 1 abilita l'interruzione (su PB7)\n *   lettura  6 \/ 0E: valore del timer (0E abilita l'interruzione, 06 la disabilita)\n *   lettura  7: stato, bit 7 = il timer \u00e8 arrivato a zero\n * Il flag si accende N \u00d7 divisore cicli dopo la scrittura; poi il timer conta un ciclo alla volta (FF, FE...).\n * I piedini in ingresso si leggono dalla funzione pinsA \/ pinsB (bit a 1 dove la linea \u00e8 alta).\n *\/\n(function (RF) {\n'use strict';\n\nfunction RIOT(name) {\n  this.name = name;\n  this.ora = 0; this.ddra = 0; this.orb = 0; this.ddrb = 0;\n  this.pinsA = function () { return 0xFF; };   \/\/ livelli esterni (pull-up: 1)\n  this.pinsB = function () { return 0xFF; };\n  this.onWrite = null;                         \/\/ callback(reg, value) dopo ogni scrittura su porte\/direzioni\n  this.ram = new Uint8Array(64);\n  \/\/ timer: il valore si calcola dai cicli, cos\u00ec non serve farlo avanzare a ogni istruzione\n  this.tStart = 0; this.tLoad = 0xFF; this.tShift = 10; this.tIrqEn = false; this.tFlagRead = false;\n  this.now = function () { return 0; };        \/\/ funzione che restituisce i cicli della CPU\n}\nvar SHIFT = [0, 3, 6, 10];\n\nRIOT.prototype.reset = function () {\n  this.ora = 0; this.ddra = 0; this.orb = 0; this.ddrb = 0;   \/\/ al reset tutte le linee sono ingressi\n  this.tIrqEn = false;\n};\n\/\/ stato del timer al ciclo attuale: { value, expired }\nRIOT.prototype.timer = function () {\n  \/\/ come sul 6530\/6532: dopo la scrittura di N il conteggio scende di 1 ogni \"divisore\" cicli e il flag\n  \/\/ si accende quando passa sotto lo zero, N \u00d7 divisore cicli dopo; poi conta un ciclo alla volta da $FF\n  var el = this.now() - this.tStart, d = 1 << this.tShift, n = this.tLoad;\n  if (el <= n * d) return { value: n - Math.ceil(el \/ d), expired: false };\n  return { value: (0xFF - (el - n * d - 1)) & 0xFF, expired: true };\n};\nRIOT.prototype.irq = function () {\n  return this.tIrqEn && this.timer().expired && !this.tFlagRead;\n};\n\/\/ valore delle porte visto dai piedini: per le uscite il registro, per gli ingressi la linea esterna\nRIOT.prototype.portA = function () { return ((this.ora & this.ddra) | (this.pinsA() & ~this.ddra)) & 0xFF; };\nRIOT.prototype.portB = function () { return ((this.orb & this.ddrb) | (this.pinsB() & ~this.ddrb)) & 0xFF; };\n\nRIOT.prototype.read = function (off) {\n  switch (off & 0x0F) {\n    case 0: case 8: return this.portA();\n    case 1: case 9: return this.ddra;\n    case 2: case 10: return this.portB();\n    case 3: case 11: return this.ddrb;\n    case 6: case 14: case 4: case 12: {\n      \/\/ lettura del timer: A3 decide se l'interruzione resta abilitata; la lettura azzera il flag\n      this.tIrqEn = !!(off & 8);\n      var t = this.timer();\n      if (t.expired) this.tFlagRead = true;\n      return t.value;\n    }\n    default: { \/\/ 5, 7, 13, 15: stato\n      var s = this.timer();\n      return s.expired ? 0x80 : 0;\n    }\n  }\n};\nRIOT.prototype.write = function (off, v) {\n  switch (off & 0x0F) {\n    case 0: case 8: this.ora = v; break;\n    case 1: case 9: this.ddra = v; break;\n    case 2: case 10: this.orb = v; break;\n    case 3: case 11: this.ddrb = v; break;\n    default:\n      if (off & 4) { \/\/ 4-7, 12-15: carica il timer\n        this.tShift = SHIFT[off & 3];\n        this.tLoad = v;\n        this.tStart = this.now();\n        this.tIrqEn = !!(off & 8);\n        this.tFlagRead = false;\n      }\n      return;\n  }\n  if (this.onWrite) this.onWrite(off & 3, v);\n};\n\nRF.RIOT6530 = RIOT;\n})(globalThis.RFKim = globalThis.RFKim || {});\n\n\/* RetroFixer \u00b7 KIM-1 \u00b7 assembler 6502 a due passate\n * Capisce le sintassi pi\u00f9 comuni dei listati dell'epoca e di oggi:\n *  - etichette in colonna 0 (con o senza i due punti) e costanti (NOME = valore, NOME EQU valore);\n *  - origine: * = $0200, .ORG $0200, ORG $0200;\n *  - dati: .BYTE\/.DB\/DC.B\/!BYTE, .WORD\/.DW\/!WORD, .TEXT\/.ASC\/!TEXT, .DS\/!FILL; .END chiude il sorgente;\n *  - espressioni con + - * \/ & | ^ << >> e i prefissi < (byte basso) e > (byte alto).\n * Base: cpu6502.js del SID Composer, stessa tabella degli opcode della CPU.\n *\/\n(function (RF) {\n'use strict';\n\nfunction Expr(src, syms, pc) { this.s = src; this.i = 0; this.syms = syms; this.pc = pc; this.unknown = false; this.depth = 0; }\nExpr.prototype.ws = function () { while (this.i < this.s.length && \/\\s\/.test(this.s[this.i])) this.i++; };\nExpr.prototype.peek = function (t) { this.ws(); return this.s.substr(this.i, t.length) === t; };\nExpr.prototype.eat = function (t) { if (this.peek(t)) { this.i += t.length; return true; } return false; };\nExpr.prototype.parse = function () {\n  this.ws();\n  if (this.eat('<')) return this.parse() & 0xFF;\n  if (this.eat('>')) return (this.parse() >> 8) & 0xFF;\n  return this.or();\n};\nExpr.prototype.or = function () { var v = this.xor(); while (this.eat('|')) v = v | this.xor(); return v; };\nExpr.prototype.xor = function () { var v = this.and(); while (this.eat('^')) v = v ^ this.and(); return v; };\nExpr.prototype.and = function () { var v = this.shift(); while (this.eat('&')) v = v & this.shift(); return v; };\nExpr.prototype.shift = function () {\n  var v = this.add();\n  for (;;) { if (this.eat('<<')) v = v << this.add(); else if (this.eat('>>')) v = v >> this.add(); else return v; }\n};\nExpr.prototype.add = function () {\n  var v = this.mul();\n  for (;;) { if (this.eat('+')) v += this.mul(); else if (this.eat('-')) v -= this.mul(); else return v; }\n};\nExpr.prototype.mul = function () {\n  var v = this.unary();\n  for (;;) {\n    if (this.eat('*')) v *= this.unary();\n    else if (this.eat('\/')) { var d = this.unary(); v = d ? Math.trunc(v \/ d) : 0; }\n    else return v;\n  }\n};\nExpr.prototype.unary = function () {\n  if (++this.depth > 64) throw new Error('espressione troppo annidata');\n  try { return this.unary1(); } finally { this.depth--; }\n};\nExpr.prototype.unary1 = function () {\n  if (this.eat('-')) return -this.unary();\n  if (this.eat('(')) { var v = this.parse(); if (!this.eat(')')) throw new Error('manca )'); return v; }\n  return this.atom();\n};\nExpr.prototype.atom = function () {\n  this.ws();\n  var s = this.s, m, rest = s.slice(this.i);\n  if ((m = \/^\\$([0-9a-fA-F]+)\/.exec(rest))) { this.i += m[0].length; return parseInt(m[1], 16); }\n  if ((m = \/^%([01]+)\/.exec(rest))) { this.i += m[0].length; return parseInt(m[1], 2); }\n  if ((m = \/^0[ xX]([0-9a-fA-F]+)\/.exec(rest))) { this.i += m[0].length; return parseInt(m[1], 16); }\n  if ((m = \/^([0-9][0-9a-fA-F]*)[ hH](?!\\w)\/.exec(rest))) { this.i += m[0].length; return parseInt(m[1], 16); }\n  if ((m = \/^[0-9]+\/.exec(rest))) { this.i += m[0].length; return parseInt(m[0], 10); }\n  if ((m = \/^'(.)'\/.exec(rest))) { this.i += m[0].length; return m[1].charCodeAt(0); }\n  if ((m = \/^[A-Za-z_][\\w]*\/.exec(rest))) {\n    this.i += m[0].length;\n    var k = m[0].toLowerCase();\n    if (Object.prototype.hasOwnProperty.call(this.syms, k)) return this.syms[ k];\n    this.unknown = true; this.undef = m[0]; return 0;\n  }\n  if (rest[0] === '*') { this.i++; return this.pc; }\n  throw new Error('espressione non valida: ' + rest);\n};\n\nfunction evalExpr(src, syms, pc) {\n  var e = new Expr(src, syms, pc), v = e.parse();\n  e.ws();\n  if (e.i < e.s.length) throw new Error('testo in pi\u00f9: ' + e.s.slice(e.i));\n  return { v: v, unknown: e.unknown, undef: e.undef };\n}\n\nfunction stripComment(line) {\n  var q = false;\n  for (var i = 0; i < line.length; i++) {\n    var ch = line[ i];\n    if (ch === '\"') q = !q;\n    else if (ch === \"'\" && !q && line[i + 2] === \"'\") i += 2;\n    else if (ch === ';' && !q) return line.slice(0, i);\n  }\n  return line;\n}\nfunction splitArgs(s) {\n  var out = [], cur = '', q = false;\n  for (var i = 0; i < s.length; i++) {\n    var ch = s[ i];\n    if (ch === '\"') q = !q;\n    if (ch === ',' && !q) { out.push(cur.trim()); cur = ''; } else cur += ch;\n  }\n  if (cur.trim() !== '') out.push(cur.trim());\n  return out;\n}\n\nvar DIRS = {\n  org: 'org', byte: 'byte', by: 'byte', db: 'byte', 'dc.b': 'byte', '8': 'byte', fcb: 'byte',\n  word: 'word', wo: 'word', dw: 'word', 'dc.w': 'word', '16': 'word', fdb: 'word',\n  text: 'text', tx: 'text', asc: 'text', ascii: 'text', raw: 'text', str: 'text',\n  fill: 'fill', fi: 'fill', ds: 'fill', res: 'fill', rmb: 'fill',\n  end: 'end', to: 'skip', cpu: 'skip', sl: 'skip', opt: 'skip', title: 'skip', page: 'skip', list: 'skip', nolist: 'skip'\n};\n\/\/ in colonna 0 ORG, END e gli opcode (seguiti da spazio o fine riga) non sono etichette\nfunction isOpOrDir(word, rest) {\n  if (RF.OPS_BYNAME[word.toUpperCase()] && (rest === '' || \/^\\s\/.test(rest))) return true;\n  return \/^(org|end)$\/i.test(word) && (rest === '' || \/^\\s\/.test(rest));\n}\n\nRF.assemble = function (src) {\n  if (src.length > 1048576) return { errors: [{ line: 0, msg: 'sorgente troppo grande (max 1 MB)', text: '' }], start: 0, end: 0, syms: {}, mem: new Uint8Array(65536), used: new Uint8Array(65536), bytes: function () { return new Uint8Array(0); } };\n  var lines = src.split(\/\\r?\\n\/), syms = {}, errors = [], zpDecision = {};\n  var mem = new Uint8Array(65536), used = new Uint8Array(65536), min = 0x10000, max = -1;\n  var BY = RF.OPS_BYNAME, LEN = RF.OPLEN;\n\n  function pass(n) {\n    var pc = 0;\n    function emit(b) {\n      if (n === 2) { mem[pc & 0xFFFF] = b & 0xFF; used[pc & 0xFFFF] = 1; if (pc < min) min = pc; if (pc > max) max = pc; }\n      pc++;\n    }\n    function val(expr, li, allowUnknown) {\n      var r = evalExpr(expr, syms, pc);\n      if (r.unknown && n === 2) throw new Error('simbolo non definito: ' + r.undef);\n      if (r.unknown && !allowUnknown) throw new Error('simbolo non ancora definito: ' + r.undef);\n      return r;\n    }\n    for (var li = 0; li < lines.length; li++) {\n      var raw = stripComment(lines[ li]), line = raw.replace(\/\\s+$\/, '');\n      if (!line.trim()) continue;\n      try {\n        var m = \/^\\s*([A-Za-z_][\\w]*):?\\s*(?:=|\\.?equ\\b)\\s*(.+)$\/i.exec(line);\n        if (m) { var cv = val(m[2], li, n === 1); if (!cv.unknown) syms[m[1].toLowerCase()] = cv.v; continue; }\n        var stmt = line;\n        m = \/^\\s*([A-Za-z_][\\w]*)(:?)(.*)$\/.exec(line);\n        \/\/ etichetta: parola in colonna 0 che non \u00e8 un'istruzione o una direttiva, oppure parola seguita da ':'\n        if (m && (m[2] === ':' || (\/^[A-Za-z_]\/.test(line) && !isOpOrDir(m[1], m[3])))) {\n          var lab = m[1].toLowerCase();\n          if (n === 1 && Object.prototype.hasOwnProperty.call(syms, lab)) throw new Error('etichetta doppia: ' + m[1]);\n          syms[ lab] = pc;\n          stmt = m[3];\n        }\n        stmt = stmt.trim();\n        if (!stmt) continue;\n        if ((m = \/^\\*\\s*=\\s*(.+)$\/.exec(stmt))) { pc = val(m[1], li, false).v; continue; }\n        m = \/^[.!]?([A-Za-z0-9][\\w.]*)\\s*(.*)$\/.exec(stmt);\n        var d = m ? DIRS[m[1].toLowerCase()] : null;\n        if (d && (stmt[0] === '.' || stmt[0] === '!' || !BY[m[1].toUpperCase()])) {\n          var args = splitArgs(m[2]);\n          if (d === 'skip') continue;\n          if (d === 'end') { li = lines.length; continue; }\n          if (d === 'org') { pc = val(args[0], li, false).v; continue; }\n          if (d === 'word') { args.forEach(function (a) { var w = val(a, li, true).v; emit(w); emit(w >> 8); }); continue; }\n          if (d === 'fill') {\n            var cnt = val(args[0], li, false).v, fv = args[1] ? val(args[1], li, true).v : 0;\n            if (cnt < 0 || cnt > 65536) throw new Error('dimensione non valida: ' + cnt);\n            for (var k = 0; k < cnt; k++) emit(fv);\n            continue;\n          }\n          \/\/ byte e text: numeri, espressioni e stringhe fra virgolette\n          args.forEach(function (a) {\n            if (a.length > 1 && (a[0] === '\"' || (a[0] === \"'\" && a.length > 3))) { for (var q = 1; q < a.length - 1; q++) emit(a.charCodeAt(q)); }\n            else emit(val(a, li, true).v);\n          });\n          continue;\n        }\n        if (stmt[0] === '.' || stmt[0] === '!') throw new Error('direttiva sconosciuta: ' + stmt.split(\/\\s\/)[0]);\n        m = \/^([A-Za-z]{3})\\b\\s*(.*)$\/.exec(stmt);\n        if (!m) throw new Error('istruzione non valida: ' + stmt);\n        var mn = m[1].toUpperCase(), opd = m[2].trim(), modes = BY[ mn];\n        if (!modes) throw new Error('istruzione sconosciuta: ' + m[1]);\n        var mode, expr = null, r;\n        if (opd === '' || \/^a$\/i.test(opd)) { mode = modes.imp !== undefined ? 'imp' : 'acc'; }\n        else if (opd[0] === '#') { mode = 'imm'; expr = opd.slice(1); }\n        else if ((r = \/^\\((.+),\\s*x\\s*\\)$\/i.exec(opd))) { mode = 'izx'; expr = r[1]; }\n        else if ((r = \/^\\((.+)\\)\\s*,\\s*y$\/i.exec(opd))) { mode = 'izy'; expr = r[1]; }\n        else if (mn === 'JMP' && (r = \/^\\((.+)\\)$\/.exec(opd))) { mode = 'ind'; expr = r[1]; }\n        else if ((r = \/^(.+),\\s*([ xy])$\/i.exec(opd))) { mode = 'ab' + r[2].toLowerCase(); expr = r[1]; }\n        else if (modes.rel !== undefined) { mode = 'rel'; expr = opd; }\n        else { mode = 'abs'; expr = opd; }\n        var value = 0;\n        if (expr !== null) {\n          var ev = val(expr, li, true);\n          value = ev.v;\n          if (mode === 'abs' || mode === 'abx' || mode === 'aby') {\n            var zm = mode === 'abs' ? 'zp' : mode === 'abx' ? 'zpx' : 'zpy';\n            if (n === 1) zpDecision[ li] = !ev.unknown && value >= 0 && value < 256 && modes[ zm] !== undefined;\n            if (zpDecision[ li]) mode = zm;\n          }\n        }\n        if (modes[ mode] === undefined) throw new Error('modo di indirizzamento non valido per ' + mn);\n        var op = modes[ mode], len = LEN[ mode];\n        if (n === 2) {\n          if (mode === 'rel') {\n            var off = value - (pc + 2);\n            if (off < -128 || off > 127) throw new Error('salto troppo lontano (' + off + ')');\n            value = off & 0xFF;\n          } else if (len === 2 && (value < -128 || value > 255)) throw new Error('valore fuori da 8 bit: ' + value);\n        }\n        emit(op);\n        if (len >= 2) emit(value);\n        if (len === 3) emit(value >> 8);\n      } catch (err) {\n        if (n === 2 || !\/non ancora definito\/.test(err.message)) errors.push({ line: li + 1, msg: err.message, text: lines[ li] });\n        else errors.push({ line: li + 1, msg: err.message, text: lines[ li] });\n      }\n    }\n  }\n  pass(1);\n  if (!errors.length) pass(2);\n  return {\n    mem: mem, used: used, start: min, end: max + 1, syms: syms, errors: errors,\n    bytes: function (from, to) { return mem.slice(from === undefined ? min : from, to === undefined ? max + 1 : to); }\n  };\n};\n})(globalThis.RFKim = globalThis.RFKim || {});\n\n\/* RetroFixer \u00b7 KIM-1 \u00b7 la macchina\n * 6502 a 1 MHz, 1 KB di RAM, due 6530 (RRIOT) con la ROM originale del monitor, tastiera a matrice e display\n * multiplexato attraverso il decoder 74145, interfaccia TTY seriale \"bit-banged\", porte utente PA\/PB del 6530-003.\n *\n * Mappa (A13-A15 non decodificate: gli 8 KB si ripetono in tutto lo spazio, cos\u00ec $FFFA = $1FFA):\n *   $0000-$03FF RAM \u00b7 $0400-$13FF libero (o RAM di espansione) \u00b7 $1700 I\/O 6530-003 (porte utente)\n *   $1740 I\/O 6530-002 (tastiera, display, TTY) \u00b7 $1780 RAM 6530-003 \u00b7 $17C0 RAM 6530-002\n *   $1800-$1BFF ROM 6530-003 \u00b7 $1C00-$1FFF ROM 6530-002\n * 6530-002: PA0-PA6 segmenti (uscita) o colonne della tastiera (ingresso, tasto premuto = 0), PA7 ingresso TTY;\n *           PB1-PB4 al 74145: 0-2 righe della tastiera, 3 riga del ponticello TTY, 4-9 cifre 1-6; PB0 uscita TTY.\n * Tasti ST (NMI) e RS (RESET); interruttore SST: NMI dopo ogni istruzione letta fuori da $1C00-$1FFF.\n *\/\n(function (RF) {\n'use strict';\nvar CLOCK = 1000000;\n\n\/\/ tasto \u2192 [riga, bit di PA]\nvar KEYMAP = {\n  '0': [0, 6], '1': [0, 5], '2': [0, 4], '3': [0, 3], '4': [0, 2], '5': [0, 1], '6': [0, 0],\n  '7': [1, 6], '8': [1, 5], '9': [1, 4], 'A': [1, 3], 'B': [1, 2], 'C': [1, 1], 'D': [1, 0],\n  'E': [2, 6], 'F': [2, 5], 'AD': [2, 4], 'DA': [2, 3], '+': [2, 2], 'GO': [2, 1], 'PC': [2, 0]\n};\nRF.KEYMAP = KEYMAP;\nvar MINHOLD = 70000;      \/\/ un tocco breve tiene premuto il tasto almeno 70 ms (il monitor fa l'antirimbalzo)\nvar DUTY = 0.145;         \/\/ frazione di tempo di una cifra accesa da SCANDS (500 cicli su circa 3450): = luminosit\u00e0 piena\n\nfunction Kim() {\n  var self = this;\n  this.ram = new Uint8Array(1024);\n  this.exp = null;                       \/\/ RAM di espansione $0400-$13FF (4 KB), se attiva\n  this.rom = RF.ROM;\n  this.r2 = new RF.RIOT6530('6530-002');\n  this.r3 = new RF.RIOT6530('6530-003');\n  this.cpu = new RF.CPU6502({\n    rd: function (a) { return self.rd(a); },\n    wr: function (a, v) { self.wr(a, v); }\n  });\n  var now = function () { return self.cpu.cycles; };\n  this.r2.now = now; this.r3.now = now;\n\n  \/\/ tastiera: righe premute (bit a 1 = tasto premuto) e rilasci ritardati\n  this.rows = [0, 0, 0];\n  this.downAt = {}; this.releaseAt = {}; this.nextRelease = Infinity;\n  this.ttyMode = false;          \/\/ ponticello TTY (A-21 \u2194 A-V)\n  this.sst = false;\n  this.irqWired = false;         \/\/ filo PB7 (timer del 6530-003) \u2192 IRQ, da fare a mano sull'originale\n  this.autoRubout = true;\n\n  \/\/ porte utente: livelli degli interruttori collegati alle linee usate come ingresso\n  this.swA = 0xFF; this.swB = 0xFF;\n  this.portT = 0; this.portHigh = new Float64Array(16); this.portFrame = 0;\n  this.spk = { port: 'A', bit: 0 }; this.spkLevel = -1; this.audioEdges = []; this.audioOn = false;\n\n  \/\/ display\n  this.dispT = 0; this.dispDigit = -1; this.dispSeg = 0; this.segTime = new Float64Array(42); this.frameT = 0;\n\n  \/\/ TTY\n  this.baud = 1200;\n  this.rxQ = []; this.rx = null; this.rxFree = 0;  \/\/ caratteri da spedire, carattere in corso, primo ciclo libero\n  this.txLevel = 1; this.txOn = false; this.txNext = 0; this.txIdx = 0; this.txBits = 0;\n  this.onTx = function () {};\n  this.onEcho = function () {};\n\n  this.bp = null;                \/\/ punti di arresto: Uint8Array(65536) o null\n  this.stopReason = '';\n\n  \/\/ registratore a cassetta: fronti del segnale (in cicli dall'inizio del nastro)\n  this.tape = { edges: [], mode: 'stop', t0: 0, pos: 0, level: 0, idx: 0 };\n\n  this.r2.pinsA = function () { return self.pinsA2(); };\n  this.r2.pinsB = function () { return 0x7F | (self.tapeIn() << 7); };\n  this.r3.pinsA = function () { return self.swA; };\n  this.r3.pinsB = function () { return self.swB; };\n  this.r2.onWrite = function () { self.dispUpdate(); self.txUpdate(); self.tapeOut(); };\n  this.r3.onWrite = function () { self.portUpdate(); };\n  this.powerOn();\n}\nvar P = Kim.prototype;\nRF.CLOCK = CLOCK;\n\n\/\/ ---- bus -----------------------------------------------------------------------------------------\nP.rd = function (a) {\n  a &= 0x1FFF;\n  if (a < 0x0400) return this.ram[ a];\n  if (a < 0x1400) return this.exp ? this.exp[a - 0x0400] : (a >> 8);\n  if (a < 0x1700) return a >> 8;\n  if (a < 0x1740) return this.r3.read(a & 0x0F);\n  if (a < 0x1780) return this.r2.read(a & 0x0F);\n  if (a < 0x17C0) return this.r3.ram[a & 0x3F];\n  if (a < 0x1800) return this.r2.ram[a & 0x3F];\n  return this.rom[a - 0x1800];\n};\nP.wr = function (a, v) {\n  a &= 0x1FFF;\n  if (a < 0x0400) { this.ram[ a] = v; return; }\n  if (a < 0x1400) { if (this.exp) this.exp[a - 0x0400] = v; return; }\n  if (a < 0x1700) return;\n  if (a < 0x1740) { this.r3.write(a & 0x0F, v); return; }\n  if (a < 0x1780) { this.r2.write(a & 0x0F, v); return; }\n  if (a < 0x17C0) { this.r3.ram[a & 0x3F] = v; return; }\n  if (a < 0x1800) { this.r2.ram[a & 0x3F] = v; return; }\n  \/\/ ROM: le scritture si perdono\n};\n\/\/ lettura senza effetti collaterali (debugger, disassemblato)\nP.peek = function (a) {\n  a &= 0x1FFF;\n  if (a >= 0x1700 && a < 0x1780) {\n    var r = a < 0x1740 ? this.r3 : this.r2, o = a & 0x0F;\n    if ((o & 7) >= 4) return (o & 7) === 6 || (o & 7) === 4 ? r.timer().value : (r.timer().expired ? 0x80 : 0);\n    return r.read(o);\n  }\n  return this.rd(a);\n};\n\/\/ scrittura diretta in memoria (caricamento programmi): solo RAM\nP.poke = function (a, v) {\n  a &= 0x1FFF;\n  if (a < 0x0400 || (a >= 0x1780 && a < 0x1800) || (this.exp && a < 0x1400)) this.wr(a, v & 0xFF);\n};\nP.writable = function (a) {\n  a &= 0x1FFF;\n  return a < 0x0400 || (a >= 0x1780 && a < 0x1800) || (!!this.exp && a < 0x1400);\n};\nP.setExpansion = function (on) {\n  if (on && !this.exp) this.exp = new Uint8Array(0x1000);\n  if (!on) this.exp = null;\n};\n\n\/\/ ---- accensione e reset --------------------------------------------------------------------------\nP.powerOn = function () {\n  this.ram.fill(0); this.r2.ram.fill(0); this.r3.ram.fill(0);\n  if (this.exp) this.exp.fill(0);\n  \/\/ Sull'originale questi vettori vanno scritti a mano dopo ogni accensione (manuale, cap. 3):\n  \/\/ NMI ($17FA) e IRQ ($17FE) \u2192 $1C00, cos\u00ec ST, SST e BRK tornano al monitor.\n  this.setVectors();\n  this.reset();\n};\nP.setVectors = function () {\n  this.wr(0x17FA, 0x00); this.wr(0x17FB, 0x1C);\n  this.wr(0x17FE, 0x00); this.wr(0x17FF, 0x1C);\n};\nP.reset = function () {\n  this.r2.reset(); this.r3.reset();\n  this.cpu.reset();\n  this.dispUpdate(); this.portUpdate(); this.txUpdate();\n  this.rx = null; this.rxQ.length = 0; this.rxFree = this.cpu.cycles;\n  if (this.ttyMode && this.autoRubout) {\n    \/\/ chi usa la telescrivente preme RUB OUT dopo il reset: il monitor misura cos\u00ec la velocit\u00e0\n    this.rxFree = this.cpu.cycles + 30000;\n    this.rxQ.push({ c: 0x7F, gap: 0, echo: false });\n  }\n};\nP.stopKey = function () { this.cpu.nmi(); };\n\n\/\/ ---- tastiera ------------------------------------------------------------------------------------\nP.key = function (k, down) {\n  if (k === 'RS') { if (down) this.reset(); return; }\n  if (k === 'ST') { if (down) this.stopKey(); return; }\n  var m = KEYMAP[ k];\n  if (!m) return;\n  if (down) {\n    this.rows[m[0]] |= 1 << m[1];\n    this.downAt[ k] = this.cpu.cycles;\n    delete this.releaseAt[ k];\n  } else {\n    var t = (this.downAt[ k] || 0) + MINHOLD;\n    if (this.cpu.cycles >= t) this.rows[m[0]] &= ~(1 << m[1]);\n    else { this.releaseAt[ k] = t; this.nextRelease = Math.min(this.nextRelease, t); }\n  }\n};\nP.checkReleases = function () {\n  this.nextRelease = Infinity;\n  for (var k in this.releaseAt) {\n    if (this.cpu.cycles >= this.releaseAt[ k]) {\n      var m = KEYMAP[ k];\n      this.rows[m[0]] &= ~(1 << m[1]);\n      delete this.releaseAt[ k];\n    } else this.nextRelease = Math.min(this.nextRelease, this.releaseAt[ k]);\n  }\n};\nP.decoder = function () { return (this.r2.portB() >> 1) & 15; };\nP.pinsA2 = function () {\n  var d = this.decoder(), v = 0x7F;\n  if (d < 3) v &= ~this.rows[ d] & 0x7F;\n  else if (d === 3 && this.ttyMode) v &= 0x7E;\n  return v | (this.rxLevel() << 7);\n};\n\n\/\/ ---- display -------------------------------------------------------------------------------------\nP.dispAccum = function () {\n  var t = this.cpu.cycles, dt = t - this.dispT;\n  if (dt > 0 && this.dispDigit >= 0 && this.dispSeg) {\n    var b = this.dispDigit * 7, s = this.dispSeg;\n    for (var k = 0; k < 7; k++) if (s & (1 << k)) this.segTime[b + k] += dt;\n  }\n  this.dispT = t;\n};\nP.dispUpdate = function () {\n  this.dispAccum();\n  var d = this.decoder();\n  this.dispDigit = d >= 4 && d <= 9 ? d - 4 : -1;\n  this.dispSeg = this.r2.ora & this.r2.ddra & 0x7F;\n};\n\/\/ luminosit\u00e0 dei 42 segmenti dall'ultima chiamata (0..1)\nP.display = function (out) {\n  this.dispAccum();\n  var total = this.cpu.cycles - this.frameT;\n  for (var i = 0; i < 42; i++) {\n    out[ i] = total > 0 ? Math.min(1, this.segTime[ i] \/ total \/ DUTY) : 0;\n    this.segTime[ i] = 0;\n  }\n  this.frameT = this.cpu.cycles;\n  return out;\n};\n\n\/\/ ---- porte utente (6530-003) ---------------------------------------------------------------------\nP.portLevels = function () { return this.r3.portA() | (this.r3.portB() << 8); };\nP.portUpdate = function () {\n  var t = this.cpu.cycles, dt = t - this.portT;\n  if (dt > 0) {\n    var lv = this.curPort === undefined ? this.portLevels() : this.curPort;\n    for (var k = 0; k < 16; k++) if (lv & (1 << k)) this.portHigh[ k] += dt;\n  }\n  this.portT = t;\n  this.curPort = this.portLevels();\n  \/\/ altoparlante collegato a una linea della porta\n  var bit = this.spk.port === 'A' ? this.spk.bit : this.spk.bit + 8, l = (this.curPort >> bit) & 1;\n  if (l !== this.spkLevel) {\n    this.spkLevel = l;\n    if (this.audioOn && this.audioEdges.length < 200000) this.audioEdges.push(t, l);\n  }\n};\nP.setSwitches = function (a, b) { this.swA = a & 0xFF; this.swB = b & 0xFF; this.portUpdate(); };\n\/\/ frazione di tempo a 1 delle 16 linee dall'ultima chiamata; ddr: direzioni (1 = uscita)\nP.ports = function (out) {\n  this.portUpdate();\n  var total = this.cpu.cycles - this.portFrame;\n  for (var k = 0; k < 16; k++) {\n    out[ k] = total > 0 ? this.portHigh[ k] \/ total : ((this.curPort >> k) & 1);\n    this.portHigh[ k] = 0;\n  }\n  this.portFrame = this.cpu.cycles;\n  return out;\n};\n\n\/\/ ---- TTY -----------------------------------------------------------------------------------------\nP.bitCycles = function () { return CLOCK \/ this.baud; };\n\/\/ livello della linea in ingresso (PA7) al ciclo attuale: 1 = riposo (mark)\nP.rxLevel = function () {\n  var t = this.cpu.cycles, bc = this.bitCycles();\n  for (;;) {\n    if (!this.rx) {\n      if (!this.rxQ.length || t < this.rxFree) return 1;\n      var it = this.rxQ.shift();\n      this.rx = { c: it.c, t0: Math.max(this.rxFree, t - 1), gap: it.gap };\n      if (it.echo) this.onEcho(it.c);\n    }\n    var i = Math.floor((t - this.rx.t0) \/ bc);\n    if (i < 0) return 1;\n    if (i === 0) return 0;                                   \/\/ bit di start\n    if (i <= 8) return ((this.rx.c | 0x80) >> (i - 1)) & 1;  \/\/ 7 bit + parit\u00e0 \"mark\", dal meno significativo\n    if (i < 11 + this.rx.gap) return 1;                      \/\/ due bit di stop (+ pausa per il testo incollato)\n    this.rxFree = this.rx.t0 + (11 + this.rx.gap) * bc;\n    this.rx = null;\n  }\n};\n\/\/ tasti premuti nel terminale (gap: bit di pausa dopo il carattere, per dare tempo al monitor)\nP.ttySend = function (c, gap) {\n  if (this.rxQ.length > 20000) return;\n  this.rxQ.push({ c: c & 0x7F, gap: gap || 0, echo: true });\n};\nP.ttyBusy = function () { return this.rxQ.length + (this.rx ? 1 : 0); };\nP.ttyClear = function () { this.rxQ.length = 0; };\n\/\/ uscita TTY (PB0 del 6530-002): un piccolo UART in software ricostruisce i caratteri dai fronti\nP.txUpdate = function () {\n  var l = this.r2.portB() & 1, t = this.cpu.cycles;\n  if (l === this.txLevel) return;\n  this.txSample(t, false);\n  this.txLevel = l;\n  if (!this.txOn && l === 0) {\n    this.txOn = true; this.txIdx = 0; this.txBits = 0;\n    this.txNext = t + this.bitCycles() * 1.5;\n  }\n};\nP.txSample = function (upto, inclusive) {\n  var bc = this.bitCycles();\n  while (this.txOn && (inclusive ? this.txNext <= upto : this.txNext < upto)) {\n    this.txBits |= this.txLevel << this.txIdx;\n    this.txNext += bc;\n    if (++this.txIdx === 8) { this.txOn = false; this.onTx(this.txBits & 0x7F); }\n  }\n};\n\n\/\/ ---- registratore a cassetta ---------------------------------------------------------------------\n\/\/ Uscita: PB7 del 6530-002 in uscita (PBDD = $BF durante DUMPT); la ROM crea treni di impulsi a 3600 Hz\n\/\/ (mezzo periodo 138 \u00b5s) e 2400 Hz (207 \u00b5s). Ingresso: sull'originale un PLL (LM565) trasforma la frequenza in un\n\/\/ livello su PB7: alta frequenza = 0, bassa frequenza = 1. Qui il \"PLL\" guarda la durata del mezzo periodo.\nvar TAPE_SPLIT = 172;   \/\/ \u00b5s fra 138 e 207\nP.tapeOut = function () {\n  var tp = this.tape;\n  if (tp.mode !== 'rec') return;\n  var l = (this.r2.orb & this.r2.ddrb & 0x80) ? 1 : 0;\n  if (l === tp.level) return;\n  tp.level = l;\n  if (tp.edges.length < 4000000) tp.edges.push(tp.pos + (this.cpu.cycles - tp.t0));\n};\nP.tapeIn = function () {\n  var tp = this.tape;\n  if (tp.mode !== 'play') return 1;\n  var t = tp.pos + (this.cpu.cycles - tp.t0), e = tp.edges, i = tp.idx;\n  if (i > 0 && e[i - 1] > t) i = 0;\n  while (i < e.length && e[ i] <= t) i++;\n  tp.idx = i;\n  if (i === 0 || i >= e.length) return 1;           \/\/ niente segnale\n  var half = e[ i] - e[i - 1];\n  if (half > 1000) return 1;                          \/\/ silenzio\n  return half < TAPE_SPLIT ? 0 : 1;\n};\n\/\/ comandi del registratore: 'rec', 'play', 'stop', 'rewind'\nP.tapeCmd = function (cmd) {\n  var tp = this.tape;\n  if (tp.mode !== 'stop') { tp.pos += this.cpu.cycles - tp.t0; }\n  if (cmd === 'rewind') { tp.pos = 0; tp.idx = 0; tp.mode = 'stop'; return; }\n  if (cmd === 'rec') {\n    \/\/ si registra dalla posizione attuale: quello che c'era dopo si perde, come su un nastro vero\n    var keep = 0; while (keep < tp.edges.length && tp.edges[ keep] < tp.pos) keep++;\n    tp.edges.length = keep; tp.level = (this.r2.orb & this.r2.ddrb & 0x80) ? 1 : 0;\n  }\n  tp.mode = cmd === 'rec' || cmd === 'play' ? cmd : 'stop';\n  tp.t0 = this.cpu.cycles; tp.idx = 0;\n};\nP.tapePos = function () { var tp = this.tape; return tp.pos + (tp.mode !== 'stop' ? this.cpu.cycles - tp.t0 : 0); };\nP.tapeLength = function () { var e = this.tape.edges; return e.length ? e[e.length - 1] : 0; };\n\n\/\/ ---- esecuzione ----------------------------------------------------------------------------------\n\/\/ esegue fino a n cicli; si ferma prima su un punto di arresto o su un opcode non valido\nP.run = function (n) {\n  var cpu = this.cpu, end = cpu.cycles + n, bp = this.bp, r3 = this.r3;\n  this.stopReason = '';\n  this.checkReleases();\n  while (cpu.cycles < end) {\n    var intr = cpu.nmiPending, pc = cpu.pc;\n    if (this.irqWired && r3.tIrqEn) cpu.irqLine = r3.irq();\n    else if (cpu.irqLine) cpu.irqLine = false;\n    cpu.step();\n    if (cpu.cycles >= this.nextRelease) this.checkReleases();\n    if (this.sst && !intr && !cpu.halted && (pc & 0x1C00) !== 0x1C00) cpu.nmi();\n    if (cpu.halted) { this.stopReason = 'halt'; break; }\n    if (bp && bp[cpu.pc] && !cpu.nmiPending) { this.stopReason = 'bp'; break; }\n  }\n  this.checkReleases();\n  this.rxLevel();\n  this.txSample(cpu.cycles, true);\n};\n\/\/ un'istruzione sola (pulsante \"passo\" del debugger, diverso dall'SST hardware)\nP.stepOne = function () {\n  this.cpu.step();\n  this.rxLevel();\n  this.txSample(this.cpu.cycles, true);\n};\n\n\/\/ stato del monitor che si vede dopo un NMI: registri salvati in pagina zero\nP.monitorRegs = function () {\n  var m = this;\n  return { pc: m.ram[0xEF] | (m.ram[0xF0] << 8), p: m.ram[0xF1], sp: m.ram[0xF2], a: m.ram[0xF3], y: m.ram[0xF4], x: m.ram[0xF5] };\n};\n\n\/\/ carica byte in memoria (solo RAM); restituisce quanti byte non sono finiti in RAM\nP.load = function (addr, bytes) {\n  var lost = 0;\n  for (var i = 0; i < bytes.length; i++) {\n    var a = (addr + i) & 0xFFFF;\n    if (this.writable(a)) this.poke(a, bytes[ i]); else lost++;\n  }\n  return lost;\n};\n\/\/ prepara il monitor come dopo \"AD indirizzo\": il display mostra l'inizio del programma e GO lo avvia\nP.pointTo = function (addr) {\n  this.ram[0xFA] = addr & 0xFF; this.ram[0xFB] = (addr >> 8) & 0xFF;\n  this.ram[0xEF] = addr & 0xFF; this.ram[0xF0] = (addr >> 8) & 0xFF;\n  this.ram[0xFF] = 1;   \/\/ MODE = indirizzo\n};\n\/\/ avvia direttamente un programma, con lo stack e i registri come dopo GO\nP.go = function (addr) {\n  var c = this.cpu;\n  this.pointTo(addr);\n  \/\/ porte del 6530-002 come le lascia il monitor quando si preme GO (INIT1): display spento, PB0 a riposo\n  this.r2.write(1, 0x00); this.r2.write(3, 0x3F); this.r2.write(2, 0x07);\n  c.halted = false; c.nmiPending = false;\n  c.sp = 0xFF; c.i = 1; c.d = 0;\n  c.pc = addr & 0xFFFF;\n};\n\nRF.Kim = Kim;\n})(globalThis.RFKim = globalThis.RFKim || {});\n\n\/* RetroFixer \u00b7 KIM-1 \u00b7 display a 6 cifre e tastiera esadecimale\n * Il display mostra la luminosit\u00e0 di ogni segmento calcolata dalla macchina (frazione di tempo in cui il segmento\n * \u00e8 stato acceso durante l'ultimo fotogramma), cos\u00ec il multiplexing del monitor si vede come sui LED veri e un\n * programma che smette di chiamare SCANDS spegne il display.\n * La tastiera ha la disposizione dell'originale: GO ST RS SST \/ AD DA PC + \/ C D E F \/ 8 9 A B \/ 4 5 6 7 \/ 0 1 2 3.\n *\/\n(function (RF) {\n'use strict';\n\nvar SVGNS = 'http:\/\/www.w3.org\/2000\/svg';\n\/\/ segmenti a..g di una cifra larga 60 e alta 100, inclinata come i LED dell'epoca\nvar SEG = [\n  '14,6 46,6 50,10 44,16 16,16 10,10',        \/\/ a\n  '51,12 56,17 53,47 48,51 43,46 46,18',      \/\/ b\n  '48,55 53,59 50,89 44,94 40,88 42,60',      \/\/ c\n  '10,90 16,84 40,84 46,90 40,96 8,96',      \/\/ d (in basso)\n  '6,59 11,55 16,60 14,88 8,94 3,89',         \/\/ e\n  '9,17 15,12 20,18 18,46 12,51 7,47',        \/\/ f\n  '13,53 18,48 42,48 47,53 42,58 18,58'       \/\/ g\n];\nvar LAYOUT = [\n  ['GO', 'ST', 'RS', 'SST'],\n  ['AD', 'DA', 'PC', '+'],\n  ['C', 'D', 'E', 'F'],\n  ['8', '9', 'A', 'B'],\n  ['4', '5', '6', '7'],\n  ['0', '1', '2', '3']\n];\n\nfunction el(tag, attrs, parent) {\n  var e = tag.indexOf('svg:') === 0 ? document.createElementNS(SVGNS, tag.slice(4)) : document.createElement(tag);\n  for (var k in attrs) if (Object.prototype.hasOwnProperty.call(attrs, k)) e.setAttribute(k, attrs[ k]);\n  if (parent) parent.appendChild(e);\n  return e;\n}\n\nfunction Board(root, opts) {\n  var self = this;\n  this.opts = opts || {};\n  this.segEls = [];\n  this.last = new Float32Array(42).fill(-1);\n  root.innerHTML = '';\n  var wrap = el('div', { 'class': 'kimboard' }, root);\n\n  \/\/ display\n  var disp = el('div', { 'class': 'kdisp' }, wrap);\n  var svg = el('svg:svg', { viewBox: '0 0 450 112', 'class': 'kdsvg', role: 'img', 'aria-label': this.opts.ariaDisplay || 'Display' }, disp);\n  for (var d = 0; d < 6; d++) {\n    var x = 12 + d * 68 + (d >= 4 ? 26 : 0);\n    var g = el('svg:g', { transform: 'translate(' + x + ',6) skewX(-6)' }, svg);\n    for (var s = 0; s < 7; s++) {\n      var p = el('svg:polygon', { points: SEG[ s], 'class': 'kseg' }, g);\n      this.segEls.push(p);\n    }\n  }\n  this.aria = el('span', { 'class': 'kvis', 'aria-live': 'off' }, disp);\n\n  \/\/ tastiera\n  var kp = el('div', { 'class': 'kpad', tabindex: '0', role: 'group', 'aria-label': this.opts.ariaKeys || 'Keypad' }, wrap);\n  this.kp = kp;\n  this.btn = {};\n  LAYOUT.forEach(function (row) {\n    row.forEach(function (k) {\n      if (k === 'SST') {\n        var sw = el('button', { type: 'button', 'class': 'kkey ksst', 'aria-pressed': 'false', 'data-k': k }, kp);\n        sw.innerHTML = '<span class=\"ksl\">SST<\/span><span class=\"ktog\"><i><\/i><\/span><span class=\"kon\">ON<\/span>';\n        sw.addEventListener('click', function () { self.setSST(!self.sst); });\n        sw.addEventListener('keydown', function (e) { if (e.key === ' ' || e.key === 'Enter') e.stopPropagation(); });\n        self.btn[ k] = sw;\n        return;\n      }\n      var b = el('button', { type: 'button', 'class': 'kkey' + (k.length > 1 || k === '+' ? ' kfn' : '') + (k === 'RS' || k === 'ST' ? ' kred' : ''), 'data-k': k }, kp);\n      b.textContent = k;\n      self.btn[ k] = b;\n      b.addEventListener('pointerdown', function (e) {\n        e.preventDefault();\n        try { kp.focus({ preventScroll: true }); } catch (x) {}   \/\/ cos\u00ec si pu\u00f2 continuare con la tastiera del PC\n        try { b.setPointerCapture(e.pointerId); } catch (x) {}\n        self.press(k);\n      });\n      b.addEventListener('pointerup', function () { self.release(k); });\n      b.addEventListener('pointercancel', function () { self.release(k); });\n      b.addEventListener('lostpointercapture', function () { self.release(k); });\n      b.addEventListener('keydown', function (e) { if ((e.key === ' ' || e.key === 'Enter') && !e.repeat) { e.preventDefault(); e.stopPropagation(); self.press(k); } });\n      b.addEventListener('keyup', function (e) { if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); e.stopPropagation(); self.release(k); } });\n    });\n  });\n  this.sst = false;\n\n  \/\/ tastiera del PC quando la tastiera del KIM ha il fuoco\n  var held = {};\n  function map(e) {\n    var k = e.key;\n    if (e.ctrlKey && !e.altKey) {\n      var c = k.toLowerCase();\n      if (c === 'a') return 'AD'; if (c === 'd') return 'DA'; if (c === 'p') return 'PC'; if (c === 'g') return 'GO';\n      if (c === 'r') return 'RS'; if (c === 's') return 'ST'; if (c === 't') return 'SST';\n      return null;\n    }\n    if (e.altKey || e.metaKey) return null;\n    if (\/^[0-9a-fA-F]$\/.test(k)) return k.toUpperCase();\n    switch (k) {\n      case '+': case 'Enter': return '+';\n      case 'ArrowUp': return 'AD';\n      case 'ArrowDown': return 'DA';\n      case 'g': case 'G': return 'GO';\n      case 'p': case 'P': return 'PC';\n      case 's': case 'S': return 'ST';\n      case 'Escape': return 'RS';\n      case 't': case 'T': return 'SST';\n    }\n    return null;\n  }\n  kp.addEventListener('keydown', function (e) {\n    if (e.target !== kp && (e.key === ' ' || e.key === 'Enter')) return;   \/\/ su un tasto lo gestisce il tasto stesso\n    var k = map(e);\n    if (!k) return;\n    e.preventDefault();\n    if (e.repeat) return;\n    if (k === 'SST') { self.setSST(!self.sst); return; }\n    held[e.code] = k;\n    self.press(k);\n  });\n  kp.addEventListener('keyup', function (e) {\n    var k = held[e.code];\n    if (!k) return;\n    delete held[e.code];\n    e.preventDefault();\n    self.release(k);\n  });\n  kp.addEventListener('blur', function () { for (var c in held) { self.release(held[ c]); } held = {}; });\n}\nvar P = Board.prototype;\nP.press = function (k) { if (this.btn[ k]) this.btn[ k].classList.add('down'); if (this.opts.onKey) this.opts.onKey(k, true); };\nP.release = function (k) { if (this.btn[ k]) this.btn[ k].classList.remove('down'); if (this.opts.onKey) this.opts.onKey(k, false); };\nP.setSST = function (on) {\n  this.sst = !!on;\n  this.btn.SST.setAttribute('aria-pressed', on ? 'true' : 'false');\n  this.btn.SST.classList.toggle('on', this.sst);\n  if (this.opts.onSST) this.opts.onSST(this.sst);\n};\n\/\/ seg: 42 valori 0..1 (6 cifre \u00d7 7 segmenti a..g)\nP.render = function (seg) {\n  for (var i = 0; i < 42; i++) {\n    var v = Math.round(seg[ i] * 20) \/ 20;\n    if (v === this.last[ i]) continue;\n    this.last[ i] = v;\n    var p = this.segEls[ i];\n    if (v <= 0.02) { p.style.opacity = ''; p.classList.remove('lit'); }\n    else { p.classList.add('lit'); p.style.opacity = (0.25 + 0.75 * v).toFixed(2); }\n  }\n};\n\/\/ testo per i lettori di schermo (cifre riconosciute dai segmenti)\nvar GLYPH = { 0x3F: '0', 0x06: '1', 0x5B: '2', 0x4F: '3', 0x66: '4', 0x6D: '5', 0x7D: '6', 0x07: '7', 0x7F: '8', 0x6F: '9',\n  0x77: 'A', 0x7C: 'b', 0x39: 'C', 0x5E: 'd', 0x79: 'E', 0x71: 'F', 0: ' ' };\nRF.segText = function (seg) {\n  var s = '';\n  for (var d = 0; d < 6; d++) {\n    var m = 0;\n    for (var k = 0; k < 7; k++) if (seg[d * 7 + k] > 0.3) m |= 1 << k;\n    s += (GLYPH[ m] !== undefined ? GLYPH[ m] : '?') + (d === 3 ? ' ' : '');\n  }\n  return s;\n};\n\nRF.Board = Board;\n})(globalThis.RFKim = globalThis.RFKim || {});\n\n\/* RetroFixer \u00b7 KIM-1 \u00b7 terminale seriale (TTY) su canvas, base: terminale ADM-3A della pagina IMSAI 8080\n * CR, LF, BS, BEL; Ctrl-Z (1Ah) cancella lo schermo; Ctrl-^ (1Eh) cursore in alto a sinistra;\n * Ctrl-K su, Ctrl-L destra, Ctrl-H sinistra, Ctrl-J gi\u00f9; ESC = riga colonna (con offset 20h) posiziona il cursore.\n *\/\n(function (RF) {\n'use strict';\n\nfunction Terminal(canvas, cols, rows) {\n  this.cv = canvas; this.ctx = canvas.getContext('2d');\n  this.cols = cols || 80; this.rows = rows || 24;\n  this.buf = new Uint8Array(this.cols * this.rows);\n  this.fg = '#5dff8a'; this.bg = '#04130a';\n  this.fontPx = 20; this.cw = 10; this.ch = 20;\n  this.esc = 0; this.escRow = 0;\n  this.bell = 0;\n  this.clear();\n  this.measure();\n}\nvar P = Terminal.prototype;\n\nP.measure = function () {\n  var c = this.ctx;\n  c.font = this.fontPx + 'px VT323, monospace';\n  this.cw = Math.ceil(c.measureText('M').width) || 10;\n  this.ch = this.fontPx;\n  this.cv.width = this.cw * this.cols + 16;\n  this.cv.height = this.ch * this.rows + 16;\n  this.dirty = true;\n};\nP.clear = function () { this.buf.fill(32); this.cx = 0; this.cy = 0; this.dirty = true; };\nP.scroll = function () {\n  this.buf.copyWithin(0, this.cols);\n  this.buf.fill(32, this.cols * (this.rows - 1));\n};\nP.lf = function () { if (++this.cy >= this.rows) { this.cy = this.rows - 1; this.scroll(); } };\n\nP.put = function (c) {\n  this.dirty = true;\n  if (this.esc === 1) { this.esc = c === 0x3D ? 2 : 0; return; }\n  if (this.esc === 2) { this.escRow = c - 32; this.esc = 3; return; }\n  if (this.esc === 3) {\n    this.esc = 0;\n    this.cy = Math.max(0, Math.min(this.rows - 1, this.escRow));\n    this.cx = Math.max(0, Math.min(this.cols - 1, c - 32));\n    return;\n  }\n  switch (c) {\n    case 0x07: this.bell = 6; return;\n    case 0x08: if (this.cx > 0) this.cx--; return;\n    case 0x0A: this.lf(); return;\n    case 0x0B: if (this.cy > 0) this.cy--; return;\n    case 0x0C: if (this.cx < this.cols - 1) this.cx++; return;\n    case 0x0D: this.cx = 0; return;\n    case 0x1A: this.clear(); return;\n    case 0x1B: this.esc = 1; return;\n    case 0x1E: this.cx = 0; this.cy = 0; return;\n  }\n  if (c < 0x20 || c > 0x7E) return;\n  this.buf[this.cy * this.cols + this.cx] = c;\n  if (++this.cx >= this.cols) { this.cx = 0; this.lf(); }\n};\nP.write = function (s) { for (var i = 0; i < s.length; i++) this.put(s.charCodeAt(i)); };\nP.text = function () {\n  var out = [];\n  for (var r = 0; r < this.rows; r++) out.push(String.fromCharCode.apply(null, this.buf.subarray(r * this.cols, (r + 1) * this.cols)).replace(\/\\s+$\/, ''));\n  return out.join('\\n').replace(\/\\n+$\/, '');\n};\n\n\/\/ cursorOn: il cursore lampeggia; il disegno avviene solo se qualcosa \u00e8 cambiato\nP.render = function (cursorOn, power) {\n  var key = (cursorOn ? 1 : 0) + (power ? 2 : 0) + (this.bell ? 4 : 0);\n  if (!this.dirty && key === this.lastKey) return;\n  this.dirty = false; this.lastKey = key;\n  var c = this.ctx, cw = this.cw, ch = this.ch, ox = 8, oy = 8;\n  c.fillStyle = this.bell ? '#0c3a1c' : this.bg;\n  c.fillRect(0, 0, this.cv.width, this.cv.height);\n  if (this.bell) this.bell--;\n  if (!power) return;\n  c.font = this.fontPx + 'px VT323, monospace';\n  c.textBaseline = 'top';\n  c.fillStyle = this.fg;\n  for (var r = 0; r < this.rows; r++) {\n    var row = this.buf.subarray(r * this.cols, (r + 1) * this.cols), s = '';\n    for (var k = 0; k < this.cols; k++) {\n      var v = row[ k];\n      if (v !== 32) c.fillText(String.fromCharCode(v), ox + k * cw, oy + r * ch + 1);\n    }\n  }\n  if (cursorOn) c.fillRect(ox + this.cx * cw, oy + this.cy * ch + ch - 4, cw, 3);\n};\n\nRF.Terminal = Terminal;\n})(globalThis.RFKim = globalThis.RFKim || {});\n\n\/* RetroFixer \u00b7 KIM-1 \u00b7 programmi di esempio\n * Scritti per questa pagina (nessun listato copiato da libri o riviste), collaudati in test\/test.js.\n * I commenti sono in due lingue con la forma {{italiano|english}}: la pagina sceglie quella giusta.\n * Usano solo le routine documentate della ROM: SCANDS $1F1F, GETKEY $1F6A, OUTCH $1EA0, GETCH $1E5A,\n * PRTBYT $1E3B, CRLF $1E2F, START $1C4F.\n *\/\n(function (RF) {\n'use strict';\n\nvar HEAD = [\n  '; {{Indirizzi del KIM-1|KIM-1 addresses}}',\n  'SAD    = $1740      ; {{6530-002 porta A: segmenti \/ colonne tastiera|6530-002 port A: segments \/ keyboard columns}}',\n  'PADD   = $1741      ; {{direzione porta A (1 = uscita)|port A direction (1 = output)}}',\n  'SBD    = $1742      ; {{6530-002 porta B: cifra \/ riga tastiera|6530-002 port B: digit \/ keyboard row}}',\n  'PA     = $1700      ; {{porta utente A (6530-003)|user port A (6530-003)}}',\n  'PADDU  = $1701',\n  'PB     = $1702      ; {{porta utente B (6530-003)|user port B (6530-003)}}',\n  'PBDDU  = $1703',\n  'SCANDS = $1F1F      ; {{mostra POINTH POINTL INH, A<>0 se un tasto \u00e8 premuto|shows POINTH POINTL INH, A<>0 if a key is down}}',\n  'GETKEY = $1F6A      ; {{A = tasto premuto ($15 = nessuno)|A = key pressed ($15 = none)}}',\n  'INH    = $F9',\n  'POINTL = $FA',\n  'POINTH = $FB'\n].join('\\n');\n\nRF.PROGRAMS = [\n  {\n    id: 'scritta',\n    name: { it: 'Scritta che scorre', en: 'Scrolling message' },\n    desc: {\n      it: 'Una scritta scorre sul display. Il programma non usa la ROM: accende da solo una cifra alla volta, come fa il monitor, scrivendo i segmenti su <code>SAD<\/code> e la cifra su <code>SBD<\/code>. Ogni carattere \u00e8 un byte: bit 0 = segmento a \u2026 bit 6 = segmento g.',\n      en: 'A message scrolls across the display. The program does not use the ROM: it lights one digit at a time by itself, as the monitor does, writing the segments to <code>SAD<\/code> and the digit to <code>SBD<\/code>. Each character is one byte: bit 0 = segment a \u2026 bit 6 = segment g.'\n    },\n    src: HEAD + '\\n' + [\n      'POS    = $00        ; {{prima lettera mostrata|first letter shown}}',\n      'REP    = $01        ; {{passate del display per ogni passo|display passes per step}}',\n      'DCNT   = $02',\n      'LEN    = MSGEND-MSG',\n      '',\n      '        .ORG $0200',\n      'BEGIN   LDA #$7F',\n      '        STA PADD     ; {{PA0-PA6 in uscita: segmenti|PA0-PA6 as outputs: segments}}',\n      '        LDA #0',\n      '        STA POS',\n      'STEP    LDA #24      ; {{24 passate = circa 0,1 s|24 passes = about 0.1 s}}',\n      '        STA REP',\n      'PASS    LDY POS',\n      '        LDX #$09     ; {{codice della prima cifra (decoder 74145 = 4)|code of the first digit (74145 decoder = 4)}}',\n      'DIGIT   LDA #0',\n      '        STA SAD      ; {{segmenti spenti mentre si cambia cifra|segments off while switching digit}}',\n      '        STX SBD      ; {{seleziona la cifra|select the digit}}',\n      '        LDA MSG,Y',\n      '        STA SAD      ; {{accende i segmenti|light the segments}}',\n      '        LDA #64      ; {{circa 500 microsecondi|about 500 microseconds}}',\n      '        STA DCNT',\n      'WAIT    DEC DCNT',\n      '        BNE WAIT',\n      '        INY',\n      '        INX',\n      '        INX',\n      '        CPX #$15     ; {{dopo la sesta cifra|after the sixth digit}}',\n      '        BNE DIGIT',\n      '        LDA #0',\n      '        STA SAD',\n      '        DEC REP',\n      '        BNE PASS',\n      '        INC POS      ; {{fa scorrere di una lettera|scroll by one letter}}',\n      '        LDA POS',\n      '        CMP #LEN',\n      '        BNE STEP',\n      '        LDA #0',\n      '        STA POS',\n      '        JMP STEP',\n      '',\n      '; {{segmenti: a=1 b=2 c=4 d=8 e=16 f=32 g=64|segments: a=1 b=2 c=4 d=8 e=16 f=32 g=64}}',\n      '{{MSG     .BYTE $39,$06,$77,$3F,$00,$5E,$77,$38,$00     ; CIAO dAL|MSG     .BYTE $76,$79,$38,$38,$3F,$00,$71,$50,$5C,$37,$00 ; HELLO FroM}}',\n      '        .BYTE $75,$06,$37,$40,$06,$00                  ; KIM-1',\n      '{{        .BYTE $5E,$79,$38,$00                          ; dEL|        .BYTE $5C,$71,$00                              ; oF}}',\n      '        .BYTE $06,$6F,$07,$7D,$00,$00,$00              ; 1976',\n      'MSGEND',\n      '{{        .BYTE $39,$06,$77,$3F,$00,$5E                  ; {{prime 6 di nuovo, per girare senza salti|first 6 again, to wrap around smoothly}}|        .BYTE $76,$79,$38,$38,$3F,$00                  ; {{prime 6 di nuovo, per girare senza salti|first 6 again, to wrap around smoothly}}}}'\n    ].join('\\n'),\n    start: 0x0200\n  },\n  {\n    id: 'cronometro',\n    name: { it: 'Cronometro (timer del 6530)', en: 'Stopwatch (6530 timer)' },\n    desc: {\n      it: 'Minuti, secondi e centesimi sul display con la routine <code>SCANDS<\/code> della ROM. Il tempo lo misura il timer del 6530-003: ogni giro aspetta 78 \u00d7 64 = 4992 cicli, circa 5 ms, e due giri fanno un centesimo. I numeri sono in BCD, sommati con l\\'istruzione <code>SED<\/code>. Tieni premuto un tasto qualsiasi per fermarlo.',\n      en: 'Minutes, seconds and hundredths on the display using the ROM routine <code>SCANDS<\/code>. Time is measured by the 6530-003 timer: every loop waits 78 \u00d7 64 = 4992 cycles, about 5 ms, and two loops make one hundredth. The numbers are BCD, added with the <code>SED<\/code> instruction. Hold any key down to stop it.'\n    },\n    src: HEAD + '\\n' + [\n      'T64    = $1706      ; {{scrittura: timer con divisore 64|write: timer with divide-by-64}}',\n      'TSTAT  = $1707      ; {{lettura: bit 7 = tempo scaduto|read: bit 7 = time out}}',\n      'HALF   = $00        ; {{conta i due giri da 5 ms|counts the two 5 ms loops}}',\n      '',\n      '        .ORG $0200',\n      'BEGIN   LDA #0',\n      '        STA POINTH   ; {{minuti|minutes}}',\n      '        STA POINTL   ; {{secondi|seconds}}',\n      '        STA INH      ; {{centesimi|hundredths}}',\n      '        STA HALF',\n      'TICK    LDA #78      ; {{78 x 64 = 4992 cicli, pi\u00f9 di una passata di SCANDS|78 x 64 = 4992 cycles, longer than one SCANDS pass}}',\n      '        STA T64',\n      '        JSR SCANDS   ; {{una passata del display (circa 4,2 ms)|one display pass (about 4.2 ms)}}',\n      '        BNE HOLD     ; {{tasto premuto: resta fermo|key down: stay still}}',\n      'WAIT    BIT TSTAT',\n      '        BPL WAIT     ; {{aspetta la fine dei 5 ms|wait for the end of the 5 ms}}',\n      '        INC HALF',\n      '        LDA HALF',\n      '        LSR A',\n      '        BCS TICK     ; {{conta solo un giro su due|only every second loop counts}}',\n      '        SED          ; {{aritmetica decimale|decimal arithmetic}}',\n      '        CLC',\n      '        LDA INH',\n      '        ADC #1',\n      '        STA INH',\n      '        BCC DONE     ; {{riporto solo dopo 99|carry only after 99}}',\n      '        LDA POINTL',\n      '        ADC #0       ; {{+1 grazie al riporto|+1 thanks to the carry}}',\n      '        CMP #$60',\n      '        BNE SECS',\n      '        LDA #0',\n      '        STA POINTL',\n      '        LDA POINTH',\n      '        CLC',\n      '        ADC #1',\n      '        STA POINTH',\n      '        JMP DONE',\n      'SECS    STA POINTL',\n      'DONE    CLD',\n      '        JMP TICK',\n      'HOLD    BIT TSTAT    ; {{fermo: aspetta lo stesso i 5 ms, cos\u00ec il display resta luminoso uguale|stopped: still wait the 5 ms, so the display keeps the same brightness}}',\n      '        BPL HOLD',\n      '        JMP TICK'\n    ].join('\\n'),\n    start: 0x0200\n  },\n  {\n    id: 'supercar',\n    name: { it: 'Luci che scorrono sulla porta PA', en: 'Running lights on port PA' },\n    desc: {\n      it: 'Un LED corre avanti e indietro sulla porta utente PA (connettore applicazione). Gli interruttori PB0-PB2 scelgono la velocit\u00e0 e il timer del 6530-003 fa le pause. Il display resta spento perch\u00e9 nessuno lo pilota: sul KIM-1 vero succede lo stesso.',\n      en: 'One LED runs back and forth on user port PA (application connector). Switches PB0-PB2 set the speed and the 6530-003 timer makes the pauses. The display stays dark because nothing drives it: the same happens on a real KIM-1.'\n    },\n    src: HEAD + '\\n' + [\n      'TKT    = $1707      ; {{scrittura: timer con divisore 1024|write: timer with divide-by-1024}}',\n      'LED    = $00',\n      'DIR    = $01',\n      '',\n      '        .ORG $0200',\n      'BEGIN   LDA #$FF',\n      '        STA PADDU    ; {{PA0-PA7 uscite: i LED|PA0-PA7 outputs: the LEDs}}',\n      '        LDA #$00',\n      '        STA PBDDU    ; {{PB ingressi: gli interruttori|PB inputs: the switches}}',\n      '        LDA #$01',\n      '        STA LED',\n      '        LDA #0',\n      '        STA DIR',\n      'LOOP    LDA LED',\n      '        STA PA',\n      '        LDA PB       ; {{velocit\u00e0 da PB0-PB2|speed from PB0-PB2}}',\n      '        AND #$07',\n      '        ASL A',\n      '        ASL A',\n      '        ASL A',\n      '        ASL A',\n      '        CLC',\n      '        ADC #20      ; {{da 20 a 132 unit\u00e0 di 1,024 ms|from 20 to 132 units of 1.024 ms}}',\n      '        STA TKT',\n      'WAIT    BIT TKT      ; {{bit 7 = tempo scaduto|bit 7 = time out}}',\n      '        BPL WAIT',\n      '        LDA DIR',\n      '        BNE RIGHT',\n      '        ASL LED      ; {{verso PA7|towards PA7}}',\n      '        LDA LED',\n      '        CMP #$80',\n      '        BNE LOOP',\n      '        INC DIR',\n      '        BNE LOOP',\n      'RIGHT   LSR LED      ; {{verso PA0|towards PA0}}',\n      '        LDA LED',\n      '        CMP #$01',\n      '        BNE LOOP',\n      '        DEC DIR',\n      '        JMP LOOP'\n    ].join('\\n'),\n    start: 0x0200\n  },\n  {\n    id: 'interruttori',\n    name: { it: 'Interruttori, LED e tasti', en: 'Switches, LEDs and keys' },\n    desc: {\n      it: 'Legge gli interruttori della porta PB, accende gli stessi bit sui LED della porta PA e mostra il valore sul display (prime due cifre). Le ultime due cifre mostrano il codice dell\\'ultimo tasto premuto, letto con <code>GETKEY<\/code>: 00-0F le cifre, 10 AD, 11 DA, 12 +, 13 GO, 14 PC.',\n      en: 'Reads the port PB switches, lights the same bits on the port PA LEDs and shows the value on the display (first two digits). The last two digits show the code of the last key pressed, read with <code>GETKEY<\/code>: 00-0F the digits, 10 AD, 11 DA, 12 +, 13 GO, 14 PC.'\n    },\n    src: HEAD + '\\n' + [\n      '        .ORG $0200',\n      'BEGIN   LDA #$FF',\n      '        STA PADDU    ; {{PA uscite (LED)|PA outputs (LEDs)}}',\n      '        LDA #$00',\n      '        STA PBDDU    ; {{PB ingressi (interruttori)|PB inputs (switches)}}',\n      '        STA POINTL',\n      '        STA INH',\n      'LOOP    LDA PB',\n      '        STA PA       ; {{copia PB sui LED di PA|copy PB to the PA LEDs}}',\n      '        STA POINTH',\n      '        JSR SCANDS   ; {{mostra POINTH POINTL INH|show POINTH POINTL INH}}',\n      '        JSR GETKEY',\n      '        CMP #$15     ; {{$15 = nessun tasto|$15 = no key}}',\n      '        BEQ LOOP',\n      '        STA INH',\n      '        JMP LOOP'\n    ].join('\\n'),\n    start: 0x0200\n  },\n  {\n    id: 'dado',\n    name: { it: 'Dado elettronico', en: 'Electronic dice' },\n    desc: {\n      it: 'Il display mostra \"d1CE\" e un numero da 1 a 6. Tieni premuto un tasto: il numero gira circa 280 volte al secondo e quando lo lasci si ferma a caso. Usa solo <code>SCANDS<\/code>, che oltre a pilotare il display dice se un tasto \u00e8 premuto.',\n      en: 'The display shows \"d1CE\" and a number from 1 to 6. Hold a key down: the number spins about 280 times a second and stops at random when you let go. It only uses <code>SCANDS<\/code>, which drives the display and also tells whether a key is down.'\n    },\n    src: HEAD + '\\n' + [\n      '        .ORG $0200',\n      'BEGIN   LDA #$D1     ; {{\"d1CE\" sulle prime quattro cifre|\"d1CE\" on the first four digits}}',\n      '        STA POINTH',\n      '        LDA #$CE',\n      '        STA POINTL',\n      '        LDA #1',\n      '        STA INH',\n      'IDLE    JSR SCANDS',\n      '        BEQ IDLE     ; {{aspetta un tasto|wait for a key}}',\n      'ROLL    INC INH',\n      '        LDA INH',\n      '        CMP #7',\n      '        BNE SHOW',\n      '        LDA #1',\n      '        STA INH',\n      'SHOW    JSR SCANDS',\n      '        BNE ROLL     ; {{gira finch\u00e9 il tasto \u00e8 premuto|spin while the key is down}}',\n      '        BEQ IDLE'\n    ].join('\\n'),\n    start: 0x0200\n  },\n  {\n    id: 'pianola',\n    name: { it: 'Pianola (altoparlante su PA0)', en: 'Toy piano (speaker on PA0)' },\n    desc: {\n      it: 'I sedici tasti esadecimali suonano due ottave di scala maggiore, da Do5 a Re7, su un altoparlante collegato a PA0. Attiva l\\'audio nel pannello della porta utente. Il suono \u00e8 un\\'onda quadra creata contando i cicli, come facevano i programmi musicali dell\\'epoca.',\n      en: 'The sixteen hex keys play two octaves of a major scale, from C5 to D7, on a speaker connected to PA0. Turn on the audio in the user port panel. The sound is a square wave made by counting cycles, as music programs did at the time.'\n    },\n    src: HEAD + '\\n' + [\n      'HALF   = $00        ; {{mezzo periodo della nota (unit\u00e0 di 5 cicli)|note half period (units of 5 cycles)}}',\n      'CNT    = $01',\n      '',\n      '        .ORG $0200',\n      'BEGIN   LDA #$01',\n      '        STA PADDU    ; {{PA0 in uscita verso l\\'altoparlante|PA0 output to the speaker}}',\n      '        LDA #$00',\n      '        STA PADD     ; {{tastiera del KIM in ingresso|KIM keypad as input}}',\n      'LOOP    JSR GETKEY',\n      '        CMP #$10     ; {{solo i tasti 0-F|only keys 0-F}}',\n      '        BCS LOOP',\n      '        TAX',\n      '        LDA NOTES,X',\n      '        STA HALF',\n      '        LDA #60      ; {{60 mezzi periodi, poi rilegge la tastiera|60 half periods, then read the keypad again}}',\n      '        STA CNT',\n      'TONE    LDA PA',\n      '        EOR #$01     ; {{inverte PA0|toggle PA0}}',\n      '        STA PA',\n      '        LDX HALF',\n      'DLY     DEX          ; {{5 cicli per giro|5 cycles per loop}}',\n      '        BNE DLY',\n      '        DEC CNT',\n      '        BNE TONE',\n      '        BEQ LOOP',\n      '',\n      '; {{N = (500000 \/ frequenza - 20) \/ 5: un giro della nota dura 20 + 5 x N cicli|N = (500000 \/ frequency - 20) \/ 5: one note loop takes 20 + 5 x N cycles}}',\n      'NOTES   .BYTE 187,166,148,139,124,110,97,92      ; {{Do5 Re5 Mi5 Fa5 Sol5 La5 Si5 Do6|C5 D5 E5 F5 G5 A5 B5 C6}}',\n      '        .BYTE 81,72,68,60,53,47,44,39            ; {{Re6 Mi6 Fa6 Sol6 La6 Si6 Do7 Re7|D6 E6 F6 G6 A6 B6 C7 D7}}'\n    ].join('\\n'),\n    start: 0x0200\n  },\n  {\n    id: 'hilo',\n    name: { it: 'Indovina il numero', en: 'Guess the number' },\n    desc: {\n      it: 'Il KIM pensa un numero da 00 a 99. Scrivi due cifre: il display risponde \"HI\" se il numero \u00e8 pi\u00f9 alto del suo, \"LO\" se \u00e8 pi\u00f9 basso, \"--\" quando hai indovinato, con il numero di tentativi. Poi premi una cifra per una nuova partita. Il display \u00e8 pilotato da una routine del programma, che mostra anche le lettere.',\n      en: 'The KIM thinks of a number from 00 to 99. Type two digits: the display answers \"HI\" if your number is higher than its own, \"LO\" if it is lower, \"--\" when you get it, together with the number of tries. Then press a digit for a new game. The display is driven by a routine in the program, which can also show letters.'\n    },\n    src: HEAD + '\\n' + [\n      'BUF    = $00        ; {{6 byte: i segmenti delle cifre|6 bytes: segments of the digits}}',\n      'SECRET = $06',\n      'GUESS  = $07',\n      'TRIES  = $08',\n      'KEY    = $09',\n      'DCNT   = $0A',\n      '',\n      '        .ORG $0200',\n      'NEW     LDA #0',\n      '        STA TRIES',\n      '        JSR CLEAR',\n      'RAND    SED          ; {{il numero segreto gira finch\u00e9 non premi un tasto|the secret number spins until you press a key}}',\n      '        CLC',\n      '        LDA SECRET',\n      '        ADC #1',\n      '        STA SECRET',\n      '        CLD',\n      '        JSR SHOW',\n      '        JSR GETKEY',\n      '        CMP #10',\n      '        BCS RAND',\n      '        BCC FIRST',\n      'ASK     JSR GETDIG   ; {{prima cifra|first digit}}',\n      'FIRST   STA KEY',\n      '        JSR RELEASE',\n      '        LDA KEY',\n      '        ASL A',\n      '        ASL A',\n      '        ASL A',\n      '        ASL A',\n      '        STA GUESS',\n      '        LDX KEY',\n      '        LDA SEG,X',\n      '        STA BUF',\n      '        LDA #0',\n      '        STA BUF+1',\n      '        STA BUF+4',\n      '        STA BUF+5',\n      '        JSR GETDIG   ; {{seconda cifra|second digit}}',\n      '        STA KEY',\n      '        ORA GUESS',\n      '        STA GUESS',\n      '        LDX KEY',\n      '        LDA SEG,X',\n      '        STA BUF+1',\n      '        JSR RELEASE',\n      '        SED',\n      '        CLC',\n      '        LDA TRIES',\n      '        ADC #1',\n      '        STA TRIES',\n      '        CLD',\n      '        LDA GUESS',\n      '        CMP SECRET',\n      '        BEQ WIN',\n      '        BCC LOW',\n      '        LDA #$76     ; H',\n      '        LDX #$06     ; I',\n      '        BNE ANSWER',\n      'LOW     LDA #$38     ; L',\n      '        LDX #$3F     ; O',\n      'ANSWER  STA BUF+4',\n      '        STX BUF+5',\n      '        JMP ASK',\n      'WIN     LDA #$40     ; {{\"--\" e i tentativi|\"--\" and the tries}}',\n      '        STA BUF+2',\n      '        STA BUF+3',\n      '        LDA TRIES',\n      '        LSR A',\n      '        LSR A',\n      '        LSR A',\n      '        LSR A',\n      '        TAX',\n      '        LDA SEG,X',\n      '        STA BUF+4',\n      '        LDA TRIES',\n      '        AND #$0F',\n      '        TAX',\n      '        LDA SEG,X',\n      '        STA BUF+5',\n      '        JSR GETDIG',\n      '        JSR RELEASE',\n      '        JMP NEW',\n      '',\n      '; {{aspetta una cifra 0-9 tenendo acceso il display|wait for a digit 0-9 keeping the display lit}}',\n      'GETDIG  JSR SHOW',\n      '        JSR GETKEY',\n      '        CMP #10',\n      '        BCS GETDIG',\n      '        RTS',\n      '; {{aspetta che il tasto sia lasciato|wait for the key to be released}}',\n      'RELEASE JSR SHOW',\n      '        JSR GETKEY',\n      '        CMP #$15',\n      '        BNE RELEASE',\n      '        RTS',\n      'CLEAR   LDA #0',\n      '        LDX #5',\n      'CLR1    STA BUF,X',\n      '        DEX',\n      '        BPL CLR1',\n      '        LDA #$40',\n      '        STA BUF+2    ; {{\"--\" in mezzo|\"--\" in the middle}}',\n      '        STA BUF+3',\n      '        RTS',\n      '; {{una passata del display dai segmenti in BUF|one display pass from the segments in BUF}}',\n      'SHOW    LDA #$7F',\n      '        STA PADD',\n      '        LDY #0',\n      '        LDX #$09',\n      'SHOW1   LDA #0',\n      '        STA SAD',\n      '        STX SBD',\n      '        LDA BUF,Y',\n      '        STA SAD',\n      '        LDA #64',\n      '        STA DCNT',\n      'SHOW2   DEC DCNT',\n      '        BNE SHOW2',\n      '        INY',\n      '        INX',\n      '        INX',\n      '        CPX #$15',\n      '        BNE SHOW1',\n      '        LDA #0',\n      '        STA SAD',\n      '        STA PADD     ; {{di nuovo in ingresso per GETKEY|back to input for GETKEY}}',\n      '        RTS',\n      'SEG     .BYTE $3F,$06,$5B,$4F,$66,$6D,$7D,$07,$7F,$6F   ; 0-9'\n    ].join('\\n'),\n    start: 0x0200\n  },\n  {\n    id: 'timerirq',\n    name: { it: 'Interruzioni dal timer (PB7 \u2192 IRQ)', en: 'Timer interrupts (PB7 \u2192 IRQ)' },\n    desc: {\n      it: 'Il timer del 6530-003 genera un\\'interruzione ogni 250 ms e la routine di servizio fa contare in binario i LED di PA, mentre il programma principale tiene acceso il display con il conteggio. Sul KIM-1 l\\'uscita IRQ del timer \u00e8 il piedino PB7: perch\u00e9 funzioni, attiva il collegamento \"PB7 \u2192 IRQ\" nel pannello della porta (sull\\'originale \u00e8 un filo da saldare sul connettore).',\n      en: 'The 6530-003 timer raises an interrupt every 250 ms and the service routine makes the PA LEDs count in binary, while the main program keeps the display lit with the count. On the KIM-1 the timer IRQ output is pin PB7: to make it work, enable the \"PB7 \u2192 IRQ\" link in the port panel (on the original it is a wire to solder on the connector).'\n    },\n    src: HEAD + '\\n' + [\n      'TKTI   = $170F      ; {{scrittura: timer \/1024 con interruzione|write: timer \/1024 with interrupt}}',\n      'IRQV   = $17FE      ; {{vettore IRQ in RAM (usato dalla ROM)|IRQ vector in RAM (used by the ROM)}}',\n      '',\n      '        .ORG $0200',\n      'BEGIN   SEI',\n      '        LDA #<ISR',\n      '        STA IRQV',\n      '        LDA #>ISR',\n      '        STA IRQV+1',\n      '        LDA #$FF',\n      '        STA PADDU    ; {{PA uscite|PA outputs}}',\n      '        LDA #0',\n      '        STA PA',\n      '        STA POINTH',\n      '        STA POINTL',\n      '        LDA #244     ; {{244 x 1024 cicli = 250 ms|244 x 1024 cycles = 250 ms}}',\n      '        STA TKTI',\n      '        CLI          ; {{interruzioni abilitate|interrupts enabled}}',\n      'MAIN    LDA PA',\n      '        STA INH',\n      '        JSR SCANDS   ; {{il display resta acceso mentre arrivano le interruzioni|the display stays lit while interrupts arrive}}',\n      '        JMP MAIN',\n      '',\n      'ISR     PHA',\n      '        INC PA       ; {{un passo del conteggio sui LED|one count step on the LEDs}}',\n      '        LDA #244',\n      '        STA TKTI     ; {{ricarica il timer e azzera la richiesta|reload the timer and clear the request}}',\n      '        PLA',\n      '        RTI'\n    ].join('\\n'),\n    start: 0x0200,\n    irq: true\n  },\n  {\n    id: 'ttyciao',\n    name: { it: 'Telescrivente: saluto e maiuscole', en: 'Teletype: greeting and capitals' },\n    desc: {\n      it: 'Programma per la modalit\u00e0 TTY. Stampa un saluto con <code>OUTCH<\/code> e poi, per ogni tasto, scrive il maiuscolo e il codice ASCII esadecimale con <code>PRTBYT<\/code>. Non serve rimandare indietro il carattere: sul KIM-1 la telescrivente stampa da sola ci\u00f2 che si scrive, perch\u00e9 tastiera e stampante stanno sullo stesso anello di corrente. RUBOUT (Canc) torna al monitor.',\n      en: 'A program for TTY mode. It prints a greeting with <code>OUTCH<\/code> and then, for every key, writes the capital letter and the hexadecimal ASCII code with <code>PRTBYT<\/code>. There is no need to echo the character: on the KIM-1 the teletype prints what you type by itself, because keyboard and printer share the same current loop. RUBOUT (Delete) goes back to the monitor.'\n    },\n    src: [\n      'OUTCH  = $1EA0      ; {{stampa il carattere in A|print the character in A}}',\n      'GETCH  = $1E5A      ; {{aspetta un carattere, lo mette in A|wait for a character, put it in A}}',\n      'PRTBYT = $1E3B      ; {{stampa A in esadecimale|print A in hexadecimal}}',\n      'CRLF   = $1E2F      ; {{a capo (usa X)|new line (uses X)}}',\n      'START  = $1C4F      ; {{ritorno al monitor|back to the monitor}}',\n      'CH     = $00',\n      '',\n      '        .ORG $0200',\n      'BEGIN   JSR CRLF',\n      '        LDX #0',\n      'MSG     LDA TEXT,X',\n      '        BEQ NEXT',\n      '        JSR OUTCH    ; {{OUTCH conserva X (ma non Y)|OUTCH keeps X (but not Y)}}',\n      '        INX',\n      '        BNE MSG',\n      'NEXT    JSR CRLF',\n      'ASK     JSR GETCH',\n      '        CMP #$7F     ; RUBOUT',\n      '        BEQ DONE',\n      '        CMP #$0D',\n      '        BEQ NEXT',\n      '        CMP #$61     ; {{minuscola?|lower case?}}',\n      '        BCC SAME',\n      '        CMP #$7B',\n      '        BCS SAME',\n      '        AND #$DF     ; {{toglie il bit 5: maiuscola|clear bit 5: capital}}',\n      'SAME    STA CH',\n      '        LDA #$20',\n      '        JSR OUTCH',\n      '        LDA #$3D     ; =',\n      '        JSR OUTCH',\n      '        LDA #$20',\n      '        JSR OUTCH',\n      '        LDA CH',\n      '        JSR OUTCH',\n      '        LDA #$20',\n      '        JSR OUTCH',\n      '        LDA #$24     ; $',\n      '        JSR OUTCH',\n      '        LDA CH',\n      '        JSR PRTBYT',\n      '        JMP NEXT',\n      'DONE    JMP START',\n      '',\n      '{{TEXT    .TEXT \"CIAO! SONO UN KIM-1 DEL 1976. SCRIVI QUALCOSA (RUBOUT = FINE)\"|TEXT    .TEXT \"HELLO! I AM A 1976 KIM-1. TYPE SOMETHING (RUBOUT = END)\"}}',\n      '        .BYTE 0'\n    ].join('\\n'),\n    start: 0x0200,\n    tty: true\n  },\n  {\n    id: 'ttyascii',\n    name: { it: 'Telescrivente: tabella ASCII', en: 'Teletype: ASCII table' },\n    desc: {\n      it: 'Stampa sulla telescrivente i caratteri da $20 a $5F con il loro codice, otto per riga: \u00e8 l\\'insieme di caratteri di una Teletype ASR-33, solo maiuscole. Alla fine torna al monitor, che risponde con l\\'indirizzo aperto.',\n      en: 'Prints the characters from $20 to $5F with their codes on the teletype, eight per line: the character set of a Teletype ASR-33, upper case only. At the end it returns to the monitor, which answers with the open address.'\n    },\n    src: [\n      'OUTCH  = $1EA0',\n      'OUTSP  = $1E9E      ; {{stampa uno spazio|print a space}}',\n      'PRTBYT = $1E3B',\n      'CRLF   = $1E2F',\n      'START  = $1C4F',\n      'CH     = $00',\n      'COL    = $01',\n      '',\n      '        .ORG $0200',\n      'BEGIN   LDA #$20',\n      '        STA CH',\n      'LINE    JSR CRLF',\n      '        LDA #8',\n      '        STA COL',\n      'ITEM    LDA CH',\n      '        JSR PRTBYT   ; {{codice in esadecimale|hexadecimal code}}',\n      '        JSR OUTSP',\n      '        LDA CH',\n      '        JSR OUTCH    ; {{il carattere|the character}}',\n      '        JSR OUTSP',\n      '        JSR OUTSP',\n      '        INC CH',\n      '        LDA CH',\n      '        CMP #$60',\n      '        BEQ DONE',\n      '        DEC COL',\n      '        BNE ITEM',\n      '        BEQ LINE',\n      'DONE    JMP START'\n    ].join('\\n'),\n    start: 0x0200,\n    tty: true\n  }\n];\n\n\/\/ sceglie la lingua dei commenti {{it|en}}\nRF.progSrc = function (p, lang) {\n  var s = p.src, k = lang === 'en' ? 2 : 1, prev;\n  do { prev = s; s = s.replace(\/\\{\\{((?:(?!\\{\\{)[^|])*?)\\|((?:(?!\\{\\{)[\\s\\S])*?)\\}\\}\/g, function (m0, a, b) { return k === 1 ? a : b; }); } while (s !== prev);\n  return s;\n};\n})(globalThis.RFKim = globalThis.RFKim || {});\n\n\/* RetroFixer \u00b7 KIM-1 \u00b7 testi (italiano e inglese), foto, repliche, passi della guida\n * Fatti verificati il 5 novembre 2026: vedi FONTI-storia.md e NOTE-hardware.md.\n *\/\n(function (RF) {\n'use strict';\n\n\/\/ Foto da Wikimedia Commons (licenze libere, attribuzione obbligatoria), caricate nella libreria media (cartella foto\/)\nRF.PHOTOS = [\n  {\n    src: 'https:\/\/retrofixer.it\/wp-content\/uploads\/2026\/11\/kim-1-musee-bolo-rama-cc-by-sa-2-fr.jpg', w: 960, h: 1184,   \/\/ media 11629\n    page: 'https:\/\/commons.wikimedia.org\/wiki\/File:MOS_KIM-1_IMG_4211_cropped_scale.jpg', author: 'Rama &amp; Mus\u00e9e Bolo', license: 'CC BY-SA 2.0 FR',\n    alt: { it: 'Scheda KIM-1 verde con il display a sei cifre rosse e la tastiera esadecimale in basso a destra', en: 'Green KIM-1 board with the red six-digit display and the hex keypad at the bottom right' },\n    cap: { it: 'Un KIM-1 di MOS Technology al Mus\u00e9e Bolo di Losanna.', en: 'A MOS Technology KIM-1 at the Mus\u00e9e Bolo in Lausanne.' }\n  },\n  {\n    src: 'https:\/\/retrofixer.it\/wp-content\/uploads\/2026\/11\/kim-1-commodore-thomas-conte-cc-by-sa-2.jpg', w: 1280, h: 960,   \/\/ media 11630\n    page: 'https:\/\/commons.wikimedia.org\/wiki\/File:Commodore_MOS_KIM-1_(5437737893).jpg', author: 'Thomas Cont\u00e9', license: 'CC BY-SA 2.0',\n    alt: { it: 'KIM-1 con il marchio Commodore visto dall\\'alto', en: 'KIM-1 with the Commodore brand seen from above' },\n    cap: { it: 'Un KIM-1 della produzione Commodore, dopo l\\'acquisto di MOS.', en: 'A KIM-1 from the Commodore production, after MOS was bought.' }\n  }\n];\n\nvar KIT = { it: 'Replica \/ kit', en: 'Replica \/ kit' }, EMU = { it: 'Emulatore', en: 'Emulator' }, DOC = { it: 'Documentazione', en: 'Documentation' };\nRF.PROJECTS = [\n  {\n    title: { it: 'Repliche e kit', en: 'Replicas and kits' },\n    items: [\n      { tag: KIT, name: 'KIM Uno (Oscar Vermeulen)', url: 'https:\/\/www.hackster.io\/obsolescence\/kim-uno-a-6502-kim-1-computer-on-arduino-e5c82c',\n        desc: { it: 'Un KIM-1 tascabile (2016) con Arduino Pro Mini: emula il 6502 con la ROM originale, su un piccolo circuito con tastiera e display a LED. Funziona anche solo con la porta seriale. Progetto aperto (GPL).',\n          en: 'A pocket KIM-1 (2016) built around an Arduino Pro Mini: it emulates the 6502 with the original ROM, on a small board with keypad and LED display. It also works with just the serial port. Open design (GPL).' } },\n      { tag: KIT, name: 'PAL-1', url: 'http:\/\/retro.hansotten.nl\/6502-sbc\/kim-1-manuals-and-software\/my-kim-1-family\/kim-replicas-and-clones\/pal-1\/',\n        desc: { it: 'Kit del 2021 con un vero 6502 e un 6532 al posto dei 6530, derivato dal Micro-KIM. Esegue il monitor KIM e i programmi dell\\'epoca; si espande con schede di memoria e I\/O.',\n          en: 'A 2021 kit with a real 6502 and a 6532 instead of the 6530s, derived from the Micro-KIM. It runs the KIM monitor and period software, and can be expanded with memory and I\/O boards.' } },\n      { tag: KIT, name: 'Micro-KIM (Briel Computers)', url: 'http:\/\/retro.hansotten.nl\/6502-sbc\/kim-1-manuals-and-software\/my-kim-1-family\/kim-replicas-and-clones\/micro-kim\/',\n        desc: { it: 'Replica del 2007 di Vince Briel con componenti veri: 6502, 5 KB di RAM e porta seriale RS-232 al posto dell\\'anello di corrente.',\n          en: 'A 2007 replica by Vince Briel with real parts: a 6502, 5 KB of RAM and an RS-232 serial port instead of the current loop.' } },\n      { tag: KIT, name: 'Corsham Technologies KIM Clone', url: 'http:\/\/retro.hansotten.nl\/6502-sbc\/corsham-projects-archive\/kim-clone\/',\n        desc: { it: 'Clone di Bob Applegate in formato KIM-1, con i 6532 e il monitor esteso xKIM. La pagina di Hans Otten ne conserva documentazione e software.',\n          en: 'A KIM-1 format clone by Bob Applegate, with 6532s and the extended xKIM monitor. Hans Otten\\'s page keeps its documentation and software.' } }\n    ]\n  },\n  {\n    title: { it: 'Documenti ed emulatori', en: 'Documents and emulators' },\n    items: [\n      { tag: DOC, name: 'KIM-1 User Manual (1976)', url: 'https:\/\/archive.org\/details\/KIM-1_Users_Manual',\n        desc: { it: 'Il manuale originale MOS Technology: uso della tastiera e della telescrivente, cassetta, interfacce e, in appendice, il listato completo del monitor.',\n          en: 'The original MOS Technology manual: keypad and teletype use, cassette, interfaces and, in the appendix, the complete monitor listing.' } },\n      { tag: DOC, name: 'The First Book of KIM', url: 'https:\/\/archive.org\/details\/The_First_Book_of_KIM',\n        desc: { it: 'Il libro di Jim Butterfield, Stan Ockers ed Eric Rehnke (1977): giochi, utilit\u00e0 e trucchi da digitare a mano sulla tastiera esadecimale.',\n          en: 'The book by Jim Butterfield, Stan Ockers and Eric Rehnke (1977): games, utilities and tricks to type in by hand on the hex keypad.' } },\n      { tag: DOC, name: 'Hans Otten: KIM-1', url: 'http:\/\/retro.hansotten.nl\/6502-sbc\/kim-1-manuals-and-software\/',\n        desc: { it: 'L\\'archivio pi\u00f9 completo sul KIM-1: manuali, revisioni della scheda, software, riviste, repliche ed emulatori.',\n          en: 'The most complete KIM-1 archive: manuals, board revisions, software, magazines, replicas and emulators.' } },\n      { tag: EMU, name: 'MAME (driver kim1)', url: 'https:\/\/github.com\/mamedev\/mame\/blob\/master\/src\/mame\/commodore\/kim1.cpp',\n        desc: { it: 'L\\'emulatore multisistema ha un driver per il KIM-1, con cassetta e telescrivente. Le ROM vanno fornite a parte.',\n          en: 'The multi-system emulator has a KIM-1 driver, with cassette and teletype. The ROMs must be supplied separately.' } }\n    ]\n  }\n];\n\nRF.TEXTS = {};\n\nRF.TEXTS.it = {\n  kicker: 'MOS Technology \u00b7 1976 \u00b7 storia ed emulatore',\n  lead: 'Il <b>KIM-1<\/b> \u00e8 la scheda con cui MOS Technology fece conoscere il <b>6502<\/b>: un computer intero su un circuito stampato, con tastiera esadecimale e display a sei cifre. Qui trovi la sua storia e un <b>emulatore<\/b> che usa il <b>monitor originale in ROM<\/b>, con la telescrivente, le porte di I\/O, la cassetta e programmi pronti da provare.',\n  navStoria: 'Storia', navEmu: 'Emulatore', navGuida: 'Primo programma', navOggi: 'Repliche e documenti',\n  storiaT: 'La storia',\n  storia: '<p>Il nome KIM-1 viene da <b>Keyboard Input Monitor<\/b>. MOS Technology lo cre\u00f2 come scheda di valutazione del suo nuovo microprocessore, il 6502: gli ingegneri potevano provarlo subito, senza progettare un circuito. Era un computer completo, gi\u00e0 montato e collaudato, con 1 KB di RAM, una tastiera esadecimale, un display a sei cifre e un monitor in ROM per scrivere e avviare i programmi.<\/p>' +\n    '<p>L\\'idea part\u00ec da Don McLaughlin e dal gruppo di <b>Chuck Peddle<\/b>. Il progetto della scheda si deve soprattutto a <b>John May<\/b>, che riprese il software del TIM, un monitor scritto per MOS da Microcomputer Associates. Il KIM-1 fu mostrato insieme al 6502 al WESCON del settembre 1975 e arriv\u00f2 in vendita all\\'inizio del 1976 a <b>245 dollari<\/b>. Nell\\'aprile 1976 la pubblicit\u00e0 su BYTE aveva gi\u00e0 il modulo d\\'ordine.<\/p>' +\n    '<p>Doveva servire agli ingegneri, ma il prezzo basso e la scheda pronta all\\'uso conquistarono gli hobbisti. Nell\\'agosto 1977 la rivista Kilobaud contava pi\u00f9 di settemila KIM-1. Quando <b>Commodore<\/b> compr\u00f2 MOS, nel settembre 1976, continu\u00f2 a venderlo con il proprio marchio, e Peddle pass\u00f2 poi al progetto del Commodore PET.<\/p>' +\n    '<p>Intorno alla scheda nacque una comunit\u00e0. La newsletter <b>KIM-1 User Notes<\/b> raccoglieva programmi e modifiche. <b>The First Book of KIM<\/b> di Jim Butterfield, Stan Ockers ed Eric Rehnke era pieno di giochi da digitare a mano. <b>Microchess<\/b> di Peter Jennings giocava a scacchi in circa 1 KB e fu spedito dal dicembre 1976. Arrivarono anche il Tiny BASIC e il BASIC di Microsoft. Dal KIM-1 discendono il SYM-1 di Synertek e l\\'AIM 65 di Rockwell.<\/p>',\n  schedaT: 'Scheda tecnica',\n  scheda: '<tr><th>CPU<\/th><td>MOS 6502 a 1 MHz, con quarzo<\/td><\/tr>' +\n    '<tr><th>RAM<\/th><td>1 KB (otto chip 6102), pi\u00f9 64 byte in ognuno dei due 6530<\/td><\/tr>' +\n    '<tr><th>ROM<\/th><td>2 KB di monitor in due 6530: il 6530-002 gestisce tastiera, display e telescrivente, il 6530-003 la cassetta<\/td><\/tr>' +\n    '<tr><th>I\/O<\/th><td>15 linee per l\\'utente (porte A e B del 6530-003) e un timer programmabile<\/td><\/tr>' +\n    '<tr><th>Tastiera<\/th><td>23 tasti e un interruttore a slitta (SST, passo-passo)<\/td><\/tr>' +\n    '<tr><th>Display<\/th><td>6 cifre LED a 7 segmenti, multiplexate dal programma<\/td><\/tr>' +\n    '<tr><th>Cassetta<\/th><td>registratore audio comune, circa 2 minuti per 1 KB<\/td><\/tr>' +\n    '<tr><th>Telescrivente<\/th><td>anello di corrente da 20 mA (Teletype ASR-33), con la velocit\u00e0 riconosciuta in automatico<\/td><\/tr>' +\n    '<tr><th>Alimentazione<\/th><td>+5 V a 1,2 A; +12 V solo per la cassetta. L\\'alimentatore non era compreso<\/td><\/tr>' +\n    '<tr><th>Connettori<\/th><td>due pettini da 44 contatti: Application (I\/O e alimentazione) ed Expansion (bus)<\/td><\/tr>' +\n    '<tr><th>Prezzo<\/th><td>245 dollari, montato (1976)<\/td><\/tr>',\n  comeFunziona: '<p><b>Come funziona (e come lo fa l\\'emulatore).<\/b> Oltre al 6502 ci sono due <b>6530<\/b> (RRIOT). Ognuno contiene 1 KB di ROM, 64 byte di RAM, due porte di I\/O e un timer. Il display non ha un controller: il monitor accende una cifra alla volta, mezzo millisecondo per volta. Scrive i segmenti sulla porta A del 6530-002 e sceglie la cifra con la porta B, attraverso un decoder 74145. Lo stesso decoder seleziona anche le righe della tastiera. La telescrivente \u00e8 gestita bit per bit dal programma, contando i cicli.<\/p>' +\n    '<p>L\\'emulatore riproduce tutto questo a livello di porte. Il 6502 esegue la <b>ROM originale<\/b> senza scorciatoie. La luminosit\u00e0 dei segmenti \u00e8 la frazione di tempo in cui restano accesi, quindi un programma che non chiama <code>SCANDS<\/code> lascia il display spento, come sull\\'originale. I caratteri della telescrivente sono ricostruiti dai fronti del piedino PB0. L\\'interruttore SST genera un NMI dopo ogni istruzione eseguita fuori dalla ROM del monitor. Unica comodit\u00e0: all\\'accensione i vettori NMI e IRQ in RAM puntano gi\u00e0 al monitor ($1C00), mentre sul KIM-1 vero andavano scritti a mano.<\/p>',\n  emuT: 'Emulatore',\n  emuIntro: 'Scegli un programma e premi <b>Carica e avvia<\/b>, oppure usa la tastiera del KIM come nel 1976: <kbd>AD<\/kbd> e quattro cifre per l\\'indirizzo, <kbd>DA<\/kbd> e due cifre per il dato, <kbd>+<\/kbd> per l\\'indirizzo successivo, <kbd>GO<\/kbd> per avviare. <kbd>ST<\/kbd> ferma il programma, <kbd>RS<\/kbd> \u00e8 il reset. Con la modalit\u00e0 <b>Telescrivente<\/b> il monitor parla con la Teletype simulata qui sotto. La <b>porta utente<\/b> mostra le linee PA e PB del 6530-003 con LED e interruttori.',\n  prog: 'Programma', loadRun: 'Carica e avvia', loadOnly: 'Solo carica',\n  speed: 'Velocit\u00e0', sp1: '1 MHz (originale)', sp4: '4 MHz', sp0: 'Massima',\n  mode: 'Modalit\u00e0', modeKb: 'Tastiera e display', modeTty: 'Telescrivente (TTY)', baud: 'Baud',\n  power: '\u23fb Spegni e riaccendi', pcbSub: 'emulatore \u00b7 ROM originale',\n  boardHint: 'Clicca i tasti oppure seleziona la tastiera e usa quella del PC: <b>0-9 A-F<\/b>, <b>\u2191<\/b> o Ctrl+A = AD, <b>\u2193<\/b> o Ctrl+D = DA, <b>+<\/b> o Invio = +, <b>G<\/b> = GO, <b>P<\/b> = PC, <b>S<\/b> = ST, <b>Esc<\/b> = RS, <b>T<\/b> = SST.',\n  portT: 'Porta utente (6530-003, connettore Application)',\n  portNote: 'Ogni linea \u00e8 un LED quando il programma la usa come uscita e un interruttore quando \u00e8 un ingresso. Registri: PA = $1700, direzione $1701; PB = $1702, direzione $1703 (1 = uscita). PB6 non arriva al connettore.',\n  audio: 'Audio (altoparlante)', spkLine: 'Altoparlante sulla linea', irqWire: 'Filo PB7 \u2192 IRQ (interruzioni del timer)', expRam: 'Espansione RAM 4 KB ($0400-$13FF)',\n  tapeT: 'Registratore a cassetta',\n  tRec: 'Reg.', tPlay: 'Play', tStop: 'Stop', tRew: 'Inizio', tWav: 'Salva WAV', tOpen: 'Apri WAV',\n  tapeNote: '<b>Salvare:<\/b> scrivi l\\'inizio in $17F5-$17F6, la fine (esclusa) in $17F7-$17F8 e un numero di nastro in $17F9, premi <b>Reg.<\/b> e poi <kbd>AD<\/kbd> 1800 <kbd>GO<\/kbd>. <b>Caricare:<\/b> scrivi il numero in $17F9, riavvolgi, premi <b>Play<\/b> e poi <kbd>AD<\/kbd> 1873 <kbd>GO<\/kbd>. Il WAV \u00e8 lo stesso segnale del KIM-1 vero (3600\/2400 Hz).',\n  termName: 'TELETYPE ASR-33 \u00b7 20 mA', rubout: 'RUBOUT', termClr: 'Strappa il foglio', termCopy: 'Copia',\n  cpuT: 'CPU 6502', step: 'Passo', bpLbl: 'Arresto a', memT: 'Memoria', memAddr: 'Da',\n  asmT: 'Assembler 6502',\n  asmNote: 'Sintassi dei listati dell\\'epoca e degli assembler di oggi: etichette in colonna 1 (anche con i due punti), <code>.ORG<\/code> o <code>*=<\/code>, <code>.BYTE<\/code>, <code>.WORD<\/code>, <code>.TEXT<\/code>, <code>.DS<\/code>, costanti con <code>=<\/code> o <code>EQU<\/code>, numeri <code>$FF<\/code>, <code>%1010<\/code>, <code>\\'A\\'<\/code>, prefissi <code>&lt;<\/code> e <code>&gt;<\/code>. Il programma parte dall\\'etichetta <code>BEGIN<\/code>, se c\\'\u00e8, altrimenti dal primo byte. Va in RAM: $0000-$03FF e $1780-$17E6, oppure nell\\'espansione se l\\'hai attivata.',\n  asmRun: 'Assembla e avvia', asmLoad: 'Assembla e carica', asmSave: 'Salva .asm',\n  hexT: 'Inserisci in esadecimale',\n  hexNote: 'Incolla byte esadecimali: righe <code>0200: A9 01 8D 00 17<\/code> o byte liberi, che partono da $0200. Vanno bene anche i record del nastro perforato KIM (<code>;18020000\u2026<\/code>). <b>Manda alla telescrivente<\/b> trasforma il programma in nastro perforato e lo fa leggere al monitor con il comando <code>L<\/code>, alla velocit\u00e0 scelta, come con il lettore di nastro di una ASR-33.',\n  hexRun: 'Carica e avvia', hexLoad: 'Carica', hexTty: 'Manda alla telescrivente (L)',\n  hexSample: '; somma $0010 + $0011 in $0012, poi BRK torna al monitor\\n0200: A5 10 18 65 11 85 12 00\\n0010: 25 17',\n  fileT: 'File',\n  fileNote: 'Salva un pezzo di memoria come nastro perforato KIM (<code>.ptp<\/code>) o binario, oppure apri un file: <code>.ptp<\/code>, <code>.hex<\/code> (Intel HEX o righe <code>indirizzo: byte<\/code>), <code>.bin<\/code> (caricato all\\'indirizzo \"Da\"), <code>.asm<\/code> nell\\'assembler. I file restano nel tuo browser: non vengono inviati a nessuno.',\n  from: 'Da', to: 'A', savePtp: 'Salva nastro .ptp', saveBin: 'Salva .bin', openFile: 'Apri file',\n  guidaT: 'Il primo programma, a mano come nel 1976',\n  guidaIntro: '<p>Sul KIM-1 i programmi si scrivevano in linguaggio macchina, un byte alla volta. Questo somma due numeri: carica il primo (<code>LDA $10<\/code> = A5 10), azzera il riporto (<code>CLC<\/code> = 18), aggiunge il secondo (<code>ADC $11<\/code> = 65 11), salva il risultato (<code>STA $12<\/code> = 85 12) e torna al monitor (<code>BRK<\/code> = 00). Segui i passi sulla tastiera dell\\'emulatore, oppure premi \"Premi i tasti per me\" e guarda il display.<\/p>',\n  guidaDopo: '<p><b>Perch\u00e9 BRK torna al monitor?<\/b> Il 6502 salta all\\'indirizzo scritto in $FFFE. Sul KIM-1 quell\\'indirizzo \u00e8 nella ROM, che lo rimanda a quello scritto in RAM in $17FE-$17FF. Sul KIM-1 vero, dopo ogni accensione, bisognava scriverci 00 1C, cio\u00e8 $1C00, e lo stesso in $17FA-$17FB perch\u00e9 funzionassero ST e SST. L\\'emulatore lo fa da solo quando lo accendi o carichi un programma.<\/p>',\n  tastiT: 'I tasti del monitor',\n  tasti: '<tr><td>AD<\/td><td>Modo indirizzo: le cifre che scrivi entrano nelle prime quattro cifre (l\\'indirizzo).<\/td><\/tr>' +\n    '<tr><td>DA<\/td><td>Modo dato: le cifre entrano nelle ultime due e cambiano il byte a quell\\'indirizzo.<\/td><\/tr>' +\n    '<tr><td>+<\/td><td>Passa all\\'indirizzo successivo.<\/td><\/tr>' +\n    '<tr><td>GO<\/td><td>Esegue dal indirizzo mostrato, con i registri salvati in $EF-$F5.<\/td><\/tr>' +\n    '<tr><td>PC<\/td><td>Mostra il contatore di programma salvato all\\'ultimo arresto.<\/td><\/tr>' +\n    '<tr><td>ST<\/td><td>Stop: genera un NMI. Il monitor salva i registri e mostra dove si \u00e8 fermato.<\/td><\/tr>' +\n    '<tr><td>RS<\/td><td>Reset: torna al monitor. La RAM resta com\\'era.<\/td><\/tr>' +\n    '<tr><td>SST<\/td><td>Passo-passo: con l\\'interruttore su ON, ogni GO esegue una sola istruzione.<\/td><\/tr>',\n  ttyT: 'I comandi della telescrivente',\n  ttyCmd: '<tr><td>RUBOUT<\/td><td>Dopo il reset fa misurare la velocit\u00e0 al monitor, che risponde KIM. In seguito torna all\\'inizio. Qui viene inviato da solo dopo ogni RS.<\/td><\/tr>' +\n    '<tr><td>1234 spazio<\/td><td>Apre l\\'indirizzo $1234 e stampa \"1234 dd\".<\/td><\/tr>' +\n    '<tr><td>A9 .<\/td><td>Scrive A9 all\\'indirizzo aperto e passa al successivo.<\/td><\/tr>' +\n    '<tr><td>CR (Invio)<\/td><td>Mostra l\\'indirizzo successivo.<\/td><\/tr>' +\n    '<tr><td>LF (Ctrl+J)<\/td><td>Mostra l\\'indirizzo precedente.<\/td><\/tr>' +\n    '<tr><td>G<\/td><td>Esegue il programma dall\\'indirizzo aperto.<\/td><\/tr>' +\n    '<tr><td>L<\/td><td>Legge un nastro perforato: record <code>;LLAAAADD\u2026CCCC<\/code>, alla fine stampa KIM.<\/td><\/tr>' +\n    '<tr><td>Q<\/td><td>Perfora un nastro dall\\'indirizzo aperto fino a quello scritto in $17F7-$17F8 (escluso).<\/td><\/tr>',\n  romT: 'Le routine della ROM da usare nei programmi',\n  rom: '<tr><td>$1F1F<\/td><td><b>SCANDS<\/b>: una passata del display con POINTH ($FB), POINTL ($FA) e INH ($F9). Torna con A diverso da 0 se un tasto \u00e8 premuto.<\/td><\/tr>' +\n    '<tr><td>$1F6A<\/td><td><b>GETKEY<\/b>: legge la tastiera. In A: 0-F le cifre, $10 AD, $11 DA, $12 +, $13 GO, $14 PC, $15 nessun tasto.<\/td><\/tr>' +\n    '<tr><td>$1EA0<\/td><td><b>OUTCH<\/b>: stampa sulla telescrivente il carattere in A. Conserva X, cambia Y.<\/td><\/tr>' +\n    '<tr><td>$1E9E<\/td><td><b>OUTSP<\/b>: stampa uno spazio.<\/td><\/tr>' +\n    '<tr><td>$1E5A<\/td><td><b>GETCH<\/b>: aspetta un carattere dalla telescrivente e lo mette in A.<\/td><\/tr>' +\n    '<tr><td>$1E3B<\/td><td><b>PRTBYT<\/b>: stampa A in esadecimale (due caratteri).<\/td><\/tr>' +\n    '<tr><td>$1E2F<\/td><td><b>CRLF<\/b>: va a capo. Usa X.<\/td><\/tr>' +\n    '<tr><td>$1C4F<\/td><td><b>START<\/b>: ritorno al monitor (con <code>JMP<\/code>).<\/td><\/tr>' +\n    '<tr><td>$1C00<\/td><td><b>SAVE<\/b>: ingresso per NMI e BRK: salva i registri in $EF-$F5.<\/td><\/tr>' +\n    '<tr><td>$1800 \u00b7 $1873<\/td><td><b>DUMPT<\/b> e <b>LOADT<\/b>: salvataggio e caricamento su cassetta.<\/td><\/tr>',\n  mappaT: 'Mappa della memoria',\n  mappa: '<tr><td>$0000-$03FF<\/td><td>RAM (1 KB). $00EF-$00FF sono usati dal monitor.<\/td><\/tr>' +\n    '<tr><td>$0400-$13FF<\/td><td>Libero, per le espansioni (KIM-2, KIM-3\u2026). Qui: RAM di espansione, se attivata.<\/td><\/tr>' +\n    '<tr><td>$1700-$173F<\/td><td>6530-003: porte utente PA\/PB e timer ($1704-$1707, con interruzione $170C-$170F).<\/td><\/tr>' +\n    '<tr><td>$1740-$177F<\/td><td>6530-002: display, tastiera, telescrivente, cassetta; timer usato dalla cassetta.<\/td><\/tr>' +\n    '<tr><td>$1780-$17BF<\/td><td>RAM del 6530-003, libera per l\\'utente.<\/td><\/tr>' +\n    '<tr><td>$17C0-$17FF<\/td><td>RAM del 6530-002. Da $17E7 la usa il monitor; $17FA-$17FF sono i vettori NMI, RST e IRQ.<\/td><\/tr>' +\n    '<tr><td>$1800-$1FFF<\/td><td>ROM: cassetta ($1800, 6530-003) e monitor ($1C00, 6530-002).<\/td><\/tr>' +\n    '<tr><td>$2000-$FFFF<\/td><td>Copie degli 8 KB precedenti: le linee A13-A15 non sono decodificate, cos\u00ec $FFFA-$FFFF coincidono con $1FFA-$1FFF.<\/td><\/tr>',\n  oggiT: 'Il KIM-1 oggi: repliche e documenti',\n  oggiIntro: 'Gli originali sono rari e costosi, ma ci sono repliche fedeli, kit da montare e un archivio enorme di documenti e programmi.',\n  fontiT: 'Fonti',\n  fonti: '<li>MOS Technology, <a href=\"https:\/\/archive.org\/details\/KIM-1_Users_Manual\" target=\"_blank\" rel=\"noopener\">KIM-1 User Manual<\/a>, rev. 6500-15B, agosto 1976: hardware, uso e listato del monitor.<\/li>' +\n    '<li>B. Bagnall, <i>On the Edge<\/i>, cap. 1.5, ripreso da <a href=\"http:\/\/retro.hansotten.nl\/6502-sbc\/kim-1-manuals-and-software\/the-story-of-the-kim-1\/\" target=\"_blank\" rel=\"noopener\">Hans Otten<\/a>: progettisti e nascita del KIM-1.<\/li>' +\n    '<li>Computer History Museum, <a href=\"https:\/\/www.computerhistory.org\/revolution\/personal-computers\/17\/296\/1134\" target=\"_blank\" rel=\"noopener\">KIM-1<\/a>.<\/li>' +\n    '<li>Bill Mensch sulla nascita del 6530, su <a href=\"http:\/\/retro.hansotten.nl\/6502-sbc\/6530-6532\/tim-6530-004\/bill-mensch-on-the-creation-of-the-6530\/\" target=\"_blank\" rel=\"noopener\">retro.hansotten.nl<\/a>.<\/li>' +\n    '<li>R. M. Tripp, <a href=\"https:\/\/www.commodore.ca\/is-the-kim-1-for-every-1-1977\/\" target=\"_blank\" rel=\"noopener\">\"Is the KIM-1 for every 1?\"<\/a>, Kilobaud, agosto 1977.<\/li>' +\n    '<li>Listato commentato del monitor e descrizione dell\\'hardware: <a href=\"http:\/\/www.zimmers.net\/anonftp\/pub\/cbm\/src\/kim-1\/index.html\" target=\"_blank\" rel=\"noopener\">zimmers.net<\/a>; driver <a href=\"https:\/\/github.com\/mamedev\/mame\/blob\/master\/src\/mame\/commodore\/kim1.cpp\" target=\"_blank\" rel=\"noopener\">MAME kim1<\/a>.<\/li>' +\n    '<li>P. Jennings, <a href=\"https:\/\/www.benlo.com\/microchess\/index.html\" target=\"_blank\" rel=\"noopener\">Microchess<\/a>.<\/li>',\n  romNote: 'Il monitor in ROM \u00e8 il programma originale \u00a9 1975 MOS Technology, Inc., pubblicato per intero nel manuale del KIM-1, ed \u00e8 incluso a scopo storico e didattico. Emulatore, programmi di esempio e testi sono di RetroFixer.',\n  fotoDi: 'Foto:',\n  ariaDisplay: 'Display del KIM-1', ariaKeys: 'Tastiera del KIM-1',\n  ttyHello: 'TELETYPE ASR-33 PRONTA.\\r\\nSCEGLI \"TELESCRIVENTE (TTY)\" PER COLLEGARLA AL KIM-1.\\r\\n',\n  ttyReset: 'RS: il monitor aspetta il RUBOUT per misurare la velocit\u00e0 della telescrivente (te lo mando io).',\n  ttyOn: 'collegata', ttyOff: 'scollegata',\n  needTty: 'La telescrivente \u00e8 scollegata: scegli la modalit\u00e0 \"Telescrivente (TTY)\".',\n  asmErrShort: 'L\\'assembler ha trovato {n} errori: guarda sotto il sorgente.', asmEmpty: 'Il sorgente non contiene istruzioni.',\n  lostBytes: 'Attenzione: {n} byte sono finiti fuori dalla RAM e sono andati persi.',\n  running: 'Programma avviato da ${a}.', loaded: 'Programma caricato da ${a}: premi GO per avviarlo.',\n  powerDone: 'Spento e riacceso: la RAM \u00e8 vuota.',\n  swLbl: 'Interruttore {p}', pb6: 'PB6 \u00e8 il chip select del 6530-003: non arriva al connettore.',\n  copied: 'Testo copiato.', cont: 'Continua', pause: 'Pausa', stHalt: 'FERMA', stPause: 'PAUSA', stRun: 'IN CORSO',\n  cycles: '{n} cicli (\u00b5s a 1 MHz)',\n  asmErr: '{n} errori', line: 'riga', asmOk: 'Assemblato: ${a}-${b} ({n} byte)',\n  hexBad: 'Riga {l}: \"{t}\" non \u00e8 un byte esadecimale.', ptpBad: 'Riga {l}: record del nastro non valido.', ptpSum: 'Riga {l}: checksum sbagliato.',\n  hexEmpty: 'Non ci sono byte da caricare.', ptpSending: 'Nastro perforato in lettura: {n} caratteri. Alla fine il monitor stampa KIM.',\n  saved: '{n} byte salvati.', ihexBad: 'Riga {l}: record Intel HEX non valido.', tooBig: 'File troppo grande.', srcLoaded: '{f} aperto nell\\'assembler.',\n  tapeLen: 'nastro {t}', tapeEmpty: 'nastro vuoto', wavBad: 'Non riesco a leggere questo file audio.', wavOk: 'WAV letto: {n} fronti. Premi Play e avvia LOADT ($1873).',\n  doKeys: 'Premi i tasti per me',\n  bpHit: 'Arresto a ${a}.', halted: 'Opcode non valido ${o} a ${a}: la CPU \u00e8 ferma. Premi RS.'\n};\n\nRF.TEXTS.en = {\n  kicker: 'MOS Technology \u00b7 1976 \u00b7 history and emulator',\n  lead: 'The <b>KIM-1<\/b> is the board MOS Technology used to introduce the <b>6502<\/b>: a whole computer on one circuit board, with a hex keypad and a six-digit display. Here you will find its history and an <b>emulator<\/b> running the <b>original monitor ROM<\/b>, with the teletype, the I\/O ports, the cassette and ready-made programs to try.',\n  navStoria: 'History', navEmu: 'Emulator', navGuida: 'First program', navOggi: 'Replicas and documents',\n  storiaT: 'History',\n  storia: '<p>The name KIM-1 stands for <b>Keyboard Input Monitor<\/b>. MOS Technology made it as an evaluation board for its new microprocessor, the 6502: engineers could try it straight away, without designing a circuit. It was a complete computer, assembled and tested, with 1 KB of RAM, a hex keypad, a six-digit display and a ROM monitor to enter and run programs.<\/p>' +\n    '<p>The idea came from Don McLaughlin and <b>Chuck Peddle<\/b>\\'s group. The board design is mostly the work of <b>John May<\/b>, who reused the software of the TIM, a monitor written for MOS by Microcomputer Associates. The KIM-1 was shown together with the 6502 at WESCON in September 1975 and went on sale in early 1976 for <b>245 dollars<\/b>. By April 1976 the BYTE advertisement already carried an order form.<\/p>' +\n    '<p>It was meant for engineers, but the low price and the ready-to-use board won over hobbyists. In August 1977 Kilobaud magazine counted more than seven thousand KIM-1s. When <b>Commodore<\/b> bought MOS in September 1976 it kept selling the board under its own name, and Peddle went on to design the Commodore PET.<\/p>' +\n    '<p>A community grew around the board. The <b>KIM-1 User Notes<\/b> newsletter collected programs and modifications. <b>The First Book of KIM<\/b> by Jim Butterfield, Stan Ockers and Eric Rehnke was full of games to type in by hand. Peter Jennings\\' <b>Microchess<\/b> played chess in about 1 KB and shipped from December 1976. Tiny BASIC and Microsoft BASIC followed. The Synertek SYM-1 and the Rockwell AIM 65 descend from the KIM-1.<\/p>',\n  schedaT: 'Specifications',\n  scheda: '<tr><th>CPU<\/th><td>MOS 6502 at 1 MHz, crystal controlled<\/td><\/tr>' +\n    '<tr><th>RAM<\/th><td>1 KB (eight 6102 chips), plus 64 bytes in each of the two 6530s<\/td><\/tr>' +\n    '<tr><th>ROM<\/th><td>2 KB of monitor in two 6530s: the 6530-002 handles keypad, display and teletype, the 6530-003 the cassette<\/td><\/tr>' +\n    '<tr><th>I\/O<\/th><td>15 user lines (ports A and B of the 6530-003) and a programmable timer<\/td><\/tr>' +\n    '<tr><th>Keypad<\/th><td>23 keys and a slide switch (SST, single step)<\/td><\/tr>' +\n    '<tr><th>Display<\/th><td>6-digit 7-segment LED, multiplexed by software<\/td><\/tr>' +\n    '<tr><th>Cassette<\/th><td>ordinary audio recorder, about 2 minutes per 1 KB<\/td><\/tr>' +\n    '<tr><th>Teletype<\/th><td>20 mA current loop (Teletype ASR-33), speed detected automatically<\/td><\/tr>' +\n    '<tr><th>Power<\/th><td>+5 V at 1.2 A; +12 V for the cassette only. No power supply included<\/td><\/tr>' +\n    '<tr><th>Connectors<\/th><td>two 44-pin edge connectors: Application (I\/O and power) and Expansion (bus)<\/td><\/tr>' +\n    '<tr><th>Price<\/th><td>245 dollars, assembled (1976)<\/td><\/tr>',\n  comeFunziona: '<p><b>How it works (and how the emulator does it).<\/b> Besides the 6502 there are two <b>6530<\/b>s (RRIOT). Each holds 1 KB of ROM, 64 bytes of RAM, two I\/O ports and a timer. The display has no controller: the monitor lights one digit at a time, half a millisecond each. It writes the segments to port A of the 6530-002 and picks the digit with port B, through a 74145 decoder. The same decoder also selects the keypad rows. The teletype is handled bit by bit in software, by counting cycles.<\/p>' +\n    '<p>The emulator reproduces all of this at port level. The 6502 runs the <b>original ROM<\/b> with no shortcuts. Segment brightness is the fraction of time each segment stays lit, so a program that does not call <code>SCANDS<\/code> leaves the display dark, as on the original. Teletype characters are rebuilt from the edges on pin PB0. The SST switch raises an NMI after every instruction executed outside the monitor ROM. One convenience: at power-on the NMI and IRQ vectors in RAM already point to the monitor ($1C00), while on a real KIM-1 they had to be entered by hand.<\/p>',\n  emuT: 'Emulator',\n  emuIntro: 'Pick a program and press <b>Load and run<\/b>, or use the KIM keypad as in 1976: <kbd>AD<\/kbd> and four digits for the address, <kbd>DA<\/kbd> and two digits for the data, <kbd>+<\/kbd> for the next address, <kbd>GO<\/kbd> to run. <kbd>ST<\/kbd> stops the program, <kbd>RS<\/kbd> is reset. In <b>Teletype<\/b> mode the monitor talks to the simulated Teletype below. The <b>user port<\/b> shows lines PA and PB of the 6530-003 with LEDs and switches.',\n  prog: 'Program', loadRun: 'Load and run', loadOnly: 'Load only',\n  speed: 'Speed', sp1: '1 MHz (original)', sp4: '4 MHz', sp0: 'Maximum',\n  mode: 'Mode', modeKb: 'Keypad and display', modeTty: 'Teletype (TTY)', baud: 'Baud',\n  power: '\u23fb Power cycle', pcbSub: 'emulator \u00b7 original ROM',\n  boardHint: 'Click the keys, or select the keypad and use your PC keyboard: <b>0-9 A-F<\/b>, <b>\u2191<\/b> or Ctrl+A = AD, <b>\u2193<\/b> or Ctrl+D = DA, <b>+<\/b> or Enter = +, <b>G<\/b> = GO, <b>P<\/b> = PC, <b>S<\/b> = ST, <b>Esc<\/b> = RS, <b>T<\/b> = SST.',\n  portT: 'User port (6530-003, Application connector)',\n  portNote: 'Each line is an LED when the program uses it as an output and a switch when it is an input. Registers: PA = $1700, direction $1701; PB = $1702, direction $1703 (1 = output). PB6 does not reach the connector.',\n  audio: 'Audio (speaker)', spkLine: 'Speaker on line', irqWire: 'PB7 \u2192 IRQ wire (timer interrupts)', expRam: '4 KB RAM expansion ($0400-$13FF)',\n  tapeT: 'Cassette recorder',\n  tRec: 'Rec', tPlay: 'Play', tStop: 'Stop', tRew: 'Rewind', tWav: 'Save WAV', tOpen: 'Open WAV',\n  tapeNote: '<b>Saving:<\/b> put the start address in $17F5-$17F6, the end (excluded) in $17F7-$17F8 and a tape ID in $17F9, press <b>Rec<\/b>, then <kbd>AD<\/kbd> 1800 <kbd>GO<\/kbd>. <b>Loading:<\/b> put the ID in $17F9, rewind, press <b>Play<\/b>, then <kbd>AD<\/kbd> 1873 <kbd>GO<\/kbd>. The WAV is the same signal as a real KIM-1 (3600\/2400 Hz).',\n  termName: 'TELETYPE ASR-33 \u00b7 20 mA', rubout: 'RUBOUT', termClr: 'Tear off the paper', termCopy: 'Copy',\n  cpuT: '6502 CPU', step: 'Step', bpLbl: 'Break at', memT: 'Memory', memAddr: 'From',\n  asmT: '6502 assembler',\n  asmNote: 'Syntax of period listings and of today\\'s assemblers: labels in column 1 (with or without a colon), <code>.ORG<\/code> or <code>*=<\/code>, <code>.BYTE<\/code>, <code>.WORD<\/code>, <code>.TEXT<\/code>, <code>.DS<\/code>, constants with <code>=<\/code> or <code>EQU<\/code>, numbers <code>$FF<\/code>, <code>%1010<\/code>, <code>\\'A\\'<\/code>, <code>&lt;<\/code> and <code>&gt;<\/code> prefixes. The program starts at the <code>BEGIN<\/code> label if there is one, otherwise at the first byte. It goes into RAM: $0000-$03FF and $1780-$17E6, or into the expansion if you enabled it.',\n  asmRun: 'Assemble and run', asmLoad: 'Assemble and load', asmSave: 'Save .asm',\n  hexT: 'Enter in hexadecimal',\n  hexNote: 'Paste hexadecimal bytes: lines like <code>0200: A9 01 8D 00 17<\/code>, or plain bytes starting at $0200. KIM paper tape records (<code>;18020000\u2026<\/code>) work too. <b>Send to the teletype<\/b> turns the program into paper tape and has the monitor read it with the <code>L<\/code> command at the chosen speed, like the tape reader of an ASR-33.',\n  hexRun: 'Load and run', hexLoad: 'Load', hexTty: 'Send to the teletype (L)',\n  hexSample: '; adds $0010 + $0011 into $0012, then BRK returns to the monitor\\n0200: A5 10 18 65 11 85 12 00\\n0010: 25 17',\n  fileT: 'Files',\n  fileNote: 'Save a piece of memory as KIM paper tape (<code>.ptp<\/code>) or binary, or open a file: <code>.ptp<\/code>, <code>.hex<\/code> (Intel HEX or <code>address: bytes<\/code> lines), <code>.bin<\/code> (loaded at the \"From\" address), <code>.asm<\/code> into the assembler. Files stay in your browser: they are not sent anywhere.',\n  from: 'From', to: 'To', savePtp: 'Save tape .ptp', saveBin: 'Save .bin', openFile: 'Open file',\n  guidaT: 'Your first program, by hand as in 1976',\n  guidaIntro: '<p>On the KIM-1 programs were entered in machine code, one byte at a time. This one adds two numbers: it loads the first (<code>LDA $10<\/code> = A5 10), clears the carry (<code>CLC<\/code> = 18), adds the second (<code>ADC $11<\/code> = 65 11), stores the result (<code>STA $12<\/code> = 85 12) and returns to the monitor (<code>BRK<\/code> = 00). Follow the steps on the emulator keypad, or press \"Press the keys for me\" and watch the display.<\/p>',\n  guidaDopo: '<p><b>Why does BRK return to the monitor?<\/b> The 6502 jumps to the address stored at $FFFE. On the KIM-1 that address is in ROM, which passes control to the address stored in RAM at $17FE-$17FF. On a real KIM-1 you had to write 00 1C there, that is $1C00, after every power-on, and the same at $17FA-$17FB for ST and SST to work. The emulator does it for you when you power it on or load a program.<\/p>',\n  tastiT: 'The monitor keys',\n  tasti: '<tr><td>AD<\/td><td>Address mode: the digits you type go into the first four digits (the address).<\/td><\/tr>' +\n    '<tr><td>DA<\/td><td>Data mode: the digits go into the last two and change the byte at that address.<\/td><\/tr>' +\n    '<tr><td>+<\/td><td>Moves to the next address.<\/td><\/tr>' +\n    '<tr><td>GO<\/td><td>Runs from the address shown, with the registers saved at $EF-$F5.<\/td><\/tr>' +\n    '<tr><td>PC<\/td><td>Shows the program counter saved at the last stop.<\/td><\/tr>' +\n    '<tr><td>ST<\/td><td>Stop: raises an NMI. The monitor saves the registers and shows where it stopped.<\/td><\/tr>' +\n    '<tr><td>RS<\/td><td>Reset: back to the monitor. RAM is left as it was.<\/td><\/tr>' +\n    '<tr><td>SST<\/td><td>Single step: with the switch ON, every GO runs one instruction only.<\/td><\/tr>',\n  ttyT: 'Teletype commands',\n  ttyCmd: '<tr><td>RUBOUT<\/td><td>After reset it lets the monitor measure the speed, and the monitor answers KIM. Later it goes back to the start. Here it is sent automatically after every RS.<\/td><\/tr>' +\n    '<tr><td>1234 space<\/td><td>Opens address $1234 and prints \"1234 dd\".<\/td><\/tr>' +\n    '<tr><td>A9 .<\/td><td>Writes A9 at the open address and moves to the next one.<\/td><\/tr>' +\n    '<tr><td>CR (Enter)<\/td><td>Shows the next address.<\/td><\/tr>' +\n    '<tr><td>LF (Ctrl+J)<\/td><td>Shows the previous address.<\/td><\/tr>' +\n    '<tr><td>G<\/td><td>Runs the program from the open address.<\/td><\/tr>' +\n    '<tr><td>L<\/td><td>Reads a paper tape: records <code>;LLAAAADD\u2026CCCC<\/code>, prints KIM at the end.<\/td><\/tr>' +\n    '<tr><td>Q<\/td><td>Punches a tape from the open address up to the one stored at $17F7-$17F8 (excluded).<\/td><\/tr>',\n  romT: 'ROM routines to use in your programs',\n  rom: '<tr><td>$1F1F<\/td><td><b>SCANDS<\/b>: one display pass with POINTH ($FB), POINTL ($FA) and INH ($F9). Returns with A not 0 if a key is down.<\/td><\/tr>' +\n    '<tr><td>$1F6A<\/td><td><b>GETKEY<\/b>: reads the keypad. In A: 0-F the digits, $10 AD, $11 DA, $12 +, $13 GO, $14 PC, $15 no key.<\/td><\/tr>' +\n    '<tr><td>$1EA0<\/td><td><b>OUTCH<\/b>: prints the character in A on the teletype. Keeps X, changes Y.<\/td><\/tr>' +\n    '<tr><td>$1E9E<\/td><td><b>OUTSP<\/b>: prints a space.<\/td><\/tr>' +\n    '<tr><td>$1E5A<\/td><td><b>GETCH<\/b>: waits for a character from the teletype and puts it in A.<\/td><\/tr>' +\n    '<tr><td>$1E3B<\/td><td><b>PRTBYT<\/b>: prints A in hexadecimal (two characters).<\/td><\/tr>' +\n    '<tr><td>$1E2F<\/td><td><b>CRLF<\/b>: new line. Uses X.<\/td><\/tr>' +\n    '<tr><td>$1C4F<\/td><td><b>START<\/b>: back to the monitor (with <code>JMP<\/code>).<\/td><\/tr>' +\n    '<tr><td>$1C00<\/td><td><b>SAVE<\/b>: entry for NMI and BRK: saves the registers at $EF-$F5.<\/td><\/tr>' +\n    '<tr><td>$1800 \u00b7 $1873<\/td><td><b>DUMPT<\/b> and <b>LOADT<\/b>: cassette save and load.<\/td><\/tr>',\n  mappaT: 'Memory map',\n  mappa: '<tr><td>$0000-$03FF<\/td><td>RAM (1 KB). $00EF-$00FF are used by the monitor.<\/td><\/tr>' +\n    '<tr><td>$0400-$13FF<\/td><td>Free, for expansions (KIM-2, KIM-3\u2026). Here: expansion RAM, if enabled.<\/td><\/tr>' +\n    '<tr><td>$1700-$173F<\/td><td>6530-003: user ports PA\/PB and timer ($1704-$1707, with interrupt $170C-$170F).<\/td><\/tr>' +\n    '<tr><td>$1740-$177F<\/td><td>6530-002: display, keypad, teletype, cassette; its timer is used by the cassette.<\/td><\/tr>' +\n    '<tr><td>$1780-$17BF<\/td><td>6530-003 RAM, free for the user.<\/td><\/tr>' +\n    '<tr><td>$17C0-$17FF<\/td><td>6530-002 RAM. From $17E7 it is used by the monitor; $17FA-$17FF hold the NMI, RST and IRQ vectors.<\/td><\/tr>' +\n    '<tr><td>$1800-$1FFF<\/td><td>ROM: cassette ($1800, 6530-003) and monitor ($1C00, 6530-002).<\/td><\/tr>' +\n    '<tr><td>$2000-$FFFF<\/td><td>Copies of the 8 KB above: lines A13-A15 are not decoded, so $FFFA-$FFFF are the same as $1FFA-$1FFF.<\/td><\/tr>',\n  oggiT: 'The KIM-1 today: replicas and documents',\n  oggiIntro: 'Originals are rare and expensive, but there are faithful replicas, kits to build and a huge archive of documents and programs.',\n  fontiT: 'Sources',\n  fonti: '<li>MOS Technology, <a href=\"https:\/\/archive.org\/details\/KIM-1_Users_Manual\" target=\"_blank\" rel=\"noopener\">KIM-1 User Manual<\/a>, rev. 6500-15B, August 1976: hardware, operation and monitor listing.<\/li>' +\n    '<li>B. Bagnall, <i>On the Edge<\/i>, ch. 1.5, reproduced by <a href=\"http:\/\/retro.hansotten.nl\/6502-sbc\/kim-1-manuals-and-software\/the-story-of-the-kim-1\/\" target=\"_blank\" rel=\"noopener\">Hans Otten<\/a>: designers and origins of the KIM-1.<\/li>' +\n    '<li>Computer History Museum, <a href=\"https:\/\/www.computerhistory.org\/revolution\/personal-computers\/17\/296\/1134\" target=\"_blank\" rel=\"noopener\">KIM-1<\/a>.<\/li>' +\n    '<li>Bill Mensch on the creation of the 6530, at <a href=\"http:\/\/retro.hansotten.nl\/6502-sbc\/6530-6532\/tim-6530-004\/bill-mensch-on-the-creation-of-the-6530\/\" target=\"_blank\" rel=\"noopener\">retro.hansotten.nl<\/a>.<\/li>' +\n    '<li>R. M. Tripp, <a href=\"https:\/\/www.commodore.ca\/is-the-kim-1-for-every-1-1977\/\" target=\"_blank\" rel=\"noopener\">\"Is the KIM-1 for every 1?\"<\/a>, Kilobaud, August 1977.<\/li>' +\n    '<li>Commented monitor listing and hardware description: <a href=\"http:\/\/www.zimmers.net\/anonftp\/pub\/cbm\/src\/kim-1\/index.html\" target=\"_blank\" rel=\"noopener\">zimmers.net<\/a>; <a href=\"https:\/\/github.com\/mamedev\/mame\/blob\/master\/src\/mame\/commodore\/kim1.cpp\" target=\"_blank\" rel=\"noopener\">MAME kim1<\/a> driver.<\/li>' +\n    '<li>P. Jennings, <a href=\"https:\/\/www.benlo.com\/microchess\/index.html\" target=\"_blank\" rel=\"noopener\">Microchess<\/a>.<\/li>',\n  romNote: 'The monitor ROM is the original program \u00a9 1975 MOS Technology, Inc., printed in full in the KIM-1 manual, and is included for historical and educational purposes. The emulator, sample programs and text are by RetroFixer.',\n  fotoDi: 'Photo:',\n  ariaDisplay: 'KIM-1 display', ariaKeys: 'KIM-1 keypad',\n  ttyHello: 'TELETYPE ASR-33 READY.\\r\\nSELECT \"TELETYPE (TTY)\" TO CONNECT IT TO THE KIM-1.\\r\\n',\n  ttyReset: 'RS: the monitor waits for RUBOUT to measure the teletype speed (I send it for you).',\n  ttyOn: 'connected', ttyOff: 'disconnected',\n  needTty: 'The teletype is disconnected: select \"Teletype (TTY)\" mode.',\n  asmErrShort: 'The assembler found {n} errors: see below the source.', asmEmpty: 'The source contains no instructions.',\n  lostBytes: 'Warning: {n} bytes fell outside RAM and were lost.',\n  running: 'Program started at ${a}.', loaded: 'Program loaded at ${a}: press GO to run it.',\n  powerDone: 'Powered off and on: RAM is empty.',\n  swLbl: 'Switch {p}', pb6: 'PB6 is the 6530-003 chip select: it does not reach the connector.',\n  copied: 'Text copied.', cont: 'Continue', pause: 'Pause', stHalt: 'HALTED', stPause: 'PAUSED', stRun: 'RUNNING',\n  cycles: '{n} cycles (\u00b5s at 1 MHz)',\n  asmErr: '{n} errors', line: 'line', asmOk: 'Assembled: ${a}-${b} ({n} bytes)',\n  hexBad: 'Line {l}: \"{t}\" is not a hexadecimal byte.', ptpBad: 'Line {l}: invalid tape record.', ptpSum: 'Line {l}: wrong checksum.',\n  hexEmpty: 'There are no bytes to load.', ptpSending: 'Reading paper tape: {n} characters. At the end the monitor prints KIM.',\n  saved: '{n} bytes saved.', ihexBad: 'Line {l}: invalid Intel HEX record.', tooBig: 'File too large.', srcLoaded: '{f} opened in the assembler.',\n  tapeLen: 'tape {t}', tapeEmpty: 'empty tape', wavBad: 'I cannot read this audio file.', wavOk: 'WAV read: {n} edges. Press Play and start LOADT ($1873).',\n  doKeys: 'Press the keys for me',\n  bpHit: 'Break at ${a}.', halted: 'Invalid opcode ${o} at ${a}: the CPU has stopped. Press RS.'\n};\n\n\/\/ passi della guida: testo e tasti da premere in automatico\nRF.STEPS = {\n  it: [\n    { text: 'Premi <kbd>RS<\/kbd>: il monitor riparte e il display mostra un indirizzo e il suo contenuto.', keys: 'RS' },\n    { text: 'Scegli l\\'indirizzo $0200: <kbd>AD<\/kbd> <kbd>0<\/kbd> <kbd>2<\/kbd> <kbd>0<\/kbd> <kbd>0<\/kbd>.', keys: 'AD 0 2 0 0' },\n    { text: 'Passa ai dati con <kbd>DA<\/kbd> e scrivi il programma: due cifre per ogni byte, <kbd>+<\/kbd> per passare al successivo. I byte sono <b>A5 10 18 65 11 85 12 00<\/b>.', keys: 'DA A 5 + 1 0 + 1 8 + 6 5 + 1 1 + 8 5 + 1 2 + 0 0' },\n    { text: 'Metti i due numeri in $0010 e $0011, per esempio $25 e $17: <kbd>AD<\/kbd> 0010 <kbd>DA<\/kbd> 25 <kbd>+<\/kbd> 17.', keys: 'AD 0 0 1 0 DA 2 5 + 1 7' },\n    { text: 'Torna all\\'inizio e avvia: <kbd>AD<\/kbd> 0200 <kbd>GO<\/kbd>. Il programma dura pochi microsecondi. BRK riporta al monitor, che mostra l\\'indirizzo dopo il BRK: <b>0209<\/b>.', keys: 'AD 0 2 0 0 GO' },\n    { text: 'Guarda il risultato: <kbd>AD<\/kbd> 0012. Il display mostra <b>0012 3C<\/b>: $25 + $17 = $3C, cio\u00e8 37 + 23 = 60.', keys: 'AD 0 0 1 2' },\n    { text: 'Prova il passo-passo: sposta <b>SST<\/b> su ON, poi <kbd>AD<\/kbd> 0200 e premi <kbd>GO<\/kbd> pi\u00f9 volte. Ogni GO esegue un\\'istruzione e il display mostra l\\'indirizzo della successiva. Rimetti SST su OFF alla fine.', keys: 'AD 0 2 0 0' }\n  ],\n  en: [\n    { text: 'Press <kbd>RS<\/kbd>: the monitor restarts and the display shows an address and its contents.', keys: 'RS' },\n    { text: 'Select address $0200: <kbd>AD<\/kbd> <kbd>0<\/kbd> <kbd>2<\/kbd> <kbd>0<\/kbd> <kbd>0<\/kbd>.', keys: 'AD 0 2 0 0' },\n    { text: 'Switch to data with <kbd>DA<\/kbd> and type the program: two digits per byte, <kbd>+<\/kbd> to move to the next one. The bytes are <b>A5 10 18 65 11 85 12 00<\/b>.', keys: 'DA A 5 + 1 0 + 1 8 + 6 5 + 1 1 + 8 5 + 1 2 + 0 0' },\n    { text: 'Put the two numbers at $0010 and $0011, for example $25 and $17: <kbd>AD<\/kbd> 0010 <kbd>DA<\/kbd> 25 <kbd>+<\/kbd> 17.', keys: 'AD 0 0 1 0 DA 2 5 + 1 7' },\n    { text: 'Go back to the start and run: <kbd>AD<\/kbd> 0200 <kbd>GO<\/kbd>. The program takes a few microseconds. BRK returns to the monitor, which shows the address after the BRK: <b>0209<\/b>.', keys: 'AD 0 2 0 0 GO' },\n    { text: 'Look at the result: <kbd>AD<\/kbd> 0012. The display shows <b>0012 3C<\/b>: $25 + $17 = $3C, that is 37 + 23 = 60.', keys: 'AD 0 0 1 2' },\n    { text: 'Try single step: slide <b>SST<\/b> to ON, then <kbd>AD<\/kbd> 0200 and press <kbd>GO<\/kbd> several times. Each GO runs one instruction and the display shows the address of the next one. Set SST back to OFF at the end.', keys: 'AD 0 2 0 0' }\n  ]\n};\n})(globalThis.RFKim = globalThis.RFKim || {});\n\n\/* RetroFixer \u00b7 KIM-1 \u00b7 interfaccia della pagina *\/\n(function (RF) {\n'use strict';\nvar root = document.querySelector('.rfkim');\nif (!root) return;\nvar EN = \/^en\/i.test(document.documentElement.lang || '');\nvar L = EN ? 'en' : 'it', T = RF.TEXTS[ L];\nfunction tr(k, v) {\n  var s = T[ k] !== undefined ? T[ k] : (RF.TEXTS.it[ k] !== undefined ? RF.TEXTS.it[ k] : k);\n  if (v) s = s.replace(\/\\{(\\w+)\\}\/g, function (m0, n) { return v[ n] !== undefined ? v[ n] : m0; });\n  return s;\n}\nfunction $(id) { return document.getElementById(id); }\nfunction H(v, n) { v = (v >>> 0).toString(16).toUpperCase(); while (v.length < n) v = '0' + v; return v; }\nfunction esc(s) { return String(s).replace(\/[&<>\"]\/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;' }[ c]; }); }\nroot.querySelectorAll('[data-t]').forEach(function (e) { e.textContent = tr(e.dataset.t); });\nroot.querySelectorAll('[data-th]').forEach(function (e) { e.innerHTML = tr(e.dataset.th); });\n\n\/\/ ---- foto e schede ---------------------------------------------------------------------------------\n[1, 2].forEach(function (n) {\n  var f = RF.PHOTOS[n - 1], img = $('rfkFoto' + n), cap = $('rfkFoto' + n + 'c');\n  if (!f) { img.closest('figure').hidden = true; return; }\n  img.src = f.src; img.alt = f.alt[ L]; if (f.w) { img.width = f.w; img.height = f.h; }\n  cap.innerHTML = f.cap[ L] + ' <span class=\"cred\">' + tr('fotoDi') + ' <a href=\"' + f.page + '\" target=\"_blank\" rel=\"noopener\">' + f.author + '<\/a>, ' + f.license + '<\/span>';\n});\nvar cards = $('rfkCards');\nRF.PROJECTS.forEach(function (g) {\n  var box = document.createElement('div'); box.className = 'cgroup';\n  box.innerHTML = '<h4>' + g.title[ L] + '<\/h4>';\n  var ul = document.createElement('div'); ul.className = 'clist';\n  g.items.forEach(function (it) {\n    var a = document.createElement('a'); a.className = 'card'; a.href = it.url; a.target = '_blank'; a.rel = 'noopener';\n    a.innerHTML = '<span class=\"ctag\">' + (it.tag ? it.tag[ L] : '') + '<\/span><b>' + it.name + '<\/b><span class=\"cdesc\">' + it.desc[ L] + '<\/span><span class=\"curl\">' + it.url.replace(\/^https?:\\\/\\\/(www\\.)?\/, '').replace(\/\\\/$\/, '') + '<\/span>';\n    ul.appendChild(a);\n  });\n  box.appendChild(ul); cards.appendChild(box);\n});\n\n\/\/ ---- macchina, scheda, telescrivente ---------------------------------------------------------------\nvar m = new RF.Kim();\nRF.machine = m;   \/\/ per chi vuole curiosare dalla console del browser\nvar seg = new Float32Array(42), lv = new Float32Array(16);\nvar board = new RF.Board($('rfkBoard'), {\n  ariaDisplay: tr('ariaDisplay'), ariaKeys: tr('ariaKeys'),\n  onKey: function (k, down) { m.key(k, down); if (down && k === 'RS') afterReset(); },\n  onSST: function (on) { m.sst = on; }\n});\nvar term = new RF.Terminal($('rfkTerm'), 72, 22);\nterm.fg = '#2b2722'; term.bg = '#efe8d0';\nif (document.fonts && document.fonts.load) document.fonts.load('20px VT323').then(function () { term.measure(); term.dirty = true; }, function () {});\nm.onTx = function (c) { if (c !== 0 && c !== 0x13 && c !== 0x11) term.put(c); };\nm.onEcho = function (c) { if (c !== 0x7F) term.put(c); };   \/\/ la telescrivente stampa ci\u00f2 che si scrive (stesso anello di corrente)\nterm.write(tr('ttyHello'));\n\nvar paused = false, speed = 1, dbgDirty = true;\nfunction flash(s, el) {\n  var e = el || $('rfkPDesc');\n  if (e.dataset.old === undefined) e.dataset.old = e.innerHTML;\n  e.innerHTML = '<b>' + s + '<\/b>';\n  clearTimeout(e._t); e._t = setTimeout(function () { e.innerHTML = e.dataset.old; delete e.dataset.old; }, 4000);\n}\nfunction afterReset() {\n  if (m.ttyMode) flash(tr('ttyReset'));\n  dbgDirty = true;\n}\n\n\/\/ ---- programmi di esempio -----------------------------------------------------------------------\nvar sel = $('rfkProg');\nRF.PROGRAMS.forEach(function (p, i) { var o = document.createElement('option'); o.value = i; o.textContent = p.name[ L]; sel.appendChild(o); });\nfunction prog() { return RF.PROGRAMS[+sel.value]; }\nfunction showDesc() { var e = $('rfkPDesc'); delete e.dataset.old; e.innerHTML = prog().desc[ L]; }\nsel.addEventListener('change', function () { showDesc(); $('rfkSrc').value = RF.progSrc(prog(), L); saveSrc(); });\n\n\/\/ prepara la macchina: RS (e misura della velocit\u00e0 TTY) prima di caricare\nfunction freshStart(tty) {\n  if (tty !== undefined && tty !== m.ttyMode) setMode(tty ? 'tty' : 'kb', true);\n  paused = false; updPause();\n  m.reset();\n  m.run(m.ttyMode ? Math.ceil(m.bitCycles() * 11 * 44) + 60000 : 30000);   \/\/ RUBOUT e risposta \"KIM 0000 00\" (circa 40 caratteri)\n}\nfunction loadAsm(src, run, tag) {\n  var r = RF.assemble(src);\n  showAsm(r);\n  if (r.errors.length) { flash(tr('asmErrShort', { n: r.errors.length })); return null; }\n  if (r.end <= r.start) { flash(tr('asmEmpty')); return null; }\n  var p = tag || {};\n  if (p.irq) { $('rfkIrq').checked = true; m.irqWired = true; }\n  freshStart(p.tty ? true : undefined);\n  var lost = 0;\n  for (var a = r.start; a < r.end; a++) if (r.used[ a]) { if (m.writable(a)) m.poke(a, r.mem[ a]); else lost++; }\n  var start = r.syms.begin !== undefined ? r.syms.begin : (r.syms.entry !== undefined ? r.syms.entry : r.start);\n  m.setVectors();\n  if (run) m.go(start); else m.pointTo(start);\n  if (lost) flash(tr('lostBytes', { n: lost }));\n  else flash(tr(run ? 'running' : 'loaded', { a: H(start, 4) }));\n  if (p.tty && run) focusTerm();\n  dbgDirty = true;\n  return r;\n}\n$('rfkLoadRun').addEventListener('click', function () { $('rfkSrc').value = RF.progSrc(prog(), L); saveSrc(); loadAsm($('rfkSrc').value, true, prog()); });\n$('rfkLoadOnly').addEventListener('click', function () { $('rfkSrc').value = RF.progSrc(prog(), L); saveSrc(); loadAsm($('rfkSrc').value, false, prog()); });\n\n\/\/ ---- velocit\u00e0, modalit\u00e0, accensione ---------------------------------------------------------------\n$('rfkSpeed').addEventListener('change', function () { speed = +this.value; });\nfunction setMode(v, quiet) {\n  $('rfkMode').value = v;\n  m.ttyMode = v === 'tty';\n  if (!quiet) { m.reset(); afterReset(); }\n  $('rfkTermMsg').textContent = m.ttyMode ? tr('ttyOn') : tr('ttyOff');\n}\n$('rfkMode').addEventListener('change', function () { setMode(this.value); if (m.ttyMode) focusTerm(); });\n$('rfkBaud').addEventListener('change', function () { m.baud = +this.value; if (m.ttyMode) { m.reset(); afterReset(); } });\nm.baud = +$('rfkBaud').value;\n$('rfkTermMsg').textContent = tr('ttyOff');\n$('rfkPower').addEventListener('click', function () { m.powerOn(); term.clear(); term.write(tr('ttyHello')); flash(tr('powerDone')); dbgDirty = true; });\n\n\/\/ ---- porta utente ---------------------------------------------------------------------------------\nvar portEls = { A: [], B: [] }, sw = { A: 0xFF, B: 0xFF };\n['A', 'B'].forEach(function (P) {\n  var box = $('rfkLeds' + P);\n  var lab = document.createElement('span'); lab.className = 'pl'; lab.textContent = 'P' + P; box.appendChild(lab);\n  for (var b = 7; b >= 0; b--) {\n    var d = document.createElement('div'); d.className = 'lbit';\n    var na = P === 'B' && b === 6;   \/\/ PB6 \u00e8 il chip select del 6530-003: non arriva al connettore\n    if (na) d.classList.add('na');\n    d.innerHTML = '<span>' + b + '<\/span><i class=\"led\"><\/i><button type=\"button\" class=\"lsw hi\" aria-pressed=\"true\"><\/button><span class=\"ldir\">in<\/span>';\n    var btn = d.querySelector('.lsw');\n    btn.setAttribute('aria-label', tr('swLbl', { p: 'P' + P + b }));\n    if (na) { btn.disabled = true; btn.title = tr('pb6'); }\n    (function (P, b, btn) {\n      btn.addEventListener('click', function () {\n        sw[ P] ^= 1 << b;\n        var hi = !!(sw[ P] & (1 << b));\n        btn.classList.toggle('hi', hi); btn.setAttribute('aria-pressed', hi ? 'true' : 'false');\n        m.setSwitches(sw.A, sw.B);\n      });\n    })(P, b, btn);\n    box.appendChild(d);\n    portEls[ P][ b] = { led: d.querySelector('.led'), dir: d.querySelector('.ldir'), box: d, last: -1, lastDir: -1 };\n  }\n});\nfunction renderPorts() {\n  m.ports(lv);\n  var dd = [m.r3.ddra, m.r3.ddrb];\n  ['A', 'B'].forEach(function (P, pi) {\n    for (var b = 0; b < 8; b++) {\n      var e = portEls[ P][ b], out = (dd[ pi] >> b) & 1, v = out ? lv[pi * 8 + b] : 0;\n      v = Math.round(v * 20) \/ 20;\n      if (out !== e.lastDir) {\n        e.lastDir = out; e.dir.textContent = out ? 'out' : 'in'; e.dir.classList.toggle('out', !!out); e.box.classList.toggle('in', !out);\n      }\n      if (v !== e.last) {\n        e.last = v;\n        e.led.style.background = v > 0.02 ? 'rgba(255,' + Math.round(40 + 30 * v) + ',40,' + (0.35 + 0.65 * v).toFixed(2) + ')' : '';\n        e.led.style.boxShadow = v > 0.3 ? '0 0 ' + Math.round(10 * v) + 'px rgba(255,60,40,.8)' : '';\n      }\n    }\n  });\n}\n\/\/ altoparlante\nvar spkSel = $('rfkSpk');\n['A', 'B'].forEach(function (P) { for (var b = 0; b < 8; b++) { if (P === 'B' && b === 6) continue; var o = document.createElement('option'); o.value = P + b; o.textContent = 'P' + P + b; spkSel.appendChild(o); } });\nspkSel.value = 'A0';\nspkSel.addEventListener('change', function () { m.spk = { port: this.value[0], bit: +this.value[1] }; m.spkLevel = -1; });\nvar actx = null, aNext = 0, aLast = 0, aLevel = 0, aPrevX = 0, aPrevY = 0;\n$('rfkAudio').addEventListener('change', function () {\n  if (this.checked) {\n    try { actx = actx || new (window.AudioContext || window.webkitAudioContext)(); actx.resume(); } catch (e) { this.checked = false; return; }\n    m.audioOn = true; m.audioEdges.length = 0; aLast = m.cpu.cycles; aNext = 0;\n  } else { m.audioOn = false; m.audioEdges.length = 0; }\n});\nfunction pumpAudio() {\n  if (!m.audioOn || !actx) return;\n  var now = m.cpu.cycles, e = m.audioEdges;\n  if (speed !== 1 || paused) { e.length = 0; aLast = now; return; }\n  var sr = actx.sampleRate, n = Math.floor((now - aLast) * sr \/ 1e6);\n  if (n <= 0) return;\n  if (n > sr) { e.length = 0; aLast = now; return; }\n  var buf = actx.createBuffer(1, n, sr), d = buf.getChannelData(0), k = 0, cyc = aLast, step = 1e6 \/ sr;\n  for (var i = 0; i < n; i++, cyc += step) {\n    while (k < e.length && e[ k] <= cyc) { aLevel = e[k + 1]; k += 2; }\n    var x = aLevel ? 0.18 : -0.18, y = x - aPrevX + 0.995 * aPrevY;   \/\/ filtro passa-alto: niente continua\n    aPrevX = x; aPrevY = y; d[ i] = y;\n  }\n  e.length = 0;\n  aLast += n * step;\n  var src = actx.createBufferSource(); src.buffer = buf; src.connect(actx.destination);\n  var t = actx.currentTime;\n  if (aNext < t + 0.02 || aNext > t + 0.3) aNext = t + 0.06;\n  src.start(aNext); aNext += n \/ sr;\n}\n$('rfkIrq').addEventListener('change', function () { m.irqWired = this.checked; });\n$('rfkExp').addEventListener('change', function () { m.setExpansion(this.checked); dbgDirty = true; });\n\n\/\/ ---- telescrivente --------------------------------------------------------------------------------\nvar kbd = $('rfkKbd'), ttyBox = kbd.parentNode;\nfunction focusTerm() { try { kbd.focus({ preventScroll: true }); } catch (e) { kbd.focus(); } }\nttyBox.addEventListener('pointerdown', function (e) { e.preventDefault(); focusTerm(); });\nkbd.addEventListener('focus', function () { ttyBox.classList.add('focus'); });\nkbd.addEventListener('blur', function () { ttyBox.classList.remove('focus'); });\nfunction sendChar(c, gap) {\n  if (!m.ttyMode) { flash(tr('needTty')); return; }\n  if (c >= 0x61 && c <= 0x7A) c -= 0x20;   \/\/ l'ASR-33 ha solo le maiuscole (e il monitor capisce solo A-F maiuscole)\n  m.ttySend(c, gap);\n}\nkbd.addEventListener('keydown', function (e) {\n  var k = e.key;\n  if (e.ctrlKey && !e.altKey && k.length === 1) {\n    var c = k.toUpperCase().charCodeAt(0);\n    if (c >= 64 && c <= 95) { e.preventDefault(); sendChar(c - 64); }\n    return;\n  }\n  if (k === 'Enter') { e.preventDefault(); sendChar(13); }\n  else if (k === 'Delete' || k === 'Backspace') { e.preventDefault(); sendChar(0x7F); }\n  else if (k === 'Escape') { e.preventDefault(); sendChar(27); }\n});\nkbd.addEventListener('input', function () {\n  var s = kbd.value; kbd.value = '';\n  var gap = s.length > 1 ? 4 : 0;   \/\/ testo incollato: una pausa dopo ogni carattere\n  for (var i = 0; i < s.length; i++) { var c = s.charCodeAt(i); if (c === 10) c = 13; if (c < 128) sendChar(c, gap); }\n});\n$('rfkRubout').addEventListener('click', function () { sendChar(0x7F); focusTerm(); });\n$('rfkTermClr').addEventListener('click', function () { term.clear(); });\n$('rfkTermCopy').addEventListener('click', function () {\n  var t = term.text();\n  if (navigator.clipboard) navigator.clipboard.writeText(t).then(function () { flash(tr('copied')); }, function () {});\n});\n\n\/\/ ---- debugger -------------------------------------------------------------------------------------\nfunction updPause() { $('rfkPause').innerHTML = paused ? '\u25b6 ' + tr('cont') : '\u23f8 ' + tr('pause'); dbgDirty = true; }\nupdPause();\n$('rfkPause').addEventListener('click', function () { paused = !paused; updPause(); });\n$('rfkStep').addEventListener('click', function () { if (!paused) { paused = true; updPause(); } m.stepOne(); dbgDirty = true; });\nfunction hexField(el, cb) {\n  el.addEventListener('input', function () { el.value = el.value.replace(\/[^0-9a-f]\/gi, '').toUpperCase(); });\n  el.addEventListener('change', function () { cb(el.value === '' ? -1 : parseInt(el.value, 16)); });\n}\nhexField($('rfkBp'), function (v) { if (v < 0) m.bp = null; else { m.bp = new Uint8Array(65536); m.bp[ v] = 1; } });\nvar memAt = 0x200;\nhexField($('rfkMemAt'), function (v) { if (v >= 0) memAt = v & 0xFFF8; dbgDirty = true; });\nfunction renderDbg() {\n  var c = m.cpu;\n  $('rfkRegs').innerHTML = [['A', H(c.a, 2)], ['X', H(c.x, 2)], ['Y', H(c.y, 2)], ['PC', H(c.pc, 4)], ['SP', '1' + H(c.sp, 2)], ['P', H(c.getP(false), 2)]]\n    .map(function (r) { return '<span><i>' + r[0] + '<\/i>' + r[1] + '<\/span>'; }).join('');\n  var fl = [['N', c.n], ['V', c.v], ['D', c.d], ['I', c.i], ['Z', c.z], ['C', c.c]];\n  var st = c.halted ? tr('stHalt') : paused ? tr('stPause') : tr('stRun');\n  $('rfkFlags').innerHTML = fl.map(function (f) { return '<span class=\"' + (f[1] ? 'on' : '') + '\">' + f[0] + '<\/span>'; }).join('') + '<span class=\"st\">' + st + '<\/span>';\n  var pc = c.pc, out = '', peek = function (a) { return m.peek(a); };\n  for (var i = 0; i < 8; i++) {\n    var d = RF.disasm(peek, pc);\n    out += '<li class=\"' + (i === 0 ? 'pc' : '') + '\"><span class=\"ad\">' + H(pc, 4) + '<\/span><span>' + d.bytes.map(function (b) { return H(b, 2); }).join(' ') + '<\/span><span>' + d.text + '<\/span><\/li>';\n    pc = (pc + d.len) & 0xFFFF;\n  }\n  $('rfkDis').innerHTML = out;\n  $('rfkCyc').textContent = tr('cycles', { n: Math.floor(c.cycles).toLocaleString(L === 'it' ? 'it-IT' : 'en-GB') });\n  var mem = '';\n  for (var r = 0; r < 8; r++) {\n    var a = (memAt + r * 8) & 0xFFFF, row = H(a, 4) + ':';\n    for (var k = 0; k < 8; k++) row += ' ' + H(m.peek((a + k) & 0xFFFF), 2);\n    mem += row + '\\n';\n  }\n  $('rfkMem').textContent = mem;\n}\n\n\/\/ ---- assembler ------------------------------------------------------------------------------------\nvar srcEl = $('rfkSrc');\nfunction saveSrc() { try { localStorage.setItem('rfkSrc', srcEl.value); } catch (e) {} }\ntry { srcEl.value = localStorage.getItem('rfkSrc') || RF.progSrc(RF.PROGRAMS[0], L); } catch (e) { srcEl.value = RF.progSrc(RF.PROGRAMS[0], L); }\nsrcEl.addEventListener('input', saveSrc);\nsrcEl.addEventListener('keydown', function (e) {\n  if (e.key === 'Tab' && !e.shiftKey && !e.ctrlKey) {\n    e.preventDefault();\n    var s = srcEl.selectionStart, en = srcEl.selectionEnd;\n    srcEl.value = srcEl.value.slice(0, s) + '\\t' + srcEl.value.slice(en);\n    srcEl.selectionStart = srcEl.selectionEnd = s + 1; saveSrc();\n  }\n});\nfunction showAsm(r) {\n  var out = $('rfkAsmOut'); out.hidden = false;\n  if (r.errors.length) {\n    out.className = 'asmout err';\n    out.innerHTML = '<b>' + tr('asmErr', { n: r.errors.length }) + '<\/b><pre>' + r.errors.slice(0, 15).map(function (e) {\n      return esc(tr('line') + ' ' + e.line + ': ' + e.msg + (e.text ? '   \u2502 ' + e.text.trim() : ''));\n    }).join('\\n') + '<\/pre>';\n    return;\n  }\n  out.className = 'asmout';\n  var lines = [], a, row;\n  for (a = r.start; a < r.end && lines.length < 48; a += 8) {\n    row = H(a, 4) + ':'; var any = false;\n    for (var k = 0; k < 8 && a + k < r.end; k++) { if (r.used[a + k]) { row += ' ' + H(r.mem[a + k], 2); any = true; } else row += ' ..'; }\n    if (any) lines.push(row);\n  }\n  var syms = Object.keys(r.syms).filter(function (k) { return !\/^(sad|padd|sbd|pa|paddu|pb|pbddu|scands|getkey|inh|pointl|pointh)$\/.test(k); })\n    .slice(0, 40).map(function (k) { return k.toUpperCase() + '=$' + H(r.syms[ k] & 0xFFFF, r.syms[ k] > 255 ? 4 : 2); });\n  out.innerHTML = '<b>' + tr('asmOk', { a: H(r.start, 4), b: H(r.end - 1, 4), n: r.end - r.start }) + '<\/b><pre>' + esc(lines.join('\\n')) + (syms.length ? '\\n\\n' + esc(syms.join('  ')) : '') + '<\/pre>';\n}\n$('rfkAsmRun').addEventListener('click', function () { loadAsm(srcEl.value, true); });\n$('rfkAsm').addEventListener('click', function () { loadAsm(srcEl.value, false); });\n$('rfkAsmSave').addEventListener('click', function () { download('kim1.asm', new Blob([srcEl.value], { type: 'text\/plain' })); });\n\n\/\/ ---- esadecimale e nastro perforato ---------------------------------------------------------------\n\/\/ formati: \"0200: A9 01 8D\", byte liberi (dalla prima riga \"0200:\" o da $0200), record \";18020000...\" del KIM\nfunction parseHex(s) {\n  var blocks = [], lines = s.split(\/\\r?\\n\/), addr = 0x200, cur = null, err = null;\n  if (\/^\\s*;[0-9a-f]{6}\/im.test(s)) return parsePtp(s);\n  lines.forEach(function (ln, i) {\n    if (err) return;\n    ln = ln.replace(\/(\\\/\\\/|;|#).*$\/, '').trim();\n    if (!ln) return;\n    var m1 = \/^\\$?([0-9a-f]{1,4})\\s*:\\s*(.*)$\/i.exec(ln);\n    if (m1) { addr = parseInt(m1[1], 16); ln = m1[2]; cur = null; }\n    var toks = ln.split(\/[\\s,]+\/).filter(Boolean);\n    toks.forEach(function (t) {\n      if (err) return;\n      t = t.replace(\/^\\$|^0x\/i, '');\n      if (!\/^[0-9a-f]{1,2}$\/i.test(t)) { err = tr('hexBad', { l: i + 1, t: t }); return; }\n      if (!cur) { cur = { addr: addr, bytes: [] }; blocks.push(cur); }\n      cur.bytes.push(parseInt(t, 16)); addr = (addr + 1) & 0xFFFF;\n    });\n  });\n  return err ? { error: err } : { blocks: blocks };\n}\nfunction parsePtp(s) {\n  var blocks = [], err = null;\n  s.split(\/[\\r\\n]+\/).forEach(function (ln, i) {\n    if (err) return;\n    var k = ln.indexOf(';'); if (k < 0) return;\n    var h = ln.slice(k + 1).replace(\/\\s+\/g, '');\n    if (!\/^[0-9a-f]+$\/i.test(h) || h.length < 10) { err = tr('ptpBad', { l: i + 1 }); return; }\n    var b = []; for (var j = 0; j + 1 < h.length; j += 2) b.push(parseInt(h.substr(j, 2), 16));\n    var n = b[0];\n    if (n === 0) return;   \/\/ ultimo record\n    if (b.length < n + 5) { err = tr('ptpBad', { l: i + 1 }); return; }\n    var sum = 0; for (j = 0; j < n + 3; j++) sum += b[ j];\n    if ((sum & 0xFFFF) !== ((b[n + 3] << 8) | b[n + 4])) { err = tr('ptpSum', { l: i + 1 }); return; }\n    blocks.push({ addr: (b[1] << 8) | b[2], bytes: b.slice(3, 3 + n) });\n  });\n  return err ? { error: err } : { blocks: blocks };\n}\n\/\/ record del nastro perforato KIM: \";\" + numero di byte + indirizzo + dati + somma a 16 bit; l'ultimo d\u00e0 il numero di record\nfunction ptpRecords(addr, bytes) {\n  var out = [];\n  for (var i = 0; i < bytes.length; i += 24) {\n    var chunk = bytes.slice(i, i + 24), a = (addr + i) & 0xFFFF, b = [chunk.length, a >> 8, a & 0xFF].concat(chunk);\n    var sum = b.reduce(function (s, x) { return s + x; }, 0) & 0xFFFF;\n    out.push(';' + b.map(function (x) { return H(x, 2); }).join('') + H(sum, 4));\n  }\n  return out;\n}\nfunction ptpEnd(rec) { return ';00' + H(rec, 4) + H(((rec >> 8) + (rec & 0xFF)) & 0xFFFF, 4); }\nfunction toPtp(blocks) {\n  var recs = [];\n  blocks.forEach(function (bl) { recs = recs.concat(ptpRecords(bl.addr, bl.bytes)); });\n  recs.push(ptpEnd(recs.length));\n  return recs.join('\\r\\n') + '\\r\\n';\n}\nfunction loadBlocks(blocks, run, msgEl) {\n  if (!blocks.length) { flash(tr('hexEmpty'), msgEl); return false; }\n  freshStart();\n  var lost = 0;\n  blocks.forEach(function (bl) { lost += m.load(bl.addr, bl.bytes); });\n  m.setVectors();\n  var start = blocks[0].addr;\n  if (run) m.go(start); else m.pointTo(start);\n  flash(lost ? tr('lostBytes', { n: lost }) : tr(run ? 'running' : 'loaded', { a: H(start, 4) }), msgEl);\n  dbgDirty = true;\n  return true;\n}\nvar hexEl = $('rfkHex');\nhexEl.value = tr('hexSample');\n$('rfkHexRun').addEventListener('click', function () { var r = parseHex(hexEl.value); if (r.error) flash(r.error, $('rfkHexMsg')); else loadBlocks(r.blocks, true, $('rfkHexMsg')); });\n$('rfkHexLoad').addEventListener('click', function () { var r = parseHex(hexEl.value); if (r.error) flash(r.error, $('rfkHexMsg')); else loadBlocks(r.blocks, false, $('rfkHexMsg')); });\n\/\/ manda il programma alla telescrivente come nastro perforato: il monitor lo legge con il comando L\n$('rfkHexTty').addEventListener('click', function () {\n  var r = parseHex(hexEl.value);\n  if (r.error) { flash(r.error, $('rfkHexMsg')); return; }\n  if (!r.blocks.length) { flash(tr('hexEmpty'), $('rfkHexMsg')); return; }\n  freshStart(true);   \/\/ RS + RUBOUT: il monitor riparte pronto a ricevere il comando L\n  var tape = toPtp(r.blocks).replace(\/\\r\\n$\/, '');   \/\/ dopo l'ultimo record niente a capo: il monitor ha gi\u00e0 finito\n  m.ttyClear();\n  m.ttySend(0x4C, 3);                                            \/\/ L\n  for (var i = 0; i < tape.length; i++) m.ttySend(tape.charCodeAt(i), 0);\n  flash(tr('ptpSending', { n: tape.length }), $('rfkHexMsg'));\n  focusTerm();\n});\n\n\/\/ ---- file ------------------------------------------------------------------------------------------\nfunction download(name, blob) {\n  var a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = name;\n  document.body.appendChild(a); a.click(); setTimeout(function () { URL.revokeObjectURL(a.href); a.remove(); }, 1000);\n}\n['rfkFrom', 'rfkTo'].forEach(function (id) { hexField($(id), function () {}); });\nfunction range() {\n  var a = parseInt($('rfkFrom').value || '0', 16) & 0xFFFF, b = parseInt($('rfkTo').value || '0', 16) & 0xFFFF;\n  if (b < a) { var t = a; a = b; b = t; }\n  var out = []; for (var i = a; i <= b; i++) out.push(m.peek(i));\n  return { a: a, b: b, bytes: out };\n}\nfunction fileMsg(s, err) { var e = $('rfkFileMsg'); e.textContent = s; e.className = 'msg' + (err ? ' err' : ''); }\n$('rfkSavePtp').addEventListener('click', function () { var r = range(); download('kim1-' + H(r.a, 4) + '.ptp', new Blob([toPtp([{ addr: r.a, bytes: r.bytes }])], { type: 'text\/plain' })); fileMsg(tr('saved', { n: r.bytes.length })); });\n$('rfkSaveBin').addEventListener('click', function () { var r = range(); download('kim1-' + H(r.a, 4) + '.bin', new Blob([new Uint8Array(r.bytes)], { type: 'application\/octet-stream' })); fileMsg(tr('saved', { n: r.bytes.length })); });\n$('rfkOpen').addEventListener('click', function () { $('rfkFile').click(); });\nfunction parseIntelHex(s) {\n  var blocks = [], base = 0, err = null;\n  s.split(\/\\r?\\n\/).forEach(function (ln, i) {\n    if (err) return;\n    ln = ln.trim(); if (!ln) return;\n    if (ln[0] !== ':' || !\/^:[0-9a-f]+$\/i.test(ln)) { err = tr('ihexBad', { l: i + 1 }); return; }\n    var b = []; for (var j = 1; j + 1 < ln.length; j += 2) b.push(parseInt(ln.substr(j, 2), 16));\n    var n = b[0], sum = 0; for (j = 0; j < b.length; j++) sum += b[ j];\n    if ((sum & 0xFF) !== 0 || b.length < n + 5) { err = tr('ihexBad', { l: i + 1 }); return; }\n    if (b[3] === 0) blocks.push({ addr: (base + ((b[1] << 8) | b[2])) & 0xFFFF, bytes: b.slice(4, 4 + n) });\n    else if (b[3] === 2) base = ((b[4] << 8) | b[5]) << 4;\n  });\n  return err ? { error: err } : { blocks: blocks };\n}\n$('rfkFile').addEventListener('change', function () {\n  var f = this.files[0]; this.value = '';\n  if (!f) return;\n  if (f.size > 1048576) { fileMsg(tr('tooBig'), true); return; }\n  var rd = new FileReader(), ext = (f.name.split('.').pop() || '').toLowerCase();\n  rd.onload = function () {\n    if (ext === 'bin') {\n      var a = parseInt($('rfkFrom').value || '200', 16) & 0xFFFF, bytes = new Uint8Array(rd.result);\n      loadBlocks([{ addr: a, bytes: Array.from(bytes) }], false, $('rfkFileMsg'));\n      return;\n    }\n    var s = String(rd.result);\n    if (ext === 'hex' || ext === 'ihx') {\n      var r = \/^\\s*:\/.test(s) ? parseIntelHex(s) : parseHex(s);\n      if (r.error) fileMsg(r.error, true); else loadBlocks(r.blocks, false, $('rfkFileMsg'));\n    } else if (ext === 'ptp' || ext === 'pap') {\n      var p = parsePtp(s);\n      if (p.error) fileMsg(p.error, true); else { hexEl.value = s.slice(0, 200000); loadBlocks(p.blocks, false, $('rfkFileMsg')); }\n    } else {\n      srcEl.value = s; saveSrc(); srcEl.closest('details').open = true;\n      fileMsg(tr('srcLoaded', { f: f.name }));\n    }\n  };\n  if (ext === 'bin') rd.readAsArrayBuffer(f); else rd.readAsText(f);\n});\n\n\/\/ ---- cassetta -------------------------------------------------------------------------------------\nvar tapeBox = root.querySelector('.tape');\nfunction tapeUI() {\n  var tp = m.tape, t = m.tapePos() \/ 1e6, len = m.tapeLength() \/ 1e6;\n  function mmss(x) { x = Math.floor(x); return ('0' + Math.floor(x \/ 60)).slice(-2) + ':' + ('0' + x % 60).slice(-2); }\n  $('rfkTapeCtr').textContent = mmss(t);\n  $('rfkTapeLen').textContent = len ? tr('tapeLen', { t: mmss(len) }) : tr('tapeEmpty');\n  tapeBox.classList.toggle('run', tp.mode !== 'stop');\n  tapeBox.classList.toggle('rec', tp.mode === 'rec');\n}\n$('rfkTapeRec').addEventListener('click', function () { m.tapeCmd('rec'); tapeUI(); });\n$('rfkTapePlay').addEventListener('click', function () { m.tapeCmd('play'); tapeUI(); });\n$('rfkTapeStop').addEventListener('click', function () { m.tapeCmd('stop'); tapeUI(); });\n$('rfkTapeRew').addEventListener('click', function () { m.tapeCmd('rewind'); tapeUI(); });\n$('rfkTapeWav').addEventListener('click', function () {\n  var e = m.tape.edges;\n  if (!e.length) { flash(tr('tapeEmpty')); return; }\n  var sr = 44100, n = Math.ceil((e[e.length - 1] + 200000) * sr \/ 1e6), data = new Int16Array(n), k = 0, lvl = 0;\n  for (var i = 0; i < n; i++) { var t = i * 1e6 \/ sr; while (k < e.length && e[ k] <= t) { lvl ^= 1; k++; } data[ i] = k ? (lvl ? 12000 : -12000) : 0; }\n  var hdr = new DataView(new ArrayBuffer(44)), w = function (o, s) { for (var j = 0; j < s.length; j++) hdr.setUint8(o + j, s.charCodeAt(j)); };\n  w(0, 'RIFF'); hdr.setUint32(4, 36 + n * 2, true); w(8, 'WAVE'); w(12, 'fmt '); hdr.setUint32(16, 16, true); hdr.setUint16(20, 1, true); hdr.setUint16(22, 1, true);\n  hdr.setUint32(24, sr, true); hdr.setUint32(28, sr * 2, true); hdr.setUint16(32, 2, true); hdr.setUint16(34, 16, true); w(36, 'data'); hdr.setUint32(40, n * 2, true);\n  download('kim1-cassetta.wav', new Blob([hdr.buffer, data.buffer], { type: 'audio\/wav' }));\n});\n$('rfkTapeOpen').addEventListener('click', function () { $('rfkTapeFile').click(); });\n$('rfkTapeFile').addEventListener('change', function () {\n  var f = this.files[0]; this.value = '';\n  if (!f) return;\n  if (f.size > 64 * 1048576) { flash(tr('tooBig')); return; }\n  var rd = new FileReader();\n  rd.onload = function () {\n    var Ctx = window.OfflineAudioContext || window.webkitOfflineAudioContext;\n    if (!Ctx) { flash(tr('wavBad')); return; }\n    var ctx = new Ctx(1, 2, 44100);\n    ctx.decodeAudioData(rd.result, function (buf) {\n      var d = buf.getChannelData(0), sr = buf.sampleRate, peak = 0, i;\n      for (i = 0; i < d.length; i++) if (Math.abs(d[ i]) > peak) peak = Math.abs(d[ i]);\n      if (peak < 0.01) { flash(tr('wavBad')); return; }\n      var th = peak * 0.15, st = 0, edges = [];\n      for (i = 0; i < d.length; i++) {\n        var nst = d[ i] > th ? 1 : d[ i] < -th ? -1 : st;\n        if (nst !== st && st !== 0) edges.push(i * 1e6 \/ sr);\n        st = nst;\n      }\n      m.tape.edges = edges; m.tapeCmd('rewind'); tapeUI();\n      flash(tr('wavOk', { n: edges.length }));\n    }, function () { flash(tr('wavBad')); });\n  };\n  rd.readAsArrayBuffer(f);\n});\n\n\/\/ ---- guida: passi con i tasti -----------------------------------------------------------------------\nvar steps = $('rfkSteps'), autoQ = [], autoT = 0;\nRF.STEPS[ L].forEach(function (st) {\n  var li = document.createElement('li'); li.innerHTML = st.text;\n  if (st.keys) {\n    var b = document.createElement('button'); b.type = 'button'; b.className = 'btn sm'; b.textContent = tr('doKeys');\n    b.addEventListener('click', function () { autoKeys(st.keys); });\n    li.appendChild(document.createTextNode(' ')); li.appendChild(b);\n  }\n  steps.appendChild(li);\n});\n\/\/ preme i tasti sulla tastiera del KIM uno alla volta, come farebbe una persona\nfunction autoKeys(seq) {\n  if (m.ttyMode) setMode('kb');\n  if (speed !== 1) { speed = 1; $('rfkSpeed').value = '1'; }\n  paused = false; updPause();\n  if (autoQ.down && autoQ.length) board.release(autoQ[0]);   \/\/ una sequenza ancora in corso: lascia il tasto\n  autoQ = seq.split(' ');\n  $('rfkEmu').scrollIntoView({ behavior: 'smooth', block: 'start' });\n  autoT = performance.now() + 500;\n}\nfunction autoTick(now) {\n  if (!autoQ.length || now < autoT) return;\n  var k = autoQ[0];\n  if (!autoQ.down) { board.press(k); autoQ.down = true; autoT = now + 110; }\n  else { board.release(k); autoQ.shift(); autoQ.down = false; autoT = now + 120; }\n}\n\n\/\/ ---- ciclo principale -----------------------------------------------------------------------------\nvar last = performance.now(), dbgT = 0, tapeT = 0, blink = 0;\nfunction frame(now) {\n  var dt = Math.min(now - last, 250); last = now;   \/\/ fino a 250 ms di recupero se il browser rallenta i fotogrammi\n  autoTick(now);\n  if (!paused) {\n    if (speed > 0) m.run(Math.round(dt * 1000 * speed));\n    else { var t0 = performance.now(); do { m.run(20000); } while (performance.now() - t0 < 14 && !m.stopReason); }\n    if (m.stopReason === 'bp') { paused = true; updPause(); flash(tr('bpHit', { a: H(m.cpu.pc, 4) })); }\n    else if (m.stopReason === 'halt') { flash(tr('halted', { o: H(m.cpu.badOp, 2), a: H(m.cpu.pc, 4) })); dbgDirty = true; }\n  }\n  board.render(m.display(seg));\n  renderPorts();\n  pumpAudio();\n  blink = (blink + 1) % 60;\n  term.render(document.activeElement === kbd && blink < 30, true);\n  if (dbgDirty || (!paused && now - dbgT > 150)) { renderDbg(); dbgT = now; dbgDirty = false; }\n  if (now - tapeT > 250) { tapeUI(); tapeT = now; }\n}\nfunction rafLoop(now) { frame(now); requestAnimationFrame(rafLoop); }\n\/\/ se il browser smette di dare fotogrammi (scheda o riquadro nascosto) l'emulatore va avanti con un timer\nsetInterval(function () { var now = performance.now(); if (now - last > 200) frame(now); }, 100);\nshowDesc();\nrequestAnimationFrame(rafLoop);\n})(globalThis.RFKim = globalThis.RFKim || {});\n<\/script>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>KIM-1 \u25b6 1103001200240048009600 MOSKIM-1 00:00 \u25cf \u25b6 \u25a0 \u23ee \u2b07 \ud83d\udcfc \u25b6 \u2b07 \u25b6 \u2b07 \u2b07 \ud83d\udcc2<\/p>","protected":false},"author":1,"featured_media":11631,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"footnotes":""},"categories":[],"tags":[477,215,602,603],"class_list":["post-11635","page","type-page","status-publish","has-post-thumbnail","hentry","tag-477","tag-emulatore","tag-kim-1","tag-mos-technology"],"_links":{"self":[{"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/pages\/11635","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/comments?post=11635"}],"version-history":[{"count":4,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/pages\/11635\/revisions"}],"predecessor-version":[{"id":11639,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/pages\/11635\/revisions\/11639"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/media\/11631"}],"wp:attachment":[{"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/media?parent=11635"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/categories?post=11635"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/tags?post=11635"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}