{"id":11562,"date":"2026-11-04T20:18:33","date_gmt":"2026-11-04T19:18:33","guid":{"rendered":"https:\/\/retrofixer.it\/imsai-8080\/"},"modified":"2026-11-05T14:02:04","modified_gmt":"2026-11-05T13:02:04","slug":"imsai-8080","status":"publish","type":"page","link":"https:\/\/retrofixer.it\/en\/imsai-8080\/","title":{"rendered":"IMSAI 8080"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"11562\" class=\"elementor elementor-11562\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-2dd1dbe e-con-full e-flex e-con e-parent\" data-id=\"2dd1dbe\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-415f739 elementor-widget elementor-widget-html\" data-id=\"415f739\" 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.rfim{--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.rfim *,.rfim *::before,.rfim *::after{box-sizing:border-box}\n.rfim h2,.rfim h3,.rfim h4,.rfim p,.rfim ul,.rfim ol,.rfim figure{margin:0;padding:0}\n.rfim h2,.rfim h3,.rfim h4{font-family:'Inter',system-ui,sans-serif!important;letter-spacing:0;text-transform:none;font-weight:700;color:var(--text)}\n.rfim a{color:var(--cya)}\n.rfim button,.rfim input,.rfim select,.rfim textarea{font:inherit;color:inherit}\n.rfim button{cursor:pointer}\n.rfim [hidden]{display:none!important}\n.rfim 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.rfim .wrap{max-width:1400px;margin:0 auto;padding:0 16px}\n.post-views.post-11562{background:#0f0c24;color:#aaa5d8;margin:0;padding:0 16px 8px;font-size:13px}\n\n\/* intestazione *\/\n.rfim .head{margin-bottom:34px}\n.rfim .kicker{font-family:'VT323',monospace;font-size:21px;color:var(--c64l);margin-bottom:6px}\n.rfim .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.rfim .lead{color:var(--muted);max-width:920px;font-size:17px}\n.rfim .lead b{color:var(--text)}\n.rfim .toc{display:flex;flex-wrap:wrap;gap:8px;margin-top:18px}\n.rfim .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.rfim .toc a:hover{background:var(--line)}\n\n\/* sezioni *\/\n.rfim .sec{margin:0 0 46px;scroll-margin-top:90px}\n.rfim .sec>h3,.rfim .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.rfim .sec h4{font-size:17px;margin:26px 0 10px;color:var(--yel)}\n.rfim .intro{color:var(--muted);max-width:960px;margin-bottom:18px}\n.rfim .prose p{margin-bottom:12px}\n.rfim .prose p:last-child{margin-bottom:0}\n.rfim .prose b{color:#fff}\n.rfim .prose ul{margin:0 0 12px 20px}\n.rfim .box{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:18px 20px;margin-top:22px}\n.rfim .two{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);gap:28px;align-items:start;margin-bottom:26px}\n.rfim .two.rev{grid-template-columns:minmax(0,1fr) minmax(0,1.15fr)}\n.rfim .two.rev .photo{order:-1}\n.rfim .photo{background:var(--panel);border:1px solid var(--line);border-radius:12px;overflow:hidden}\n.rfim .photo img{display:block;width:100%;height:auto;background:#0a0820}\n.rfim .photo figcaption{padding:10px 14px;font-size:13.5px;color:var(--muted)}\n.rfim .photo .cred{display:block;font-size:12px;opacity:.85;margin-top:2px}\n.rfim .specs,.rfim .ref{width:100%;border-collapse:collapse;font-size:15px}\n.rfim .specs th,.rfim .specs td,.rfim .ref th,.rfim .ref td{text-align:left;vertical-align:top;padding:7px 10px;border-bottom:1px solid var(--line)}\n.rfim .specs th,.rfim .ref th{color:var(--muted);font-weight:600;white-space:nowrap;width:1%}\n.rfim .ref td:first-child{font-family:'VT323',monospace;font-size:19px;color:var(--yel);white-space:nowrap;width:1%}\n\n\/* progetti *\/\n.rfim .cgroup{margin-bottom:22px}\n.rfim .cgroup h4{margin-top:0}\n.rfim .clist{display:grid;grid-template-columns:repeat(auto-fill,minmax(290px,1fr));gap:12px}\n.rfim .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.rfim .card:hover{border-color:var(--cya);transform:translateY(-2px)}\n.rfim .card b{font-size:16px}\n.rfim .ctag{font-family:'VT323',monospace;font-size:17px;color:var(--grn);line-height:1.1}\n.rfim .cdesc{font-size:14px;color:var(--muted)}\n.rfim .curl{font-size:12.5px;color:var(--cya);margin-top:auto;padding-top:4px;word-break:break-all}\n\n\/* barra comandi *\/\n.rfim .bar{display:flex;flex-wrap:wrap;align-items:flex-end;gap:10px;margin:10px 0}\n.rfim .bar .sp{flex:1}\n.rfim .fld{display:flex;flex-direction:column;gap:3px;font-size:12.5px;color:var(--muted)}\n.rfim select,.rfim 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.rfim select:focus,.rfim input[type=text]:focus{border-color:var(--c64l)}\n.rfim select option{background:var(--bg2)}\n.rfim .hex4{width:78px;font-family:ui-monospace,Consolas,monospace;text-transform:uppercase}\n.rfim .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.rfim .btn:hover{background:var(--line);color:#fff}\n.rfim .btn.pri{background:var(--grn);border-color:var(--grn);color:#0f0c24;font-weight:700}\n.rfim .btn.pri:hover{background:#7be27f;color:#0f0c24}\n.rfim .btn.sm{padding:3px 9px;font-size:13px;border-radius:6px}\n.rfim .btn .i{font-size:13px;line-height:1}\n.rfim label.ck{display:inline-flex;align-items:center;gap:6px;font-size:13.5px;color:var(--muted);cursor:pointer}\n.rfim label.ck input{accent-color:var(--grn);width:15px;height:15px}\n.rfim .pdesc{min-height:26px;color:var(--muted);font-size:15px;margin:2px 0 14px;max-width:1100px}\n.rfim .pdesc b{color:var(--yel)}\n.rfim .hint{font-size:13.5px;color:var(--muted);margin:8px 0 18px}\n.rfim .hint b{color:var(--text)}\n\n\/* pannello frontale *\/\n.rfim .panelbox{border-radius:14px;overflow-x:auto;box-shadow:0 14px 40px rgba(0,0,0,.55)}\n.rfim .imsai-panel{display:block;width:100%;min-width:780px;height:auto;user-select:none;-webkit-user-select:none;touch-action:manipulation}\n.rfim .imsai-panel .cabinet{fill:#2f78ab}\n.rfim .imsai-panel .glass{stroke:#17181a;stroke-width:2}\n.rfim .imsai-panel .bezel{fill:#4a4d4f}\n.rfim .imsai-panel .sheen{fill:rgba(255,255,255,.035);pointer-events:none}\n.rfim .imsai-panel .ledwin{fill:#3b3c41;stroke:#1d1e21;stroke-width:1}\n.rfim .imsai-panel text{font-family:'Inter',Arial,sans-serif;fill:#e6e3d6}\n.rfim .imsai-panel .num{font-size:11px}\n.rfim .imsai-panel .num.sm{font-size:9.5px;letter-spacing:.02em}\n.rfim .imsai-panel .tiny{font-size:9px;fill:#cfcbbd}\n.rfim .imsai-panel .side{font-size:9.5px;font-weight:600;letter-spacing:.03em}\n.rfim .imsai-panel .grp{font-size:10.5px;font-weight:700;letter-spacing:.06em}\n.rfim .imsai-panel .cl{font-size:9px;font-weight:600;letter-spacing:.02em}\n.rfim .imsai-panel .rule,.rfim .imsai-panel .ctline,.rfim .imsai-panel .over,.rfim .imsai-panel .logoline{stroke:#e6e3d6;stroke-width:2}\n.rfim .imsai-panel .over{stroke-width:1}\n.rfim .imsai-panel .logo{font-size:62px;font-weight:700;letter-spacing:.02em;fill:#e8e2a8}\n.rfim .imsai-panel .logoline{stroke:#e8e2a8;stroke-width:1.5}\n.rfim .imsai-panel .logo2{font-size:9.5px;fill:#e8e2a8;letter-spacing:.04em}\n.rfim .imsai-panel .credit{font-size:11px;fill:#9fa3a6}\n.rfim .led-lens{fill:#331513;stroke:#140807;stroke-width:.8}\n.rfim .led-lens.on{fill:url(#imsaiLedOn)}\n.rfim .led-glow{fill:url(#imsaiGlow);opacity:0;pointer-events:none}\n.rfim .paddle{cursor:pointer;outline:none}\n.rfim .paddle .slot{fill:#1d1f21;stroke:#111;stroke-width:1}\n.rfim .paddle .pad{stroke:rgba(0,0,0,.5);stroke-width:1}\n.rfim .paddle .pad.red{fill:#b8371f}\n.rfim .paddle .pad.blue{fill:#1f4fb0}\n.rfim .paddle .face{pointer-events:none}\n.rfim .paddle .face.red{fill:#f0603a}\n.rfim .paddle .face.blue{fill:#4f84f0}\n.rfim .paddle .hit{fill:transparent}\n.rfim .paddle:hover .pad,.rfim .paddle:hover .face{filter:brightness(1.12)}\n.rfim .paddle:focus-visible .slot{stroke:#ffd23f;stroke-width:3}\n\n\/* terminale *\/\n.rfim .row2{display:grid;grid-template-columns:minmax(0,1fr) 330px;gap:18px;align-items:start}\n.rfim .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.rfim .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.rfim .crt canvas{display:block;width:100%;height:auto;filter:drop-shadow(0 0 3px rgba(93,255,138,.55))}\n.rfim .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.rfim .crt.focus{box-shadow:inset 0 0 40px rgba(0,0,0,.85),0 0 0 3px #3a7a4c}\n.rfim .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.rfim .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.rfim .termbar{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:9px}\n.rfim .termbar .tname{font-family:'VT323',monospace;font-size:18px;color:#b9b2c9;flex:1;letter-spacing:.04em}\n.rfim .termbar .btn{background:#34303d;border-color:#47414f}\n\n\/* registri *\/\n.rfim .dbg{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px 16px;font-family:'VT323',monospace;font-size:19px}\n.rfim .dbg h4{font-family:'Inter',sans-serif!important;font-size:14px;margin:0 0 8px;color:var(--muted)}\n.rfim .regs{display:grid;grid-template-columns:repeat(3,1fr);gap:4px 10px}\n.rfim .regs span{color:var(--text)}\n.rfim .regs i{font-style:normal;color:var(--c64l);margin-right:6px}\n.rfim .flags{display:flex;gap:6px;margin:10px 0;flex-wrap:wrap}\n.rfim .flags span{border:1px solid var(--line);border-radius:5px;padding:0 6px;color:#5d578f}\n.rfim .flags span.on{color:#0f0c24;background:var(--yel);border-color:var(--yel)}\n.rfim .flags .st{margin-left:auto;color:var(--grn);border-color:transparent}\n.rfim .dis{list-style:none;border-top:1px solid var(--line);padding-top:6px}\n.rfim .dis li{display:grid;grid-template-columns:48px 78px 1fr;gap:6px;line-height:1.2;color:var(--muted);white-space:nowrap}\n.rfim .dis li.pc{color:#0f0c24;background:var(--grn);border-radius:3px}\n.rfim .dis .ad{opacity:.8}\n.rfim .cyc{font-size:16px;color:#6f69a6;margin-top:8px}\n\n\/* strumenti *\/\n.rfim .tools{margin-top:22px;display:grid;gap:12px}\n.rfim .tool{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:12px 16px}\n.rfim .tool summary{cursor:pointer;font-weight:700;font-size:16px;color:var(--yel)}\n.rfim .tool .note{font-size:14px;color:var(--muted);margin:8px 0}\n.rfim .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.rfim .src:focus{border-color:var(--c64l)}\n.rfim .asmout{background:#0a0820;border:1px solid var(--line);border-radius:8px;padding:10px 12px;font-size:14px}\n.rfim .asmout.err{border-color:var(--red);color:#ffb3b3}\n.rfim .asmout b{display:block;margin-bottom:6px}\n.rfim .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.rfim .msg{font-size:14px;color:var(--grn);min-height:20px}\n.rfim .msg.err{color:var(--red)}\n\n\/* guida *\/\n.rfim .steps{margin:14px 0 18px 22px;display:grid;gap:8px;max-width:1000px}\n.rfim .steps li{padding-left:4px}\n.rfim .steps li b{color:#fff}\n.rfim .steps .setsw{margin-left:4px;font-family:'VT323',monospace;font-size:16px;padding:0 8px;vertical-align:1px}\n.rfim .fonti ul{margin-left:20px;color:var(--muted);font-size:14.5px;display:grid;gap:4px}\n\n@media (max-width:1050px){.rfim .row2{grid-template-columns:minmax(0,1fr)}}\n@media (max-width:820px){.rfim .two,.rfim .two.rev{grid-template-columns:minmax(0,1fr)}.rfim .two.rev .photo{order:0}}\n@media (max-width:600px){.rfim{padding-top:22px}.rfim .lead{font-size:16px}.rfim .termbox{padding:10px 10px 8px;border-radius:12px}.rfim .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.rfim select,.rfim select:hover,.rfim 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.rfim select option{background:#171338!important;color:#eceaff!important}\n.rfim input[type=text],.rfim 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.rfim textarea.src,.rfim 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.rfim textarea.kbd,.rfim textarea.kbd:focus{background:transparent!important;border:0!important;box-shadow:none!important;color:transparent!important;padding:0!important;margin:0!important}\n.rfim table,.rfim table tr,.rfim table td,.rfim table th{background:transparent!important;box-shadow:none!important}\n.rfim table{border:0!important;margin:0!important}\n.rfim .sec>h3,.rfim .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.rfim .sec h4{font-size:17px!important;line-height:1.4!important;color:var(--yel)!important;margin:26px 0 10px!important}\n.rfim .cgroup h4{margin-top:0!important}\n.rfim .dbg h4{font-family:'Inter',sans-serif!important;font-size:14px!important;margin:0 0 8px!important;color:var(--muted)!important}\n.rfim .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.rfim .btn,.rfim .btn:hover{box-shadow:none!important;text-decoration:none!important;margin:0!important}\n.rfim .card,.rfim .card:hover,.rfim .toc a{text-decoration:none!important}\n.rfim img{border:0!important;box-shadow:none!important;max-width:100%}\n.rfim ol.dis,.rfim ol.dis li{list-style:none!important;margin:0!important}\n.rfim pre,.rfim .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.rfim .bar select{width:auto!important;max-width:100%}\n<\/style>\n\n<div class=\"rfim\" data-no-translation>\n <div class=\"wrap\">\n\n  <header class=\"head\">\n   <p class=\"kicker\" data-t=\"kicker\"><\/p>\n   <h2>IMSAI 8080<\/h2>\n   <p class=\"lead\" data-th=\"lead\"><\/p>\n   <nav class=\"toc\" aria-label=\"Indice\">\n    <a href=\"#rfimStoria\" data-t=\"navStoria\"><\/a>\n    <a href=\"#rfimCostruire\" data-t=\"navCostruire\"><\/a>\n    <a href=\"#rfimEmu\" data-t=\"navEmu\"><\/a>\n    <a href=\"#rfimGuida\" data-t=\"navGuida\"><\/a>\n   <\/nav>\n  <\/header>\n\n  <section class=\"sec\" id=\"rfimStoria\">\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=\"rfimFoto1\" alt=\"\" loading=\"lazy\" decoding=\"async\">\n     <figcaption id=\"rfimFoto1c\"><\/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=\"rfimFoto2\" alt=\"\" loading=\"lazy\" decoding=\"async\">\n     <figcaption id=\"rfimFoto2c\"><\/figcaption>\n    <\/figure>\n   <\/div>\n   <div class=\"prose box\" data-th=\"pannelloSpiega\"><\/div>\n  <\/section>\n\n  <section class=\"sec\" id=\"rfimCostruire\">\n   <h3 data-t=\"costruireT\"><\/h3>\n   <p class=\"intro\" data-th=\"costruireIntro\"><\/p>\n   <div class=\"cards\" id=\"rfimCards\"><\/div>\n  <\/section>\n\n  <section class=\"sec emu\" id=\"rfimEmu\">\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=\"rfimProg\"><\/select><\/label>\n    <button class=\"btn pri\" id=\"rfimLoadRun\" type=\"button\"><span class=\"i\">\u25b6<\/span><span data-t=\"loadRun\"><\/span><\/button>\n    <button class=\"btn\" id=\"rfimLoadOnly\" type=\"button\" data-t=\"loadOnly\"><\/button>\n    <span class=\"sp\"><\/span>\n    <label class=\"fld\"><span data-t=\"speed\"><\/span><select id=\"rfimSpeed\">\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=\"baud\"><\/span><select id=\"rfimBaud\">\n     <option value=\"300\">300<\/option><option value=\"1200\">1200<\/option><option value=\"9600\" selected>9600<\/option><option value=\"0\" data-t=\"baud0\"><\/option><\/select><\/label>\n   <\/div>\n   <p class=\"pdesc\" id=\"rfimPDesc\"><\/p>\n\n   <div class=\"panelbox\" id=\"rfimPanel\"><\/div>\n   <p class=\"hint\" data-th=\"panelHint\"><\/p>\n\n   <div class=\"row2\">\n    <div class=\"termbox\">\n     <div class=\"crt\">\n      <canvas id=\"rfimTerm\" aria-label=\"Terminale video\"><\/canvas>\n      <textarea id=\"rfimKbd\" class=\"kbd\" autocapitalize=\"off\" autocomplete=\"off\" autocorrect=\"off\" spellcheck=\"false\" aria-label=\"Tastiera del terminale\"><\/textarea>\n      <div class=\"crtmsg\" id=\"rfimTermMsg\" data-t=\"termClick\"><\/div>\n     <\/div>\n     <div class=\"termbar\">\n      <span class=\"tname\" data-t=\"termName\"><\/span>\n      <label class=\"ck\"><input type=\"checkbox\" id=\"rfimCaps\" checked><span data-t=\"caps\"><\/span><\/label>\n      <button class=\"btn sm\" id=\"rfimTermClr\" type=\"button\" data-t=\"termClr\"><\/button>\n      <button class=\"btn sm\" id=\"rfimTermCopy\" 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=\"rfimRegs\"><\/div>\n     <div class=\"flags\" id=\"rfimFlags\"><\/div>\n     <ol class=\"dis\" id=\"rfimDis\"><\/ol>\n     <p class=\"cyc\" id=\"rfimCyc\"><\/p>\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=\"rfimSrc\" class=\"src\" spellcheck=\"false\" autocapitalize=\"off\" autocomplete=\"off\" rows=\"18\"><\/textarea>\n     <div class=\"bar\">\n      <button class=\"btn pri\" id=\"rfimAsmRun\" type=\"button\"><span class=\"i\">\u25b6<\/span><span data-t=\"asmRun\"><\/span><\/button>\n      <button class=\"btn\" id=\"rfimAsm\" type=\"button\" data-t=\"asmLoad\"><\/button>\n      <button class=\"btn\" id=\"rfimAsmSave\" type=\"button\"><span class=\"i\">\u2b07<\/span><span data-t=\"asmSave\"><\/span><\/button>\n     <\/div>\n     <div class=\"asmout\" id=\"rfimAsmOut\" hidden><\/div>\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=\"rfimFrom\" value=\"0000\" maxlength=\"4\" class=\"hex4\"><\/label>\n      <label class=\"fld\"><span data-t=\"to\"><\/span><input type=\"text\" id=\"rfimTo\" value=\"00FF\" maxlength=\"4\" class=\"hex4\"><\/label>\n      <button class=\"btn\" id=\"rfimSaveHex\" type=\"button\"><span class=\"i\">\u2b07<\/span><span data-t=\"saveHex\"><\/span><\/button>\n      <button class=\"btn\" id=\"rfimSaveBin\" type=\"button\"><span class=\"i\">\u2b07<\/span><span data-t=\"saveBin\"><\/span><\/button>\n     <\/div>\n     <div class=\"bar\">\n      <button class=\"btn\" id=\"rfimOpen\" type=\"button\"><span class=\"i\">\ud83d\udcc2<\/span><span data-t=\"openFile\"><\/span><\/button>\n      <input type=\"file\" id=\"rfimFile\" accept=\".hex,.ihx,.ihex,.bin,.com,.asm,.a80,.s,.txt\" hidden>\n      <span class=\"sp\"><\/span>\n      <button class=\"btn\" id=\"rfimSnapSave\" type=\"button\" data-t=\"snapSave\"><\/button>\n      <button class=\"btn\" id=\"rfimSnapLoad\" type=\"button\" data-t=\"snapLoad\"><\/button>\n     <\/div>\n     <p class=\"msg\" id=\"rfimFileMsg\"><\/p>\n    <\/details>\n   <\/div>\n  <\/section>\n\n  <section class=\"sec\" id=\"rfimGuida\">\n   <h3 data-t=\"guidaT\"><\/h3>\n   <div class=\"prose\" data-th=\"guidaIntro\"><\/div>\n   <ol class=\"steps\" id=\"rfimSteps\"><\/ol>\n   <div class=\"prose\" data-th=\"guidaDopo\"><\/div>\n   <h4 data-t=\"leveT\"><\/h4>\n   <table class=\"ref\"><tbody data-th=\"leve\"><\/tbody><\/table>\n   <h4 data-t=\"porteT\"><\/h4>\n   <table class=\"ref\"><tbody data-th=\"porte\"><\/tbody><\/table>\n  <\/section>\n\n  <section class=\"sec fonti\">\n   <h3 data-t=\"fontiT\"><\/h3>\n   <ul data-th=\"fonti\"><\/ul>\n  <\/section>\n <\/div>\n<\/div>\n\n<script>\n\/* RetroFixer \u00b7 IMSAI 8080 \u00b7 CPU Intel 8080\n * Emulazione completa del set di istruzioni (anche gli opcode non documentati, alias di NOP\/JMP\/CALL\/RET),\n * flag come sull'8080 originale (AC di ANA, DAA, CMP), tempi in stati di clock.\n * Ogni ciclo di bus (lettura, scrittura, I\/O, stack, fetch M1) pu\u00f2 essere contato negli istogrammi\n * usati dal pannello frontale per far brillare i LED di indirizzo, dati e stato in proporzione al tempo.\n *\/\n(function (RF) {\n'use strict';\n\n\/\/ Parola di stato dell'8080 (bit D0..D7)\nvar ST = { INTA: 0x01, WO: 0x02, STACK: 0x04, HLTA: 0x08, OUT: 0x10, M1: 0x20, INP: 0x40, MEMR: 0x80 };\nvar S_FETCH = 0xA2, S_READ = 0x82, S_WRITE = 0x00, S_SREAD = 0x86, S_SWRITE = 0x04,\n  S_IN = 0x42, S_OUT = 0x10, S_HALT = 0x8A, S_INTA = 0x23;\n\nvar PAR = new Uint8Array(256);\nfor (var i = 0; i < 256; i++) { var p = 0, v = i; while (v) { p ^= v & 1; v >>= 1; } PAR[i] = p ? 0 : 1; }\n\n\/\/ Stati di clock per opcode (per i salti\/chiamate condizionali: valore se NON eseguiti)\nvar CYC = [\n  4, 10, 7, 5, 5, 5, 7, 4, 4, 10, 7, 5, 5, 5, 7, 4,\n  4, 10, 7, 5, 5, 5, 7, 4, 4, 10, 7, 5, 5, 5, 7, 4,\n  4, 10, 16, 5, 5, 5, 7, 4, 4, 10, 16, 5, 5, 5, 7, 4,\n  4, 10, 13, 5, 10, 10, 10, 4, 4, 10, 13, 5, 5, 5, 7, 4,\n  5, 5, 5, 5, 5, 5, 7, 5, 5, 5, 5, 5, 5, 5, 7, 5,\n  5, 5, 5, 5, 5, 5, 7, 5, 5, 5, 5, 5, 5, 5, 7, 5,\n  5, 5, 5, 5, 5, 5, 7, 5, 5, 5, 5, 5, 5, 5, 7, 5,\n  7, 7, 7, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 5, 7, 5,\n  4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4,\n  4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4,\n  4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4,\n  4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4,\n  5, 10, 10, 10, 11, 11, 7, 11, 5, 10, 10, 10, 11, 17, 7, 11,\n  5, 10, 10, 10, 11, 11, 7, 11, 5, 10, 10, 10, 11, 17, 7, 11,\n  5, 10, 10, 18, 11, 11, 7, 11, 5, 5, 10, 4, 11, 17, 7, 11,\n  5, 10, 10, 4, 11, 11, 7, 11, 5, 5, 10, 4, 11, 17, 7, 11\n];\n\nfunction I8080() {\n  this.mem = new Uint8Array(65536);\n  this.prot = new Uint8Array(256);       \/\/ pagine da 256 byte protette (PROTECT del pannello)\n  this.io = { input: function () { return 0xFF; }, output: function () {} };\n  this.hAH = new Uint32Array(256); this.hAL = new Uint32Array(256);\n  this.hD = new Uint32Array(256); this.hS = new Uint32Array(256);\n  this.hN = 0;\n  this.acc = false;                      \/\/ conta i cicli di bus negli istogrammi\n  this.reset();\n  this.cycles = 0;\n}\nvar P = I8080.prototype;\n\nP.reset = function () {\n  this.a = this.b = this.c = this.d = this.e = this.h = this.l = 0;\n  this.fS = this.fZ = this.fA = this.fP = this.fC = 0;\n  this.sp = 0; this.pc = 0;\n  this.inte = 0; this.halted = false; this.eiDelay = 0;\n  this.intReq = -1;\n  this.lastA = 0; this.lastD = 0; this.lastS = S_FETCH;\n};\n\nP.clearHist = function () {\n  this.hAH.fill(0); this.hAL.fill(0); this.hD.fill(0); this.hS.fill(0); this.hN = 0;\n};\n\nP.bus = function (s, a, d) {\n  this.lastA = a; this.lastD = d; this.lastS = s;\n  if (this.acc) { this.hAH[a >> 8]++; this.hAL[a & 255]++; this.hD[d]++; this.hS[s]++; this.hN++; }\n};\n\n\/\/ ---- accessi alla memoria e all'I\/O -------------------------------------------------\nP.rd = function (a, s) { var d = this.mem[a]; this.bus(s, a, d); return d; };\nP.wr = function (a, d, s) {\n  this.bus(s, a, d);\n  if (!this.prot[a >> 8]) this.mem[a] = d;\n};\nP.fetch = function () { var v = this.rd(this.pc, S_READ); this.pc = (this.pc + 1) & 0xFFFF; return v; };\nP.fetch16 = function () { var l = this.fetch(); return l | (this.fetch() << 8); };\nP.push = function (v) {\n  this.sp = (this.sp - 1) & 0xFFFF; this.wr(this.sp, v >> 8, S_SWRITE);\n  this.sp = (this.sp - 1) & 0xFFFF; this.wr(this.sp, v & 255, S_SWRITE);\n};\nP.pop = function () {\n  var l = this.rd(this.sp, S_SREAD); this.sp = (this.sp + 1) & 0xFFFF;\n  var h = this.rd(this.sp, S_SREAD); this.sp = (this.sp + 1) & 0xFFFF;\n  return l | (h << 8);\n};\n\n\/\/ ---- registri ------------------------------------------------------------------------\nP.getR = function (r) {\n  switch (r) {\n    case 0: return this.b; case 1: return this.c; case 2: return this.d; case 3: return this.e;\n    case 4: return this.h; case 5: return this.l; case 6: return this.rd((this.h << 8) | this.l, S_READ);\n    default: return this.a;\n  }\n};\nP.setR = function (r, v) {\n  switch (r) {\n    case 0: this.b = v; break; case 1: this.c = v; break; case 2: this.d = v; break; case 3: this.e = v; break;\n    case 4: this.h = v; break; case 5: this.l = v; break; case 6: this.wr((this.h << 8) | this.l, v, S_WRITE); break;\n    default: this.a = v;\n  }\n};\nP.getRP = function (rp) {   \/\/ 0 BC, 1 DE, 2 HL, 3 SP\n  switch (rp) {\n    case 0: return (this.b << 8) | this.c; case 1: return (this.d << 8) | this.e;\n    case 2: return (this.h << 8) | this.l; default: return this.sp;\n  }\n};\nP.setRP = function (rp, v) {\n  v &= 0xFFFF;\n  switch (rp) {\n    case 0: this.b = v >> 8; this.c = v & 255; break; case 1: this.d = v >> 8; this.e = v & 255; break;\n    case 2: this.h = v >> 8; this.l = v & 255; break; default: this.sp = v;\n  }\n};\nP.getF = function () {\n  return (this.fS << 7) | (this.fZ << 6) | (this.fA << 4) | (this.fP << 2) | 0x02 | this.fC;\n};\nP.setF = function (f) {\n  this.fS = (f >> 7) & 1; this.fZ = (f >> 6) & 1; this.fA = (f >> 4) & 1; this.fP = (f >> 2) & 1; this.fC = f & 1;\n};\nP.szp = function (v) { this.fS = v >> 7; this.fZ = v === 0 ? 1 : 0; this.fP = PAR[v]; };\nP.cond = function (cc) {\n  switch (cc) {\n    case 0: return !this.fZ; case 1: return !!this.fZ; case 2: return !this.fC; case 3: return !!this.fC;\n    case 4: return !this.fP; case 5: return !!this.fP; case 6: return !this.fS; default: return !!this.fS;\n  }\n};\n\n\/\/ ---- ALU -------------------------------------------------------------------------------\nP.alu = function (op, v) {\n  var a = this.a, r, c;\n  switch (op) {\n    case 0: case 1:   \/\/ ADD, ADC\n      c = op === 1 ? this.fC : 0; r = a + v + c;\n      this.fA = ((a & 15) + (v & 15) + c) > 15 ? 1 : 0; this.fC = r > 255 ? 1 : 0; r &= 255; this.szp(r); this.a = r; break;\n    case 2: case 3: case 7:   \/\/ SUB, SBB, CMP\n      c = op === 3 ? this.fC : 0; r = a - v - c;\n      this.fA = ((a & 15) + (~v & 15) + (c ? 0 : 1)) > 15 ? 1 : 0; this.fC = r < 0 ? 1 : 0; r &= 255; this.szp(r);\n      if (op !== 7) this.a = r; break;\n    case 4:   \/\/ ANA: sull'8080 AC = OR dei bit 3 degli operandi\n      r = a & v; this.fA = ((a | v) & 8) ? 1 : 0; this.fC = 0; this.szp(r); this.a = r; break;\n    case 5: r = a ^ v; this.fA = 0; this.fC = 0; this.szp(r); this.a = r; break;\n    case 6: r = a | v; this.fA = 0; this.fC = 0; this.szp(r); this.a = r; break;\n  }\n};\nP.inr = function (v) { v = (v + 1) & 255; this.fA = (v & 15) === 0 ? 1 : 0; this.szp(v); return v; };\nP.dcr = function (v) { v = (v - 1) & 255; this.fA = (v & 15) !== 15 ? 1 : 0; this.szp(v); return v; };\nP.daa = function () {\n  var a = this.a, lo = a & 15, hi = a >> 4, corr = 0, cy = this.fC;\n  if (this.fA || lo > 9) corr |= 0x06;\n  if (this.fC || hi > 9 || (hi >= 9 && lo > 9)) { corr |= 0x60; cy = 1; }\n  this.alu(0, corr); this.fC = cy;\n};\n\n\/\/ ---- interruzioni ---------------------------------------------------------------------\n\/\/ opcode: istruzione posta sul bus durante INTA (di solito un RST n)\nP.interrupt = function (opcode) { this.intReq = opcode & 255; };\n\n\/\/ ---- esecuzione di una istruzione: restituisce gli stati di clock ---------------------\nP.step = function () {\n  if (this.intReq >= 0 && this.inte && !this.eiDelay) {\n    var iop = this.intReq; this.intReq = -1; this.inte = 0; this.halted = false;\n    this.bus(S_INTA, this.pc, iop);\n    return this.exec(iop, true);\n  }\n  if (this.eiDelay) this.eiDelay = 0;\n  if (this.halted) { this.bus(S_HALT, this.pc, this.lastD); return 4; }\n  var op = this.mem[this.pc];\n  this.bus(S_FETCH, this.pc, op);\n  this.pc = (this.pc + 1) & 0xFFFF;\n  return this.exec(op, false);\n};\n\nP.exec = function (op, isInt) {\n  var cyc = CYC[op], t, r, a;\n  if (op >= 0x40 && op < 0x80) {            \/\/ MOV \/ HLT\n    if (op === 0x76) { this.halted = true; this.bus(S_HALT, this.pc, this.lastD); }\n    else this.setR((op >> 3) & 7, this.getR(op & 7));\n    return this.add(cyc);\n  }\n  if (op >= 0x80 && op < 0xC0) { this.alu((op >> 3) & 7, this.getR(op & 7)); return this.add(cyc); }\n  switch (op) {\n    case 0x00: case 0x08: case 0x10: case 0x18: case 0x20: case 0x28: case 0x30: case 0x38: break;   \/\/ NOP\n    case 0x01: case 0x11: case 0x21: case 0x31: this.setRP(op >> 4, this.fetch16()); break;            \/\/ LXI\n    case 0x02: case 0x12: this.wr(this.getRP(op >> 4), this.a, S_WRITE); break;                         \/\/ STAX\n    case 0x0A: case 0x1A: this.a = this.rd(this.getRP(op >> 4), S_READ); break;                         \/\/ LDAX\n    case 0x03: case 0x13: case 0x23: case 0x33: this.setRP(op >> 4, this.getRP(op >> 4) + 1); break;  \/\/ INX\n    case 0x0B: case 0x1B: case 0x2B: case 0x3B: this.setRP(op >> 4, this.getRP(op >> 4) - 1); break;  \/\/ DCX\n    case 0x09: case 0x19: case 0x29: case 0x39:                                                         \/\/ DAD\n      r = this.getRP(2) + this.getRP(op >> 4); this.fC = r > 0xFFFF ? 1 : 0; this.setRP(2, r); break;\n    case 0x04: case 0x0C: case 0x14: case 0x1C: case 0x24: case 0x2C: case 0x34: case 0x3C:             \/\/ INR\n      t = (op >> 3) & 7; this.setR(t, this.inr(this.getR(t))); break;\n    case 0x05: case 0x0D: case 0x15: case 0x1D: case 0x25: case 0x2D: case 0x35: case 0x3D:             \/\/ DCR\n      t = (op >> 3) & 7; this.setR(t, this.dcr(this.getR(t))); break;\n    case 0x06: case 0x0E: case 0x16: case 0x1E: case 0x26: case 0x2E: case 0x36: case 0x3E:             \/\/ MVI\n      t = this.fetch(); this.setR((op >> 3) & 7, t); break;\n    case 0x07: this.fC = this.a >> 7; this.a = ((this.a << 1) | this.fC) & 255; break;                   \/\/ RLC\n    case 0x0F: this.fC = this.a & 1; this.a = (this.a >> 1) | (this.fC << 7); break;                      \/\/ RRC\n    case 0x17: t = this.fC; this.fC = this.a >> 7; this.a = ((this.a << 1) | t) & 255; break;             \/\/ RAL\n    case 0x1F: t = this.fC; this.fC = this.a & 1; this.a = (this.a >> 1) | (t << 7); break;               \/\/ RAR\n    case 0x22: a = this.fetch16(); this.wr(a, this.l, S_WRITE); this.wr((a + 1) & 0xFFFF, this.h, S_WRITE); break; \/\/ SHLD\n    case 0x2A: a = this.fetch16(); this.l = this.rd(a, S_READ); this.h = this.rd((a + 1) & 0xFFFF, S_READ); break; \/\/ LHLD\n    case 0x27: this.daa(); break;\n    case 0x2F: this.a ^= 255; break;                                                                       \/\/ CMA\n    case 0x32: this.wr(this.fetch16(), this.a, S_WRITE); break;                                            \/\/ STA\n    case 0x3A: this.a = this.rd(this.fetch16(), S_READ); break;                                            \/\/ LDA\n    case 0x37: this.fC = 1; break;                                                                         \/\/ STC\n    case 0x3F: this.fC ^= 1; break;                                                                        \/\/ CMC\n    case 0xC6: case 0xCE: case 0xD6: case 0xDE: case 0xE6: case 0xEE: case 0xF6: case 0xFE:               \/\/ ALU immediato\n      this.alu((op >> 3) & 7, this.fetch()); break;\n    case 0xC3: case 0xCB: this.pc = this.fetch16(); break;                                                 \/\/ JMP\n    case 0xC2: case 0xCA: case 0xD2: case 0xDA: case 0xE2: case 0xEA: case 0xF2: case 0xFA:               \/\/ Jcc\n      a = this.fetch16(); if (this.cond((op >> 3) & 7)) this.pc = a; break;\n    case 0xCD: case 0xDD: case 0xED: case 0xFD: a = this.fetch16(); this.push(this.pc); this.pc = a; break; \/\/ CALL\n    case 0xC4: case 0xCC: case 0xD4: case 0xDC: case 0xE4: case 0xEC: case 0xF4: case 0xFC:               \/\/ Ccc\n      a = this.fetch16(); if (this.cond((op >> 3) & 7)) { this.push(this.pc); this.pc = a; cyc = 17; } break;\n    case 0xC9: case 0xD9: this.pc = this.pop(); break;                                                    \/\/ RET\n    case 0xC0: case 0xC8: case 0xD0: case 0xD8: case 0xE0: case 0xE8: case 0xF0: case 0xF8:               \/\/ Rcc\n      if (this.cond((op >> 3) & 7)) { this.pc = this.pop(); cyc = 11; } break;\n    case 0xC7: case 0xCF: case 0xD7: case 0xDF: case 0xE7: case 0xEF: case 0xF7: case 0xFF:               \/\/ RST\n      this.push(this.pc); this.pc = op & 0x38; break;\n    case 0xC5: case 0xD5: case 0xE5: this.push(this.getRP((op >> 4) & 3)); break;                         \/\/ PUSH\n    case 0xF5: this.push((this.a << 8) | this.getF()); break;                                             \/\/ PUSH PSW\n    case 0xC1: case 0xD1: case 0xE1: this.setRP((op >> 4) & 3, this.pop()); break;                        \/\/ POP\n    case 0xF1: t = this.pop(); this.a = t >> 8; this.setF(t & 255); break;                                 \/\/ POP PSW\n    case 0xE3:                                                                                             \/\/ XTHL\n      t = this.rd(this.sp, S_SREAD) | (this.rd((this.sp + 1) & 0xFFFF, S_SREAD) << 8);\n      this.wr((this.sp + 1) & 0xFFFF, this.h, S_SWRITE); this.wr(this.sp, this.l, S_SWRITE); this.setRP(2, t); break;\n    case 0xF9: this.sp = this.getRP(2); break;                                                             \/\/ SPHL\n    case 0xE9: this.pc = this.getRP(2); break;                                                             \/\/ PCHL\n    case 0xEB: t = this.h; this.h = this.d; this.d = t; t = this.l; this.l = this.e; this.e = t; break;    \/\/ XCHG\n    case 0xF3: this.inte = 0; break;                                                                       \/\/ DI\n    case 0xFB: this.inte = 1; this.eiDelay = 1; break;                                                     \/\/ EI\n    case 0xDB: t = this.fetch(); r = this.io.input(t) & 255; this.bus(S_IN, (t << 8) | t, r); this.a = r; break; \/\/ IN\n    case 0xD3: t = this.fetch(); this.bus(S_OUT, (t << 8) | t, this.a); this.io.output(t, this.a); break;  \/\/ OUT\n  }\n  return this.add(cyc);\n};\nP.add = function (c) { this.cycles += c; return c; };\n\n\/\/ Somma, per ogni bit, quante volte \u00e8 stato a 1 negli ultimi cicli di bus: per i LED del pannello\nfunction bits(hist, n, out, off) {\n  for (var b = 0; b < n; b++) out[off + b] = 0;\n  for (var v = 1; v < 256; v++) {\n    var k = hist[v]; if (!k) continue;\n    for (var b2 = 0; b2 < n; b2++) if (v & (1 << b2)) out[off + b2] += k;\n  }\n}\nP.ledLevels = function () {\n  var n = this.hN, out = { addr: new Float32Array(16), data: new Float32Array(8), status: new Float32Array(8), n: n };\n  if (!n) return out;\n  var t = new Float64Array(16);\n  bits(this.hAL, 8, t, 0); bits(this.hAH, 8, t, 8);\n  for (var i = 0; i < 16; i++) out.addr[i] = t[i] \/ n;\n  bits(this.hD, 8, t, 0); for (i = 0; i < 8; i++) out.data[i] = t[i] \/ n;\n  bits(this.hS, 8, t, 0); for (i = 0; i < 8; i++) out.status[i] = t[i] \/ n;\n  return out;\n};\n\nRF.I8080 = I8080;\nRF.ST = ST;\nRF.STATUS = { FETCH: S_FETCH, READ: S_READ, WRITE: S_WRITE, SREAD: S_SREAD, SWRITE: S_SWRITE, IN: S_IN, OUT: S_OUT, HALT: S_HALT };\n})(window.RFImsai = window.RFImsai || {});\n\n\/* RetroFixer \u00b7 IMSAI 8080 \u00b7 assembler e disassembler Intel 8080\n * Sintassi Intel: etichette (con o senza \":\"), ORG, EQU, SET, DB, DW, DS, END, commenti con \";\".\n * Numeri: 123, 0FFH, 377Q \/ 377O, 1010B, 0x1F, $1F, 'A'. Espressioni: + - * \/ MOD AND OR XOR NOT SHL SHR\n * HIGH LOW, parentesi, $ = indirizzo corrente.\n *\/\n(function (RF) {\n'use strict';\n\nvar REG = { B: 0, C: 1, D: 2, E: 3, H: 4, L: 5, M: 6, A: 7 };\nvar RP = { B: 0, D: 1, H: 2, SP: 3 };\nvar RPS = { B: 0, D: 1, H: 2, PSW: 3 };\nvar ALU = { ADD: 0x80, ADC: 0x88, SUB: 0x90, SBB: 0x98, ANA: 0xA0, XRA: 0xA8, ORA: 0xB0, CMP: 0xB8 };\nvar ALUI = { ADI: 0xC6, ACI: 0xCE, SUI: 0xD6, SBI: 0xDE, ANI: 0xE6, XRI: 0xEE, ORI: 0xF6, CPI: 0xFE };\nvar IMPL = { NOP: 0x00, RLC: 0x07, RRC: 0x0F, RAL: 0x17, RAR: 0x1F, DAA: 0x27, CMA: 0x2F, STC: 0x37, CMC: 0x3F,\n  HLT: 0x76, RET: 0xC9, XCHG: 0xEB, XTHL: 0xE3, SPHL: 0xF9, PCHL: 0xE9, DI: 0xF3, EI: 0xFB,\n  RNZ: 0xC0, RZ: 0xC8, RNC: 0xD0, RC: 0xD8, RPO: 0xE0, RPE: 0xE8, RP: 0xF0, RM: 0xF8 };\nvar ADDR16 = { JMP: 0xC3, JNZ: 0xC2, JZ: 0xCA, JNC: 0xD2, JC: 0xDA, JPO: 0xE2, JPE: 0xEA, JP: 0xF2, JM: 0xFA,\n  CALL: 0xCD, CNZ: 0xC4, CZ: 0xCC, CNC: 0xD4, CC: 0xDC, CPO: 0xE4, CPE: 0xEC, CP: 0xF4, CM: 0xFC,\n  LDA: 0x3A, STA: 0x32, LHLD: 0x2A, SHLD: 0x22 };\n\nfunction AsmError(msg) { this.message = msg; }\n\n\/\/ ---- espressioni -------------------------------------------------------------------------\nfunction tokenize(s) {\n  var t = [], i = 0, m;\n  while (i < s.length) {\n    var c = s[i];\n    if (\/\\s\/.test(c)) { i++; continue; }\n    if (c === \"'\" || c === '\"') {\n      var j = s.indexOf(c, i + 1); if (j < 0) throw new AsmError('stringa non chiusa');\n      t.push({ k: 'str', v: s.slice(i + 1, j) }); i = j + 1; continue;\n    }\n    if ((m = \/^(0x[0-9a-f]+|\\$[0-9a-f]+|[0-9][0-9a-f]*h|[01]+b(?![0-9a-z_])|[0-7]+[oq]|[0-9]+d?)(?![0-9a-z_])\/i.exec(s.slice(i)))) {\n      t.push({ k: 'num', v: parseNum(m[1]) }); i += m[1].length; continue;\n    }\n    if ((m = \/^[a-z_?@.][a-z0-9_?@.]*\/i.exec(s.slice(i)))) { t.push({ k: 'id', v: m[0].toUpperCase() }); i += m[0].length; continue; }\n    if ((m = \/^(<<|>>)\/.exec(s.slice(i)))) { t.push({ k: 'op', v: m[0] }); i += 2; continue; }\n    if ('+-*\/()&|^~%$'.indexOf(c) >= 0) { t.push({ k: 'op', v: c }); i++; continue; }\n    throw new AsmError('carattere inatteso \"' + c + '\"');\n  }\n  return t;\n}\nfunction parseNum(s) {\n  var u = s.toUpperCase(), v;\n  if (\/^0X\/.test(u)) v = parseInt(u.slice(2), 16);\n  else if (u[0] === '$') v = parseInt(u.slice(1), 16);\n  else if (\/H$\/.test(u)) v = parseInt(u.slice(0, -1), 16);\n  else if (\/[OQ]$\/.test(u)) v = parseInt(u.slice(0, -1), 8);\n  else if (\/B$\/.test(u)) v = parseInt(u.slice(0, -1), 2);\n  else v = parseInt(u.replace(\/D$\/, ''), 10);\n  if (isNaN(v)) throw new AsmError('numero non valido \"' + s + '\"');\n  return v;\n}\n\nfunction evalExpr(src, ctx) {\n  var t = tokenize(src), p = 0;\n  function peek() { return t[p]; }\n  function isOp(v) { var x = t[p]; return x && ((x.k === 'op' && x.v === v) || (x.k === 'id' && x.v === v)); }\n  var depth = 0;\n  function prim() {\n    if (++depth > 64) throw new AsmError('espressione troppo annidata');\n    try { return prim1(); } finally { depth--; }\n  }\n  function prim1() {\n    var x = t[p++];\n    if (!x) throw new AsmError('espressione incompleta');\n    if (x.k === 'num') return x.v;\n    if (x.k === 'str') {\n      if (x.v.length === 1) return x.v.charCodeAt(0);\n      if (x.v.length === 2) return (x.v.charCodeAt(0) << 8) | x.v.charCodeAt(1);\n      throw new AsmError('stringa troppo lunga in un\\'espressione');\n    }\n    if (x.k === 'op' && x.v === '(') { var v = or(); if (!isOp(')')) throw new AsmError('manca \")\"'); p++; return v; }\n    if (x.k === 'op' && x.v === '$') return ctx.pc;\n    if (x.k === 'op' && x.v === '-') return -prim();\n    if (x.k === 'op' && x.v === '+') return prim();\n    if ((x.k === 'op' && x.v === '~') || (x.k === 'id' && x.v === 'NOT')) return ~prim();\n    if (x.k === 'id' && x.v === 'HIGH') return (prim() >> 8) & 255;\n    if (x.k === 'id' && x.v === 'LOW') return prim() & 255;\n    if (x.k === 'id') {\n      if (Object.prototype.hasOwnProperty.call(ctx.sym, x.v)) return ctx.sym[x.v];\n      if (ctx.pass === 1) { ctx.undef = true; return 0; }\n      throw new AsmError('simbolo non definito \"' + x.v + '\"');\n    }\n    throw new AsmError('\"' + x.v + '\" inatteso');\n  }\n  function mul() {\n    var v = prim();\n    for (;;) {\n      if (isOp('*')) { p++; v *= prim(); }\n      else if (isOp('\/')) { p++; var d = prim(); if (!d) throw new AsmError('divisione per zero'); v = Math.trunc(v \/ d); }\n      else if (isOp('%') || isOp('MOD')) { p++; var d2 = prim(); if (!d2) throw new AsmError('divisione per zero'); v %= d2; }\n      else if (isOp('<<') || isOp('SHL')) { p++; v <<= prim(); }\n      else if (isOp('>>') || isOp('SHR')) { p++; v >>>= prim(); }\n      else return v;\n    }\n  }\n  function sum() {\n    var v = mul();\n    for (;;) { if (isOp('+')) { p++; v += mul(); } else if (isOp('-')) { p++; v -= mul(); } else return v; }\n  }\n  function and() { var v = sum(); while (isOp('&') || isOp('AND')) { p++; v &= sum(); } return v; }\n  function or() {\n    var v = and();\n    for (;;) {\n      if (isOp('|') || isOp('OR')) { p++; v |= and(); }\n      else if (isOp('^') || isOp('XOR')) { p++; v ^= and(); }\n      else return v;\n    }\n  }\n  var v = or();\n  if (peek()) throw new AsmError('\"' + peek().v + '\" inatteso');\n  return v;\n}\n\n\/\/ separa gli operandi sulle virgole, rispettando le stringhe\nfunction splitOps(s) {\n  var out = [], cur = '', q = null;\n  for (var i = 0; i < s.length; i++) {\n    var c = s[i];\n    if (q) { cur += c; if (c === q) q = null; continue; }\n    if (c === \"'\" || c === '\"') { q = c; cur += c; continue; }\n    if (c === ',') { out.push(cur.trim()); cur = ''; continue; }\n    cur += c;\n  }\n  if (cur.trim() !== '' || out.length) out.push(cur.trim());\n  return out;\n}\nfunction stripComment(s) {\n  var q = null;\n  for (var i = 0; i < s.length; i++) {\n    var c = s[i];\n    if (q) { if (c === q) q = null; continue; }\n    if (c === \"'\" || c === '\"') q = c;\n    else if (c === ';') return s.slice(0, i);\n  }\n  return s;\n}\n\n\/\/ ---- assemblaggio ------------------------------------------------------------------------\nfunction assemble(src) {\n  var lines = src.replace(\/\\r\/g, '').split('\\n');\n  if (src.length > 1000000) return { mem: {}, list: [], errors: [{ line: 1, msg: 'sorgente troppo grande (massimo 1 MB)' }], sym: {}, start: 0, min: 0, max: -1, size: 0 };\n  var sym = Object.create(null), out, list, errors, start;\n  for (var pass = 1; pass <= 2; pass++) {\n    PASS1 = pass === 1;\n    var pc = 0, ended = false; out = {}; list = []; errors = []; start = null;\n    for (var ln = 0; ln < lines.length; ln++) {\n      var raw = lines[ln], item = { line: ln + 1, addr: pc, bytes: [], src: raw };\n      if (ended) { list.push(item); continue; }\n      try {\n        var s0 = stripComment(raw).replace(\/\\s+$\/, ''), s = s0.trim(), label = null, m;\n        \/\/ etichetta: \"NOME:\", \"NOME EQU x\", \"NOME = x\", oppure una parola in prima colonna che non \u00e8 un'istruzione\n        if ((m = \/^([a-z_?@.][a-z0-9_?@.]*):\/i.exec(s))) { label = m[1].toUpperCase(); s = s.slice(m[0].length).trim(); }\n        else if ((m = \/^([a-z_?@.][a-z0-9_?@.]*)\\s+(EQU|SET)\\b\/i.exec(s)) || (m = \/^([a-z_?@.][a-z0-9_?@.]*)\\s*=\/i.exec(s))) {\n          label = m[1].toUpperCase(); s = s.slice(m[1].length).trim();\n        } else if (\/^[a-z_?@.]\/i.test(s0) && (m = \/^([a-z_?@.][a-z0-9_?@.]*)(?=\\s|$)\/i.exec(s)) && !isMnemonic(m[1])) {\n          label = m[1].toUpperCase(); s = s.slice(m[0].length).trim();\n        }\n        var mm = \/^([a-z]+|=)\\s*(.*)$\/i.exec(s), mn = mm ? mm[1].toUpperCase() : '', ops = mm ? splitOps(mm[2]) : [];\n        if (mn === 'EQU' || mn === 'SET' || mn === '=') {\n          if (!label) throw new AsmError(mn + ' senza nome');\n          var ctxE = { sym: sym, pc: pc, pass: pass };\n          sym[label] = evalExpr(mm[2], ctxE) & 0xFFFF;\n          item.equ = sym[label]; list.push(item); continue;\n        }\n        if (label) {\n          if (pass === 1 && Object.prototype.hasOwnProperty.call(sym, label)) throw new AsmError('etichetta \"' + label + '\" definita due volte');\n          sym[label] = pc;\n        }\n        var ctx = { sym: sym, pc: pc, pass: pass };\n        var ev = function (e) { return evalExpr(e, ctx); };\n        var bytes = [];\n        if (!mn) { list.push(item); continue; }\n        if (mn === 'ORG') { pc = ev(ops[0]) & 0xFFFF; item.addr = pc; list.push(item); continue; }\n        if (mn === 'END') { if (ops[0]) start = ev(ops[0]) & 0xFFFF; ended = true; list.push(item); continue; }\n        if (mn === 'DS') { pc = (pc + ev(ops[0])) & 0xFFFF; list.push(item); continue; }\n        if (mn === 'DB') {\n          ops.forEach(function (o) {\n            var sm = \/^(['\"])(.*)\\1$\/.exec(o);\n            if (sm && sm[2].length !== 1) { for (var k = 0; k < sm[2].length; k++) bytes.push(sm[2].charCodeAt(k) & 255); }\n            else bytes.push(byte8(ev(o)));\n          });\n        } else if (mn === 'DW') {\n          ops.forEach(function (o) { var v = ev(o) & 0xFFFF; bytes.push(v & 255, v >> 8); });\n        } else bytes = encode(mn, ops, ev);\n        item.bytes = bytes;\n        for (var b = 0; b < bytes.length; b++) out[(pc + b) & 0xFFFF] = bytes[b];\n        pc = (pc + bytes.length) & 0xFFFF;\n      } catch (e) {\n        if (!(e instanceof AsmError)) throw e;\n        if (pass === 2) errors.push({ line: ln + 1, msg: e.message });\n      }\n      list.push(item);\n    }\n  }\n  var addrs = Object.keys(out).map(Number).sort(function (x, y) { return x - y; });\n  return { mem: out, list: list, errors: errors, sym: sym, start: start !== null ? start : (addrs.length ? addrs[0] : 0),\n    min: addrs.length ? addrs[0] : 0, max: addrs.length ? addrs[addrs.length - 1] : -1, size: addrs.length };\n}\n\/\/ nella prima passata le etichette in avanti valgono 0: il controllo si fa solo nella seconda\nvar PASS1 = false;\nfunction byte8(v) {\n  if (PASS1) return v & 255;\n  if (v > 255 || v < -128) throw new AsmError('valore ' + v + ' troppo grande per un byte');\n  return v & 255;\n}\nfunction isMnemonic(w) {\n  w = w.toUpperCase();\n  return !!(IMPL[w] !== undefined || ADDR16[w] !== undefined || ALU[w] !== undefined || ALUI[w] !== undefined ||\n    \/^(MOV|MVI|LXI|INR|DCR|INX|DCX|DAD|LDAX|STAX|PUSH|POP|RST|IN|OUT|ORG|EQU|SET|DB|DW|DS|END)$\/.test(w));\n}\nfunction reg(o) {\n  var r = REG[(o || '').toUpperCase()];\n  if (r === undefined) throw new AsmError('registro non valido \"' + (o || '') + '\" (attesi A B C D E H L M)');\n  return r;\n}\nfunction need(ops, n, mn) { if (ops.length !== n) throw new AsmError(mn + ' vuole ' + n + (n === 1 ? ' operando' : ' operandi')); }\n\nfunction encode(mn, ops, ev) {\n  var v, r;\n  if (IMPL[mn] !== undefined) { need(ops, 0, mn); return [IMPL[mn]]; }\n  if (ADDR16[mn] !== undefined) { need(ops, 1, mn); v = ev(ops[0]) & 0xFFFF; return [ADDR16[mn], v & 255, v >> 8]; }\n  if (ALU[mn] !== undefined) { need(ops, 1, mn); return [ALU[mn] | reg(ops[0])]; }\n  if (ALUI[mn] !== undefined) { need(ops, 1, mn); return [ALUI[mn], byte8(ev(ops[0]))]; }\n  switch (mn) {\n    case 'MOV':\n      need(ops, 2, mn); var d = reg(ops[0]), s = reg(ops[1]);\n      if (d === 6 && s === 6) throw new AsmError('MOV M,M non esiste (\u00e8 HLT)');\n      return [0x40 | (d << 3) | s];\n    case 'MVI': need(ops, 2, mn); return [0x06 | (reg(ops[0]) << 3), byte8(ev(ops[1]))];\n    case 'INR': need(ops, 1, mn); return [0x04 | (reg(ops[0]) << 3)];\n    case 'DCR': need(ops, 1, mn); return [0x05 | (reg(ops[0]) << 3)];\n    case 'LXI': need(ops, 2, mn); v = ev(ops[1]) & 0xFFFF; return [0x01 | (rp(ops[0], RP) << 4), v & 255, v >> 8];\n    case 'INX': need(ops, 1, mn); return [0x03 | (rp(ops[0], RP) << 4)];\n    case 'DCX': need(ops, 1, mn); return [0x0B | (rp(ops[0], RP) << 4)];\n    case 'DAD': need(ops, 1, mn); return [0x09 | (rp(ops[0], RP) << 4)];\n    case 'LDAX': need(ops, 1, mn); r = rp(ops[0], RP); if (r > 1) throw new AsmError('LDAX vuole B o D'); return [0x0A | (r << 4)];\n    case 'STAX': need(ops, 1, mn); r = rp(ops[0], RP); if (r > 1) throw new AsmError('STAX vuole B o D'); return [0x02 | (r << 4)];\n    case 'PUSH': need(ops, 1, mn); return [0xC5 | (rp(ops[0], RPS) << 4)];\n    case 'POP': need(ops, 1, mn); return [0xC1 | (rp(ops[0], RPS) << 4)];\n    case 'RST': need(ops, 1, mn); v = ev(ops[0]); if (v < 0 || v > 7) throw new AsmError('RST vuole un numero da 0 a 7'); return [0xC7 | (v << 3)];\n    case 'IN': need(ops, 1, mn); return [0xDB, byte8(ev(ops[0]))];\n    case 'OUT': need(ops, 1, mn); return [0xD3, byte8(ev(ops[0]))];\n  }\n  throw new AsmError('istruzione sconosciuta \"' + mn + '\"');\n}\nfunction rp(o, tab) {\n  var r = tab[(o || '').toUpperCase()];\n  if (r === undefined) throw new AsmError('coppia di registri non valida \"' + (o || '') + '\" (attese ' + Object.keys(tab).join(' ') + ')');\n  return r;\n}\n\n\/\/ ---- disassembler --------------------------------------------------------------------------\nvar RN = ['B', 'C', 'D', 'E', 'H', 'L', 'M', 'A'], RPN = ['B', 'D', 'H', 'SP'], CCN = ['NZ', 'Z', 'NC', 'C', 'PO', 'PE', 'P', 'M'];\nvar ALUN = ['ADD', 'ADC', 'SUB', 'SBB', 'ANA', 'XRA', 'ORA', 'CMP'], ALUIN = ['ADI', 'ACI', 'SUI', 'SBI', 'ANI', 'XRI', 'ORI', 'CPI'];\nvar DIS = [];\n(function () {\n  for (var op = 0; op < 256; op++) {\n    var x = op >> 6, y = (op >> 3) & 7, z = op & 7, p = y >> 1, q = y & 1, d;\n    if (x === 1) d = op === 0x76 ? ['HLT', 1] : ['MOV ' + RN[y] + ',' + RN[z], 1];\n    else if (x === 2) d = [ALUN[y] + ' ' + RN[z], 1];\n    else if (x === 0) {\n      if (z === 0) d = [y ? '*NOP' : 'NOP', 1];\n      else if (z === 1) d = q ? ['DAD ' + RPN[p], 1] : ['LXI ' + RPN[p] + ',#', 3];\n      else if (z === 2) d = [['STAX B', 1], ['LDAX B', 1], ['STAX D', 1], ['LDAX D', 1], ['SHLD @', 3], ['LHLD @', 3], ['STA @', 3], ['LDA @', 3]][y];\n      else if (z === 3) d = [(q ? 'DCX ' : 'INX ') + RPN[p], 1];\n      else if (z === 4) d = ['INR ' + RN[y], 1];\n      else if (z === 5) d = ['DCR ' + RN[y], 1];\n      else if (z === 6) d = ['MVI ' + RN[y] + ',%', 2];\n      else d = [['RLC', 'RRC', 'RAL', 'RAR', 'DAA', 'CMA', 'STC', 'CMC'][y], 1];\n    } else {\n      if (z === 0) d = ['R' + CCN[y], 1];\n      else if (z === 1) d = q ? [['RET', 'RET*', 'PCHL', 'SPHL'][p], 1] : ['POP ' + (p === 3 ? 'PSW' : RPN[p]), 1];\n      else if (z === 2) d = ['J' + CCN[y] + ' @', 3];\n      else if (z === 3) d = [['JMP @', 3], ['JMP* @', 3], ['OUT %', 2], ['IN %', 2], ['XTHL', 1], ['XCHG', 1], ['DI', 1], ['EI', 1]][y];\n      else if (z === 4) d = ['C' + CCN[y] + ' @', 3];\n      else if (z === 5) d = q ? [p === 0 ? 'CALL @' : 'CALL* @', 3] : ['PUSH ' + (p === 3 ? 'PSW' : RPN[p]), 1];\n      else if (z === 6) d = [ALUIN[y] + ' %', 2];\n      else d = ['RST ' + y, 1];\n    }\n    DIS[op] = d;\n  }\n})();\nfunction hex(v, n) { return ('000' + v.toString(16).toUpperCase()).slice(-n); }\nfunction disasm(mem, addr) {\n  var op = mem[addr & 0xFFFF], d = DIS[op], b1 = mem[(addr + 1) & 0xFFFF], b2 = mem[(addr + 2) & 0xFFFF];\n  var t = d[0].replace('#', hex(b1 | (b2 << 8), 4) + 'H').replace('@', hex(b1 | (b2 << 8), 4) + 'H').replace('%', hex(b1, 2) + 'H');\n  var bytes = []; for (var i = 0; i < d[1]; i++) bytes.push(mem[(addr + i) & 0xFFFF]);\n  return { addr: addr & 0xFFFF, len: d[1], text: t.replace(\/([ ,])([A-F][0-9A-F]{1,3}H)$\/, '$10$2'), bytes: bytes };\n}\n\n\/\/ ---- Intel HEX -------------------------------------------------------------------------------\nfunction toIntelHex(mem, from, to, start) {\n  var out = [];\n  for (var a = from; a <= to; a += 16) {\n    var n = Math.min(16, to - a + 1), rec = [n, a >> 8, a & 255, 0];\n    for (var i = 0; i < n; i++) rec.push(mem[a + i]);\n    out.push(':' + rec.map(function (b) { return hex(b, 2); }).join('') + hex(chk(rec), 2));\n  }\n  if (start !== undefined && start !== null) out.push(':00' + hex(start, 4) + '01' + hex(chk([0, start >> 8, start & 255, 1]), 2));\n  else out.push(':00000001FF');\n  return out.join('\\r\\n') + '\\r\\n';\n}\nfunction chk(rec) { var s = 0; rec.forEach(function (b) { s += b; }); return (-s) & 255; }\nfunction fromIntelHex(text) {\n  var mem = {}, start = null, n = 0, min = 65536, max = -1;\n  text.split(\/\\r?\\n\/).forEach(function (l, i) {\n    l = l.trim(); if (!l) return;\n    if (l[0] !== ':' || !\/^:[0-9a-f]+$\/i.test(l) || l.length % 2 !== 1) throw new Error('riga ' + (i + 1) + ': non \u00e8 un record Intel HEX');\n    var b = []; for (var k = 1; k < l.length; k += 2) b.push(parseInt(l.substr(k, 2), 16));\n    var s = 0; b.forEach(function (x) { s += x; });\n    if ((s & 255) !== 0) throw new Error('riga ' + (i + 1) + ': checksum sbagliato');\n    var len = b[0], addr = (b[1] << 8) | b[2], type = b[3];\n    if (type === 0) for (var j = 0; j < len; j++) { var a = (addr + j) & 0xFFFF; mem[a] = b[4 + j]; n++; if (a < min) min = a; if (a > max) max = a; }\n    else if (type === 1) { if (addr) start = addr; }\n  });\n  return { mem: mem, start: start !== null ? start : (n ? min : 0), size: n, min: n ? min : 0, max: max };\n}\n\nRF.assemble = assemble;\nRF.disasm = disasm;\nRF.hex = hex;\nRF.toIntelHex = toIntelHex;\nRF.fromIntelHex = fromIntelHex;\n})(window.RFImsai = window.RFImsai || {});\n\n\/* RetroFixer \u00b7 IMSAI 8080 \u00b7 la macchina\n * CPU 8080 a 2 MHz, 64 KB di RAM, pannello frontale (interruttori, LED, funzioni EXAMINE\/DEPOSIT\/RUN\/STOP\u2026),\n * porta FFh = LED \"PROGRAMMED OUTPUT\" (OUT) e interruttori di senso A15-A8 (IN),\n * scheda seriale IMSAI SIO-2 (porte 02h dati \/ 03h stato) e MITS 88-2SIO (10h stato \/ 11h dati) verso il terminale.\n *\/\n(function (RF) {\n'use strict';\nvar CLOCK = 2000000;\n\nfunction Imsai() {\n  var self = this;\n  this.cpu = new RF.I8080();\n  this.cpu.acc = true;\n  this.switches = 0;           \/\/ A15..A0 sul pannello (1 = interruttore in su)\n  this.power = true;\n  this.running = false;\n  this.outLatch = 0xFF;        \/\/ ultimo valore scritto sulla porta FFh\n  this.rx = [];                \/\/ carattere ricevuto dalla seriale (al massimo uno, come un vero UART)\n  this.typed = [];             \/\/ tasti premuti o testo incollato, in coda: entrano uno alla volta\n  this.txReadyAt = 0;          \/\/ ciclo in cui la seriale potr\u00e0 trasmettere il prossimo carattere\n  this.baud = 9600;\n  this.speed = 1;              \/\/ 1 = 2 MHz originali; 0 = massima\n  this.onTx = function () {};\n  this.onChange = function () {};\n  this.cpu.io = {\n    input: function (p) { return self.input(p); },\n    output: function (p, v) { self.output(p, v); }\n  };\n  this.cpu.clearHist();\n}\nvar P = Imsai.prototype;\n\nP.cyclesPerChar = function () { return this.baud ? Math.round(CLOCK * 10 \/ this.baud) : 0; };\nP.txReady = function () { return this.cpu.cycles >= this.txReadyAt; };\n\nP.input = function (p) {\n  if (!this.rx.length && this.typed.length) this.rx.push(this.typed.shift());\n  switch (p) {\n    case 0xFF: return (this.switches >> 8) & 255;                                  \/\/ interruttori di senso\n    case 0x03: return (this.txReady() ? 0x01 : 0) | (this.rx.length ? 0x02 : 0);   \/\/ SIO-2: bit0 TxRDY, bit1 RxRDY\n    case 0x02: return this.rx.length ? this.rx.shift() : 0;\n    case 0x10: return (this.rx.length ? 0x01 : 0) | (this.txReady() ? 0x02 : 0);   \/\/ 2SIO (6850): bit0 RDRF, bit1 TDRE\n    case 0x11: return this.rx.length ? this.rx.shift() : 0;\n  }\n  return 0xFF;\n};\nP.output = function (p, v) {\n  switch (p) {\n    case 0xFF: this.outLatch = v; break;\n    case 0x02: case 0x11:\n      this.txReadyAt = this.cpu.cycles + this.cyclesPerChar();\n      this.onTx(v & 0x7F); break;\n  }\n};\nP.key = function (c) { if (this.typed.length < 8000) this.typed.push(c & 0x7F); };\n\n\/\/ LED \"PROGRAMMED OUTPUT\": sull'IMSAI sono pilotati in logica negativa (un bit a 0 accende il LED)\nP.progLeds = function () { return (~this.outLatch) & 255; };\n\n\/\/ ---- esecuzione --------------------------------------------------------------------------\nP.runCycles = function (n) {\n  var cpu = this.cpu, end = cpu.cycles + n;\n  while (cpu.cycles < end && this.running) {\n    if (cpu.halted && (cpu.intReq < 0 || !cpu.inte)) {   \/\/ in HALT non succede niente fino alla fine del blocco\n      var k = Math.ceil((end - cpu.cycles) \/ 4);\n      cpu.hS[RF.STATUS.HALT] += k; cpu.hAH[cpu.pc >> 8] += k; cpu.hAL[cpu.pc & 255] += k; cpu.hD[cpu.lastD] += k; cpu.hN += k;\n      cpu.cycles += k * 4; break;\n    }\n    cpu.step();\n  }\n};\n\/\/ esegue per un tempo reale (ms); a velocit\u00e0 massima per un budget di tempo di calcolo\nP.tick = function (ms) {\n  if (!this.power || !this.running) return;\n  if (this.speed) this.runCycles(Math.min(ms, 100) * CLOCK \/ 1000 * this.speed);\n  else {\n    var t0 = performance.now();\n    do this.runCycles(50000); while (this.running && performance.now() - t0 < 12);\n  }\n};\n\n\/\/ ---- funzioni del pannello frontale --------------------------------------------------------\nP.showAt = function (a) {          \/\/ con la CPU ferma i LED mostrano l'indirizzo e il byte puntati\n  this.cpu.pc = a & 0xFFFF;\n  this.cpu.lastA = this.cpu.pc; this.cpu.lastD = this.cpu.mem[this.cpu.pc]; this.cpu.lastS = RF.STATUS.FETCH;\n};\nP.cmd = function (name) {\n  var cpu = this.cpu;\n  if (!this.power) return;\n  switch (name) {\n    case 'run': if (!this.running) { this.running = true; cpu.clearHist(); } break;\n    case 'stop': if (this.running) { this.running = false; if (!cpu.halted) this.showAt(cpu.pc); } break;\n    case 'step':\n      if (this.running) break;\n      cpu.halted = false; cpu.step(); this.showAt(cpu.pc); break;\n    case 'examine': if (!this.running) this.showAt(this.switches); break;\n    case 'examineNext': if (!this.running) this.showAt(cpu.pc + 1); break;\n    case 'deposit': if (!this.running) { this.poke(cpu.pc, this.switches & 255); this.showAt(cpu.pc); } break;\n    case 'depositNext': if (!this.running) { this.showAt(cpu.pc + 1); this.poke(cpu.pc, this.switches & 255); this.showAt(cpu.pc); } break;\n    case 'reset': cpu.reset(); this.showAt(0); if (this.running) cpu.clearHist(); break;\n    case 'extClr': this.rx.length = 0; this.typed.length = 0; this.txReadyAt = 0; this.outLatch = 0xFF; break;\n    case 'protect': if (!this.running) cpu.prot[cpu.pc >> 8] = 1; break;\n    case 'unprotect': if (!this.running) cpu.prot[cpu.pc >> 8] = 0; break;\n  }\n  this.onChange(name);\n};\nP.poke = function (a, v) { if (!this.cpu.prot[a >> 8]) this.cpu.mem[a] = v; };\nP.setPower = function (on) {\n  if (on === this.power) return;\n  this.power = on;\n  if (!on) {\n    \/\/ spegnendo la RAM dinamica perde il contenuto\n    this.running = false; this.cpu.mem.fill(0); this.cpu.prot.fill(0); this.rx.length = 0; this.typed.length = 0;\n  } else {\n    this.cpu.reset(); this.outLatch = 0xFF; this.showAt(0);\n  }\n  this.onChange(on ? 'powerOn' : 'powerOff');\n};\n\n\/\/ carica un blocco {indirizzo: byte} in memoria\nP.load = function (mem) {\n  var n = 0;\n  for (var k in mem) if (Object.prototype.hasOwnProperty.call(mem, k)) { this.cpu.mem[+k & 0xFFFF] = mem[k]; n++; }\n  return n;\n};\n\n\/\/ valori dei LED: con la CPU in esecuzione la luminosit\u00e0 \u00e8 la frazione di cicli di bus con quel bit a 1\nP.leds = function () {\n  var cpu = this.cpu, o;\n  if (!this.power) return null;\n  if (this.running && cpu.hN) { o = cpu.ledLevels(); cpu.clearHist(); }\n  else {\n    o = { addr: new Float32Array(16), data: new Float32Array(8), status: new Float32Array(8) };\n    var a = cpu.halted && this.running ? cpu.pc : cpu.lastA, d = cpu.lastD, s = this.running && cpu.halted ? RF.STATUS.HALT : cpu.lastS;\n    for (var i = 0; i < 16; i++) o.addr[i] = (a >> i) & 1;\n    for (i = 0; i < 8; i++) { o.data[i] = (d >> i) & 1; o.status[i] = (s >> i) & 1; }\n  }\n  o.out = this.progLeds();\n  o.inte = cpu.inte; o.prot = cpu.prot[cpu.pc >> 8];\n  o.run = this.running; o.wait = !this.running;\n  return o;\n};\n\nRF.Imsai = Imsai;\nRF.CLOCK = CLOCK;\n})(window.RFImsai = window.RFImsai || {});\n\n\/* RetroFixer \u00b7 IMSAI 8080 \u00b7 terminale video 80x24 (comandi come il Lear Siegler ADM-3A)\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})(window.RFImsai = window.RFImsai || {});\n\n\/* RetroFixer \u00b7 IMSAI 8080 \u00b7 pannello frontale (CP-A) in SVG\n * Disposizione come l'originale: a sinistra PROGRAMMED OUTPUT, STATUS BYTE e ADDRESS BUS 15-8 sopra le leve\n * ADDRESS-PROGRAMMED INPUT; al centro DATA BUS e ADDRESS BUS 7-0 sopra le leve ADDRESS-DATA; a destra il logo,\n * INTERRUPTS ENABLED \/ RUN \/ WAIT \/ HOLD e le sei leve di comando.\n * Leve 15-12 e 7-4 blu, 11-8 e 3-0 rosse; i LED brillano in proporzione al tempo di accensione.\n *\/\n(function (RF) {\n'use strict';\nvar NS = 'http:\/\/www.w3.org\/2000\/svg';\nfunction el(tag, attrs, parent, text) {\n  var e = document.createElementNS(NS, tag);\n  for (var k in attrs) e.setAttribute(k, attrs[k]);\n  if (text !== undefined) e.textContent = text;\n  if (parent) parent.appendChild(e);\n  return e;\n}\n\nvar W = 1280, H = 466;\nvar DX = 44;\nfunction colX(i) { return 74 + i * DX + (i >= 8 ? 92 : 0); }   \/\/ i = 0 per A15 \u2026 15 per A0\nvar CX0 = 902, CDX = 62;                                         \/\/ leve di comando\nvar Y1 = 74, Y2 = 150, Y3 = 238, YSW = 384;\n\nvar CONTROLS = [   \/\/ [etichetta su, etichetta gi\u00f9, comando su, comando gi\u00f9, colore]\n  ['EXAMINE', 'EXAMINE NEXT', 'examine', 'examineNext', 'blue'],\n  ['DEPOSIT', 'DEPOSIT NEXT', 'deposit', 'depositNext', 'red'],\n  ['RESET', 'EXT. CLR.', 'reset', 'extClr', 'blue'],\n  ['RUN', 'STOP', 'run', 'stop', 'red'],\n  ['SINGLE STEP', 'SINGLE STEP', 'step', 'step', 'blue']\n];\nvar STATUS = ['MEMR', 'INP', 'M1', 'OUT', 'HLTA', 'STACK', 'WO', 'INTA'];   \/\/ bit 7 \u2026 0\n\nfunction Panel(host, machine, opts) {\n  this.m = machine; this.opts = opts || {};\n  this.onCmd = this.opts.onCmd || function () {};\n  this.svg = el('svg', { viewBox: '0 0 ' + W + ' ' + H, class: 'imsai-panel', role: 'group', 'aria-label': 'IMSAI 8080 front panel' }, host);\n  this.leds = { out: [], data: [], addr: [], status: [] };\n  this.build();\n}\nvar P = Panel.prototype;\n\nP.t = function (x, y, s, cls, anchor) { return el('text', { x: x, y: y, class: cls || 'lbl', 'text-anchor': anchor || 'middle' }, this.svg, s); };\nP.led = function (x, y, group, idx) {\n  var g = el('g', {}, this.svg);\n  var c = el('circle', { cx: x, cy: y, r: 7, class: 'led-lens' }, g);\n  var glow = el('circle', { cx: x, cy: y, r: 15, class: 'led-glow' }, g);\n  var o = { c: c, glow: glow, v: -1 };\n  if (idx === undefined) this.leds[group] = o; else this.leds[group][idx] = o;\n};\nP.window = function (x0, x1, y) { el('rect', { x: x0 - 20, y: y - 14, width: x1 - x0 + 40, height: 28, rx: 2, class: 'ledwin' }, this.svg); };\n\/\/ righello esadecimale\/ottale sotto una fila di 8 LED\nP.rule = function (i0, y, full) {\n  var s = this.svg, x0 = colX(i0) - 18, x1 = colX(i0 + 7) + 18, i;\n  el('line', { x1: x0, y1: y, x2: x1, y2: y, class: 'rule' }, s);\n  [0, 2, 5, 8].forEach(function (k) {   \/\/ tacche ottali: 2 | 3 | 3 bit\n    var x = k === 0 ? x0 : k === 8 ? x1 : (colX(i0 + k - 1) + colX(i0 + k)) \/ 2;\n    el('line', { x1: x, y1: y - 7, x2: x, y2: y + 7, class: 'rule' }, s);\n  });\n  el('line', { x1: (colX(i0 + 3) + colX(i0 + 4)) \/ 2, y1: y - 7, x2: (colX(i0 + 3) + colX(i0 + 4)) \/ 2, y2: y, class: 'rule' }, s);\n  var hexw = [8, 4, 2, 1, 8, 4, 2, 1], octw = [2, 1, 4, 2, 1, 4, 2, 1];\n  for (i = 0; i < 8; i++) { this.t(colX(i0 + i), y - 6, String(hexw[i]), 'tiny'); this.t(colX(i0 + i), y + 16, String(octw[i]), 'tiny'); }\n  if (full) { this.t(x1 + 8, y - 4, 'HEXADECIMAL', 'side', 'start'); this.t(x1 + 8, y + 12, 'OCTAL', 'side', 'start'); }\n};\n\nP.build = function () {\n  var s = this.svg, i, x;\n  var defs = el('defs', {}, s);\n  var rg = el('radialGradient', { id: 'imsaiLedOn' }, defs);\n  el('stop', { offset: '0', 'stop-color': '#fff3e8' }, rg); el('stop', { offset: '.35', 'stop-color': '#ff4a2e' }, rg); el('stop', { offset: '1', 'stop-color': '#b81208' }, rg);\n  var rg2 = el('radialGradient', { id: 'imsaiGlow' }, defs);\n  el('stop', { offset: '0', 'stop-color': 'rgba(255,60,30,.6)' }, rg2); el('stop', { offset: '1', 'stop-color': 'rgba(255,40,20,0)' }, rg2);\n  var lg = el('linearGradient', { id: 'imsaiGlass', x1: 0, y1: 0, x2: 0, y2: 1 }, defs);\n  el('stop', { offset: '0', 'stop-color': '#2c2d31' }, lg); el('stop', { offset: '1', 'stop-color': '#232427' }, lg);\n\n  el('rect', { x: 0, y: 0, width: W, height: H, rx: 12, class: 'cabinet' }, s);              \/\/ mobile blu\n  el('rect', { x: 18, y: 16, width: W - 36, height: H - 30, rx: 3, fill: 'url(#imsaiGlass)', class: 'glass' }, s);\n  el('rect', { x: 18, y: YSW - 52, width: W - 36, height: H - (YSW - 52) - 14, class: 'bezel' }, s);   \/\/ fascia grigia delle leve\n  \/\/ riflesso sul vetro\n  el('path', { d: 'M18 16 H' + (W * 0.55) + ' L' + (W * 0.38) + ' ' + (YSW - 52) + ' H18 Z', class: 'sheen' }, s);\n\n  \/\/ ---- blocco sinistro\n  this.window(colX(0), colX(7), Y1); this.window(colX(0), colX(7), Y2); this.window(colX(0), colX(7), Y3);\n  for (i = 0; i < 8; i++) {\n    x = colX(i);\n    this.led(x, Y1, 'out', 7 - i); this.t(x, Y1 + 30, String(7 - i), 'num');\n    this.led(x, Y2, 'status', 7 - i); this.t(x, Y2 - 22, STATUS[i], 'num sm');\n    this.led(x, Y3, 'addr', 15 - i); this.t(x, Y3 + 30, String(15 - i), 'num'); this.t(x, Y3 + 46, String(7 - i), 'num');\n  }\n  el('line', { x1: colX(6) - 8, y1: Y2 - 33, x2: colX(6) + 8, y2: Y2 - 33, class: 'over' }, s);   \/\/ W\u0304O\n  x = colX(7) + 30;\n  this.t(x, Y1 - 4, 'PROGRAMMED', 'side', 'start'); this.t(x, Y1 + 10, 'OUTPUT', 'side', 'start');\n  this.t(x, Y2 - 4, 'STATUS', 'side', 'start'); this.t(x, Y2 + 10, 'BYTE', 'side', 'start');\n  this.t(x, Y3 - 4, 'ADDRESS', 'side', 'start'); this.t(x, Y3 + 10, 'BUS', 'side', 'start');\n  this.rule(0, (Y2 + Y3) \/ 2 + 4, false);\n  this.t((colX(0) + colX(7)) \/ 2, YSW - 60, 'ADDRESS\u2013PROGRAMMED INPUT', 'grp');\n\n  \/\/ ---- blocco centrale\n  this.window(colX(8), colX(15), Y2); this.window(colX(8), colX(15), Y3);\n  for (i = 0; i < 8; i++) {\n    x = colX(8 + i);\n    this.led(x, Y2, 'data', 7 - i); this.t(x, Y2 - 22, String(7 - i), 'num');\n    this.led(x, Y3, 'addr', 7 - i); this.t(x, Y3 + 30, String(7 - i), 'num');\n  }\n  x = colX(15) + 30;\n  this.t(x, Y2 - 4, 'DATA', 'side', 'start'); this.t(x, Y2 + 10, 'BUS', 'side', 'start');\n  this.t(x, Y3 - 4, 'ADDRESS', 'side', 'start'); this.t(x, Y3 + 10, 'BUS', 'side', 'start');\n  this.rule(8, (Y2 + Y3) \/ 2 + 4, true);\n  this.t((colX(8) + colX(15)) \/ 2, YSW - 60, 'ADDRESS\u2013DATA', 'grp');\n\n  \/\/ ---- blocco destro: logo, LED di stato della CPU, leve di comando\n  var lx0 = CX0 - 30, lx1 = CX0 + 5 * CDX + 30;\n  this.t(lx1, 96, 'IMSAI 8080', 'logo', 'end');\n  el('line', { x1: lx0 + 30, y1: 112, x2: lx1 - 160, y2: 112, class: 'logoline' }, s);\n  this.t(lx1, 116, 'MICROCOMPUTER SYSTEM', 'logo2', 'end');\n  var top = [['INTERRUPTS', 'ENABLED'], ['RUN'], ['WAIT'], ['HOLD']], keys = ['inte', 'run', 'wait', 'hold'];\n  this.window(CX0 + 2 * CDX, CX0 + 5 * CDX, Y3);\n  for (i = 0; i < 4; i++) {\n    x = CX0 + (2 + i) * CDX; this.led(x, Y3, keys[i]);\n    if (top[i].length > 1) { this.t(x, Y3 - 34, top[i][0], 'side'); this.t(x, Y3 - 22, top[i][1], 'side'); }\n    else this.t(x, Y3 - 22, top[i][0], 'side');\n  }\n  this.ctl = [];\n  for (i = 0; i < CONTROLS.length; i++) this.ctl.push(this.control(CX0 + i * CDX, CONTROLS[i]));\n  x = CX0 + 5 * CDX;\n  this.ctlLabel(x, 'PWR ON', 'PWR OFF');\n  this.power = this.paddle(x, YSW, 'power', null, 'red');\n\n  \/\/ ---- leve di indirizzo e dati\n  this.sw = [];\n  for (i = 0; i < 16; i++) this.sw.push(this.paddle(colX(i), YSW, 'addr', 15 - i, (i % 8) < 4 ? 'blue' : 'red'));\n\n  this.t(W - 30, H - 22, 'emulatore \u00b7 retrofixer.it', 'credit', 'end');\n};\nP.ctlLabel = function (x, up, down) {\n  var a = up.split(' '), b = down.split(' ');\n  var yl = YSW - 76;\n  if (a.length > 1 && up !== 'PWR ON') { this.t(x, yl - 26, a[0], 'cl'); this.t(x, yl - 14, a.slice(1).join(' '), 'cl'); }\n  else this.t(x, yl - 14, up, 'cl');\n  el('line', { x1: x - 26, y1: yl - 8, x2: x + 26, y2: yl - 8, class: 'ctline' }, this.svg);\n  if (b.length > 1 && down !== 'PWR OFF') { this.t(x, yl + 5, b[0], 'cl'); this.t(x, yl + 17, b.slice(1).join(' '), 'cl'); }\n  else this.t(x, yl + 5, down, 'cl');\n};\nP.control = function (x, c) { this.ctlLabel(x, c[0], c[1]); return this.paddle(x, YSW, 'ctl', c, c[4]); };\n\n\/\/ leva a bilanciere: kind = 'addr' (resta su\/gi\u00f9), 'ctl' (momentanea, torna al centro), 'power'\nP.paddle = function (x, y, kind, arg, color) {\n  var self = this, g = el('g', { class: 'paddle ' + kind, transform: 'translate(' + x + ',' + y + ')', tabindex: 0, role: 'button' }, this.svg);\n  var wd = kind === 'addr' ? 36 : 46;\n  el('rect', { x: -wd \/ 2 - 3, y: -44, width: wd + 6, height: 88, rx: 3, class: 'slot' }, g);\n  var body = el('rect', { x: -wd \/ 2, width: wd, rx: 3, class: 'pad ' + color }, g);\n  var face = el('rect', { x: -wd \/ 2, width: wd, rx: 3, class: 'face ' + color }, g);\n  el('rect', { x: -wd \/ 2 - 3, y: -44, width: wd + 6, height: 44, class: 'hit', 'data-dir': 'up' }, g);\n  el('rect', { x: -wd \/ 2 - 3, y: 0, width: wd + 6, height: 44, class: 'hit', 'data-dir': 'down' }, g);\n  var o = { g: g, body: body, face: face, kind: kind, arg: arg, pos: 0 };\n  g.setAttribute('aria-label', kind === 'addr' ? 'A' + arg : kind === 'ctl' ? arg[0] + ' \/ ' + arg[1] : 'POWER');\n\n  function act(dir) {\n    if (kind === 'addr') {\n      var bit = 1 << arg, up = dir === 'up' ? true : dir === 'down' ? false : !(self.m.switches & bit);\n      self.m.switches = up ? (self.m.switches | bit) : (self.m.switches & ~bit);\n      self.setPos(o, up ? -1 : 1);\n    } else if (kind === 'power') {\n      var on = dir === 'up' ? true : dir === 'down' ? false : !self.m.power;\n      self.m.setPower(on); self.setPos(o, on ? -1 : 1);\n      self.onCmd(on ? 'powerOn' : 'powerOff');\n    } else {\n      self.setPos(o, dir === 'up' ? -1 : 1);\n      clearTimeout(o.t); o.t = setTimeout(function () { self.setPos(o, 0); }, 200);\n      var cmd = dir === 'up' ? arg[2] : arg[3];\n      self.m.cmd(cmd); self.onCmd(cmd);\n    }\n  }\n  g.addEventListener('pointerdown', function (e) {\n    var d = e.target.getAttribute('data-dir'); if (!d) return;\n    e.preventDefault(); act(d);\n  });\n  g.addEventListener('keydown', function (e) {\n    if (e.key === 'ArrowUp') { e.preventDefault(); act('up'); }\n    else if (e.key === 'ArrowDown') { e.preventDefault(); act('down'); }\n    else if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); act(kind === 'ctl' ? 'up' : 'toggle'); }\n  });\n  this.setPos(o, kind === 'addr' ? 1 : kind === 'power' ? (this.m.power ? -1 : 1) : 0);\n  return o;\n};\n\/\/ -1 su, 0 centro, 1 gi\u00f9: la faccia chiara \u00e8 la parte della paletta che sporge verso chi guarda\nP.setPos = function (o, pos) {\n  o.pos = pos;\n  if (pos < 0) { o.body.setAttribute('y', -40); o.body.setAttribute('height', 52); o.face.setAttribute('y', -40); o.face.setAttribute('height', 16); }\n  else if (pos > 0) { o.body.setAttribute('y', -12); o.body.setAttribute('height', 52); o.face.setAttribute('y', 24); o.face.setAttribute('height', 16); }\n  else { o.body.setAttribute('y', -26); o.body.setAttribute('height', 52); o.face.setAttribute('y', -6); o.face.setAttribute('height', 12); }\n  o.g.setAttribute('data-pos', pos < 0 ? 'up' : pos > 0 ? 'down' : 'mid');\n};\nP.syncSwitches = function () {\n  for (var i = 0; i < 16; i++) { var o = this.sw[i]; this.setPos(o, (this.m.switches >> o.arg) & 1 ? -1 : 1); }\n  this.setPos(this.power, this.m.power ? -1 : 1);\n};\n\n\/\/ luminosit\u00e0 percepita: anche un LED acceso per una piccola frazione del tempo si vede\nfunction bright(v) { return v <= 0.002 ? 0 : Math.min(1, 0.18 + 0.82 * Math.pow(v, 0.55)); }\nP.setLed = function (o, v) {\n  var q = Math.round(bright(v) * 40) \/ 40;\n  if (q === o.v) return;\n  o.v = q;\n  o.c.setAttribute('class', q > 0 ? 'led-lens on' : 'led-lens');\n  o.c.style.opacity = q > 0 ? (0.3 + 0.7 * q).toFixed(3) : '';\n  o.glow.style.opacity = (q * q).toFixed(3);\n};\nP.update = function (L) {\n  var i, k = this.leds;\n  if (!L) {\n    [].concat(k.out, k.data, k.addr, k.status, [k.inte, k.run, k.wait, k.hold]).forEach(function (o) { this.setLed(o, 0); }, this);\n    return;\n  }\n  for (i = 0; i < 8; i++) { this.setLed(k.out[i], (L.out >> i) & 1); this.setLed(k.data[i], L.data[i]); this.setLed(k.status[i], L.status[i]); }\n  for (i = 0; i < 16; i++) this.setLed(k.addr[i], L.addr[i]);\n  this.setLed(k.inte, L.inte); this.setLed(k.run, L.run ? 1 : 0); this.setLed(k.wait, L.wait ? 1 : 0); this.setLed(k.hold, 0);\n};\n\nRF.Panel = Panel;\n})(window.RFImsai = window.RFImsai || {});\n\n\/* RetroFixer \u00b7 IMSAI 8080 \u00b7 programmi di esempio (sorgenti assembler 8080) *\/\n(function (RF) {\n'use strict';\nRF.PROGRAMS = [\n{\n  id: 'specchio',\n  name: { it: 'Specchio (da inserire a mano)', en: 'Mirror (toggle it in by hand)' },\n  desc: {\n    it: 'Il primo programma da provare con gli interruttori: 8 byte che copiano gli interruttori A15-A8 sui LED PROGRAMMED OUTPUT. Avvialo con RUN e muovi le levette di sinistra.',\n    en: 'The first program to try with the switches: 8 bytes that copy switches A15-A8 to the PROGRAMMED OUTPUT LEDs. Start it with RUN and flip the left-hand switches.'\n  },\n  src: [\n    '; SPECCHIO - interruttori A15-A8 -> LED PROGRAMMED OUTPUT',\n    '; 8 byte: si possono inserire a mano con EXAMINE e DEPOSIT NEXT',\n    '        ORG 0',\n    'INIZIO: IN 0FFH        ; legge gli interruttori di senso (A15-A8)',\n    '        CMA            ; i LED dell\\'IMSAI si accendono con i bit a 0',\n    '        OUT 0FFH       ; scrive sui LED PROGRAMMED OUTPUT',\n    '        JMP INIZIO     ; e ricomincia',\n    '        END INIZIO'\n  ]\n},\n{\n  id: 'contatore',\n  name: { it: 'Contatore binario', en: 'Binary counter' },\n  desc: {\n    it: 'Conta in binario sui LED PROGRAMMED OUTPUT. Gli interruttori A15-A8 regolano la velocit\u00e0: alzane qualcuno per rallentare.',\n    en: 'Counts in binary on the PROGRAMMED OUTPUT LEDs. Switches A15-A8 set the speed: raise some of them to slow it down.'\n  },\n  src: [\n    '; CONTATORE BINARIO sui LED PROGRAMMED OUTPUT (porta FFh)',\n    '        ORG 0',\n    'INIZIO: MVI B,0        ; B = contatore',\n    'CICLO:  MOV A,B',\n    '        CMA            ; logica negativa: bit a 0 = LED acceso',\n    '        OUT 0FFH',\n    '        IN 0FFH        ; interruttori A15-A8 = durata della pausa',\n    '        ORI 10H        ; mai troppo veloce',\n    '        MOV D,A',\n    '        MVI E,0',\n    'PAUSA:  DCX D          ; 24 stati di clock per giro',\n    '        MOV A,D',\n    '        ORA E',\n    '        JNZ PAUSA',\n    '        INR B          ; numero successivo',\n    '        JMP CICLO',\n    '        END INIZIO'\n  ]\n},\n{\n  id: 'supercar',\n  name: { it: 'Luce che scorre', en: 'Scanning light' },\n  desc: {\n    it: 'Una luce che va avanti e indietro sui LED PROGRAMMED OUTPUT, con una subroutine e lo stack.',\n    en: 'A light running back and forth on the PROGRAMMED OUTPUT LEDs, using a subroutine and the stack.'\n  },\n  src: [\n    '; LUCE CHE SCORRE avanti e indietro',\n    '        ORG 0',\n    'INIZIO: LXI SP,0100H   ; stack sotto l\\'indirizzo 0100h',\n    '        MVI A,01H',\n    'SINIS:  CALL MOSTRA',\n    '        RLC            ; ruota a sinistra',\n    '        CPI 80H',\n    '        JNZ SINIS',\n    'DESTRA: CALL MOSTRA',\n    '        RRC            ; ruota a destra',\n    '        CPI 01H',\n    '        JNZ DESTRA',\n    '        JMP SINIS',\n    '',\n    '; mostra A sui LED e aspetta un po\\'',\n    'MOSTRA: PUSH PSW',\n    '        CMA',\n    '        OUT 0FFH',\n    '        LXI D,1800H',\n    'PAUSA:  DCX D',\n    '        MOV A,D',\n    '        ORA E',\n    '        JNZ PAUSA',\n    '        POP PSW',\n    '        RET',\n    '        END INIZIO'\n  ]\n},\n{\n  id: 'killbit',\n  name: { it: 'Kill the Bit (gioco, 1975)', en: 'Kill the Bit (game, 1975)' },\n  desc: {\n    it: 'Il gioco di Dean McDaniel (1975) nato per l\\'Altair: un bit gira sui LED di indirizzo A15-A8. Alza e riabbassa l\\'interruttore sotto il bit quando passa per \"ucciderlo\"; se sbagli ne compaiono altri.',\n    en: 'Dean McDaniel\\'s 1975 game, born on the Altair: a bit rotates on address LEDs A15-A8. Flip the switch under the bit up and down as it passes to \"kill\" it; miss and more bits appear.'\n  },\n  src: [\n    '; KILL THE BIT - Dean McDaniel, 15 maggio 1975',\n    '; Il bit gira sui LED di indirizzo A15-A8: LDAX D legge dall\\'indirizzo',\n    '; in DE, e D contiene il bit, cos\u00ec i LED alti dell\\'indirizzo lo mostrano.',\n    '        ORG 0',\n    'INIZIO: LXI H,0        ; contatore per la velocit\u00e0',\n    '        MVI D,080H     ; il bit da colpire',\n    '        LXI B,0EH      ; velocit\u00e0 (pi\u00f9 alto = pi\u00f9 veloce)',\n    'GIRO:   LDAX D         ; mostra il bit sul bus indirizzi',\n    '        LDAX D',\n    '        LDAX D',\n    '        LDAX D',\n    '        DAD B          ; avanza il contatore',\n    '        JNC GIRO       ; finch\u00e9 non trabocca',\n    '        IN 0FFH        ; legge gli interruttori A15-A8',\n    '        XRA D          ; un interruttore alzato sotto il bit lo spegne',\n    '        RRC            ; ruota a destra',\n    '        MOV D,A',\n    '        JMP GIRO',\n    '        END INIZIO'\n  ]\n},\n{\n  id: 'ciao',\n  name: { it: 'Ciao dal terminale', en: 'Hello on the terminal' },\n  desc: {\n    it: 'Scrive un messaggio sul terminale video attraverso la scheda seriale (porte 02h e 03h), aspettando che la seriale sia pronta prima di ogni carattere.',\n    en: 'Writes a message on the video terminal through the serial card (ports 02h and 03h), waiting for the serial port to be ready before each character.'\n  },\n  terminal: true,\n  src: [\n    '; CIAO DAL TERMINALE - scheda seriale IMSAI SIO: 02h dati, 03h stato',\n    'DATI    EQU 02H',\n    'STATO   EQU 03H',\n    '        ORG 0',\n    'INIZIO: LXI SP,0100H',\n    '        LXI H,MSG',\n    'STAMPA: MOV A,M        ; prossimo carattere',\n    '        ORA A          ; 0 = fine del testo',\n    '        JZ FINE',\n    '        CALL PUTC',\n    '        INX H',\n    '        JMP STAMPA',\n    'FINE:   HLT            ; fermo: il LED HLTA si accende',\n    '',\n    'PUTC:   PUSH PSW',\n    'ATTESA: IN STATO       ; bit 0 = pronta a trasmettere (TxRDY)',\n    '        ANI 01H',\n    '        JZ ATTESA',\n    '        POP PSW',\n    '        OUT DATI',\n    '        RET',\n    '',\n    'MSG:    DB 1AH         ; ADM-3A: cancella lo schermo',\n    '        DB \\'CIAO! SONO UN IMSAI 8080 DEL 1975.\\',13,10',\n    '        DB \\'CPU INTEL 8080 A 2 MHZ, BUS S-100.\\',13,10,0',\n    '        END INIZIO'\n  ]\n},\n{\n  id: 'eco',\n  name: { it: 'Eco della tastiera', en: 'Keyboard echo' },\n  desc: {\n    it: 'Scrivi sul terminale: ogni tasto torna indietro sullo schermo e il suo codice ASCII si accende sui LED PROGRAMMED OUTPUT.',\n    en: 'Type on the terminal: every key is echoed on screen and its ASCII code lights up on the PROGRAMMED OUTPUT LEDs.'\n  },\n  terminal: true,\n  src: [\n    '; ECO: tastiera -> schermo, codice ASCII -> LED',\n    'DATI    EQU 02H',\n    'STATO   EQU 03H',\n    '        ORG 0',\n    'INIZIO: LXI SP,0100H',\n    '        LXI H,MSG',\n    '        CALL PRINT',\n    'CICLO:  CALL GETC      ; aspetta un tasto',\n    '        MOV B,A',\n    '        CMA',\n    '        OUT 0FFH       ; codice ASCII sui LED',\n    '        MOV A,B',\n    '        CALL PUTC      ; eco sullo schermo',\n    '        CPI 13         ; Invio?',\n    '        JNZ CICLO',\n    '        MVI A,10       ; dopo CR manda anche LF',\n    '        CALL PUTC',\n    '        JMP CICLO',\n    '',\n    'GETC:   IN STATO       ; bit 1 = carattere ricevuto (RxRDY)',\n    '        ANI 02H',\n    '        JZ GETC',\n    '        IN DATI',\n    '        ANI 7FH',\n    '        RET',\n    'PUTC:   PUSH PSW',\n    'PUTC1:  IN STATO',\n    '        ANI 01H',\n    '        JZ PUTC1',\n    '        POP PSW',\n    '        OUT DATI',\n    '        RET',\n    'PRINT:  MOV A,M',\n    '        ORA A',\n    '        RZ',\n    '        CALL PUTC',\n    '        INX H',\n    '        JMP PRINT',\n    '',\n    'MSG:    DB 1AH,\\'SCRIVI QUALCOSA:\\',13,10,0',\n    '        END INIZIO'\n  ]\n},\n{\n  id: 'monitor',\n  name: { it: 'Mini monitor (D, E, G)', en: 'Mini monitor (D, E, G)' },\n  desc: {\n    it: 'Un piccolo programma monitor come quelli dell\\'epoca: <b>D 1000<\/b> mostra la memoria, <b>E 1000<\/b> la modifica (scrivi i byte separati da spazi, uno spazio da solo salta un byte, Invio su una riga vuota finisce), <b>G 1000<\/b> esegue (un RET torna al monitor). Il monitor occupa 0000-01FFh: usa gli indirizzi da 1000h in su.',\n    en: 'A small monitor program like those of the era: <b>D 1000<\/b> dumps memory, <b>E 1000<\/b> edits it (type bytes separated by spaces, a lone space skips a byte, Enter on an empty entry finishes), <b>G 1000<\/b> runs code (a RET returns to the monitor). The monitor lives at 0000-01FFh: use addresses from 1000h up.'\n  },\n  terminal: true,\n  src: [\n    '; MINI MONITOR 8080 - RetroFixer',\n    '; D aaaa  mostra 128 byte     E aaaa  modifica     G aaaa  esegue',\n    'CR      EQU 13',\n    'LF      EQU 10',\n    'DATI    EQU 02H',\n    'STATO   EQU 03H',\n    '        ORG 0',\n    'INIZIO: LXI SP,0       ; lo stack parte dalla cima della memoria',\n    '        LXI H,TITOLO',\n    '        CALL PRINT',\n    'PROMPT: LXI SP,0',\n    '        LXI H,PRMSG',\n    '        CALL PRINT',\n    '        CALL GETCE     ; comando',\n    '        CPI \\'D\\'',\n    '        JZ DUMP',\n    '        CPI \\'E\\'',\n    '        JZ ENTER',\n    '        CPI \\'G\\'',\n    '        JZ GO',\n    '        CPI CR',\n    '        JZ PROMPT',\n    '        LXI H,AIUTO',\n    '        CALL PRINT',\n    '        JMP PROMPT',\n    '',\n    '; ---- D aaaa: 8 righe da 16 byte, in esadecimale e in ASCII',\n    'DUMP:   CALL GETADR',\n    '        JC ERRORE',\n    '        MVI C,8',\n    'DRIGA:  CALL CRLF',\n    '        CALL PADDR',\n    '        MVI A,\\':\\'',\n    '        CALL PUTC',\n    '        MVI B,16',\n    '        PUSH H',\n    'DBYTE:  MVI A,\\' \\'',\n    '        CALL PUTC',\n    '        MOV A,M',\n    '        CALL PHEX',\n    '        INX H',\n    '        DCR B',\n    '        JNZ DBYTE',\n    '        POP H',\n    '        MVI A,\\' \\'',\n    '        CALL PUTC',\n    '        CALL PUTC',\n    '        MVI B,16',\n    'DASC:   MOV A,M',\n    '        CPI \\' \\'',\n    '        JC DPUNTO      ; caratteri di controllo -> \".\"',\n    '        CPI 7FH',\n    '        JC DCAR',\n    'DPUNTO: MVI A,\\'.\\'',\n    'DCAR:   CALL PUTC',\n    '        INX H',\n    '        DCR B',\n    '        JNZ DASC',\n    '        DCR C',\n    '        JNZ DRIGA',\n    '        JMP PROMPT',\n    '',\n    '; ---- E aaaa: modifica un byte alla volta',\n    'ENTER:  CALL GETADR',\n    '        JC ERRORE',\n    'ELOOP:  CALL CRLF',\n    '        CALL PADDR',\n    '        MVI A,\\' \\'',\n    '        CALL PUTC',\n    '        MOV A,M        ; valore attuale',\n    '        CALL PHEX',\n    '        MVI A,\\' \\'',\n    '        CALL PUTC',\n    '        CALL GETBYTE',\n    '        JC PROMPT      ; Invio da solo: fine',\n    '        DCR E          ; E=1: valore nuovo, E=0: spazio = salta',\n    '        JNZ ESALTA',\n    '        MOV M,A',\n    'ESALTA: INX H',\n    '        JMP ELOOP',\n    '',\n    '; ---- G aaaa: chiama il programma; il suo RET torna al prompt',\n    'GO:     CALL GETADR',\n    '        JC ERRORE',\n    '        CALL CRLF',\n    '        LXI D,PROMPT',\n    '        PUSH D',\n    '        PCHL',\n    '',\n    'ERRORE: LXI H,ERRMSG',\n    '        CALL PRINT',\n    '        JMP PROMPT',\n    '',\n    '; legge un indirizzo esadecimale in HL (carry = errore)',\n    'GETADR: LXI H,0',\n    '        MVI B,0        ; cifre lette',\n    'GA1:    CALL GETCE',\n    '        CPI \\' \\'',\n    '        JNZ GA2',\n    '        MOV A,B',\n    '        ORA A',\n    '        JZ GA1         ; spazi prima dell\\'indirizzo',\n    '        JMP GAOK       ; spazio dopo: fine',\n    'GA2:    CPI CR',\n    '        JZ GAFINE',\n    '        CALL HEXVAL',\n    '        RC',\n    '        DAD H          ; HL = HL * 16 + cifra',\n    '        DAD H',\n    '        DAD H',\n    '        DAD H',\n    '        ORA L',\n    '        MOV L,A',\n    '        INR B',\n    '        JMP GA1',\n    'GAFINE: MOV A,B',\n    '        ORA A',\n    '        JZ GAERR',\n    'GAOK:   ORA A          ; carry a 0',\n    '        RET',\n    'GAERR:  STC',\n    '        RET',\n    '',\n    '; legge un byte: cifre esadecimali chiuse da spazio o Invio',\n    '; E=1: valore nuovo in A; E=0: solo spazio (salta); carry: Invio da solo o errore',\n    'GETBYTE: MVI E,0',\n    '        MVI D,0',\n    'GB1:    CALL GETCE',\n    '        CPI \\' \\'',\n    '        JZ GBOK',\n    '        CPI CR',\n    '        JZ GBCR',\n    '        CALL HEXVAL',\n    '        RC',\n    '        MOV B,A',\n    '        MOV A,D        ; D = D * 16 + cifra',\n    '        RLC',\n    '        RLC',\n    '        RLC',\n    '        RLC',\n    '        ANI 0F0H',\n    '        ORA B',\n    '        MOV D,A',\n    '        MVI E,1',\n    '        JMP GB1',\n    'GBCR:   MOV A,E',\n    '        ORA A',\n    '        JZ GBFINE      ; Invio senza cifre: fine',\n    'GBOK:   MOV A,D',\n    '        ORA A          ; carry a 0',\n    '        RET',\n    'GBFINE: STC',\n    '        RET',\n    '',\n    '; carattere ASCII in A -> valore 0-15 (carry = non esadecimale)',\n    'HEXVAL: SUI \\'0\\'',\n    '        RC',\n    '        CPI 10',\n    '        CMC',\n    '        RNC',\n    '        SUI 7',\n    '        CPI 10',\n    '        RC',\n    '        CPI 16',\n    '        CMC',\n    '        RET',\n    '',\n    '; stampa HL in esadecimale',\n    'PADDR:  MOV A,H',\n    '        CALL PHEX',\n    '        MOV A,L',\n    '; stampa A in esadecimale (il trucco classico con DAA)',\n    'PHEX:   PUSH PSW',\n    '        RRC',\n    '        RRC',\n    '        RRC',\n    '        RRC',\n    '        CALL PNIB',\n    '        POP PSW',\n    'PNIB:   PUSH PSW',\n    '        ANI 0FH',\n    '        ADI 90H',\n    '        DAA',\n    '        ACI 40H',\n    '        DAA',\n    '        CALL PUTC',\n    '        POP PSW',\n    '        RET',\n    '',\n    '; tastiera con eco, lettere in maiuscolo',\n    'GETCE:  IN STATO',\n    '        ANI 02H',\n    '        JZ GETCE',\n    '        IN DATI',\n    '        ANI 7FH',\n    '        CPI \\'a\\'',\n    '        JC GCE1',\n    '        CPI \\'z\\'+1',\n    '        JNC GCE1',\n    '        SUI 20H',\n    'GCE1:   CALL PUTC',\n    '        RET',\n    '',\n    'PUTC:   PUSH PSW',\n    'PUTC1:  IN STATO',\n    '        ANI 01H',\n    '        JZ PUTC1',\n    '        POP PSW',\n    '        OUT DATI',\n    '        RET',\n    'CRLF:   MVI A,CR',\n    '        CALL PUTC',\n    '        MVI A,LF',\n    '        JMP PUTC',\n    'PRINT:  MOV A,M',\n    '        ORA A',\n    '        RZ',\n    '        CALL PUTC',\n    '        INX H',\n    '        JMP PRINT',\n    '',\n    'TITOLO: DB 1AH,\\'MINI MONITOR 8080 - RETROFIXER\\',CR,LF',\n    '        DB \\'D=MOSTRA  E=MODIFICA  G=ESEGUI  ?=AIUTO\\',CR,LF,0',\n    'PRMSG:  DB CR,LF,\\'>\\',0',\n    'AIUTO:  DB CR,LF,\\'D AAAA  MOSTRA 128 BYTE DA AAAA\\'',\n    '        DB CR,LF,\\'E AAAA  MODIFICA: BYTE + SPAZIO, SPAZIO=SALTA, INVIO=FINE\\'',\n    '        DB CR,LF,\\'G AAAA  ESEGUE DA AAAA (RET TORNA QUI)\\',0',\n    'ERRMSG: DB \\' ?\\',0',\n    '        END INIZIO'\n  ]\n}\n];\nRF.PROGRAMS.forEach(function (p) { p.src = p.src.join('\\n') + '\\n'; });\n})(window.RFImsai = window.RFImsai || {});\n\n\/* RetroFixer \u00b7 IMSAI 8080 \u00b7 testi (italiano e inglese), foto, progetti\n * Fatti e link verificati il 4 novembre 2026 (vedi README.md, sezione Fonti).\n *\/\n(function (RF) {\n'use strict';\n\n\/\/ Foto da Wikimedia Commons (licenze libere, attribuzione obbligatoria), caricate nella libreria media di retrofixer.it\n\/\/ (media 11556 e 11557; i file sono nella cartella foto\/).\nRF.PHOTOS = [\n  {\n    src: 'https:\/\/retrofixer.it\/wp-content\/uploads\/2026\/11\/imsai-8080-musee-bolo-morn-cc-by-sa-4.jpg',\n    w: 1280, h: 553,\n    page: 'https:\/\/commons.wikimedia.org\/wiki\/File:IMSAI_8080,_cropped.jpg', author: 'Morn', license: 'CC BY-SA 4.0',\n    alt: { it: 'IMSAI 8080 con il mobile blu e il pannello frontale nero con le leve rosse e blu', en: 'IMSAI 8080 with its blue cabinet and black front panel with red and blue paddle switches' },\n    cap: { it: 'Un IMSAI 8080 al Mus\u00e9e Bolo di Losanna.', en: 'An IMSAI 8080 at the Mus\u00e9e Bolo in Lausanne.' }\n  },\n  {\n    src: 'https:\/\/retrofixer.it\/wp-content\/uploads\/2026\/11\/imsai-8080-chm-don-debold-cc-by-2.jpg',\n    w: 1280, h: 720,\n    page: 'https:\/\/commons.wikimedia.org\/wiki\/File:IMSAI_8080_computer_at_the_Computer_History_Museum.jpg', author: 'Don DeBold', license: 'CC BY 2.0',\n    alt: { it: 'Il pannello frontale dell\\'IMSAI 8080 visto da vicino', en: 'Close view of the IMSAI 8080 front panel' },\n    cap: { it: 'Il pannello frontale al Computer History Museum di Mountain View.', en: 'The front panel at the Computer History Museum in Mountain View.' }\n  }\n];\n\nvar KIT = { it: 'Kit \/ replica', en: 'Kit \/ replica' }, DIY = { it: 'Fai da te', en: 'DIY' }, P3D = { it: 'Stampa 3D', en: '3D printing' },\n  SW = { it: 'Emulatore', en: 'Emulator' }, DOC = { it: 'Documentazione', en: 'Documentation' }, SRC = { it: 'Ricerca', en: 'Search' };\n\nRF.PROJECTS = [\n  {\n    title: { it: 'Repliche con microcontrollore (ESP32, Arduino, Raspberry Pi Pico)', en: 'Microcontroller replicas (ESP32, Arduino, Raspberry Pi Pico)' },\n    items: [\n      { tag: KIT, name: 'The High Nibble \u2013 IMSAI 8080 replica', url: 'https:\/\/thehighnibble.com\/imsai8080\/',\n        desc: { it: 'La replica pi\u00f9 fedele: pannello a grandezza naturale con 44 LED e 22 leve nei colori originali, un ESP32 che fa girare z80pack, interfaccia web via Wi-Fi e due seriali RS-232. Kit da saldare (circa 330 dollari).',\n          en: 'The most faithful replica: full-size panel with 44 LEDs and 22 paddle switches in the original colours, an ESP32 running z80pack, a web interface over Wi-Fi and two RS-232 ports. Solder-it-yourself kit (about USD 330).' } },\n      { tag: DIY, name: 'Pocket-Sized IMSAI-Style Z80 Board', url: 'https:\/\/hackaday.io\/project\/177345-pocket-sized-imsai-style-z80-board',\n        desc: { it: 'Un computer tascabile in stile IMSAI con una vera CPU Z80 e un ATmega328 (il chip di Arduino Uno), 39 LED, interruttori e contenitore stampato in 3D.',\n          en: 'A pocket IMSAI-style computer with a real Z80 CPU and an ATmega328 (the Arduino Uno chip), 39 LEDs, switches and a 3D-printed case.' } },\n      { tag: DIY, name: 'gzalo\/microcomputer', url: 'https:\/\/github.com\/gzalo\/microcomputer',\n        desc: { it: 'Un Raspberry Pi Pico emula l\\'8080 e pilota leve e LED ispirati a IMSAI e Altair. Schema, circuito stampato e firmware aperti.',\n          en: 'A Raspberry Pi Pico emulates the 8080 and drives switches and LEDs inspired by the IMSAI and Altair. Open schematic, PCB and firmware.' } },\n      { tag: DIY, name: 'Tiny IMSAI 8080', url: 'https:\/\/www.pcbway.com\/project\/shareproject\/Tiny_IMSAI_8080_Computer_993a0c20.html',\n        desc: { it: 'Un IMSAI in miniatura da scrivania: un ATtiny814 e dei 74HC595 fanno lampeggiare le luci come nel film WarGames. Non \u00e8 un computer, ma \u00e8 un bel primo progetto da saldare.',\n          en: 'A miniature desk IMSAI: an ATtiny814 and some 74HC595s blink the lights like in WarGames. Not a computer, but a nice first soldering project.' } },\n      { tag: { it: 'Cugino Altair', en: 'Altair cousin' }, name: 'Altair 8800 simulator (Arduino Due)', url: 'https:\/\/github.com\/dhansel\/Altair8800',\n        desc: { it: 'Il simulatore di David Hansel per Arduino Due (alla base dell\\'Altair-Duino): stessa CPU 8080 e stesso bus S-100, ma con il pannello dell\\'Altair.',\n          en: 'David Hansel\\'s Arduino Due simulator (the heart of the Altair-Duino): same 8080 CPU and S-100 bus, but with the Altair panel.' } }\n    ]\n  },\n  {\n    title: { it: 'Con parti stampate in 3D', en: 'With 3D-printed parts' },\n    items: [\n      { tag: P3D, name: 'IMSPI \u2013 RaspberyPi-IMSAI-8080', url: 'https:\/\/github.com\/sean-galloway\/RaspberyPi-IMSAI-8080',\n        desc: { it: 'Pannello in stile IMSAI per un rack da 10 pollici, con RP2040: nel progetto ci sono il contenitore da stampare (OpenSCAD\/STL) e i cappucci delle leve a paletta.',\n          en: 'IMSAI-style panel for a 10-inch rack with an RP2040: the repository includes the printable case (OpenSCAD\/STL) and the paddle switch caps.' } },\n      { tag: SRC, name: 'Thingiverse: \"IMSAI\"', url: 'https:\/\/www.thingiverse.com\/search?q=imsai',\n        desc: { it: 'Ricerca tra i modelli della comunit\u00e0 (mobili in scala, leve, contenitori). Controlla licenza e misure di ogni modello.',\n          en: 'Search the community models (scale cabinets, switches, cases). Check each model\\'s licence and dimensions.' } },\n      { tag: SRC, name: 'MakerWorld: \"IMSAI 8080\"', url: 'https:\/\/makerworld.com\/en\/search\/models?keyword=imsai%208080',\n        desc: { it: 'La stessa ricerca su MakerWorld, il sito di modelli di Bambu Lab.',\n          en: 'The same search on MakerWorld, Bambu Lab\\'s model site.' } }\n    ]\n  },\n  {\n    title: { it: 'Emulatori e documentazione', en: 'Emulators and documentation' },\n    items: [\n      { tag: SW, name: 'z80pack (imsaisim) \u2013 Udo Munk', url: 'https:\/\/github.com\/udo-munk\/z80pack',\n        desc: { it: 'L\\'emulatore di riferimento per PC: IMSAI 8080 con pannello frontale grafico, terminale, dischi e CP\/M. \u00c8 lo stesso software della replica High Nibble.',\n          en: 'The reference emulator for PCs: IMSAI 8080 with a graphical front panel, terminal, disks and CP\/M. The same software runs in the High Nibble replica.' } },\n      { tag: DOC, name: 'IMSAI 8080 User Manual (1976)', url: 'https:\/\/www.retrocmp.de\/imsai\/1976_imsai-8080_user-manual_ocr.pdf',\n        desc: { it: 'Il manuale originale, con montaggio, schemi e uso del pannello frontale (PDF in inglese, 34 MB).',\n          en: 'The original manual: assembly, schematics and front panel operation (PDF, 34 MB).' } },\n      { tag: DOC, name: 'Intel 8080 Assembly Language Programming Manual', url: 'https:\/\/bitsavers.org\/components\/intel\/MCS80\/9800301D_8080_8085_Assembly_Language_Programming_Manual_May81.pdf',\n        desc: { it: 'Il manuale Intel di tutte le istruzioni dell\\'8080\/8085: la sintassi dell\\'assembler di questa pagina \u00e8 la stessa.',\n          en: 'Intel\\'s reference for every 8080\/8085 instruction: the assembler on this page uses the same syntax.' } },\n      { tag: DOC, name: 'bitsavers.org \u2013 IMSAI', url: 'https:\/\/www.bitsavers.org\/pdf\/imsai\/',\n        desc: { it: 'Archivio di documenti IMSAI: listini, depliant, manuali delle schede e dei dischi.',\n          en: 'Archive of IMSAI documents: price lists, brochures, board and disk manuals.' } },\n      { tag: DOC, name: 'Kill the Bit (1975)', url: 'https:\/\/altairclone.com\/downloads\/killbits.pdf',\n        desc: { it: 'Il listato originale di Dean McDaniel del gioco \"uccidi il bit\", lo stesso caricato nell\\'emulatore qui sotto.',\n          en: 'Dean McDaniel\\'s original listing of the \"Kill the Bit\" game, the same one loaded in the emulator below.' } }\n    ]\n  }\n];\n\nRF.TEXTS = {\nit: {\n  kicker: '> retrocomputer del 1975',\n  lead: 'Il microcomputer con le leve rosse e blu che ha reso famoso il film <b>WarGames<\/b>. Qui trovi la sua storia, le foto, i progetti per costruirne una replica e, pi\u00f9 sotto, un <b>emulatore completo<\/b>: pannello frontale funzionante, terminale video e assembler per scrivere i tuoi programmi.',\n  navStoria: 'Storia', navCostruire: 'Costruirne uno', navEmu: 'Emulatore', navGuida: 'Come si usa',\n  storiaT: 'Che cos\\'\u00e8',\n  storia: '<p>L\\'<b>IMSAI 8080<\/b> \u00e8 uno dei primi personal computer. Lo produceva <b>IMS Associates<\/b> di San Leandro, in California, l\\'azienda di <b>Bill Millard<\/b>; il capo ingegnere era Joe Killian. I primi kit furono spediti a <b>dicembre 1975<\/b>.<\/p>' +\n    '<p>Nasce come risposta all\\'<b>Altair 8800<\/b> della MITS: usa la stessa CPU <b>Intel 8080A a 2 MHz<\/b> e lo stesso <b>bus S-100<\/b>, per cui le schede dell\\'Altair funzionano anche nell\\'IMSAI. Rispetto all\\'Altair aveva un pannello frontale pi\u00f9 comodo, con grandi leve a bilanciere al posto dei piccoli interruttori a levetta.<\/p>' +\n    '<p>Non c\\'erano tastiera n\u00e9 schermo: il computer si programmava <b>byte per byte<\/b> con le leve del pannello e si leggevano i risultati sui LED. Con una scheda seriale si collegava un terminale (una telescrivente o un terminale video) e pi\u00f9 avanti i dischi e il sistema operativo CP\/M.<\/p>' +\n    '<p>Tra il 1975 e il 1978 ne furono venduti circa <b>17.000-20.000<\/b>. IMSAI fall\u00ec nell\\'ottobre 1979. Il marchio pass\u00f2 a Thomas Fischer e Nancy Freitas (Fischer-Freitas), che continuarono ad assistere le macchine. Bill Millard aveva intanto fondato la catena di negozi <b>ComputerLand<\/b>.<\/p>' +\n    '<p>Nel 1983 un IMSAI 8080 \u00e8 il computer del protagonista di <b>WarGames - Giochi di guerra<\/b>: con quello David Lightman entra per sbaglio nel computer militare WOPR. L\\'esemplare del film era stato prestato da Fischer-Freitas.<\/p>',\n  schedaT: 'Scheda tecnica',\n  scheda: '<tr><th>Produttore<\/th><td>IMS Associates, San Leandro (California)<\/td><\/tr>' +\n    '<tr><th>Uscita<\/th><td>dicembre 1975<\/td><\/tr>' +\n    '<tr><th>CPU<\/th><td>Intel 8080A a 2 MHz, 8 bit, 64 KB indirizzabili<\/td><\/tr>' +\n    '<tr><th>Bus<\/th><td>S-100, compatibile con le schede dell\\'Altair 8800<\/td><\/tr>' +\n    '<tr><th>Memoria<\/th><td>256 byte nella configurazione base (su una scheda da 4 KB); fino a 64 KB con altre schede<\/td><\/tr>' +\n    '<tr><th>Pannello<\/th><td>44 LED e 22 leve: indirizzo, dati, stato, uscita programmabile<\/td><\/tr>' +\n    '<tr><th>Prezzo<\/th><td>439 $ in kit, 621 $ montato (pubblicit\u00e0 di gennaio 1976); in seguito 599 $ e 931 $<\/td><\/tr>' +\n    '<tr><th>Esemplari<\/th><td>circa 17.000-20.000 (1975-1978)<\/td><\/tr>' +\n    '<tr><th>Sistemi operativi<\/th><td>nessuno di base; pi\u00f9 avanti CP\/M, con i dischi<\/td><\/tr>',\n  pannelloSpiega: '<p><b>Come si legge il pannello.<\/b> A sinistra, in alto, gli 8 LED <b>PROGRAMMED OUTPUT<\/b> sono una porta di uscita (FFh) che il programma accende come vuole. Sotto, lo <b>STATUS BYTE<\/b> dice che cosa sta facendo la CPU: legge la memoria (MEMR), legge un\\'istruzione (M1), usa lo stack, fa input o output, \u00e8 ferma su HLT. Al centro il <b>DATA BUS<\/b>, sotto le due met\u00e0 dell\\'<b>ADDRESS BUS<\/b>. A destra RUN, WAIT (CPU ferma) e INTERRUPTS ENABLED.<\/p>' +\n    '<p>Le 16 leve in basso servono per scrivere indirizzi e dati in binario: <b>su = 1<\/b>, <b>gi\u00f9 = 0<\/b>. I colori le dividono in gruppi da 4 (esadecimale); il righello sopra le divide anche in gruppi da 3 (ottale), il sistema usato da molti manuali dell\\'epoca. Le 8 leve di sinistra (<b>PROGRAMMED INPUT<\/b>) si possono anche leggere da programma con <code>IN 0FFH<\/code>.<\/p>',\n\n  costruireT: 'Costruirne uno',\n  costruireIntro: 'Gli originali sono rari e costano cari, ma ci sono molti modi per averne uno sulla scrivania: kit di repliche con un microcontrollore che emula l\\'8080 al posto della CPU originale, progetti fai da te con Arduino o Raspberry Pi Pico, e mobili o leve stampati in 3D, in plastica ma con l\\'aspetto dell\\'originale.',\n  fotoDi: 'Foto:',\n\n  emuT: 'Emulatore IMSAI 8080',\n  emuIntro: 'Un IMSAI 8080 con 64 KB di RAM, una scheda seriale e un terminale video. La CPU 8080 \u00e8 emulata istruzione per istruzione, alla velocit\u00e0 originale di 2 MHz. Scegli un programma e premi <b>Carica ed esegui<\/b>, oppure usa le leve come nel 1975: la guida passo passo \u00e8 <a href=\"#rfimGuida\">pi\u00f9 sotto<\/a>.',\n  prog: 'Programma di esempio', loadRun: 'Carica ed esegui', loadOnly: 'Carica soltanto',\n  speed: 'Velocit\u00e0', sp1: '2 MHz (originale)', sp4: '8 MHz (4\u00d7)', sp0: 'massima',\n  baud: 'Seriale (baud)', baud0: 'istantanea',\n  panelHint: 'Clicca sulla <b>met\u00e0 alta<\/b> di una leva per alzarla e sulla <b>met\u00e0 bassa<\/b> per abbassarla (anche con Tab e le frecce su\/gi\u00f9). Le leve di comando tornano al centro da sole. Mentre il programma gira i LED brillano in proporzione al tempo in cui il segnale \u00e8 a 1, come sull\\'originale.',\n  termName: 'TERMINALE VIDEO 80\u00d724 \u00b7 ADM-3A',\n  termClick: 'clicca qui per scrivere',\n  caps: 'MAIUSCOLE', termClr: 'Pulisci', termCopy: 'Copia il testo',\n  termBoot: '\\x1aIMSAI 8080 - 64K RAM - TERMINALE PRONTO\\r\\n\\r\\nSCEGLI UN PROGRAMMA E PREMI \"CARICA ED ESEGUI\",\\r\\nOPPURE USA LE LEVE DEL PANNELLO.\\r\\n',\n  cpuT: 'CPU 8080', off: 'spento',\n  cycles: '{n} stati di clock \u00b7 {s} s a 2 MHz',\n  asmT: 'Assembler: scrivi il tuo programma',\n  asmNote: 'Sintassi Intel 8080: etichette, <code>ORG<\/code>, <code>EQU<\/code>, <code>DB<\/code>, <code>DW<\/code>, <code>DS<\/code>, <code>END<\/code>, commenti con <code>;<\/code>, numeri come <code>0FFH<\/code>, <code>10110B<\/code>, <code>377Q<\/code>, <code>\\'A\\'<\/code>. Il sorgente resta salvato in questo browser. Porte utili: <code>OUT 0FFH<\/code> LED, <code>IN 0FFH<\/code> leve A15-A8, <code>02H<\/code>\/<code>03H<\/code> terminale.',\n  asmRun: 'Assembla ed esegui', asmLoad: 'Assembla e carica', asmSave: 'Salva il sorgente (.asm)',\n  asmErr: '{n} errori:', asmOk: '{n} byte da {a} a {b}, partenza da {s}.', asmEmpty: 'Nessuna istruzione da assemblare.', riga: 'riga',\n  loaded: 'Programma in memoria: premi RUN sul pannello (o SINGLE STEP per un\\'istruzione alla volta).',\n  fileT: 'Salva e carica',\n  fileNote: 'Salva un pezzo di memoria in un file <b>Intel HEX<\/b> (il formato pi\u00f9 usato per i programmi 8080, leggibile da z80pack e dalle repliche) o in un file <b>binario<\/b>. Puoi caricare file <code>.hex<\/code>, <code>.bin<\/code>, <code>.com<\/code> (all\\'indirizzo \"Da\", 0100h per i .com) e sorgenti <code>.asm<\/code>. Con \"Salva nel browser\" conservi tutto lo stato della macchina in questo browser.',\n  from: 'Da (hex)', to: 'A (hex)', saveHex: 'Salva Intel HEX', saveBin: 'Salva binario', openFile: 'Carica un file\u2026',\n  snapSave: 'Salva nel browser', snapLoad: 'Riprendi dal browser',\n  badRange: 'Intervallo non valido: usa indirizzi esadecimali da 0000 a FFFF.',\n  savedHex: 'Salvati {n} byte in Intel HEX.', savedBin: 'Salvati {n} byte (da caricare all\\'indirizzo {a}).',\n  tooBig: 'File troppo grande per 64 KB di memoria.', emptyFile: 'Il file non contiene dati.',\n  loadedHex: 'Caricati {n} byte ({a}-{b}), partenza da {s}: premi RUN.', loadedAsm: 'Sorgente caricato e assemblato.', loadedBin: 'Caricati {n} byte da {a}: premi RUN.',\n  snapSaved: 'Stato della macchina salvato in questo browser.', snapErr: 'Impossibile salvare nel browser (spazio esaurito o navigazione privata).',\n  snapNone: 'Nessuno stato salvato in questo browser.', snapBad: 'Lo stato salvato in questo browser non \u00e8 valido.',\n  asmFail: 'Il sorgente \u00e8 troppo complesso da assemblare.', snapLoaded: 'Stato del {d} ripristinato.', copied: 'Testo del terminale copiato.',\n  setSw: 'imposta le leve: {v} = {b}',\n\n  guidaT: 'Come si usa: il primo programma a mano',\n  guidaIntro: '<p>Nel 1975 non c\\'era un sistema operativo da caricare: quando si accendeva l\\'IMSAI la memoria era vuota e il programma si inseriva <b>un byte alla volta con le leve<\/b>. Proviamo con un programma di 8 byte che copia le leve di sinistra sui LED PROGRAMMED OUTPUT:<\/p>' +\n    '<p><code>0000 DB FF<\/code> IN 0FFH \u00b7 <code>0002 2F<\/code> CMA \u00b7 <code>0003 D3 FF<\/code> OUT 0FFH \u00b7 <code>0005 C3 00 00<\/code> JMP 0000<\/p>' +\n    '<p>Ogni pulsante \"imposta le leve\" mette le 8 leve ADDRESS-DATA nella posizione giusta, cos\u00ec vedi il numero binario; poi tocca a te azionare la leva di comando.<\/p>',\n  steps: [\n    ['Controlla che il computer sia acceso: la leva <b>PWR ON<\/b> deve essere alzata. Se la abbassi la RAM si cancella, come sull\\'originale.'],\n    ['Alza <b>RESET<\/b>: la CPU torna all\\'indirizzo 0000 e i LED ADDRESS BUS si spengono.'],\n    ['Primo byte, <b>DB<\/b> (l\\'istruzione IN). Imposta le leve e alza <b>DEPOSIT<\/b>: il valore appare sui LED DATA BUS.', 0xDB],\n    ['Secondo byte, <b>FF<\/b> (il numero della porta): imposta le leve e abbassa <b>DEPOSIT NEXT<\/b>, che passa all\\'indirizzo 0001 e ci scrive il byte.', 0xFF],\n    ['<b>2F<\/b> (CMA, inverte i bit perch\u00e9 i LED si accendono con gli 0): DEPOSIT NEXT.', 0x2F],\n    ['<b>D3<\/b> (OUT): DEPOSIT NEXT.', 0xD3],\n    ['<b>FF<\/b> (porta dei LED): DEPOSIT NEXT.', 0xFF],\n    ['<b>C3<\/b> (JMP): DEPOSIT NEXT.', 0xC3],\n    ['<b>00<\/b>: DEPOSIT NEXT.', 0x00],\n    ['<b>00<\/b>: DEPOSIT NEXT. Il programma \u00e8 in memoria, agli indirizzi 0000-0007.', 0x00],\n    ['Per controllare: <b>RESET<\/b>, poi abbassa pi\u00f9 volte <b>EXAMINE NEXT<\/b> e leggi ogni byte sui LED DATA BUS (l\\'indirizzo \u00e8 sui LED ADDRESS BUS).'],\n    ['<b>RESET<\/b> e poi <b>RUN<\/b>: si accende il LED RUN. Ora alza e abbassa le 8 leve di <b>sinistra<\/b> (A15-A8): i LED PROGRAMMED OUTPUT le seguono. Con <b>STOP<\/b> la CPU si ferma e i LED mostrano dove era arrivata.']\n  ],\n  guidaDopo: '<p>Sbagliato un byte? Imposta l\\'indirizzo con tutte le 16 leve, alza <b>EXAMINE<\/b> per andarci e poi correggi con <b>DEPOSIT<\/b>. Con <b>SINGLE STEP<\/b> (CPU ferma) esegui un\\'istruzione alla volta e segui registri e istruzioni nel riquadro CPU 8080.<\/p>' +\n    '<p>Poi prova <b>Kill the Bit<\/b>, il gioco del 1975: un bit gira sui LED ADDRESS BUS 15-8 e per \"ucciderlo\" devi alzare e riabbassare la leva sotto di lui proprio mentre passa. Per i programmi pi\u00f9 lunghi c\\'\u00e8 il <b>mini monitor<\/b> sul terminale: con <code>E 1000<\/code> inserisci i byte da tastiera e con <code>G 1000<\/code> li esegui, come si faceva con un terminale collegato alla seriale.<\/p>',\n  leveT: 'Leve e LED del pannello',\n  leve: '<tr><td>15-8<\/td><td><b>ADDRESS-PROGRAMMED INPUT<\/b>: parte alta dell\\'indirizzo per EXAMINE; un programma le legge con <code>IN 0FFH<\/code>.<\/td><\/tr>' +\n    '<tr><td>7-0<\/td><td><b>ADDRESS-DATA<\/b>: parte bassa dell\\'indirizzo e byte da scrivere con DEPOSIT.<\/td><\/tr>' +\n    '<tr><td>EXAMINE<\/td><td>Va all\\'indirizzo impostato sulle 16 leve e ne mostra il contenuto. <b>EXAMINE NEXT<\/b> (gi\u00f9): indirizzo successivo.<\/td><\/tr>' +\n    '<tr><td>DEPOSIT<\/td><td>Scrive le leve 7-0 all\\'indirizzo attuale. <b>DEPOSIT NEXT<\/b> (gi\u00f9): passa all\\'indirizzo successivo e scrive l\u00ec.<\/td><\/tr>' +\n    '<tr><td>RESET<\/td><td>Azzera la CPU: il programma ripartir\u00e0 da 0000. <b>EXT. CLR.<\/b> (gi\u00f9) azzera le schede di I\/O: qui svuota la seriale e spegne i LED PROGRAMMED OUTPUT.<\/td><\/tr>' +\n    '<tr><td>RUN \/ STOP<\/td><td>Avvia la CPU dall\\'indirizzo attuale \/ la ferma.<\/td><\/tr>' +\n    '<tr><td>SINGLE STEP<\/td><td>Con la CPU ferma esegue un\\'istruzione (in questo emulatore sempre un\\'istruzione intera).<\/td><\/tr>' +\n    '<tr><td>PWR ON\/OFF<\/td><td>Accende e spegne. Spegnendo si perde il contenuto della RAM.<\/td><\/tr>' +\n    '<tr><td>STATUS BYTE<\/td><td>MEMR lettura dalla memoria \u00b7 INP input \u00b7 M1 lettura del codice di un\\'istruzione \u00b7 OUT output \u00b7 HLTA CPU ferma su HLT \u00b7 STACK accesso allo stack \u00b7 W\u0304O acceso quando la CPU <i>non<\/i> scrive \u00b7 INTA risposta a un interrupt.<\/td><\/tr>',\n  porteT: 'Porte di I\/O dell\\'emulatore',\n  porte: '<tr><td>OUT FFh<\/td><td>LED PROGRAMMED OUTPUT. Logica negativa: un bit a 0 accende il LED, per questo i programmi usano <code>CMA<\/code> prima di <code>OUT<\/code>.<\/td><\/tr>' +\n    '<tr><td>IN FFh<\/td><td>Le leve A15-A8 (programmed input \/ sense switches).<\/td><\/tr>' +\n    '<tr><td>02h<\/td><td>Scheda seriale in stile IMSAI SIO-2, dati: <code>IN<\/code> legge il tasto premuto, <code>OUT<\/code> scrive sul terminale.<\/td><\/tr>' +\n    '<tr><td>03h<\/td><td>Stato della seriale: bit 0 = pronta a trasmettere (TxRDY), bit 1 = carattere ricevuto (RxRDY).<\/td><\/tr>' +\n    '<tr><td>10h \/ 11h<\/td><td>Seriale compatibile MITS 88-2SIO (stato: bit 0 ricevuto, bit 1 pronta; dati) per i programmi scritti per l\\'Altair.<\/td><\/tr>' +\n    '<tr><td>Terminale<\/td><td>80\u00d724 come un Lear Siegler ADM-3A: <code>1Ah<\/code> cancella lo schermo, <code>1Eh<\/code> cursore in alto, <code>ESC = riga colonna<\/code> (pi\u00f9 20h) posiziona il cursore, <code>07h<\/code> campanello. La velocit\u00e0 in baud si sceglie sopra il pannello: a 300 baud si vede scrivere un carattere alla volta.<\/td><\/tr>',\n  fontiT: 'Fonti',\n  fonti: '<li>Wikipedia: <a href=\"https:\/\/en.wikipedia.org\/wiki\/IMSAI_8080\" target=\"_blank\" rel=\"noopener\">IMSAI 8080<\/a>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/IMS_Associates\" target=\"_blank\" rel=\"noopener\">IMS Associates<\/a>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/ComputerLand\" target=\"_blank\" rel=\"noopener\">ComputerLand<\/a><\/li>' +\n    '<li>Pubblicit\u00e0 IMSAI su Byte, gennaio 1976: <a href=\"https:\/\/commons.wikimedia.org\/wiki\/File:IMSAI_8080_-_Jan_1976_Byte_advert.jpg\" target=\"_blank\" rel=\"noopener\">Wikimedia Commons<\/a><\/li>' +\n    '<li><a href=\"https:\/\/www.computerhistory.org\/revolution\/artifact\/297\/1153\" target=\"_blank\" rel=\"noopener\">Computer History Museum<\/a> e <a href=\"https:\/\/oldcomputers.net\/imsai8080\" target=\"_blank\" rel=\"noopener\">oldcomputers.net<\/a><\/li>' +\n    '<li>Thomas Fischer, <a href=\"https:\/\/www.imsai.net\/the-wargames-imsai\/\" target=\"_blank\" rel=\"noopener\">The WarGames IMSAI<\/a> (imsai.net)<\/li>' +\n    '<li>Porte del pannello verificate sul codice di <a href=\"https:\/\/github.com\/udo-munk\/z80pack\" target=\"_blank\" rel=\"noopener\">z80pack<\/a> e sulla <a href=\"https:\/\/grantmestrength.github.io\/RetroComputerInstructionManual\/imsai_front_panel\" target=\"_blank\" rel=\"noopener\">guida al pannello IMSAI<\/a><\/li>'\n},\n\nen: {\n  kicker: '> a 1975 retro computer',\n  lead: 'The microcomputer with the red and blue paddle switches made famous by the film <b>WarGames<\/b>. Here you\\'ll find its history, photos, projects to build a replica and, further down, a <b>complete emulator<\/b>: a working front panel, a video terminal and an assembler to write your own programs.',\n  navStoria: 'History', navCostruire: 'Build one', navEmu: 'Emulator', navGuida: 'How to use it',\n  storiaT: 'What it is',\n  storia: '<p>The <b>IMSAI 8080<\/b> is one of the first personal computers. It was made by <b>IMS Associates<\/b> of San Leandro, California, the company of <b>Bill Millard<\/b>; the chief engineer was Joe Killian. The first kits shipped in <b>December 1975<\/b>.<\/p>' +\n    '<p>It was an answer to MITS\\'s <b>Altair 8800<\/b>: same <b>Intel 8080A CPU at 2 MHz<\/b> and same <b>S-100 bus<\/b>, so Altair boards also work in the IMSAI. Compared with the Altair it had a friendlier front panel, with large paddle switches instead of small toggles.<\/p>' +\n    '<p>There was no keyboard and no screen: you programmed it <b>byte by byte<\/b> with the panel switches and read the results on the LEDs. A serial card let you connect a terminal (a teletype or a video terminal), and later came disks and the CP\/M operating system.<\/p>' +\n    '<p>About <b>17,000-20,000<\/b> were sold between 1975 and 1978. IMSAI went bankrupt in October 1979. The name passed to Thomas Fischer and Nancy Freitas (Fischer-Freitas), who kept supporting the machines. Bill Millard had meanwhile founded the <b>ComputerLand<\/b> store chain.<\/p>' +\n    '<p>In 1983 an IMSAI 8080 is the hero\\'s computer in <b>WarGames<\/b>: David Lightman uses it to break, by mistake, into the WOPR military computer. The machine in the film was lent by Fischer-Freitas.<\/p>',\n  schedaT: 'Specifications',\n  scheda: '<tr><th>Maker<\/th><td>IMS Associates, San Leandro (California)<\/td><\/tr>' +\n    '<tr><th>Released<\/th><td>December 1975<\/td><\/tr>' +\n    '<tr><th>CPU<\/th><td>Intel 8080A at 2 MHz, 8-bit, 64 KB address space<\/td><\/tr>' +\n    '<tr><th>Bus<\/th><td>S-100, compatible with Altair 8800 boards<\/td><\/tr>' +\n    '<tr><th>Memory<\/th><td>256 bytes in the base configuration (on a 4 KB board); up to 64 KB with more boards<\/td><\/tr>' +\n    '<tr><th>Panel<\/th><td>44 LEDs and 22 paddle switches: address, data, status, programmed output<\/td><\/tr>' +\n    '<tr><th>Price<\/th><td>$439 kit, $621 assembled (January 1976 ad); later $599 and $931<\/td><\/tr>' +\n    '<tr><th>Units<\/th><td>about 17,000-20,000 (1975-1978)<\/td><\/tr>' +\n    '<tr><th>Operating systems<\/th><td>none built in; later CP\/M, with disks<\/td><\/tr>',\n  pannelloSpiega: '<p><b>Reading the panel.<\/b> Top left, the 8 <b>PROGRAMMED OUTPUT<\/b> LEDs are an output port (FFh) the program can light as it likes. Below, the <b>STATUS BYTE<\/b> shows what the CPU is doing: reading memory (MEMR), fetching an instruction (M1), using the stack, doing input or output, halted on HLT. In the middle the <b>DATA BUS<\/b>, and below the two halves of the <b>ADDRESS BUS<\/b>. On the right RUN, WAIT (CPU stopped) and INTERRUPTS ENABLED.<\/p>' +\n    '<p>The 16 switches at the bottom enter addresses and data in binary: <b>up = 1<\/b>, <b>down = 0<\/b>. The colours split them into groups of 4 (hexadecimal); the ruler above also splits them into groups of 3 (octal), the system used by many manuals of the time. The 8 left-hand switches (<b>PROGRAMMED INPUT<\/b>) can also be read by a program with <code>IN 0FFH<\/code>.<\/p>',\n\n  costruireT: 'Build one',\n  costruireIntro: 'Originals are rare and expensive, but there are many ways to get one on your desk: replica kits where a microcontroller emulates the 8080, DIY projects with Arduino or Raspberry Pi Pico, and 3D-printed cabinets or switches, plastic but true to the original look.',\n  fotoDi: 'Photo:',\n\n  emuT: 'IMSAI 8080 emulator',\n  emuIntro: 'An IMSAI 8080 with 64 KB of RAM, a serial card and a video terminal. The 8080 CPU is emulated instruction by instruction at the original 2 MHz. Pick a program and press <b>Load and run<\/b>, or use the switches like in 1975: the step-by-step guide is <a href=\"#rfimGuida\">below<\/a>.',\n  prog: 'Sample program', loadRun: 'Load and run', loadOnly: 'Load only',\n  speed: 'Speed', sp1: '2 MHz (original)', sp4: '8 MHz (4\u00d7)', sp0: 'maximum',\n  baud: 'Serial (baud)', baud0: 'instant',\n  panelHint: 'Click the <b>upper half<\/b> of a switch to raise it and the <b>lower half<\/b> to lower it (Tab and the up\/down arrows work too). Command switches spring back to the centre. While a program runs the LEDs glow in proportion to how long each signal is 1, like on the original.',\n  termName: 'VIDEO TERMINAL 80\u00d724 \u00b7 ADM-3A',\n  termClick: 'click here to type',\n  caps: 'UPPER CASE', termClr: 'Clear', termCopy: 'Copy text',\n  termBoot: '\\x1aIMSAI 8080 - 64K RAM - TERMINAL READY\\r\\n\\r\\nPICK A PROGRAM AND PRESS \"LOAD AND RUN\",\\r\\nOR USE THE PANEL SWITCHES.\\r\\n',\n  cpuT: '8080 CPU', off: 'off',\n  cycles: '{n} clock states \u00b7 {s} s at 2 MHz',\n  asmT: 'Assembler: write your own program',\n  asmNote: 'Intel 8080 syntax: labels, <code>ORG<\/code>, <code>EQU<\/code>, <code>DB<\/code>, <code>DW<\/code>, <code>DS<\/code>, <code>END<\/code>, <code>;<\/code> comments, numbers like <code>0FFH<\/code>, <code>10110B<\/code>, <code>377Q<\/code>, <code>\\'A\\'<\/code>. The source is kept in this browser. Useful ports: <code>OUT 0FFH<\/code> LEDs, <code>IN 0FFH<\/code> switches A15-A8, <code>02H<\/code>\/<code>03H<\/code> terminal. (The sample programs are commented in Italian.)',\n  asmRun: 'Assemble and run', asmLoad: 'Assemble and load', asmSave: 'Save source (.asm)',\n  asmErr: '{n} errors:', asmOk: '{n} bytes from {a} to {b}, start at {s}.', asmEmpty: 'Nothing to assemble.', riga: 'line',\n  loaded: 'Program in memory: press RUN on the panel (or SINGLE STEP for one instruction at a time).',\n  fileT: 'Save and load',\n  fileNote: 'Save a block of memory as an <b>Intel HEX<\/b> file (the most common format for 8080 programs, readable by z80pack and the replicas) or as a <b>binary<\/b> file. You can load <code>.hex<\/code>, <code>.bin<\/code>, <code>.com<\/code> (at the \"From\" address, 0100h for .com) and <code>.asm<\/code> sources. \"Save in browser\" keeps the whole machine state in this browser.',\n  from: 'From (hex)', to: 'To (hex)', saveHex: 'Save Intel HEX', saveBin: 'Save binary', openFile: 'Load a file\u2026',\n  snapSave: 'Save in browser', snapLoad: 'Restore from browser',\n  badRange: 'Invalid range: use hex addresses from 0000 to FFFF.',\n  savedHex: 'Saved {n} bytes as Intel HEX.', savedBin: 'Saved {n} bytes (load them at {a}).',\n  tooBig: 'File too big for 64 KB of memory.', emptyFile: 'The file contains no data.',\n  loadedHex: 'Loaded {n} bytes ({a}-{b}), start at {s}: press RUN.', loadedAsm: 'Source loaded and assembled.', loadedBin: 'Loaded {n} bytes at {a}: press RUN.',\n  snapSaved: 'Machine state saved in this browser.', snapErr: 'Could not save in the browser (storage full or private browsing).',\n  snapNone: 'No state saved in this browser.', snapBad: 'The state saved in this browser is not valid.',\n  asmFail: 'The source is too complex to assemble.', snapLoaded: 'State from {d} restored.', copied: 'Terminal text copied.',\n  setSw: 'set switches: {v} = {b}',\n\n  guidaT: 'How to use it: your first program by hand',\n  guidaIntro: '<p>In 1975 there was no operating system to load: when you switched the IMSAI on, memory was empty and you entered the program <b>one byte at a time with the switches<\/b>. Let\\'s try an 8-byte program that copies the left-hand switches to the PROGRAMMED OUTPUT LEDs:<\/p>' +\n    '<p><code>0000 DB FF<\/code> IN 0FFH \u00b7 <code>0002 2F<\/code> CMA \u00b7 <code>0003 D3 FF<\/code> OUT 0FFH \u00b7 <code>0005 C3 00 00<\/code> JMP 0000<\/p>' +\n    '<p>Each \"set switches\" button puts the 8 ADDRESS-DATA switches in the right position so you can see the binary number; then it\\'s up to you to flip the command switch.<\/p>',\n  steps: [\n    ['Make sure the computer is on: the <b>PWR ON<\/b> switch must be up. Turning it off erases the RAM, like on the original.'],\n    ['Raise <b>RESET<\/b>: the CPU goes back to address 0000 and the ADDRESS BUS LEDs go dark.'],\n    ['First byte, <b>DB<\/b> (the IN instruction). Set the switches and raise <b>DEPOSIT<\/b>: the value shows on the DATA BUS LEDs.', 0xDB],\n    ['Second byte, <b>FF<\/b> (the port number): set the switches and press <b>DEPOSIT NEXT<\/b> (down), which moves to address 0001 and writes the byte there.', 0xFF],\n    ['<b>2F<\/b> (CMA, inverts the bits because the LEDs light on 0s): DEPOSIT NEXT.', 0x2F],\n    ['<b>D3<\/b> (OUT): DEPOSIT NEXT.', 0xD3],\n    ['<b>FF<\/b> (the LED port): DEPOSIT NEXT.', 0xFF],\n    ['<b>C3<\/b> (JMP): DEPOSIT NEXT.', 0xC3],\n    ['<b>00<\/b>: DEPOSIT NEXT.', 0x00],\n    ['<b>00<\/b>: DEPOSIT NEXT. The program is now in memory at 0000-0007.', 0x00],\n    ['To check it: <b>RESET<\/b>, then press <b>EXAMINE NEXT<\/b> (down) several times and read each byte on the DATA BUS LEDs (the address is on the ADDRESS BUS LEDs).'],\n    ['<b>RESET<\/b>, then <b>RUN<\/b>: the RUN LED lights up. Now flip the 8 <b>left-hand<\/b> switches (A15-A8): the PROGRAMMED OUTPUT LEDs follow them. <b>STOP<\/b> halts the CPU and the LEDs show where it was.']\n  ],\n  guidaDopo: '<p>Wrong byte? Set the address on all 16 switches, raise <b>EXAMINE<\/b> to go there, then fix it with <b>DEPOSIT<\/b>. With <b>SINGLE STEP<\/b> (CPU stopped) you run one instruction at a time and follow registers and code in the 8080 CPU box.<\/p>' +\n    '<p>Then try <b>Kill the Bit<\/b>, the 1975 game: a bit rotates on ADDRESS BUS LEDs 15-8 and to \"kill\" it you flip the switch under it up and down just as it passes. For longer programs there\\'s the <b>mini monitor<\/b> on the terminal: <code>E 1000<\/code> lets you type bytes from the keyboard and <code>G 1000<\/code> runs them, just like with a terminal on the serial port.<\/p>',\n  leveT: 'Panel switches and LEDs',\n  leve: '<tr><td>15-8<\/td><td><b>ADDRESS-PROGRAMMED INPUT<\/b>: high half of the address for EXAMINE; a program reads them with <code>IN 0FFH<\/code>.<\/td><\/tr>' +\n    '<tr><td>7-0<\/td><td><b>ADDRESS-DATA<\/b>: low half of the address and the byte written by DEPOSIT.<\/td><\/tr>' +\n    '<tr><td>EXAMINE<\/td><td>Goes to the address set on the 16 switches and shows its contents. <b>EXAMINE NEXT<\/b> (down): next address.<\/td><\/tr>' +\n    '<tr><td>DEPOSIT<\/td><td>Writes switches 7-0 to the current address. <b>DEPOSIT NEXT<\/b> (down): moves to the next address and writes there.<\/td><\/tr>' +\n    '<tr><td>RESET<\/td><td>Resets the CPU: the program will restart at 0000. <b>EXT. CLR.<\/b> (down) resets the I\/O boards: here it empties the serial buffer and turns off the PROGRAMMED OUTPUT LEDs.<\/td><\/tr>' +\n    '<tr><td>RUN \/ STOP<\/td><td>Starts the CPU from the current address \/ stops it.<\/td><\/tr>' +\n    '<tr><td>SINGLE STEP<\/td><td>With the CPU stopped, runs one instruction (always a whole instruction in this emulator).<\/td><\/tr>' +\n    '<tr><td>PWR ON\/OFF<\/td><td>Power. Turning it off loses the RAM contents.<\/td><\/tr>' +\n    '<tr><td>STATUS BYTE<\/td><td>MEMR memory read \u00b7 INP input \u00b7 M1 instruction fetch \u00b7 OUT output \u00b7 HLTA halted on HLT \u00b7 STACK stack access \u00b7 W\u0304O lit when the CPU is <i>not<\/i> writing \u00b7 INTA interrupt acknowledge.<\/td><\/tr>',\n  porteT: 'Emulator I\/O ports',\n  porte: '<tr><td>OUT FFh<\/td><td>PROGRAMMED OUTPUT LEDs. Active low: a 0 bit lights the LED, which is why programs use <code>CMA<\/code> before <code>OUT<\/code>.<\/td><\/tr>' +\n    '<tr><td>IN FFh<\/td><td>Switches A15-A8 (programmed input \/ sense switches).<\/td><\/tr>' +\n    '<tr><td>02h<\/td><td>IMSAI SIO-2 style serial card, data: <code>IN<\/code> reads the key pressed, <code>OUT<\/code> writes to the terminal.<\/td><\/tr>' +\n    '<tr><td>03h<\/td><td>Serial status: bit 0 = ready to transmit (TxRDY), bit 1 = character received (RxRDY).<\/td><\/tr>' +\n    '<tr><td>10h \/ 11h<\/td><td>MITS 88-2SIO compatible serial (status: bit 0 received, bit 1 ready; data) for programs written for the Altair.<\/td><\/tr>' +\n    '<tr><td>Terminal<\/td><td>80\u00d724 like a Lear Siegler ADM-3A: <code>1Ah<\/code> clears the screen, <code>1Eh<\/code> homes the cursor, <code>ESC = row column<\/code> (plus 20h) positions it, <code>07h<\/code> bell. Choose the baud rate above the panel: at 300 baud you see each character being typed.<\/td><\/tr>',\n  fontiT: 'Sources',\n  fonti: '<li>Wikipedia: <a href=\"https:\/\/en.wikipedia.org\/wiki\/IMSAI_8080\" target=\"_blank\" rel=\"noopener\">IMSAI 8080<\/a>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/IMS_Associates\" target=\"_blank\" rel=\"noopener\">IMS Associates<\/a>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/ComputerLand\" target=\"_blank\" rel=\"noopener\">ComputerLand<\/a><\/li>' +\n    '<li>IMSAI advertisement in Byte, January 1976: <a href=\"https:\/\/commons.wikimedia.org\/wiki\/File:IMSAI_8080_-_Jan_1976_Byte_advert.jpg\" target=\"_blank\" rel=\"noopener\">Wikimedia Commons<\/a><\/li>' +\n    '<li><a href=\"https:\/\/www.computerhistory.org\/revolution\/artifact\/297\/1153\" target=\"_blank\" rel=\"noopener\">Computer History Museum<\/a> and <a href=\"https:\/\/oldcomputers.net\/imsai8080\" target=\"_blank\" rel=\"noopener\">oldcomputers.net<\/a><\/li>' +\n    '<li>Thomas Fischer, <a href=\"https:\/\/www.imsai.net\/the-wargames-imsai\/\" target=\"_blank\" rel=\"noopener\">The WarGames IMSAI<\/a> (imsai.net)<\/li>' +\n    '<li>Panel ports checked against the <a href=\"https:\/\/github.com\/udo-munk\/z80pack\" target=\"_blank\" rel=\"noopener\">z80pack<\/a> source and the <a href=\"https:\/\/grantmestrength.github.io\/RetroComputerInstructionManual\/imsai_front_panel\" target=\"_blank\" rel=\"noopener\">IMSAI front panel guide<\/a><\/li>'\n}\n};\n})(window.RFImsai = window.RFImsai || {});\n\n\/* RetroFixer \u00b7 IMSAI 8080 \u00b7 interfaccia della pagina *\/\n(function (RF) {\n'use strict';\nvar root = document.querySelector('.rfim');\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); }\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); });\nvar H = RF.hex;\n\n\/\/ ---- foto e schede dei progetti --------------------------------------------------------------\n[1, 2].forEach(function (n) {\n  var f = RF.PHOTOS[n - 1], img = $('rfimFoto' + n), cap = $('rfimFoto' + 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 = $('rfimCards');\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, pannello, terminale --------------------------------------------------------------\nvar m = new RF.Imsai();\nvar term = new RF.Terminal($('rfimTerm'), 80, 24);\nm.onTx = function (c) { term.put(c); };\nvar panel = new RF.Panel($('rfimPanel'), m, { onCmd: function (c) { afterCmd(c); } });\nif (document.fonts && document.fonts.load) document.fonts.load('20px VT323').then(function () { term.measure(); term.dirty = true; }, function () {});\n\nvar current = null;   \/\/ programma caricato: {start, min, max, list}\n\nfunction afterCmd(c) {\n  if (c === 'powerOff') term.clear();\n  if (c === 'powerOn') term.write(tr('termBoot'));\n  dbgDirty = true;\n}\nvar flashT;\nfunction flash(s) {\n  var e = $('rfimPDesc'); e.dataset.old = e.dataset.old || e.innerHTML; e.innerHTML = '<b>' + s + '<\/b>';\n  clearTimeout(flashT); flashT = setTimeout(function () { e.innerHTML = e.dataset.old; delete e.dataset.old; }, 3500);\n}\n\n\/\/ ---- programmi di esempio ---------------------------------------------------------------------\nvar sel = $('rfimProg');\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() { $('rfimPDesc').innerHTML = prog().desc[L]; delete $('rfimPDesc').dataset.old; }\nsel.addEventListener('change', function () { showDesc(); $('rfimSrc').value = prog().src; saveSrc(); });\n\nfunction loadAssembled(r, run, name) {\n  if (!m.power) { m.setPower(true); panel.syncSwitches(); afterCmd('powerOn'); }\n  m.running = false;\n  m.cpu.reset(); m.cpu.prot.fill(0);\n  m.outLatch = 0xFF; m.rx.length = 0; m.typed.length = 0;\n  m.load(r.mem);\n  m.showAt(r.start);\n  current = { start: r.start, min: r.min, max: r.max, name: name };\n  $('rfimFrom').value = H(r.min, 4); $('rfimTo').value = H(Math.max(r.min, r.max), 4);\n  if (run) m.cmd('run');\n  dbgDirty = true;\n}\nfunction assembleEditor(run) {\n  var src = $('rfimSrc').value, r, out = $('rfimAsmOut');\n  out.hidden = false;\n  try { r = RF.assemble(src); }\n  catch (e) { out.className = 'asmout err'; out.textContent = tr('asmFail'); return null; }\n  if (r.errors.length) {\n    out.className = 'asmout err';\n    out.innerHTML = '<b>' + tr('asmErr', { n: r.errors.length }) + '<\/b>' + r.errors.slice(0, 12).map(function (e) {\n      return '<div>' + tr('riga') + ' ' + e.line + ': ' + esc(e.msg) + ' <code>' + esc(src.split('\\n')[e.line - 1].trim()) + '<\/code><\/div>';\n    }).join('');\n    return null;\n  }\n  if (!r.size) { out.className = 'asmout err'; out.textContent = tr('asmEmpty'); return null; }\n  loadAssembled(r, run, 'asm');\n  out.className = 'asmout';\n  out.innerHTML = '<b>' + tr('asmOk', { n: r.size, a: H(r.min, 4), b: H(r.max, 4), s: H(r.start, 4) }) + '<\/b>' +\n    '<pre>' + r.list.map(function (l) {\n      var addr = l.equ !== undefined ? '= ' + H(l.equ, 4) : (l.bytes.length ? H(l.addr, 4) : '    ');\n      var by = l.bytes.slice(0, 4).map(function (b) { return H(b, 2); }).join(' ');\n      return esc(pad(addr, 7) + pad(by, 13) + l.src);\n    }).join('\\n') + '<\/pre>';\n  return r;\n}\nfunction pad(s, n) { while (s.length < n) s += ' '; return s; }\nfunction esc(s) { return String(s).replace(\/[&<>\"]\/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;' }[c]; }); }\n\n$('rfimLoadRun').addEventListener('click', function () { $('rfimSrc').value = prog().src; saveSrc(); var r = assembleEditor(true); if (r && prog().terminal) focusTerm(); });\n$('rfimLoadOnly').addEventListener('click', function () { $('rfimSrc').value = prog().src; saveSrc(); assembleEditor(false); flash(tr('loaded')); });\n$('rfimAsmRun').addEventListener('click', function () { assembleEditor(true); });\n$('rfimAsm').addEventListener('click', function () { if (assembleEditor(false)) flash(tr('loaded')); });\n$('rfimAsmSave').addEventListener('click', function () { download('programma.asm', new Blob([$('rfimSrc').value], { type: 'text\/plain' })); });\nfunction saveSrc() { try { localStorage.setItem('rfimSrc', $('rfimSrc').value); } catch (e) {} }\n$('rfimSrc').addEventListener('input', saveSrc);\n$('rfimSrc').addEventListener('keydown', function (e) {   \/\/ Tab inserisce una tabulazione\n  if (e.key === 'Tab' && !e.shiftKey) {\n    e.preventDefault(); var t = e.target, s = t.selectionStart;\n    t.value = t.value.slice(0, s) + '        '.slice(0, 8 - ((s - t.value.lastIndexOf('\\n', s - 1) - 1) % 8)) + t.value.slice(t.selectionEnd);\n    t.selectionStart = t.selectionEnd = s + (8 - ((s - t.value.lastIndexOf('\\n', s - 1) - 1) % 8)); saveSrc();\n  }\n});\n\n$('rfimSpeed').addEventListener('change', function () { m.speed = +this.value; });\n$('rfimBaud').addEventListener('change', function () { m.baud = +this.value; });\n\n\/\/ ---- tastiera del terminale ----------------------------------------------------------------------\nvar kbd = $('rfimKbd'), crt = kbd.parentNode;\nfunction focusTerm() { try { kbd.focus({ preventScroll: true }); } catch (e) { kbd.focus(); } }\ncrt.addEventListener('pointerdown', function (e) { e.preventDefault(); focusTerm(); });\nkbd.addEventListener('focus', function () { crt.classList.add('focus'); });\nkbd.addEventListener('blur', function () { crt.classList.remove('focus'); });\nfunction sendChar(c) {\n  if (!m.power) return;\n  if ($('rfimCaps').checked && c >= 97 && c <= 122) c -= 32;\n  m.key(c);\n}\nkbd.addEventListener('keydown', function (e) {\n  var k = e.key, c = -1;\n  if (e.ctrlKey && !e.altKey && k.length === 1) {\n    var u = k.toUpperCase().charCodeAt(0);\n    if (u >= 64 && u <= 95) c = u - 64;\n    if (k === 'c' && window.getSelection && String(window.getSelection())) return;\n    if (k === 'v') return;   \/\/ incolla: arriva con l'evento input\n  }\n  else if (k === 'Enter') c = 13;\n  else if (k === 'Backspace') c = 8;\n  else if (k === 'Escape') c = 27;\n  else if (k === 'Tab') c = 9;\n  else if (k === 'Delete') c = 127;\n  else if (k === 'ArrowLeft') c = 8;\n  else if (k === 'ArrowRight') c = 12;\n  else if (k === 'ArrowUp') c = 11;\n  else if (k === 'ArrowDown') c = 10;\n  if (c >= 0) { e.preventDefault(); sendChar(c); }\n});\nkbd.addEventListener('input', function () {\n  var v = kbd.value; kbd.value = '';\n  v = v.replace(\/\\r\\n?\/g, '\\n');\n  for (var i = 0; i < v.length; i++) {\n    var c = v.charCodeAt(i); if (c === 10) c = 13;\n    if (c < 128) sendChar(c);\n  }\n});\n$('rfimTermClr').addEventListener('click', function () { term.clear(); });\n$('rfimTermCopy').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\/\/ ---- salva e carica ---------------------------------------------------------------------------\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}\nfunction range() {\n  var a = parseInt($('rfimFrom').value, 16), b = parseInt($('rfimTo').value, 16);\n  if (isNaN(a) || isNaN(b) || a > 0xFFFF || b > 0xFFFF || b < a) { fileMsg(tr('badRange'), true); return null; }\n  return [a, b];\n}\nfunction fileMsg(s, err) { var e = $('rfimFileMsg'); e.textContent = s; e.className = 'msg' + (err ? ' err' : ''); }\n$('rfimSaveHex').addEventListener('click', function () {\n  var r = range(); if (!r) return;\n  var start = current && current.start >= r[0] && current.start <= r[1] ? current.start : r[0];\n  download('imsai-' + H(r[0], 4) + '-' + H(r[1], 4) + '.hex', new Blob([RF.toIntelHex(m.cpu.mem, r[0], r[1], start)], { type: 'text\/plain' }));\n  fileMsg(tr('savedHex', { n: r[1] - r[0] + 1 }));\n});\n$('rfimSaveBin').addEventListener('click', function () {\n  var r = range(); if (!r) return;\n  download('imsai-' + H(r[0], 4) + '-' + H(r[1], 4) + '.bin', new Blob([m.cpu.mem.slice(r[0], r[1] + 1)], { type: 'application\/octet-stream' }));\n  fileMsg(tr('savedBin', { n: r[1] - r[0] + 1, a: H(r[0], 4) }));\n});\n$('rfimOpen').addEventListener('click', function () { $('rfimFile').click(); });\n$('rfimFile').addEventListener('change', function () {\n  var f = this.files[0]; this.value = ''; if (!f) return;\n  if (f.size > 65536 * 3) { fileMsg(tr('tooBig'), true); return; }\n  var name = f.name.toLowerCase(), rd = new FileReader();\n  rd.onload = function () {\n    try {\n      if (\/\\.(hex|ihx|ihex)$\/.test(name)) {\n        var r = RF.fromIntelHex(rd.result);\n        if (!r.size) throw new Error(tr('emptyFile'));\n        loadAssembled(r, false, f.name);\n        fileMsg(tr('loadedHex', { n: r.size, a: H(r.min, 4), b: H(r.max, 4), s: H(r.start, 4) }));\n      } else if (\/\\.(asm|a80|s|txt)$\/.test(name)) {\n        $('rfimSrc').value = rd.result; saveSrc(); $('rfimSrc').closest('details').open = true;\n        var ok = assembleEditor(false);\n        fileMsg(ok ? tr('loadedAsm') : tr('asmErr', { n: '' }), !ok);\n      } else {\n        var data = new Uint8Array(rd.result), at = parseInt($('rfimFrom').value, 16) || 0;\n        if (\/\\.com$\/.test(name) && !at) at = 0x100;\n        if (at + data.length > 65536) throw new Error(tr('tooBig'));\n        var mem = {}; for (var i = 0; i < data.length; i++) mem[at + i] = data[i];\n        loadAssembled({ mem: mem, start: at, min: at, max: at + data.length - 1 }, false, f.name);\n        fileMsg(tr('loadedBin', { n: data.length, a: H(at, 4) }));\n      }\n    } catch (e) { fileMsg(e.message, true); }\n  };\n  if (\/\\.(hex|ihx|ihex|asm|a80|s|txt)$\/.test(name)) rd.readAsText(f); else rd.readAsArrayBuffer(f);\n});\n\/\/ stato completo nel browser (memoria, registri, interruttori)\n$('rfimSnapSave').addEventListener('click', function () {\n  var c = m.cpu, s = '', mem = c.mem;\n  for (var i = 0; i < mem.length; i += 8192) s += String.fromCharCode.apply(null, mem.subarray(i, i + 8192));\n  var st = { v: 1, mem: btoa(s), r: [c.a, c.b, c.c, c.d, c.e, c.h, c.l, c.sp, c.pc, c.getF(), c.inte, c.halted ? 1 : 0],\n    sw: m.switches, out: m.outLatch, prot: Array.from(c.prot), cur: current, src: $('rfimSrc').value, t: Date.now() };\n  try { localStorage.setItem('rfimSnap', JSON.stringify(st)); fileMsg(tr('snapSaved')); }\n  catch (e) { fileMsg(tr('snapErr'), true); }\n});\n$('rfimSnapLoad').addEventListener('click', function () {\n  var st; try { st = JSON.parse(localStorage.getItem('rfimSnap') || 'null'); } catch (e) {}\n  if (!st) { fileMsg(tr('snapNone'), true); return; }\n  \/\/ i dati salvati vengono controllati: solo numeri nei registri, testo nel sorgente\n  var okSnap = st && typeof st.mem === 'string' && Array.isArray(st.r) && st.r.length >= 12 &&\n    st.r.every(function (x) { return typeof x === 'number' && isFinite(x); });\n  var raw = null; if (okSnap) { try { raw = atob(st.mem); } catch (e) { okSnap = false; } }\n  if (!okSnap || raw.length !== 65536) { fileMsg(tr('snapBad'), true); return; }\n  st.mem = raw;\n  st.r = st.r.map(function (x, i) { return i === 7 || i === 8 ? x & 0xFFFF : x & 0xFF; });\n  st.sw = (+st.sw || 0) & 0xFFFF; st.out = (+st.out || 0) & 0xFF;\n  st.prot = Array.isArray(st.prot) ? st.prot.slice(0, 256).map(function (x) { return x ? 1 : 0; }) : [];\n  st.src = typeof st.src === 'string' ? st.src : '';\n  st.t = +st.t || Date.now();\n  st.cur = st.cur && typeof st.cur === 'object' ? { start: (+st.cur.start || 0) & 0xFFFF, min: (+st.cur.min || 0) & 0xFFFF, max: (+st.cur.max || 0) & 0xFFFF } : null;\n  if (!m.power) { m.setPower(true); afterCmd('powerOn'); }\n  m.running = false;\n  var c = m.cpu, s = st.mem;\n  for (var i = 0; i < 65536; i++) c.mem[i] = s.charCodeAt(i);\n  c.a = st.r[0]; c.b = st.r[1]; c.c = st.r[2]; c.d = st.r[3]; c.e = st.r[4]; c.h = st.r[5]; c.l = st.r[6];\n  c.sp = st.r[7]; c.setF(st.r[9]); c.inte = st.r[10]; c.halted = !!st.r[11];\n  m.showAt(st.r[8]); c.prot.set(st.prot || []);\n  m.switches = st.sw; m.outLatch = st.out; current = st.cur; panel.syncSwitches();\n  if (st.src) { $('rfimSrc').value = st.src; saveSrc(); }\n  fileMsg(tr('snapLoaded', { d: new Date(st.t).toLocaleString(EN ? 'en-GB' : 'it-IT') }));\n  dbgDirty = true;\n});\n\n\/\/ ---- guida passo passo: clic su un byte = imposta le leve ----------------------------------------\nvar steps = $('rfimSteps');\nT.steps.forEach(function (st) {\n  var li = document.createElement('li'); li.innerHTML = st[0];\n  if (st[1] !== undefined) {\n    var b = document.createElement('button'); b.type = 'button'; b.className = 'btn sm setsw';\n    b.innerHTML = tr('setSw', { v: H(st[1], st[1] > 255 ? 4 : 2), b: bin(st[1], st[1] > 255 ? 16 : 8) });\n    b.addEventListener('click', function () {\n      m.switches = st[1] > 255 ? st[1] : (m.switches & 0xFF00) | st[1]; panel.syncSwitches();\n      $('rfimPanel').scrollIntoView({ behavior: 'smooth', block: 'center' });\n    });\n    li.appendChild(document.createTextNode(' ')); li.appendChild(b);\n  }\n  steps.appendChild(li);\n});\nfunction bin(v, n) { var s = v.toString(2); while (s.length < n) s = '0' + s; return s.replace(\/(\\d{4})(?=\\d)\/g, '$1 '); }\n\n\/\/ ---- registri e disassemblato ----------------------------------------------------------------\nvar dbgDirty = true, lastDbg = 0, regsEl = $('rfimRegs'), flagsEl = $('rfimFlags'), disEl = $('rfimDis'), cycEl = $('rfimCyc');\nfunction updateDbg(now) {\n  var c = m.cpu;\n  if (!m.power) { regsEl.innerHTML = ''; flagsEl.innerHTML = ''; disEl.innerHTML = ''; cycEl.textContent = tr('off'); return; }\n  var r = [['A', H(c.a, 2)], ['BC', H(c.b, 2) + H(c.c, 2)], ['DE', H(c.d, 2) + H(c.e, 2)], ['HL', H(c.h, 2) + H(c.l, 2)], ['SP', H(c.sp, 4)], ['PC', H(c.pc, 4)]];\n  regsEl.innerHTML = r.map(function (x) { return '<span><i>' + x[0] + '<\/i>' + x[1] + '<\/span>'; }).join('');\n  flagsEl.innerHTML = [['S', c.fS], ['Z', c.fZ], ['AC', c.fA], ['P', c.fP], ['CY', c.fC]].map(function (x) {\n    return '<span class=\"' + (x[1] ? 'on' : '') + '\">' + x[0] + '<\/span>'; }).join('') +\n    '<span class=\"st\">' + (m.running ? (c.halted ? 'HALT' : 'RUN') : 'STOP') + '<\/span>';\n  \/\/ disassemblato: qualche istruzione prima (se coincide con il programma caricato) e dopo il PC\n  var a = c.pc, lines = [], start = disStart(a), x = start;\n  for (var i = 0; i < 12; i++) { var d = RF.disasm(c.mem, x); lines.push(d); x = (x + d.len) & 0xFFFF; }\n  disEl.innerHTML = lines.map(function (d) {\n    return '<li class=\"' + (d.addr === a ? 'pc' : '') + '\"><span class=\"ad\">' + H(d.addr, 4) + '<\/span><span class=\"by\">' +\n      d.bytes.map(function (b) { return H(b, 2); }).join(' ') + '<\/span><span class=\"mn\">' + d.text + '<\/span><\/li>';\n  }).join('');\n  cycEl.textContent = tr('cycles', { n: c.cycles.toLocaleString(EN ? 'en-GB' : 'it-IT'), s: (c.cycles \/ RF.CLOCK).toFixed(2) });\n}\nvar disCache = { from: -1, addrs: [] };\n\/\/ cerca un punto di partenza da cui la decodifica arriva esattamente al PC (al massimo 4 istruzioni prima)\nfunction disStart(pc) {\n  var best = pc;\n  for (var back = Math.min(12, pc); back >= 1; back--) {\n    var s = (pc - back) & 0xFFFF, x = s, n = 0;\n    while (n < 8 && x !== pc && ((pc - x) & 0xFFFF) < 13) { x = (x + RF.disasm(m.cpu.mem, x).len) & 0xFFFF; n++; }\n    if (x === pc && n <= 4) { best = s; break; }\n  }\n  return best;\n}\n\n\/\/ ---- ciclo principale ---------------------------------------------------------------------------\nvar last = performance.now();\nfunction frame(now) {\n  var dt = now - last; last = now;\n  m.tick(dt);\n  panel.update(m.leds());\n  term.render(m.power && (Math.floor(now \/ 530) % 2 === 0), m.power);\n  $('rfimTermMsg').hidden = document.activeElement === kbd || !m.power;\n  if (dbgDirty || (m.running && now - lastDbg > 120)) { dbgDirty = false; lastDbg = now; updateDbg(now); }\n  requestAnimationFrame(frame);\n}\n\n\/\/ ---- avvio ---------------------------------------------------------------------------------------\nvar saved = null; try { saved = localStorage.getItem('rfimSrc'); } catch (e) {}\nsel.value = '0'; showDesc();\n$('rfimSrc').value = saved || prog().src;\nm.showAt(0);\nterm.write(tr('termBoot'));\nRF.ui = { machine: m, terminal: term, panel: panel };   \/\/ per provare dalla console\nrequestAnimationFrame(frame);\n})(window.RFImsai = window.RFImsai || {});\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>L&#8217;IMSAI 8080 del 1975, il computer di WarGames: storia, foto, repliche da costruire con Arduino o stampa 3D e un emulatore online con pannello e terminale.<\/p>","protected":false},"author":1,"featured_media":11558,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"footnotes":""},"categories":[],"tags":[215,597,598,599],"class_list":["post-11562","page","type-page","status-publish","has-post-thumbnail","hentry","tag-emulatore","tag-imsai-8080","tag-intel-8080","tag-s-100"],"_links":{"self":[{"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/pages\/11562","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=11562"}],"version-history":[{"count":13,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/pages\/11562\/revisions"}],"predecessor-version":[{"id":11613,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/pages\/11562\/revisions\/11613"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/media\/11558"}],"wp:attachment":[{"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/media?parent=11562"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/categories?post=11562"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/tags?post=11562"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}