{"id":11605,"date":"2026-11-05T13:00:49","date_gmt":"2026-11-05T12:00:49","guid":{"rendered":"https:\/\/retrofixer.it\/?page_id=11605"},"modified":"2026-11-05T13:00:49","modified_gmt":"2026-11-05T12:00:49","slug":"altair-8800","status":"publish","type":"page","link":"https:\/\/retrofixer.it\/en\/altair-8800\/","title":{"rendered":"Altair 8800"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"11605\" class=\"elementor elementor-11605\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-0d414d4 e-con-full e-flex e-con e-parent\" data-id=\"0d414d4\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-266e19e elementor-widget elementor-widget-html\" data-id=\"266e19e\" 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&amp;family=Press+Start+2P&amp;family=VT323&amp;display=swap\" rel=\"stylesheet\">\n\n<style>\n.rfal{--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.rfal *,.rfal *::before,.rfal *::after{box-sizing:border-box}\n.rfal h2,.rfal h3,.rfal h4,.rfal p,.rfal ul,.rfal ol,.rfal figure{margin:0;padding:0}\n.rfal h2,.rfal h3,.rfal h4{font-family:'Inter',system-ui,sans-serif!important;letter-spacing:0;text-transform:none;font-weight:700;color:var(--text)}\n.rfal a{color:var(--cya)}\n.rfal button,.rfal input,.rfal select,.rfal textarea{font:inherit;color:inherit}\n.rfal button{cursor:pointer}\n.rfal [hidden]{display:none!important}\n.rfal 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.rfal .wrap{max-width:1400px;margin:0 auto;padding:0 16px}\n.post-views.post-11605{background:#0f0c24;color:#aaa5d8;margin:0;padding:0 16px 8px;font-size:13px}\n\n\/* intestazione *\/\n.rfal .head{margin-bottom:34px}\n.rfal .kicker{font-family:'VT323',monospace;font-size:21px;color:var(--c64l);margin-bottom:6px}\n.rfal .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.rfal .lead{color:var(--muted);max-width:920px;font-size:17px}\n.rfal .lead b{color:var(--text)}\n.rfal .toc{display:flex;flex-wrap:wrap;gap:8px;margin-top:18px}\n.rfal .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.rfal .toc a:hover{background:var(--line)}\n\n\/* sezioni *\/\n.rfal .sec{margin:0 0 46px;scroll-margin-top:90px}\n.rfal .sec>h3,.rfal .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.rfal .sec h4{font-size:17px;margin:26px 0 10px;color:var(--yel)}\n.rfal .intro{color:var(--muted);max-width:960px;margin-bottom:18px}\n.rfal .prose p{margin-bottom:12px}\n.rfal .prose p:last-child{margin-bottom:0}\n.rfal .prose b{color:#fff}\n.rfal .prose ul{margin:0 0 12px 20px}\n.rfal .box{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:18px 20px;margin-top:22px}\n.rfal .two{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);gap:28px;align-items:start;margin-bottom:26px}\n.rfal .two.rev{grid-template-columns:minmax(0,1fr) minmax(0,1.15fr)}\n.rfal .two.rev .photo{order:-1}\n.rfal .photo{background:var(--panel);border:1px solid var(--line);border-radius:12px;overflow:hidden}\n.rfal .photo img{display:block;width:100%;height:auto;background:#0a0820}\n.rfal .photo figcaption{padding:10px 14px;font-size:13.5px;color:var(--muted)}\n.rfal .photo .cred{display:block;font-size:12px;opacity:.85;margin-top:2px}\n.rfal .specs,.rfal .ref{width:100%;border-collapse:collapse;font-size:15px}\n.rfal .specs th,.rfal .specs td,.rfal .ref th,.rfal .ref td{text-align:left;vertical-align:top;padding:7px 10px;border-bottom:1px solid var(--line)}\n.rfal .specs th,.rfal .ref th{color:var(--muted);font-weight:600;white-space:nowrap;width:1%}\n.rfal .ref td:first-child{font-family:'VT323',monospace;font-size:19px;color:var(--yel);white-space:nowrap;width:1%}\n\n\/* progetti *\/\n.rfal .cgroup{margin-bottom:22px}\n.rfal .cgroup h4{margin-top:0}\n.rfal .clist{display:grid;grid-template-columns:repeat(auto-fill,minmax(290px,1fr));gap:12px}\n.rfal .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.rfal .card:hover{border-color:var(--cya);transform:translateY(-2px)}\n.rfal .card b{font-size:16px}\n.rfal .ctag{font-family:'VT323',monospace;font-size:17px;color:var(--grn);line-height:1.1}\n.rfal .cdesc{font-size:14px;color:var(--muted)}\n.rfal .curl{font-size:12.5px;color:var(--cya);margin-top:auto;padding-top:4px;word-break:break-all}\n\n\/* barra comandi *\/\n.rfal .bar{display:flex;flex-wrap:wrap;align-items:flex-end;gap:10px;margin:10px 0}\n.rfal .bar .sp{flex:1}\n.rfal .fld{display:flex;flex-direction:column;gap:3px;font-size:12.5px;color:var(--muted)}\n.rfal select,.rfal 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.rfal select:focus,.rfal input[type=text]:focus{border-color:var(--c64l)}\n.rfal select option{background:var(--bg2)}\n.rfal .hex4{width:78px;font-family:ui-monospace,Consolas,monospace;text-transform:uppercase}\n.rfal .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.rfal .btn:hover{background:var(--line);color:#fff}\n.rfal .btn.pri{background:var(--grn);border-color:var(--grn);color:#0f0c24;font-weight:700}\n.rfal .btn.pri:hover{background:#7be27f;color:#0f0c24}\n.rfal .btn.sm{padding:3px 9px;font-size:13px;border-radius:6px}\n.rfal .btn .i{font-size:13px;line-height:1}\n.rfal label.ck{display:inline-flex;align-items:center;gap:6px;font-size:13.5px;color:var(--muted);cursor:pointer}\n.rfal label.ck input{accent-color:var(--grn);width:15px;height:15px}\n.rfal .pdesc{min-height:26px;color:var(--muted);font-size:15px;margin:2px 0 14px;max-width:1100px}\n.rfal .pdesc b{color:var(--yel)}\n.rfal .hint{font-size:13.5px;color:var(--muted);margin:8px 0 18px}\n.rfal .hint b{color:var(--text)}\n\n\/* pannello frontale *\/\n.rfal .panelbox{border-radius:14px;overflow-x:auto;box-shadow:0 14px 40px rgba(0,0,0,.55)}\n.rfal .altair-panel{display:block;width:100%;min-width:820px;height:auto;user-select:none;-webkit-user-select:none;touch-action:manipulation}\n.rfal .altair-panel .cabinet{fill:#5e9ad3}\n.rfal .altair-panel .face{fill:#3d4043;stroke:#24272a;stroke-width:2}\n.rfal .altair-panel .shade{fill:rgba(0,0,0,.25)}\n.rfal .altair-panel text{font-family:'Inter',Arial,sans-serif;fill:#ecebe4}\n.rfal .altair-panel .num{font-size:10.5px;font-weight:600;letter-spacing:.02em}\n.rfal .altair-panel .grp{font-size:10px;font-weight:600;letter-spacing:.06em}\n.rfal .altair-panel .side{font-size:10px;font-weight:600}\n.rfal .altair-panel .cl{font-size:10px;font-weight:600;letter-spacing:.03em}\n.rfal .altair-panel .rule{stroke:#ecebe4;stroke-width:1.3}\n.rfal .altair-panel .dash{stroke:#ecebe4;stroke-width:1.1;stroke-dasharray:4 4;opacity:.8}\n.rfal .altair-panel .alu{stroke:#8e959b;stroke-width:1}\n.rfal .altair-panel .logo{font-family:'Arial Black','Inter',Arial,sans-serif;font-size:38px;font-weight:900;letter-spacing:.02em;fill:#151515}\n.rfal .altair-panel .logo2{font-family:'Arial Black','Inter',Arial,sans-serif;font-size:26px;font-weight:900;letter-spacing:.04em;fill:#151515}\n.rfal .altair-panel .mits{font-size:15px;font-weight:800;font-style:italic;fill:#151515}\n.rfal .altair-panel .mits7{fill:none;stroke:#151515;stroke-width:4;stroke-linejoin:round}\n.rfal .altair-panel .credit{font-size:11px;fill:#5d646a}\n.rfal .led-ring{fill:#1d1f21;stroke:#151618;stroke-width:1}\n.rfal .led-lens{fill:#3a1512;stroke:#140807;stroke-width:.8}\n.rfal .led-lens.on{fill:url(#altairLedOn)}\n.rfal .led-glow{fill:url(#altairGlow);opacity:0;pointer-events:none}\n.rfal .tog{cursor:pointer;outline:none}\n.rfal .tog .hole{fill:#2a2d33}\n.rfal .tog .lever,.rfal .tog .tip{stroke:rgba(0,0,0,.45);stroke-width:.8}\n.rfal .tog .hit{fill:transparent}\n.rfal .tog:hover .lever,.rfal .tog:hover .tip{filter:brightness(1.1)}\n.rfal .tog:focus-visible .nut{stroke:#ffd23f;stroke-width:3}\n.rfal .tog.aux{cursor:default}\n\n\/* terminale *\/\n.rfal .row2{display:grid;grid-template-columns:minmax(0,1fr) 330px;gap:18px;align-items:start}\n.rfal .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.rfal .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.rfal .crt canvas{display:block;width:100%;height:auto;filter:drop-shadow(0 0 3px rgba(93,255,138,.55))}\n.rfal .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.rfal .crt.focus{box-shadow:inset 0 0 40px rgba(0,0,0,.85),0 0 0 3px #3a7a4c}\n.rfal .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.rfal .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.rfal .termbar{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:9px}\n.rfal .termbar .tname{font-family:'VT323',monospace;font-size:18px;color:#b9b2c9;flex:1;letter-spacing:.04em}\n.rfal .termbar .btn{background:#34303d;border-color:#47414f}\n\n\/* registri *\/\n.rfal .dbg{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px 16px;font-family:'VT323',monospace;font-size:19px}\n.rfal .dbg h4{font-family:'Inter',sans-serif!important;font-size:14px;margin:0 0 8px;color:var(--muted)}\n.rfal .regs{display:grid;grid-template-columns:repeat(3,1fr);gap:4px 10px}\n.rfal .regs span{color:var(--text)}\n.rfal .regs i{font-style:normal;color:var(--c64l);margin-right:6px}\n.rfal .flags{display:flex;gap:6px;margin:10px 0;flex-wrap:wrap}\n.rfal .flags span{border:1px solid var(--line);border-radius:5px;padding:0 6px;color:#5d578f}\n.rfal .flags span.on{color:#0f0c24;background:var(--yel);border-color:var(--yel)}\n.rfal .flags .st{margin-left:auto;color:var(--grn);border-color:transparent}\n.rfal .dis{list-style:none;border-top:1px solid var(--line);padding-top:6px}\n.rfal .dis li{display:grid;grid-template-columns:48px 78px 1fr;gap:6px;line-height:1.2;color:var(--muted);white-space:nowrap}\n.rfal .dis li.pc{color:#0f0c24;background:var(--grn);border-radius:3px}\n.rfal .dis .ad{opacity:.8}\n.rfal .cyc{font-size:16px;color:#6f69a6;margin-top:8px}\n\n\/* strumenti *\/\n.rfal .tools{margin-top:22px;display:grid;gap:12px}\n.rfal .tool{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:12px 16px}\n.rfal .tool summary{cursor:pointer;font-weight:700;font-size:16px;color:var(--yel)}\n.rfal .tool .note{font-size:14px;color:var(--muted);margin:8px 0}\n.rfal .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.rfal .src:focus{border-color:var(--c64l)}\n.rfal .asmout{background:#0a0820;border:1px solid var(--line);border-radius:8px;padding:10px 12px;font-size:14px}\n.rfal .asmout.err{border-color:var(--red);color:#ffb3b3}\n.rfal .asmout b{display:block;margin-bottom:6px}\n.rfal .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.rfal .msg{font-size:14px;color:var(--grn);min-height:20px}\n.rfal .msg.err{color:var(--red)}\n\n\/* guida *\/\n.rfal .steps{margin:14px 0 18px 22px;display:grid;gap:8px;max-width:1000px}\n.rfal .steps li{padding-left:4px}\n.rfal .steps li b{color:#fff}\n.rfal .steps .setsw{margin-left:4px;font-family:'VT323',monospace;font-size:16px;padding:0 8px;vertical-align:1px}\n.rfal .fonti ul{margin-left:20px;color:var(--muted);font-size:14.5px;display:grid;gap:4px}\n\n@media (max-width:1050px){.rfal .row2{grid-template-columns:minmax(0,1fr)}}\n@media (max-width:820px){.rfal .two,.rfal .two.rev{grid-template-columns:minmax(0,1fr)}.rfal .two.rev .photo{order:0}}\n@media (max-width:600px){.rfal{padding-top:22px}.rfal .lead{font-size:16px}.rfal .termbox{padding:10px 10px 8px;border-radius:12px}.rfal .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.rfal select,.rfal select:hover,.rfal 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.rfal select option{background:#171338!important;color:#eceaff!important}\n.rfal input[type=text],.rfal 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.rfal textarea.src,.rfal 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.rfal textarea.kbd,.rfal textarea.kbd:focus{background:transparent!important;border:0!important;box-shadow:none!important;color:transparent!important;padding:0!important;margin:0!important}\n.rfal table,.rfal table tr,.rfal table td,.rfal table th{background:transparent!important;box-shadow:none!important}\n.rfal table{border:0!important;margin:0!important}\n.rfal .sec>h3,.rfal .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.rfal .sec h4{font-size:17px!important;line-height:1.4!important;color:var(--yel)!important;margin:26px 0 10px!important}\n.rfal .cgroup h4{margin-top:0!important}\n.rfal .dbg h4{font-family:'Inter',sans-serif!important;font-size:14px!important;margin:0 0 8px!important;color:var(--muted)!important}\n.rfal .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.rfal .btn,.rfal .btn:hover{box-shadow:none!important;text-decoration:none!important;margin:0!important}\n.rfal .card,.rfal .card:hover,.rfal .toc a{text-decoration:none!important}\n.rfal img{border:0!important;box-shadow:none!important;max-width:100%}\n.rfal ol.dis,.rfal ol.dis li{list-style:none!important;margin:0!important}\n.rfal pre,.rfal .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.rfal .bar select{width:auto!important;max-width:100%}\n.rfal pre.snip{display:block;background:#04130a!important;border:1px solid #1d5a30!important;border-radius:8px!important;padding:10px 14px!important;margin:6px 0 14px!important;color:#5dff8a!important;font-family:'VT323',monospace!important;font-size:20px!important;line-height:1.15!important;max-width:520px}\n.rfal .prose ol{margin:0 0 12px 22px}\n<\/style>\n\n<div class=\"rfal\" data-no-translation=\"\">\n <div class=\"wrap\">\n\n  <header class=\"head\">\n   <p class=\"kicker\" data-t=\"kicker\"><\/p>\n   <h2>Altair 8800<\/h2>\n   <p class=\"lead\" data-th=\"lead\"><\/p>\n   <nav class=\"toc\" aria-label=\"Indice\">\n    <a href=\"#rfalStoria\" data-t=\"navStoria\"><\/a>\n    <a href=\"#rfalCostruire\" data-t=\"navCostruire\"><\/a>\n    <a href=\"#rfalEmu\" data-t=\"navEmu\"><\/a>\n    <a href=\"#rfalBasic\" data-t=\"navBasic\"><\/a>\n    <a href=\"#rfalGuida\" data-t=\"navGuida\"><\/a>\n   <\/nav>\n  <\/header>\n\n  <section class=\"sec\" id=\"rfalStoria\">\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=\"rfalFoto1\" alt=\"\" loading=\"lazy\" decoding=\"async\">\n     <figcaption id=\"rfalFoto1c\"><\/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=\"rfalFoto2\" alt=\"\" loading=\"lazy\" decoding=\"async\">\n     <figcaption id=\"rfalFoto2c\"><\/figcaption>\n    <\/figure>\n   <\/div>\n   <div class=\"prose box\" data-th=\"pannelloSpiega\"><\/div>\n  <\/section>\n\n  <section class=\"sec\" id=\"rfalCostruire\">\n   <h3 data-t=\"costruireT\"><\/h3>\n   <p class=\"intro\" data-th=\"costruireIntro\"><\/p>\n   <div class=\"cards\" id=\"rfalCards\"><\/div>\n  <\/section>\n\n  <section class=\"sec emu\" id=\"rfalEmu\">\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=\"rfalProg\"><\/select><\/label>\n    <button class=\"btn pri\" id=\"rfalLoadRun\" type=\"button\"><span class=\"i\">\u25b6<\/span><span data-t=\"loadRun\"><\/span><\/button>\n    <button class=\"btn\" id=\"rfalLoadOnly\" type=\"button\" data-t=\"loadOnly\"><\/button>\n    <span class=\"sp\"><\/span>\n    <label class=\"fld\"><span data-t=\"speed\"><\/span><select id=\"rfalSpeed\">\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=\"rfalBaud\">\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=\"rfalPDesc\"><\/p>\n\n   <div class=\"panelbox\" id=\"rfalPanel\"><\/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=\"rfalTerm\" aria-label=\"Terminale video\"><\/canvas>\n      <textarea id=\"rfalKbd\" class=\"kbd\" autocapitalize=\"off\" autocomplete=\"off\" autocorrect=\"off\" spellcheck=\"false\" aria-label=\"Tastiera del terminale\"><\/textarea>\n      <div class=\"crtmsg\" id=\"rfalTermMsg\" 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=\"rfalCaps\" checked=\"\"><span data-t=\"caps\"><\/span><\/label>\n      <button class=\"btn sm\" id=\"rfalTermClr\" type=\"button\" data-t=\"termClr\"><\/button>\n      <button class=\"btn sm\" id=\"rfalTermCopy\" 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=\"rfalRegs\"><\/div>\n     <div class=\"flags\" id=\"rfalFlags\"><\/div>\n     <ol class=\"dis\" id=\"rfalDis\"><\/ol>\n     <p class=\"cyc\" id=\"rfalCyc\"><\/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=\"rfalSrc\" class=\"src\" spellcheck=\"false\" autocapitalize=\"off\" autocomplete=\"off\" rows=\"18\"><\/textarea>\n     <div class=\"bar\">\n      <button class=\"btn pri\" id=\"rfalAsmRun\" type=\"button\"><span class=\"i\">\u25b6<\/span><span data-t=\"asmRun\"><\/span><\/button>\n      <button class=\"btn\" id=\"rfalAsm\" type=\"button\" data-t=\"asmLoad\"><\/button>\n      <button class=\"btn\" id=\"rfalAsmSave\" type=\"button\"><span class=\"i\">\u2b07<\/span><span data-t=\"asmSave\"><\/span><\/button>\n     <\/div>\n     <div class=\"asmout\" id=\"rfalAsmOut\" 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=\"rfalFrom\" value=\"0000\" maxlength=\"4\" class=\"hex4\"><\/label>\n      <label class=\"fld\"><span data-t=\"to\"><\/span><input type=\"text\" id=\"rfalTo\" value=\"00FF\" maxlength=\"4\" class=\"hex4\"><\/label>\n      <button class=\"btn\" id=\"rfalSaveHex\" type=\"button\"><span class=\"i\">\u2b07<\/span><span data-t=\"saveHex\"><\/span><\/button>\n      <button class=\"btn\" id=\"rfalSaveBin\" type=\"button\"><span class=\"i\">\u2b07<\/span><span data-t=\"saveBin\"><\/span><\/button>\n     <\/div>\n     <div class=\"bar\">\n      <button class=\"btn\" id=\"rfalOpen\" type=\"button\"><span class=\"i\">\ud83d\udcc2<\/span><span data-t=\"openFile\"><\/span><\/button>\n      <input type=\"file\" id=\"rfalFile\" accept=\".hex,.ihx,.ihex,.bin,.com,.asm,.a80,.s,.txt\" hidden=\"\">\n      <span class=\"sp\"><\/span>\n      <button class=\"btn\" id=\"rfalSnapSave\" type=\"button\" data-t=\"snapSave\"><\/button>\n      <button class=\"btn\" id=\"rfalSnapLoad\" type=\"button\" data-t=\"snapLoad\"><\/button>\n     <\/div>\n     <div class=\"bar\">\n      <button class=\"btn\" id=\"rfalTape\" type=\"button\"><span class=\"i\">\ud83d\udcfc<\/span><span data-t=\"tapeBtn\"><\/span><\/button>\n      <input type=\"file\" id=\"rfalTapeFile\" accept=\".tap,.bin,.ptp\" hidden=\"\">\n      <span class=\"fld\" id=\"rfalTapeState\"><\/span>\n     <\/div>\n     <p class=\"msg\" id=\"rfalFileMsg\"><\/p>\n    <\/details>\n   <\/div>\n  <\/section>\n\n  <section class=\"sec\" id=\"rfalBasic\">\n   <h3 data-t=\"basicT\"><\/h3>\n   <div class=\"prose\" data-th=\"basic\"><\/div>\n  <\/section>\n\n  <section class=\"sec\" id=\"rfalGuida\">\n   <h3 data-t=\"guidaT\"><\/h3>\n   <div class=\"prose\" data-th=\"guidaIntro\"><\/div>\n   <ol class=\"steps\" id=\"rfalSteps\"><\/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.RFAltair = window.RFAltair || {});\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.RFAltair = window.RFAltair || {});\n\n\/* RetroFixer \u00b7 Altair 8800 \u00b7 la macchina\n * CPU 8080 a 2 MHz, 64 KB di RAM, pannello frontale (interruttori, LED, EXAMINE\/DEPOSIT\/RUN\/STOP\u2026),\n * porta FFh in ingresso = interruttori di senso A15-A8,\n * MITS 88-SIO (00h stato, 01h dati; bit di stato attivi bassi) e 88-2SIO (10h stato\/controllo, 11h dati; ACIA 6850)\n * collegate allo stesso terminale, come se il terminale fosse sulla scheda che il programma usa.\n *\/\n(function (RF) {\n'use strict';\nvar CLOCK = 2000000;\n\nfunction Altair() {\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.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.tape = null;            \/\/ nastro perforato nel lettore (Uint8Array) e posizione: ha la precedenza sulla tastiera\n  this.tapePos = 0;\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 = Altair.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) {\n    if (this.tape && this.tapePos < this.tape.length) this.rx.push(this.tape[this.tapePos++]);\n    else if (this.typed.length) this.rx.push(this.typed.shift());\n  }\n  switch (p) {\n    case 0xFF: return (this.switches >> 8) & 255;                                   \/\/ interruttori di senso\n    case 0x00: return (this.rx.length ? 0 : 0x01) | (this.txReady() ? 0 : 0x80);   \/\/ 88-SIO: bit0 \/IDR, bit7 \/ODR (0 = pronto)\n    case 0x01: return this.rx.length ? this.rx.shift() : 0;\n    case 0x10: return (this.rx.length ? 0x01 : 0) | (this.txReady() ? 0x02 : 0);    \/\/ 88-2SIO (6850): bit0 RDRF, bit1 TDRE\n    case 0x11: return this.rx.length ? this.rx.shift() : 0;\n    case 0x12: return 0x02;                                                          \/\/ seconda porta della 2SIO: niente collegato\n  }\n  return 0xFF;\n};\nP.output = function (p, v) {\n  switch (p) {\n    case 0x01: 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\/\/ lettore di nastro collegato alla stessa seriale del terminale: i byte (tutti gli 8 bit) arrivano quando il programma li legge\nP.loadTape = function (bytes) { this.tape = bytes; this.tapePos = 0; this.rx.length = 0; };\nP.tapeLeft = function () { return this.tape ? this.tape.length - this.tapePos : 0; };\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 'clr': this.rx.length = 0; this.typed.length = 0; this.txReadyAt = 0; this.tape = null; break;   \/\/ CLR: azzera le periferiche\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 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.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.inte = cpu.inte; o.prot = cpu.prot[cpu.pc >> 8];\n  o.wait = !this.running; o.hlda = 0;\n  return o;\n};\n\nRF.Altair = Altair;\nRF.CLOCK = CLOCK;\n})(window.RFAltair = window.RFAltair || {});\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.RFAltair = window.RFAltair || {});\n\n\/* RetroFixer \u00b7 Altair 8800 \u00b7 pannello frontale in SVG\n * Misure prese dalle foto del pannello originale: a sinistra i LED INTE, PROT e quelli di STATUS (MEMR \u2026 INT),\n * a destra i LED DATA D7-D0 sopra A7-A0; sotto WAIT, HLDA e i LED ADDRESS A15-A0, ognuno collegato con una linea\n * tratteggiata alla sua levetta. Levette raggruppate a tre (le cifre ottali); A15-A8 sono anche le SENSE SW.\n * In basso i comandi a tre posizioni, l'interruttore OFF\/ON e la fascia in alluminio con il logo.\n * 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 = 600;\nfunction colX(b) { return 1105 - b * 45 - 22 * Math.floor(b \/ 3); }   \/\/ b = bit (0 = A0, a destra)\nvar Y1 = 118, Y2 = 208, YN = 278, YS = 306, YC = 412, YLOGO = 530;\nvar STATUS = [['INTE', 'inte'], ['PROT', 'prot'], ['MEMR', 7], ['INP', 6], ['M1', 5], ['OUT', 4], ['HLTA', 3], ['STACK', 2], ['WO', 1], ['INT', 0]];\nvar CONTROLS = [   \/\/ [etichetta su, etichetta gi\u00f9, comando su, comando gi\u00f9, x]\n  ['STOP', 'RUN', 'stop', 'run', 324],\n  ['SINGLE STEP', '', 'step', 'step', 412],\n  ['EXAMINE', 'EXAMINE NEXT', 'examine', 'examineNext', 500],\n  ['DEPOSIT', 'DEPOSIT NEXT', 'deposit', 'depositNext', 589],\n  ['RESET', 'CLR', 'reset', 'clr', 679],\n  ['PROTECT', 'UNPROTECT', 'protect', 'unprotect', 769],\n  ['AUX', '', '', '', 856],\n  ['AUX', '', '', '', 946]\n];\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: 'altair-panel', role: 'group', 'aria-label': 'Altair 8800 front panel' }, host);\n  this.leds = { 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.line = function (x1, y1, x2, y2, cls) { return el('line', { x1: x1, y1: y1, x2: x2, y2: y2, class: cls || 'rule' }, this.svg); };\nP.led = function (x, y) {\n  var g = el('g', {}, this.svg);\n  el('circle', { cx: x, cy: y, r: 8.5, class: 'led-ring' }, g);\n  var c = el('circle', { cx: x, cy: y, r: 6.5, class: 'led-lens' }, g);\n  var glow = el('circle', { cx: x, cy: y, r: 16, class: 'led-glow' }, g);\n  return { c: c, glow: glow, v: -1 };\n};\n\nP.build = function () {\n  var s = this.svg, i, x, b;\n  var defs = el('defs', {}, s);\n  var rg = el('radialGradient', { id: 'altairLedOn' }, defs);\n  el('stop', { offset: '0', 'stop-color': '#fff1e6' }, rg); el('stop', { offset: '.35', 'stop-color': '#ff4a2e' }, rg); el('stop', { offset: '1', 'stop-color': '#b81208' }, rg);\n  var rg2 = el('radialGradient', { id: 'altairGlow' }, 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 lv = el('linearGradient', { id: 'altairLever', x1: 0, y1: 0, x2: 1, y2: 0 }, defs);\n  el('stop', { offset: '0', 'stop-color': '#8d9399' }, lv); el('stop', { offset: '.45', 'stop-color': '#f4f6f8' }, lv); el('stop', { offset: '1', 'stop-color': '#7a8086' }, lv);\n  var nut = el('radialGradient', { id: 'altairNut', cx: '.4', cy: '.35' }, defs);\n  el('stop', { offset: '0', 'stop-color': '#e9ecef' }, nut); el('stop', { offset: '1', 'stop-color': '#5c6167' }, nut);\n  var al = el('linearGradient', { id: 'altairAlu', x1: 0, y1: 0, x2: 0, y2: 1 }, defs);\n  el('stop', { offset: '0', 'stop-color': '#e3e6e8' }, al); el('stop', { offset: '.5', 'stop-color': '#bcc1c5' }, al); el('stop', { offset: '1', 'stop-color': '#d6dadd' }, al);\n\n  el('rect', { x: 0, y: 0, width: W, height: H, rx: 8, class: 'cabinet' }, s);                \/\/ cornice azzurra\n  el('rect', { x: 26, y: 24, width: W - 52, height: H - 48, rx: 2, class: 'face' }, s);       \/\/ pannello grigio scuro\n  el('rect', { x: 26, y: 24, width: W - 52, height: 24, class: 'shade' }, s);\n\n  \/\/ ---- LED di stato\n  var sx = function (i) { return 186 + i * 45; };\n  for (i = 0; i < STATUS.length; i++) {\n    x = sx(i);\n    var o = this.led(x, Y1);\n    this.t(x, Y1 - 17, STATUS[i][0], 'num');\n    o.key = STATUS[i][1];\n    this.leds.status.push(o);\n  }\n  this.line(sx(2) - 16, Y1 + 18, sx(9) + 16, Y1 + 18);\n  this.t((sx(2) + sx(9)) \/ 2, Y1 + 32, 'STATUS', 'grp');\n\n  \/\/ ---- LED DATA sopra A7-A0, LED ADDRESS, linee tratteggiate e levette\n  for (b = 7; b >= 0; b--) {\n    x = colX(b); this.leds.data[b] = this.led(x, Y1); this.t(x, Y1 - 17, 'D' + b, 'num');\n    this.line(x, Y1 + 12, x, Y2 - 28, 'dash');\n  }\n  this.leds.wait = this.led(189, Y2); this.t(189, Y2 - 17, 'WAIT', 'num');\n  this.leds.hlda = this.led(233, Y2); this.t(233, Y2 - 17, 'HLDA', 'num');\n  this.sw = [];\n  for (b = 15; b >= 0; b--) {\n    x = colX(b);\n    this.leds.addr[b] = this.led(x, Y2); this.t(x, Y2 - 17, 'A' + b, 'num');\n    this.line(x, Y2 + 12, x, YN - 14, 'dash');\n    this.t(x, YN, String(b), 'num');\n    this.sw.push(this.toggle(x, YS, 'addr', b));\n  }\n  \/\/ SENSE SW. e le linee dei gruppi ottali sotto le levette\n  this.t(150, YN + 4, 'SENSE SW.', 'grp', 'start');\n  this.line(232, YN - 4, colX(15) - 14, YN - 4);\n  for (b = 15; b > 8; b--) this.line(colX(b) + 12, YN - 4, colX(b - 1) - 12, YN - 4);\n  for (b = 0; b < 16; b += 3) this.line(colX(Math.min(15, b + 2)) - 20, YS + 28, colX(b) + 20, YS + 28);\n  this.t(colX(0) + 26, YS + 24, 'Data', 'side', 'start');\n  this.t(colX(0) + 26, YS + 38, 'Address', 'side', 'start');\n  this.line(colX(2) - 20, YS + 31, colX(0) + 76, YS + 31);\n\n  \/\/ ---- comandi\n  this.ctl = [];\n  for (i = 0; i < CONTROLS.length; i++) {\n    var c = CONTROLS[i];\n    this.ctlLabel(c[4], c[0], c[1]);\n    this.ctl.push(this.toggle(c[4], YC, c[2] ? 'ctl' : 'aux', c));\n  }\n\n  \/\/ ---- alimentazione\n  this.t(110, YC - 28, 'OFF', 'cl'); this.t(110, YC + 40, 'ON', 'cl');\n  this.power = this.toggle(110, YC, 'power', null);\n\n  \/\/ ---- fascia con il logo\n  el('rect', { x: 26, y: YLOGO - 32, width: W - 52, height: 52, fill: 'url(#altairAlu)', class: 'alu' }, s);\n  this.t(150, YLOGO + 2, 'MITS', 'mits', 'start');\n  el('path', { d: 'M200 ' + (YLOGO - 18) + ' l14 0 l-12 24', class: 'mits7' }, s);\n  this.t(250, YLOGO + 8, 'ALTAIR 8800', 'logo', 'start');\n  this.t(560, YLOGO + 8, 'COMPUTER', 'logo2', 'start');\n  this.t(W - 40, YLOGO + 6, 'emulatore \u00b7 retrofixer.it', 'credit', 'end');\n};\nP.ctlLabel = function (x, up, down) {\n  var self = this;\n  var u = up.split(' ');\n  if (u.length > 1) { self.t(x, YC - 40, u[0], 'cl'); self.t(x, YC - 28, u.slice(1).join(' '), 'cl'); }\n  else if (up) self.t(x, YC - 28, up, 'cl');\n  var d = down.split(' ');\n  if (down) self.t(x, YC + 40, d[0], 'cl');\n  if (d.length > 1) self.t(x, YC + 52, d.slice(1).join(' '), 'cl');\n};\n\n\/\/ levetta: kind = 'addr' (resta su\/gi\u00f9), 'ctl' (momentanea, torna al centro), 'power' (gi\u00f9 = ON), 'aux' (non collegata)\nP.toggle = function (x, y, kind, arg) {\n  var self = this, g = el('g', { class: 'tog ' + kind, transform: 'translate(' + x + ',' + y + ')', tabindex: kind === 'aux' ? -1 : 0, role: 'button' }, this.svg);\n  el('circle', { r: 11, fill: 'url(#altairNut)', class: 'nut' }, g);\n  el('circle', { r: 5.5, class: 'hole' }, g);\n  var lever = el('rect', { x: -3.5, width: 7, rx: 3.5, fill: 'url(#altairLever)', class: 'lever' }, g);\n  var tip = el('ellipse', { rx: 6, ry: 4.5, fill: 'url(#altairLever)', class: 'tip' }, g);\n  el('rect', { x: -20, y: -26, width: 40, height: 26, class: 'hit', 'data-dir': 'up' }, g);\n  el('rect', { x: -20, y: 0, width: 40, height: 26, class: 'hit', 'data-dir': 'down' }, g);\n  var o = { g: g, lever: lever, tip: tip, kind: kind, arg: arg, pos: 0 };\n  g.setAttribute('aria-label', kind === 'addr' ? 'A' + arg : kind === 'power' ? 'OFF \/ ON' : arg[0] + (arg[1] ? ' \/ ' + arg[1] : ''));\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') {         \/\/ sull'Altair OFF \u00e8 in alto e ON in basso\n      var on = dir === 'down' ? true : dir === 'up' ? false : !self.m.power;\n      self.m.setPower(on); self.setPos(o, on ? 1 : -1);\n      self.onCmd(on ? 'powerOn' : 'powerOff');\n    } else if (kind === 'ctl') {\n      self.setPos(o, dir === 'up' ? -1 : 1);\n      clearTimeout(o.t); o.t = setTimeout(function () { self.setPos(o, 0); }, 220);\n      var cmd = dir === 'up' ? arg[2] : arg[3];\n      if (cmd) { 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\nP.setPos = function (o, pos) {\n  o.pos = pos;\n  if (pos < 0) { o.lever.setAttribute('y', -22); o.lever.setAttribute('height', 22); o.tip.setAttribute('cy', -22); }\n  else if (pos > 0) { o.lever.setAttribute('y', 0); o.lever.setAttribute('height', 22); o.tip.setAttribute('cy', 22); }\n  else { o.lever.setAttribute('y', -4); o.lever.setAttribute('height', 8); o.tip.setAttribute('cy', 0); }\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.data, k.addr, k.status, [k.wait, k.hlda]).forEach(function (o) { this.setLed(o, 0); }, this);\n    return;\n  }\n  for (i = 0; i < 8; i++) this.setLed(k.data[i], L.data[i]);\n  for (i = 0; i < 16; i++) this.setLed(k.addr[i], L.addr[i]);\n  k.status.forEach(function (o) { this.setLed(o, typeof o.key === 'number' ? L.status[o.key] : L[o.key]); }, this);\n  this.setLed(k.wait, L.wait ? 1 : 0); this.setLed(k.hlda, L.hlda);\n};\n\nRF.Panel = Panel;\n})(window.RFAltair = window.RFAltair || {});\n\n\/* RetroFixer \u00b7 Altair 8800 \u00b7 programmi di esempio (sorgenti assembler 8080)\n * Console sulla MITS 88-2SIO: 10h stato (bit 0 = carattere ricevuto, bit 1 = pronta a trasmettere), 11h dati.\n * L'Altair non ha LED di uscita: i programmi \"luminosi\" mostrano i dati sui LED di indirizzo A15-A8\n * leggendo con LDAX D dall'indirizzo che ha in D il valore da mostrare (lo stesso trucco di Kill the Bit).\n *\/\n(function (RF) {\n'use strict';\nvar ACIA_INIT = [\n  '        MVI A,03H      ; 6850: reset',\n  '        OUT STATO',\n  '        MVI A,15H      ; 8 bit, nessuna parita, 1 stop, clock \/16',\n  '        OUT STATO'\n];\nvar IO2SIO = [\n  '; ---- console sulla 88-2SIO',\n  'GETC:   IN STATO       ; bit 0 = carattere ricevuto',\n  '        RRC',\n  '        JNC GETC',\n  '        IN DATI',\n  '        ANI 7FH',\n  '        RET',\n  'PUTC:   PUSH PSW',\n  'PUTC1:  IN STATO       ; bit 1 = pronta a trasmettere',\n  '        ANI 02H',\n  '        JZ PUTC1',\n  '        POP PSW',\n  '        OUT DATI',\n  '        RET'\n];\nRF.PROGRAMS = [\n{\n  id: 'tinybasic',\n  name: { it: 'Palo Alto Tiny BASIC (1976)', en: 'Palo Alto Tiny BASIC (1976)' },\n  desc: {\n    it: 'Il BASIC di Li-Chen Wang, pubblicato gratis nel 1976 proprio per l\\'Altair con la seriale 88-2SIO. Scrivi i comandi nel terminale: <b>PRINT 2+2<\\\/b>, poi un programma con i numeri di riga e <b>RUN<\\\/b>, <b>LIST<\\\/b>, <b>NEW<\\\/b>. Ctrl-C ferma un programma. Numeri interi da -32767 a 32767, variabili A-Z e la matrice @(i).',\n    en: 'Li-Chen Wang\\'s BASIC, published for free in 1976 for the Altair with the 88-2SIO serial board. Type commands in the terminal: <b>PRINT 2+2<\\\/b>, then a program with line numbers and <b>RUN<\\\/b>, <b>LIST<\\\/b>, <b>NEW<\\\/b>. Ctrl-C stops a program. Integers from -32767 to 32767, variables A-Z and the array @(i).'\n  },\n  terminal: true,\n  src: \"; Palo Alto Tiny BASIC 2.0 - Li-Chen Wang (1976), traduzione in mnemonici Intel di Roger Rauskolb,\\n; correzione di CHGSGN di Udo Munk (2019), adattamento ASM80 di Martin Maly (CC0).\\n; Console: seriale MITS 88-2SIO (ACIA 6850) alle porte 10h\/11h. Programmi da 0800h a 7F00h.\\n;*************************************************************\\n;\\n;                 TINY BASIC FOR INTEL 8080\\n;                       VERSION 2.0\\n;                     BY LI-CHEN WANG\\n;                  MODIFIED AND TRANSLATED\\n;                    TO INTEL MNEMONICS\\n;                     BY ROGER RAUSKOLB\\n;                      10 OCTOBER,1976\\n;                        @COPYLEFT\\n;                   ALL WRONGS RESERVED\\n;\\n;                 ADDED FIX FOR BUGGY CHGSGN\\n;                 UDO MUNK, 10 DECEMBER 2019\\n;*************************************************************\\n;\\n; *** ZERO PAGE SUBROUTINES ***\\n;\\n; THE 8080 INSTRUCTION SET LETS YOU HAVE 8 ROUTINES IN LOW\\n; MEMORY THAT MAY BE CALLED BY RST N, N BEING 0 THROUGH 7.\\n; THIS IS A ONE BYTE INSTRUCTION AND HAS THE SAME POWER AS\\n; THE THREE BYTE INSTRUCTION CALL LLHH.  TINY BASIC WILL\\n; USE RST 0 AS START AND RST 1 THROUGH RST 7 FOR\\n; THE SEVEN MOST FREQUENTLY USED SUBROUTINES.\\n; TWO OTHER SUBROUTINES (CRLF AND TSTNUM) ARE ALSO IN THIS\\n; SECTION.  THEY CAN BE REACHED ONLY BY 3-BYTE CALLS.\\n;\\n;\\n; -- MM variables\\nACIA_DATA   EQU     17\\nACIA_CTRL   EQU     16\\n; -- bit 0: char received\\n; -- bit 1: clear to send\\n;\\n;\\n            ORG     0H\\nSTART:      LXI     SP,STACK ;*** COLD START ***\\n            MVI     A,0FFH\\n            JMP     INIT\\n;\\n            XTHL     ;*** TSTC OR RST 1 ***\\n            RST     5 ;IGNORE BLANKS AND\\n            CMP     M ;TEST CHARACTER\\n            JMP     TC1 ;REST OF THIS IS AT TC1\\n;\\nCRLF:       MVI     A,CR ;*** CRLF ***\\n;\\n            PUSH    PSW ;*** OUTC OR RST 2 ***\\n            LDA     OCSW ;PRINT CHARACTER ONLY\\n            ORA     A ;IF OCSW SWITCH IS ON\\n            JMP     OC2 ;REST OF THIS IS AT OC2\\n;\\n            CALL    EXPR2 ;*** EXPR OR RST 3 ***\\n            PUSH    H ;EVALUATE AN EXPRESSION\\n            JMP     EXPR1 ;REST OF IT AT EXPR1\\n            DB      \\\"W\\\"\\n;\\n            MOV     A,H ;*** COMP OR RST 4 ***\\n            CMP     D ;COMPARE HL WITH DE\\n            RNZ      ;RETURN CORRECT C AND\\n            MOV     A,L ;Z FLAGS\\n            CMP     E ;BUT OLD A IS LOST\\n            RET\\n            DB      \\\"AN\\\"\\n;\\nSS1:        LDAX    D ;*** IGNBLK\/RST 5 ***\\n            CPI     20H ;IGNORE BLANKS\\n            RNZ      ;IN TEXT (WHERE DE->)\\n            INX     D ;AND RETURN THE FIRST\\n            JMP     SS1 ;NON-BLANK CHAR. IN A\\n;\\n            POP     PSW ;*** FINISH\/RST 6 ***\\n            CALL    FIN ;CHECK END OF COMMAND\\n            JMP     QWHAT ;PRINT \\\"WHAT?\\\" IF WRONG\\n            DB      \\\"G\\\"\\n;\\n            RST     5 ;*** TSTV OR RST 7 ***\\n            SUI     40H ;TEST VARIABLES\\n            RC       ;C:NOT A VARIABLE\\n            JNZ     TV1 ;NOT \\\"@\\\" ARRAY\\n            INX     D ;IT IS THE \\\"@\\\" ARRAY\\n            CALL    PARN ;@ SHOULD BE FOLLOWED\\n            DAD     H ;BY (EXPR) AS ITS INDEX\\n            JC      QHOW ;IS INDEX TOO BIG?\\n            PUSH    D ;WILL IT OVERWRITE\\n            XCHG     ;TEXT?\\n            CALL    SIZE ;FIND SIZE OF FREE\\n            RST     4 ;AND CHECK THAT\\n            JC      ASORRY ;IF SO, SAY \\\"SORRY\\\"\\n            LXI     H,VARBGN ;IF NOT GET ADDRESS\\n            CALL    SUBDE ;OF @(EXPR) AND PUT IT\\n            POP     D ;IN HL\\n            RET      ;C FLAG IS CLEARED\\nTV1:        CPI     1BH ;NOT @, IS IT A TO Z?\\n            CMC      ;IF NOT RETURN C FLAG\\n            RC\\n            INX     D ;IF A THROUGH Z\\n            LXI     H,VARBGN ;COMPUTE ADDRESS OF\\n            RLC      ;THAT VARIABLE\\n            ADD     L ;AND RETURN IT IN HL\\n            MOV     L,A ;WITH C FLAG CLEARED\\n            MVI     A,0\\n            ADC     H\\n            MOV     H,A\\n            RET\\n;\\n;TSTC:  XTHL                            ;*** TSTC OR RST 1 ***\\n;       RST  5                          ;THIS IS AT LOC. 8\\n;       CMP  M                          ;AND THEN JUMP HERE\\nTC1:        INX     H ;COMPARE THE BYTE THAT\\n            JZ      TC2 ;FOLLOWS THE RST INST.\\n            PUSH    B ;WITH THE TEXT (DE->)\\n            MOV     C,M ;IF NOT =, ADD THE 2ND\\n            MVI     B,0 ;BYTE THAT FOLLOWS THE\\n            DAD     B ;RST TO THE OLD PC\\n            POP     B ;I.E., DO A RELATIVE\\n            DCX     D ;JUMP IF NOT =\\nTC2:        INX     D ;IF =, SKIP THOSE BYTES\\n            INX     H ;AND CONTINUE\\n            XTHL\\n            RET\\n;\\nTSTNUM:     LXI     H,0 ;*** TSTNUM ***\\n            MOV     B,H ;TEST IF THE TEXT IS\\n            RST     5 ;A NUMBER\\nTN1:        CPI     30H ;IF NOT, RETURN 0 IN\\n            RC       ;B AND HL\\n            CPI     3AH ;IF NUMBERS, CONVERT\\n            RNC      ;TO BINARY IN HL AND\\n            MVI     A,0F0H ;SET B TO # OF DIGITS\\n            ANA     H ;IF H>255, THERE IS NO\\n            JNZ     QHOW ;ROOM FOR NEXT DIGIT\\n            INR     B ;B COUNTS # OF DIGITS\\n            PUSH    B\\n            MOV     B,H ;HL=10*HL+(NEW DIGIT)\\n            MOV     C,L\\n            DAD     H ;WHERE 10* IS DONE BY\\n            DAD     H ;SHIFT AND ADD\\n            DAD     B\\n            DAD     H\\n            LDAX    D ;AND (DIGIT) IS FROM\\n            INX     D ;STRIPPING THE ASCII\\n            ANI     0FH ;CODE\\n            ADD     L\\n            MOV     L,A\\n            MVI     A,0\\n            ADC     H\\n            MOV     H,A\\n            POP     B\\n            LDAX    D ;DO THIS DIGIT AFTER\\n            JP      TN1 ;DIGIT. S SAYS OVERFLOW\\nQHOW:       PUSH    D ;*** ERROR \\\"HOW?\\\" ***\\nAHOW:       LXI     D,HOW\\n            JMP     ERROR\\nHOW:        DB      \\\"HOW?\\\"\\n            DB      CR\\nOK:         DB      \\\"OK\\\"\\n            DB      CR\\nWHAT:       DB      \\\"WHAT?\\\"\\n            DB      CR\\nSORRY:      DB      \\\"SORRY\\\"\\n            DB      CR\\n;\\n;*************************************************************\\n;\\n; *** MAIN ***\\n;\\n; THIS IS THE MAIN LOOP THAT COLLECTS THE TINY BASIC PROGRAM\\n; AND STORES IT IN THE MEMORY.\\n;\\n; AT START, IT PRINTS OUT \\\"(CR)OK(CR)\\\", AND INITIALIZES THE\\n; STACK AND SOME OTHER INTERNAL VARIABLES.  THEN IT PROMPTS\\n; \\\">\\\" AND READS A LINE.  IF THE LINE STARTS WITH A NON-ZERO\\n; NUMBER, THIS NUMBER IS THE LINE NUMBER.  THE LINE NUMBER\\n; (IN 16 BIT BINARY) AND THE REST OF THE LINE (INCLUDING CR)\\n; IS STORED IN THE MEMORY.  IF A LINE WITH THE SAME LINE\\n; NUMBER IS ALREADY THERE, IT IS REPLACED BY THE NEW ONE.  IF\\n; THE REST OF THE LINE CONSISTS OF A CR ONLY, IT IS NOT STORED\\n; AND ANY EXISTING LINE WITH THE SAME LINE NUMBER IS DELETED.\\n;\\n; AFTER A LINE IS INSERTED, REPLACED, OR DELETED, THE PROGRAM\\n; LOOPS BACK AND ASKS FOR ANOTHER LINE.  THIS LOOP WILL BE\\n; TERMINATED WHEN IT READS A LINE WITH ZERO OR NO LINE\\n; NUMBER; AND CONTROL IS TRANSFERED TO \\\"DIRECT\\\".\\n;\\n; TINY BASIC PROGRAM SAVE AREA STARTS AT THE MEMORY LOCATION\\n; LABELED \\\"TXTBGN\\\" AND ENDS AT \\\"TXTEND\\\".  WE ALWAYS FILL THIS\\n; AREA STARTING AT \\\"TXTBGN\\\", THE UNFILLED PORTION IS POINTED\\n; BY THE CONTENT OF A MEMORY LOCATION LABELED \\\"TXTUNF\\\".\\n;\\n; THE MEMORY LOCATION \\\"CURRNT\\\" POINTS TO THE LINE NUMBER\\n; THAT IS CURRENTLY BEING INTERPRETED.  WHILE WE ARE IN\\n; THIS LOOP OR WHILE WE ARE INTERPRETING A DIRECT COMMAND\\n; (SEE NEXT SECTION). \\\"CURRNT\\\" SHOULD POINT TO A 0.\\n;\\nRSTART:     LXI     SP,STACK\\nST1:        CALL    CRLF ;AND JUMP TO HERE\\n            LXI     D,OK ;DE->STRING\\n            SUB     A ;A=0\\n            CALL    PRTSTG ;PRINT STRING UNTIL CR\\n            LXI     H,ST2+1 ;LITERAL 0\\n            SHLD    CURRNT ;CURRENT->LINE # = 0\\nST2:        LXI     H,0\\n            SHLD    LOPVAR\\n            SHLD    STKGOS\\nST3:        MVI     A,3EH ;PROMPT \\\">\\\" AND\\n            CALL    GETLN ;READ A LINE\\n            PUSH    D ;DE->END OF LINE\\n            LXI     D,BUFFER ;DE->BEGINNING OF LINE\\n            CALL    TSTNUM ;TEST IF IT IS A NUMBER\\n            RST     5\\n            MOV     A,H ;HL=VALUE OF THE # OR\\n            ORA     L ;0 IF NO # WAS FOUND\\n            POP     B ;BC->END OF LINE\\n            JZ      DIRECT\\n            DCX     D ;BACKUP DE AND SAVE\\n            MOV     A,H ;VALUE OF LINE # THERE\\n            STAX    D\\n            DCX     D\\n            MOV     A,L\\n            STAX    D\\n            PUSH    B ;BC,DE->BEGIN, END\\n            PUSH    D\\n            MOV     A,C\\n            SUB     E\\n            PUSH    PSW ;A=# OF BYTES IN LINE\\n            CALL    FNDLN ;FIND THIS LINE IN SAVE\\n            PUSH    D ;AREA, DE->SAVE AREA\\n            JNZ     ST4 ;NZ:NOT FOUND, INSERT\\n            PUSH    D ;Z:FOUND, DELETE IT\\n            CALL    FNDNXT ;FIND NEXT LINE\\n;DE->NEXT LINE\\n            POP     B ;BC->LINE TO BE DELETED\\n            LHLD    TXTUNF ;HL->UNFILLED SAVE AREA\\n            CALL    MVUP ;MOVE UP TO DELETE\\n            MOV     H,B ;TXTUNF->UNFILLED AREA\\n            MOV     L,C\\n            SHLD    TXTUNF ;UPDATE\\nST4:        POP     B ;GET READY TO INSERT\\n            LHLD    TXTUNF ;BUT FIRST CHECK IF\\n            POP     PSW ;THE LENGTH OF NEW LINE\\n            PUSH    H ;IS 3 (LINE # AND CR)\\n            CPI     3 ;THEN DO NOT INSERT\\n            JZ      RSTART ;MUST CLEAR THE STACK\\n            ADD     L ;COMPUTE NEW TXTUNF\\n            MOV     L,A\\n            MVI     A,0\\n            ADC     H\\n            MOV     H,A ;HL->NEW UNFILLED AREA\\n            LXI     D,TXTEND ;CHECK TO SEE IF THERE\\n            RST     4 ;IS ENOUGH SPACE\\n            JNC     QSORRY ;SORRY, NO ROOM FOR IT\\n            SHLD    TXTUNF ;OK, UPDATE TXTUNF\\n            POP     D ;DE->OLD UNFILLED AREA\\n            CALL    MVDOWN\\n            POP     D ;DE->BEGIN, HL->END\\n            POP     H\\n            CALL    MVUP ;MOVE NEW LINE TO SAVE\\n            JMP     ST3 ;AREA\\n;\\n;*************************************************************\\n;\\n; WHAT FOLLOWS IS THE CODE TO EXECUTE DIRECT AND STATEMENT\\n; COMMANDS.  CONTROL IS TRANSFERED TO THESE POINTS VIA THE\\n; COMMAND TABLE LOOKUP CODE OF \\\"DIRECT\\\" AND \\\"EXEC\\\" IN LAST\\n; SECTION.  AFTER THE COMMAND IS EXECUTED, CONTROL IS\\n; TRANSFERED TO OTHERS SECTIONS AS FOLLOWS:\\n;\\n; FOR \\\"LIST\\\", \\\"NEW\\\", AND \\\"STOP\\\": GO BACK TO \\\"RSTART\\\"\\n; FOR \\\"RUN\\\": GO EXECUTE THE FIRST STORED LINE IF ANY, ELSE\\n; GO BACK TO \\\"RSTART\\\".\\n; FOR \\\"GOTO\\\" AND \\\"GOSUB\\\": GO EXECUTE THE TARGET LINE.\\n; FOR \\\"RETURN\\\" AND \\\"NEXT\\\": GO BACK TO SAVED RETURN LINE.\\n; FOR ALL OTHERS: IF \\\"CURRENT\\\" -> 0, GO TO \\\"RSTART\\\", ELSE\\n; GO EXECUTE NEXT COMMAND.  (THIS IS DONE IN \\\"FINISH\\\".)\\n;*************************************************************\\n;\\n; *** NEW *** STOP *** RUN (& FRIENDS) *** & GOTO ***\\n;\\n; \\\"NEW(CR)\\\" SETS \\\"TXTUNF\\\" TO POINT TO \\\"TXTBGN\\\"\\n;\\n; \\\"STOP(CR)\\\" GOES BACK TO \\\"RSTART\\\"\\n;\\n; \\\"RUN(CR)\\\" FINDS THE FIRST STORED LINE, STORE ITS ADDRESS (IN\\n; \\\"CURRENT\\\"), AND START EXECUTE IT.  NOTE THAT ONLY THOSE\\n; COMMANDS IN TAB2 ARE LEGAL FOR STORED PROGRAM.\\n;\\n; THERE ARE 3 MORE ENTRIES IN \\\"RUN\\\":\\n; \\\"RUNNXL\\\" FINDS NEXT LINE, STORES ITS ADDR. AND EXECUTES IT.\\n; \\\"RUNTSL\\\" STORES THE ADDRESS OF THIS LINE AND EXECUTES IT.\\n; \\\"RUNSML\\\" CONTINUES THE EXECUTION ON SAME LINE.\\n;\\n; \\\"GOTO EXPR(CR)\\\" EVALUATES THE EXPRESSION, FIND THE TARGET\\n; LINE, AND JUMP TO \\\"RUNTSL\\\" TO DO IT.\\n;\\nNEW:        CALL    ENDCHK ;*** NEW(CR) ***\\n            LXI     H,TXTBGN\\n            SHLD    TXTUNF\\n;\\nSTOP:       CALL    ENDCHK ;*** STOP(CR) ***\\n            JMP     RSTART\\n;\\nRUN:        CALL    ENDCHK ;*** RUN(CR) ***\\n            LXI     D,TXTBGN ;FIRST SAVED LINE\\n;\\nRUNNXL:     LXI     H,0 ;*** RUNNXL ***\\n            CALL    FNDLP ;FIND WHATEVER LINE #\\n            JC      RSTART ;C:PASSED TXTUNF, QUIT\\n;\\nRUNTSL:     XCHG     ;*** RUNTSL ***\\n            SHLD    CURRNT ;SET \\\"CURRENT\\\"->LINE #\\n            XCHG\\n            INX     D ;BUMP PASS LINE #\\n            INX     D\\n;\\nRUNSML:     CALL    CHKIO ;*** RUNSML ***\\n            LXI     H,TAB2-1 ;FIND COMMAND IN TAB2\\n            JMP     EXEC ;AND EXECUTE IT\\n;\\nGOTO:       RST     3 ;*** GOTO EXPR ***\\n            PUSH    D ;SAVE FOR ERROR ROUTINE\\n            CALL    ENDCHK ;MUST FIND A CR\\n            CALL    FNDLN ;FIND THE TARGET LINE\\n            JNZ     AHOW ;NO SUCH LINE #\\n            POP     PSW ;CLEAR THE PUSH DE\\n            JMP     RUNTSL ;GO DO IT\\n;\\n;*************************************************************\\n;\\n; *** LIST *** & PRINT ***\\n;\\n; LIST HAS TWO FORMS:\\n; \\\"LIST(CR)\\\" LISTS ALL SAVED LINES\\n; \\\"LIST #(CR)\\\" START LIST AT THIS LINE #\\n; YOU CAN STOP THE LISTING BY CONTROL C KEY\\n;\\n; PRINT COMMAND IS \\\"PRINT ....;\\\" OR \\\"PRINT ....(CR)\\\"\\n; WHERE \\\"....\\\" IS A LIST OF EXPRESIONS, FORMATS, BACK-\\n; ARROWS, AND STRINGS.  THESE ITEMS ARE SEPERATED BY COMMAS.\\n;\\n; A FORMAT IS A POUND SIGN FOLLOWED BY A NUMBER.  IT CONTROLS\\n; THE NUMBER OF SPACES THE VALUE OF A EXPRESION IS GOING TO\\n; BE PRINTED.  IT STAYS EFFECTIVE FOR THE REST OF THE PRINT\\n; COMMAND UNLESS CHANGED BY ANOTHER FORMAT.  IF NO FORMAT IS\\n; SPECIFIED, 6 POSITIONS WILL BE USED.\\n;\\n; A STRING IS QUOTED IN A PAIR OF SINGLE QUOTES OR A PAIR OF\\n; DOUBLE QUOTES.\\n;\\n; A BACK-ARROW MEANS GENERATE A (CR) WITHOUT (LF)\\n;\\n; A (CRLF) IS GENERATED AFTER THE ENTIRE LIST HAS BEEN\\n; PRINTED OR IF THE LIST IS A NULL LIST.  HOWEVER IF THE LIST\\n; ENDED WITH A COMMA, NO (CRLF) IS GENERATED.\\n;\\nLIST:       CALL    TSTNUM ;TEST IF THERE IS A #\\n            CALL    ENDCHK ;IF NO # WE GET A 0\\n            CALL    FNDLN ;FIND THIS OR NEXT LINE\\nLS1:        JC      RSTART ;C:PASSED TXTUNF\\n            CALL    PRTLN ;PRINT THE LINE\\n            CALL    CHKIO ;STOP IF HIT CONTROL-C\\n            CALL    FNDLP ;FIND NEXT LINE\\n            JMP     LS1 ;AND LOOP BACK\\n;\\nPRINT:      MVI     C,6 ;C = # OF SPACES\\n            RST     1 ;IF NULL LIST & \\\";\\\"\\n            DB      3BH\\n            DB      PR2-$-1\\n            CALL    CRLF ;GIVE CR-LF AND\\n            JMP     RUNSML ;CONTINUE SAME LINE\\nPR2:        RST     1 ;IF NULL LIST (CR)\\n            DB      CR\\n            DB      PR0-$-1\\n            CALL    CRLF ;ALSO GIVE CR-LF AND\\n            JMP     RUNNXL ;GO TO NEXT LINE\\nPR0:        RST     1 ;ELSE IS IT FORMAT?\\n            DB      \\\"#\\\"\\n            DB      PR1-$-1\\n            RST     3 ;YES, EVALUATE EXPR.\\n            MOV     C,L ;AND SAVE IT IN C\\n            JMP     PR3 ;LOOK FOR MORE TO PRINT\\nPR1:        CALL    QTSTG ;OR IS IT A STRING?\\n            JMP     PR8 ;IF NOT, MUST BE EXPR.\\nPR3:        RST     1 ;IF \\\",\\\", GO FIND NEXT\\n            DB      \\\",\\\"\\n            DB      PR6-$-1\\n            CALL    FIN ;IN THE LIST.\\n            JMP     PR0 ;LIST CONTINUES\\nPR6:        CALL    CRLF ;LIST ENDS\\n            RST     6\\nPR8:        RST     3 ;EVALUATE THE EXPR\\n            PUSH    B\\n            CALL    PRTNUM ;PRINT THE VALUE\\n            POP     B\\n            JMP     PR3 ;MORE TO PRINT?\\n;\\n;*************************************************************\\n;\\n; *** GOSUB *** & RETURN ***\\n;\\n; \\\"GOSUB EXPR;\\\" OR \\\"GOSUB EXPR (CR)\\\" IS LIKE THE \\\"GOTO\\\"\\n; COMMAND, EXCEPT THAT THE CURRENT TEXT POINTER, STACK POINTER\\n; ETC. ARE SAVE SO THAT EXECUTION CAN BE CONTINUED AFTER THE\\n; SUBROUTINE \\\"RETURN\\\".  IN ORDER THAT \\\"GOSUB\\\" CAN BE NESTED\\n; (AND EVEN RECURSIVE), THE SAVE AREA MUST BE STACKED.\\n; THE STACK POINTER IS SAVED IN \\\"STKGOS\\\", THE OLD \\\"STKGOS\\\" IS\\n; SAVED IN THE STACK.  IF WE ARE IN THE MAIN ROUTINE, \\\"STKGOS\\\"\\n; IS ZERO (THIS WAS DONE BY THE \\\"MAIN\\\" SECTION OF THE CODE),\\n; BUT WE STILL SAVE IT AS A FLAG FOR NO FURTHER \\\"RETURN\\\"S.\\n;\\n; \\\"RETURN(CR)\\\" UNDOS EVERYTHING THAT \\\"GOSUB\\\" DID, AND THUS\\n; RETURN THE EXECUTION TO THE COMMAND AFTER THE MOST RECENT\\n; \\\"GOSUB\\\".  IF \\\"STKGOS\\\" IS ZERO, IT INDICATES THAT WE\\n; NEVER HAD A \\\"GOSUB\\\" AND IS THUS AN ERROR.\\n;\\nGOSUB:      CALL    PUSHA ;SAVE THE CURRENT \\\"FOR\\\"\\n            RST     3 ;PARAMETERS\\n            PUSH    D ;AND TEXT POINTER\\n            CALL    FNDLN ;FIND THE TARGET LINE\\n            JNZ     AHOW ;NOT THERE. SAY \\\"HOW?\\\"\\n            LHLD    CURRNT ;FOUND IT, SAVE OLD\\n            PUSH    H ;\\\"CURRNT\\\" OLD \\\"STKGOS\\\"\\n            LHLD    STKGOS\\n            PUSH    H\\n            LXI     H,0 ;AND LOAD NEW ONES\\n            SHLD    LOPVAR\\n            DAD     SP\\n            SHLD    STKGOS\\n            JMP     RUNTSL ;THEN RUN THAT LINE\\nRETURN:     CALL    ENDCHK ;THERE MUST BE A CR\\n            LHLD    STKGOS ;OLD STACK POINTER\\n            MOV     A,H ;0 MEANS NOT EXIST\\n            ORA     L\\n            JZ      QWHAT ;SO, WE SAY: \\\"WHAT?\\\"\\n            SPHL     ;ELSE, RESTORE IT\\n            POP     H\\n            SHLD    STKGOS ;AND THE OLD \\\"STKGOS\\\"\\n            POP     H\\n            SHLD    CURRNT ;AND THE OLD \\\"CURRNT\\\"\\n            POP     D ;OLD TEXT POINTER\\n            CALL    POPA ;OLD \\\"FOR\\\" PARAMETERS\\n            RST     6 ;AND WE ARE BACK HOME\\n;\\n;*************************************************************\\n;\\n; *** FOR *** & NEXT ***\\n;\\n; \\\"FOR\\\" HAS TWO FORMS:\\n; \\\"FOR VAR=EXP1 TO EXP2 STEP EXP3\\\" AND \\\"FOR VAR=EXP1 TO EXP2\\\"\\n; THE SECOND FORM MEANS THE SAME THING AS THE FIRST FORM WITH\\n; EXP3=1.  (I.E., WITH A STEP OF +1.)\\n; TBI WILL FIND THE VARIABLE VAR, AND SET ITS VALUE TO THE\\n; CURRENT VALUE OF EXP1.  IT ALSO EVALUATES EXP2 AND EXP3\\n; AND SAVE ALL THESE TOGETHER WITH THE TEXT POINTER ETC. IN\\n; THE \\\"FOR\\\" SAVE AREA, WHICH CONSISTS OF \\\"LOPVAR\\\", \\\"LOPINC\\\",\\n; \\\"LOPLMT\\\", \\\"LOPLN\\\", AND \\\"LOPPT\\\".  IF THERE IS ALREADY SOME-\\n; THING IN THE SAVE AREA (THIS IS INDICATED BY A NON-ZERO\\n; \\\"LOPVAR\\\"), THEN THE OLD SAVE AREA IS SAVED IN THE STACK\\n; BEFORE THE NEW ONE OVERWRITES IT.\\n; TBI WILL THEN DIG IN THE STACK AND FIND OUT IF THIS SAME\\n; VARIABLE WAS USED IN ANOTHER CURRENTLY ACTIVE \\\"FOR\\\" LOOP.\\n; IF THAT IS THE CASE, THEN THE OLD \\\"FOR\\\" LOOP IS DEACTIVATED.\\n; (PURGED FROM THE STACK..)\\n;\\n; \\\"NEXT VAR\\\" SERVES AS THE LOGICAL (NOT NECESSARILLY PHYSICAL)\\n; END OF THE \\\"FOR\\\" LOOP.  THE CONTROL VARIABLE VAR. IS CHECKED\\n; WITH THE \\\"LOPVAR\\\".  IF THEY ARE NOT THE SAME, TBI DIGS IN\\n; THE STACK TO FIND THE RIGHT ONE AND PURGES ALL THOSE THAT\\n; DID NOT MATCH.  EITHER WAY, TBI THEN ADDS THE \\\"STEP\\\" TO\\n; THAT VARIABLE AND CHECK THE RESULT WITH THE LIMIT.  IF IT\\n; IS WITHIN THE LIMIT, CONTROL LOOPS BACK TO THE COMMAND\\n; FOLLOWING THE \\\"FOR\\\".  IF OUTSIDE THE LIMIT, THE SAVE AREA\\n; IS PURGED AND EXECUTION CONTINUES.\\n;\\nFOR:        CALL    PUSHA ;SAVE THE OLD SAVE AREA\\n            CALL    SETVAL ;SET THE CONTROL VAR.\\n            DCX     H ;HL IS ITS ADDRESS\\n            SHLD    LOPVAR ;SAVE THAT\\n            LXI     H,TAB5-1 ;USE \\\"EXEC\\\" TO LOOK\\n            JMP     EXEC ;FOR THE WORD \\\"TO\\\"\\nFR1:        RST     3 ;EVALUATE THE LIMIT\\n            SHLD    LOPLMT ;SAVE THAT\\n            LXI     H,TAB6-1 ;USE \\\"EXEC\\\" TO LOOK\\n            JMP     EXEC ;FOR THE WORD \\\"STEP\\\"\\nFR2:        RST     3 ;FOUND IT, GET STEP\\n            JMP     FR4\\nFR3:        LXI     H,1H ;NOT FOUND, SET TO 1\\nFR4:        SHLD    LOPINC ;SAVE THAT TOO\\nFR5:        LHLD    CURRNT ;SAVE CURRENT LINE #\\n            SHLD    LOPLN\\n            XCHG     ;AND TEXT POINTER\\n            SHLD    LOPPT\\n            LXI     B,0AH ;DIG INTO STACK TO\\n            LHLD    LOPVAR ;FIND \\\"LOPVAR\\\"\\n            XCHG\\n            MOV     H,B\\n            MOV     L,B ;HL=0 NOW\\n            DAD     SP ;HERE IS THE STACK\\n            DB      3EH\\nFR7:        DAD     B ;EACH LEVEL IS 10 DEEP\\n            MOV     A,M ;GET THAT OLD \\\"LOPVAR\\\"\\n            INX     H\\n            ORA     M\\n            JZ      FR8 ;0 SAYS NO MORE IN IT\\n            MOV     A,M\\n            DCX     H\\n            CMP     D ;SAME AS THIS ONE?\\n            JNZ     FR7\\n            MOV     A,M ;THE OTHER HALF?\\n            CMP     E\\n            JNZ     FR7\\n            XCHG     ;YES, FOUND ONE\\n            LXI     H,0H\\n            DAD     SP ;TRY TO MOVE SP\\n            MOV     B,H\\n            MOV     C,L\\n            LXI     H,0AH\\n            DAD     D\\n            CALL    MVDOWN ;AND PURGE 10 WORDS\\n            SPHL     ;IN THE STACK\\nFR8:        LHLD    LOPPT ;JOB DONE, RESTORE DE\\n            XCHG\\n            RST     6 ;AND CONTINUE\\n;\\nNEXT:       RST     7 ;GET ADDRESS OF VAR.\\n            JC      QWHAT ;NO VARIABLE, \\\"WHAT?\\\"\\n            SHLD    VARNXT ;YES, SAVE IT\\nNX0:        PUSH    D ;SAVE TEXT POINTER\\n            XCHG\\n            LHLD    LOPVAR ;GET VAR. IN \\\"FOR\\\"\\n            MOV     A,H\\n            ORA     L ;0 SAYS NEVER HAD ONE\\n            JZ      AWHAT ;SO WE ASK: \\\"WHAT?\\\"\\n            RST     4 ;ELSE WE CHECK THEM\\n            JZ      NX3 ;OK, THEY AGREE\\n            POP     D ;NO, LET'S SEE\\n            CALL    POPA ;PURGE CURRENT LOOP\\n            LHLD    VARNXT ;AND POP ONE LEVEL\\n            JMP     NX0 ;GO CHECK AGAIN\\nNX3:        MOV     E,M ;COME HERE WHEN AGREED\\n            INX     H\\n            MOV     D,M ;DE=VALUE OF VAR.\\n            LHLD    LOPINC\\n            PUSH    H\\n            MOV     A,H\\n            XRA     D\\n            MOV     A,D\\n            DAD     D ;ADD ONE STEP\\n            JM      NX4\\n            XRA     H\\n            JM      NX5\\nNX4:        XCHG\\n            LHLD    LOPVAR ;PUT IT BACK\\n            MOV     M,E\\n            INX     H\\n            MOV     M,D\\n            LHLD    LOPLMT ;HL->LIMIT\\n            POP     PSW ;OLD HL\\n            ORA     A\\n            JP      NX1 ;STEP > 0\\n            XCHG     ;STEP < 0\\nNX1:        CALL    CKHLDE ;COMPARE WITH LIMIT\\n            POP     D ;RESTORE TEXT POINTER\\n            JC      NX2 ;OUTSIDE LIMIT\\n            LHLD    LOPLN ;WITHIN LIMIT, GO\\n            SHLD    CURRNT ;BACK TO THE SAVED\\n            LHLD    LOPPT ;\\\"CURRNT\\\" AND TEXT\\n            XCHG     ;POINTER\\n            RST     6\\nNX5:        POP     H\\n            POP     D\\nNX2:        CALL    POPA ;PURGE THIS LOOP\\n            RST     6\\n;\\n;*************************************************************\\n;\\n; *** REM *** IF *** INPUT *** & LET (& DEFLT) ***\\n;\\n; \\\"REM\\\" CAN BE FOLLOWED BY ANYTHING AND IS IGNORED BY TBI.\\n; TBI TREATS IT LIKE AN \\\"IF\\\" WITH A FALSE CONDITION.\\n;\\n; \\\"IF\\\" IS FOLLOWED BY AN EXPR. AS A CONDITION AND ONE OR MORE\\n; COMMANDS (INCLUDING OTHER \\\"IF\\\"S) SEPERATED BY SEMI-COLONS.\\n; NOTE THAT THE WORD \\\"THEN\\\" IS NOT USED.  TBI EVALUATES THE\\n; EXPR. IF IT IS NON-ZERO, EXECUTION CONTINUES.  IF THE\\n; EXPR. IS ZERO, THE COMMANDS THAT FOLLOWS ARE IGNORED AND\\n; EXECUTION CONTINUES AT THE NEXT LINE.\\n;\\n; \\\"INPUT\\\" COMMAND IS LIKE THE \\\"PRINT\\\" COMMAND, AND IS FOLLOWED\\n; BY A LIST OF ITEMS.  IF THE ITEM IS A STRING IN SINGLE OR\\n; DOUBLE QUOTES, OR IS A BACK-ARROW, IT HAS THE SAME EFFECT AS\\n; IN \\\"PRINT\\\".  IF AN ITEM IS A VARIABLE, THIS VARIABLE NAME IS\\n; PRINTED OUT FOLLOWED BY A COLON.  THEN TBI WAITS FOR AN\\n; EXPR. TO BE TYPED IN.  THE VARIABLE IS THEN SET TO THE\\n; VALUE OF THIS EXPR.  IF THE VARIABLE IS PROCEDED BY A STRING\\n; (AGAIN IN SINGLE OR DOUBLE QUOTES), THE STRING WILL BE\\n; PRINTED FOLLOWED BY A COLON.  TBI THEN WAITS FOR INPUT EXPR.\\n; AND SET THE VARIABLE TO THE VALUE OF THE EXPR.\\n;\\n; IF THE INPUT EXPR. IS INVALID, TBI WILL PRINT \\\"WHAT?\\\",\\n; \\\"HOW?\\\" OR \\\"SORRY\\\" AND REPRINT THE PROMPT AND REDO THE INPUT.\\n; THE EXECUTION WILL NOT TERMINATE UNLESS YOU TYPE CONTROL-C.\\n; THIS IS HANDLED IN \\\"INPERR\\\".\\n;\\n; \\\"LET\\\" IS FOLLOWED BY A LIST OF ITEMS SEPERATED BY COMMAS.\\n; EACH ITEM CONSISTS OF A VARIABLE, AN EQUAL SIGN, AND AN EXPR.\\n; TBI EVALUATES THE EXPR. AND SET THE VARIABLE TO THAT VALUE.\\n; TBI WILL ALSO HANDLE \\\"LET\\\" COMMAND WITHOUT THE WORD \\\"LET\\\".\\n; THIS IS DONE BY \\\"DEFLT\\\".\\n;\\nREM:        LXI     H,0H ;*** REM ***\\n            DB      3EH ;THIS IS LIKE \\\"IF 0\\\"\\n;\\nIFF:        RST     3 ;*** IF ***\\n            MOV     A,H ;IS THE EXPR.=0?\\n            ORA     L\\n            JNZ     RUNSML ;NO, CONTINUE\\n            CALL    FNDSKP ;YES, SKIP REST OF LINE\\n            JNC     RUNTSL ;AND RUN THE NEXT LINE\\n            JMP     RSTART ;IF NO NEXT, RE-START\\n;\\nINPERR:     LHLD    STKINP ;*** INPERR ***\\n            SPHL     ;RESTORE OLD SP\\n            POP     H ;AND OLD \\\"CURRNT\\\"\\n            SHLD    CURRNT\\n            POP     D ;AND OLD TEXT POINTER\\n            POP     D ;REDO INPUT\\n;\\nINPUT:               ;*** INPUT ***\\nIP1:        PUSH    D ;SAVE IN CASE OF ERROR\\n            CALL    QTSTG ;IS NEXT ITEM A STRING?\\n            JMP     IP2 ;NO\\n            RST     7 ;YES, BUT FOLLOWED BY A\\n            JC      IP4 ;VARIABLE?   NO.\\n            JMP     IP3 ;YES.  INPUT VARIABLE\\nIP2:        PUSH    D ;SAVE FOR \\\"PRTSTG\\\"\\n            RST     7 ;MUST BE VARIABLE NOW\\n            JC      QWHAT ;\\\"WHAT?\\\" IT IS NOT?\\n            LDAX    D ;GET READY FOR \\\"PRTSTR\\\"\\n            MOV     C,A\\n            SUB     A\\n            STAX    D\\n            POP     D\\n            CALL    PRTSTG ;PRINT STRING AS PROMPT\\n            MOV     A,C ;RESTORE TEXT\\n            DCX     D\\n            STAX    D\\nIP3:        PUSH    D ;SAVE TEXT POINTER\\n            XCHG\\n            LHLD    CURRNT ;ALSO SAVE \\\"CURRNT\\\"\\n            PUSH    H\\n            LXI     H,IP1 ;A NEGATIVE NUMBER\\n            SHLD    CURRNT ;AS A FLAG\\n            LXI     H,0H ;SAVE SP TOO\\n            DAD     SP\\n            SHLD    STKINP\\n            PUSH    D ;OLD HL\\n            MVI     A,3AH ;PRINT THIS TOO\\n            CALL    GETLN ;AND GET A LINE\\n            LXI     D,BUFFER ;POINTS TO BUFFER\\n            RST     3 ;EVALUATE INPUT\\n            NOP      ;CAN BE \\\"CALL ENDCHK\\\"\\n            NOP\\n            NOP\\n            POP     D ;OK, GET OLD HL\\n            XCHG\\n            MOV     M,E ;SAVE VALUE IN VAR.\\n            INX     H\\n            MOV     M,D\\n            POP     H ;GET OLD \\\"CURRNT\\\"\\n            SHLD    CURRNT\\n            POP     D ;AND OLD TEXT POINTER\\nIP4:        POP     PSW ;PURGE JUNK IN STACK\\n            RST     1 ;IS NEXT CH. \\\",\\\"?\\n            DB      \\\",\\\"\\n            DB      IP5-$-1\\n            JMP     IP1 ;YES, MORE ITEMS.\\nIP5:        RST     6\\n;\\nDEFLT:      LDAX    D ;***  DEFLT ***\\n            CPI     CR ;EMPTY LINE IS OK\\n            JZ      LT1 ;ELSE IT IS \\\"LET\\\"\\n;\\nLET:        CALL    SETVAL ;*** LET ***\\n            RST     1 ;SET VALUE TO VAR.\\n            DB      \\\",\\\"\\n            DB      LT1-$-1\\n            JMP     LET ;ITEM BY ITEM\\nLT1:        RST     6 ;UNTIL FINISH\\n;\\n;*************************************************************\\n;\\n; *** EXPR ***\\n;\\n; \\\"EXPR\\\" EVALUATES ARITHMETICAL OR LOGICAL EXPRESSIONS.\\n; <EXPR>::<EXPR2>\\n;         <EXPR2><REL.OP.><EXPR2>\\n; WHERE <REL.OP.> IS ONE OF THE OPERATORS IN TAB8 AND THE\\n; RESULT OF THESE OPERATIONS IS 1 IF TRUE AND 0 IF FALSE.\\n; <EXPR2>::=(+ OR -)<EXPR3>(+ OR -<EXPR3>)(....)\\n; WHERE () ARE OPTIONAL AND (....) ARE OPTIONAL REPEATS.\\n; <EXPR3>::=<EXPR4>(* OR \/><EXPR4>)(....)\\n; <EXPR4>::=<VARIABLE>\\n;           <FUNCTION>\\n;           (<EXPR>)\\n; <EXPR> IS RECURSIVE SO THAT VARIABLE \\\"@\\\" CAN HAVE AN <EXPR>\\n; AS INDEX, FUNCTIONS CAN HAVE AN <EXPR> AS ARGUMENTS, AND\\n; <EXPR4> CAN BE AN <EXPR> IN PARANTHESE.\\n;\\n;EXPR:  CALL EXPR2                      ;THIS IS AT LOC. 18\\n;       PUSH H                          ;SAVE <EXPR2> VALUE\\nEXPR1:      LXI     H,TAB8-1 ;LOOKUP REL.OP.\\n            JMP     EXEC ;GO DO IT\\nXP11:       CALL    XP18 ;REL.OP.\\\">=\\\"\\n            RC       ;NO, RETURN HL=0\\n            MOV     L,A ;YES, RETURN HL=1\\n            RET\\nXP12:       CALL    XP18 ;REL.OP.\\\"#\\\"\\n            RZ       ;FALSE, RETURN HL=0\\n            MOV     L,A ;TRUE, RETURN HL=1\\n            RET\\nXP13:       CALL    XP18 ;REL.OP.\\\">\\\"\\n            RZ       ;FALSE\\n            RC       ;ALSO FALSE, HL=0\\n            MOV     L,A ;TRUE, HL=1\\n            RET\\nXP14:       CALL    XP18 ;REL.OP.\\\"<=\\\"\\n            MOV     L,A ;SET HL=1\\n            RZ       ;REL. TRUE, RETURN\\n            RC\\n            MOV     L,H ;ELSE SET HL=0\\n            RET\\nXP15:       CALL    XP18 ;REL.OP.\\\"=\\\"\\n            RNZ      ;FALSE, RETURN HL=0\\n            MOV     L,A ;ELSE SET HL=1\\n            RET\\nXP16:       CALL    XP18 ;REL.OP.\\\"<\\\"\\n            RNC      ;FALSE, RETURN HL=0\\n            MOV     L,A ;ELSE SET HL=1\\n            RET\\nXP17:       POP     H ;NOT .REL.OP\\n            RET      ;RETURN HL=<EXPR2>\\nXP18:       MOV     A,C ;SUBROUTINE FOR ALL\\n            POP     H ;REL.OP.'S\\n            POP     B\\n            PUSH    H ;REVERSE TOP OF STACK\\n            PUSH    B\\n            MOV     C,A\\n            CALL    EXPR2 ;GET 2ND <EXPR2>\\n            XCHG     ;VALUE IN DE NOW\\n            XTHL     ;1ST <EXPR2> IN HL\\n            CALL    CKHLDE ;COMPARE 1ST WITH 2ND\\n            POP     D ;RESTORE TEXT POINTER\\n            LXI     H,0H ;SET HL=0, A=1\\n            MVI     A,1\\n            RET\\n;\\nEXPR2:      RST     1 ;NEGATIVE SIGN?\\n            DB      \\\"-\\\"\\n            DB      XP21-$-1\\n            LXI     H,0H ;YES, FAKE \\\"0-\\\"\\n            JMP     XP26 ;TREAT LIKE SUBTRACT\\nXP21:       RST     1 ;POSITIVE SIGN? IGNORE\\n            DB      \\\"+\\\"\\n            DB      XP22-$-1\\nXP22:       CALL    EXPR3 ;1ST <EXPR3>\\nXP23:       RST     1 ;ADD?\\n            DB      \\\"+\\\"\\n            DB      XP25-$-1\\n            PUSH    H ;YES, SAVE VALUE\\n            CALL    EXPR3 ;GET 2ND <EXPR3>\\nXP24:       XCHG     ;2ND IN DE\\n            XTHL     ;1ST IN HL\\n            MOV     A,H ;COMPARE SIGN\\n            XRA     D\\n            MOV     A,D\\n            DAD     D\\n            POP     D ;RESTORE TEXT POINTER\\n            JM      XP23 ;1ST AND 2ND SIGN DIFFER\\n            XRA     H ;1ST AND 2ND SIGN EQUAL\\n            JP      XP23 ;SO IS RESULT\\n            JMP     QHOW ;ELSE WE HAVE OVERFLOW\\nXP25:       RST     1 ;SUBTRACT?\\n            DB      \\\"-\\\"\\n            DB      XP42-$-1\\nXP26:       PUSH    H ;YES, SAVE 1ST <EXPR3>\\n            CALL    EXPR3 ;GET 2ND <EXPR3>\\n            CALL    CHGSGN ;NEGATE\\n            JMP     XP24 ;AND ADD THEM\\n;\\nEXPR3:      CALL    EXPR4 ;GET 1ST <EXPR4>\\nXP31:       RST     1 ;MULTIPLY?\\n            DB      \\\"*\\\"\\n            DB      XP34-$-1\\n            PUSH    H ;YES, SAVE 1ST\\n            CALL    EXPR4 ;AND GET 2ND <EXPR4>\\n            MVI     B,0H ;CLEAR B FOR SIGN\\n            CALL    CHKSGN ;CHECK SIGN\\n            XTHL     ;1ST IN HL\\n            CALL    CHKSGN ;CHECK SIGN OF 1ST\\n            XCHG\\n            XTHL\\n            MOV     A,H ;IS HL > 255 ?\\n            ORA     A\\n            JZ      XP32 ;NO\\n            MOV     A,D ;YES, HOW ABOUT DE\\n            ORA     D\\n            XCHG     ;PUT SMALLER IN HL\\n            JNZ     AHOW ;ALSO >, WILL OVERFLOW\\nXP32:       MOV     A,L ;THIS IS DUMB\\n            LXI     H,0H ;CLEAR RESULT\\n            ORA     A ;ADD AND COUNT\\n            JZ      XP35\\nXP33:       DAD     D\\n            JC      AHOW ;OVERFLOW\\n            DCR     A\\n            JNZ     XP33\\n            JMP     XP35 ;FINISHED\\nXP34:       RST     1 ;DIVIDE?\\n            DB      \\\"\/\\\"\\n            DB      XP42-$-1\\n            PUSH    H ;YES, SAVE 1ST <EXPR4>\\n            CALL    EXPR4 ;AND GET THE SECOND ONE\\n            MVI     B,0H ;CLEAR B FOR SIGN\\n            CALL    CHKSGN ;CHECK SIGN OF 2ND\\n            XTHL     ;GET 1ST IN HL\\n            CALL    CHKSGN ;CHECK SIGN OF 1ST\\n            XCHG\\n            XTHL\\n            XCHG\\n            MOV     A,D ;DIVIDE BY 0?\\n            ORA     E\\n            JZ      AHOW ;SAY \\\"HOW?\\\"\\n            PUSH    B ;ELSE SAVE SIGN\\n            CALL    DIVIDE ;USE SUBROUTINE\\n            MOV     H,B ;RESULT IN HL NOW\\n            MOV     L,C\\n            POP     B ;GET SIGN BACK\\nXP35:       POP     D ;AND TEXT POINTER\\n            MOV     A,H ;HL MUST BE +\\n            ORA     A\\n            JM      QHOW ;ELSE IT IS OVERFLOW\\n            MOV     A,B\\n            ORA     A\\n            CM      CHGSGN ;CHANGE SIGN IF NEEDED\\n            JMP     XP31 ;LOOK FOR MORE TERMS\\n;\\nEXPR4:      LXI     H,TAB4-1 ;FIND FUNCTION IN TAB4\\n            JMP     EXEC ;AND GO DO IT\\nXP40:       RST     7 ;NO, NOT A FUNCTION\\n            JC      XP41 ;NOR A VARIABLE\\n            MOV     A,M ;VARIABLE\\n            INX     H\\n            MOV     H,M ;VALUE IN HL\\n            MOV     L,A\\n            RET\\nXP41:       CALL    TSTNUM ;OR IS IT A NUMBER\\n            MOV     A,B ;# OF DIGIT\\n            ORA     A\\n            RNZ      ;OK\\nPARN:       RST     1\\n            DB      \\\"(\\\"\\n            DB      XP43-$-1\\n            RST     3 ;\\\"(EXPR)\\\"\\n            RST     1\\n            DB      \\\")\\\"\\n            DB      XP43-$-1\\nXP42:       RET\\nXP43:       JMP     QWHAT ;ELSE SAY: \\\"WHAT?\\\"\\n;\\nRND:        CALL    PARN ;*** RND(EXPR) ***\\n            MOV     A,H ;EXPR MUST BE +\\n            ORA     A\\n            JM      QHOW\\n            ORA     L ;AND NON-ZERO\\n            JZ      QHOW\\n            PUSH    D ;SAVE BOTH\\n            PUSH    H\\n            LHLD    RANPNT ;GET MEMORY AS RANDOM\\n            LXI     D,LSTROM ;NUMBER\\n            RST     4\\n            JC      RA1 ;WRAP AROUND IF LAST\\n            LXI     H,START\\nRA1:        MOV     E,M\\n            INX     H\\n            MOV     D,M\\n            SHLD    RANPNT\\n            POP     H\\n            XCHG\\n            PUSH    B\\n            CALL    DIVIDE ;RND(N)=MOD(M,N)+1\\n            POP     B\\n            POP     D\\n            INX     H\\n            RET\\n;\\nABS:        CALL    PARN ;*** ABS(EXPR) ***\\n            DCX     D\\n            CALL    CHKSGN ;CHECK SIGN\\n            INX     D\\n            RET\\n;\\nSIZE:       LHLD    TXTUNF ;*** SIZE ***\\n            PUSH    D ;GET THE NUMBER OF FREE\\n            XCHG     ;BYTES BETWEEN \\\"TXTUNF\\\"\\n            LXI     H,VARBGN ;AND \\\"VARBGN\\\"\\n            CALL    SUBDE\\n            POP     D\\n            RET\\n;\\n;*************************************************************\\n;\\n; *** DIVIDE *** SUBDE *** CHKSGN *** CHGSGN *** & CKHLDE ***\\n;\\n; \\\"DIVIDE\\\" DIVIDES HL BY DE, RESULT IN BC, REMAINDER IN HL\\n;\\n; \\\"SUBDE\\\" SUBSTRACTS DE FROM HL\\n;\\n; \\\"CHKSGN\\\" CHECKS SIGN OF HL.  IF +, NO CHANGE.  IF -, CHANGE\\n; SIGN AND FLIP SIGN OF B.\\n;\\n; \\\"CHGSGN\\\" CHECKS SIGN N OF HL AND B UNCONDITIONALLY.\\n;\\n; \\\"CKHLDE\\\" CHECKS SIGN OF HL AND DE.  IF DIFFERENT, HL AND DE\\n; ARE INTERCHANGED.  IF SAME SIGN, NOT INTERCHANGED.  EITHER\\n; CASE, HL DE ARE THEN COMPARED TO SET THE FLAGS.\\n;\\nDIVIDE:     PUSH    H ;*** DIVIDE ***\\n            MOV     L,H ;DIVIDE H BY DE\\n            MVI     H,0\\n            CALL    DV1\\n            MOV     B,C ;SAVE RESULT IN B\\n            MOV     A,L ;(REMINDER+L)\/DE\\n            POP     H\\n            MOV     H,A\\nDV1:        MVI     C,0FFH ;RESULT IN C\\nDV2:        INR     C ;DUMB ROUTINE\\n            CALL    SUBDE ;DIVIDE BY SUBTRACT\\n            JNC     DV2 ;AND COUNT\\n            DAD     D\\n            RET\\n;\\nSUBDE:      MOV     A,L ;*** SUBDE ***\\n            SUB     E ;SUBSTRACT DE FROM\\n            MOV     L,A ;HL\\n            MOV     A,H\\n            SBB     D\\n            MOV     H,A\\n            RET\\n;\\nCHKSGN:     MOV     A,H ;*** CHKSGN ***\\n            ORA     A ;CHECK SIGN OF HL\\n            RP       ;IF -, CHANGE SIGN\\n;\\nCHGSGN:     MOV     A,H ;*** CHGSGN ***\\n            ORA     L ;*UM*\\n            RZ       ;*UM* NOT ON ZERO VALUE\\n            MOV     A,H ;*UM*\\n            PUSH    PSW\\n            CMA      ;CHANGE SIGN OF HL\\n            MOV     H,A\\n            MOV     A,L\\n            CMA\\n            MOV     L,A\\n            INX     H\\n            POP     PSW\\n            XRA     H\\n            JP      QHOW\\n            MOV     A,B ;AND ALSO FLIP B\\n            XRI     80H\\n            MOV     B,A\\n            RET\\n;\\nCKHLDE:     MOV     A,H\\n            XRA     D ;SAME SIGN?\\n            JP      CK1 ;YES, COMPARE\\n            XCHG     ;NO, XCH AND COMP\\nCK1:        RST     4\\n            RET\\n;\\n;*************************************************************\\n;\\n; *** SETVAL *** FIN *** ENDCHK *** & ERROR (& FRIENDS) ***\\n;\\n; \\\"SETVAL\\\" EXPECTS A VARIABLE, FOLLOWED BY AN EQUAL SIGN AND\\n; THEN AN EXPR.  IT EVALUATES THE EXPR. AND SET THE VARIABLE\\n; TO THAT VALUE.\\n;\\n; \\\"FIN\\\" CHECKS THE END OF A COMMAND.  IF IT ENDED WITH \\\";\\\",\\n; EXECUTION CONTINUES.  IF IT ENDED WITH A CR, IT FINDS THE\\n; NEXT LINE AND CONTINUE FROM THERE.\\n;\\n; \\\"ENDCHK\\\" CHECKS IF A COMMAND IS ENDED WITH CR.  THIS IS\\n; REQUIRED IN CERTAIN COMMANDS.  (GOTO, RETURN, AND STOP ETC.)\\n;\\n; \\\"ERROR\\\" PRINTS THE STRING POINTED BY DE (AND ENDS WITH CR).\\n; IT THEN PRINTS THE LINE POINTED BY \\\"CURRNT\\\" WITH A \\\"?\\\"\\n; INSERTED AT WHERE THE OLD TEXT POINTER (SHOULD BE ON TOP\\n; OF THE STACK) POINTS TO.  EXECUTION OF TB IS STOPPED\\n; AND TBI IS RESTARTED.  HOWEVER, IF \\\"CURRNT\\\" -> ZERO\\n; (INDICATING A DIRECT COMMAND), THE DIRECT COMMAND IS NOT\\n; PRINTED.  AND IF \\\"CURRNT\\\" -> NEGATIVE # (INDICATING \\\"INPUT\\\"\\n; COMMAND), THE INPUT LINE IS NOT PRINTED AND EXECUTION IS\\n; NOT TERMINATED BUT CONTINUED AT \\\"INPERR\\\".\\n;\\n; RELATED TO \\\"ERROR\\\" ARE THE FOLLOWING:\\n; \\\"QWHAT\\\" SAVES TEXT POINTER IN STACK AND GET MESSAGE \\\"WHAT?\\\"\\n; \\\"AWHAT\\\" JUST GET MESSAGE \\\"WHAT?\\\" AND JUMP TO \\\"ERROR\\\".\\n; \\\"QSORRY\\\" AND \\\"ASORRY\\\" DO SAME KIND OF THING.\\n; \\\"AHOW\\\" AND \\\"AHOW\\\" IN THE ZERO PAGE SECTION ALSO DO THIS.\\n;\\nSETVAL:     RST     7 ;*** SETVAL ***\\n            JC      QWHAT ;\\\"WHAT?\\\" NO VARIABLE\\n            PUSH    H ;SAVE ADDRESS OF VAR.\\n            RST     1 ;PASS \\\"=\\\" SIGN\\n            DB      \\\"=\\\"\\n            DB      SV1-$-1\\n            RST     3 ;EVALUATE EXPR.\\n            MOV     B,H ;VALUE IS IN BC NOW\\n            MOV     C,L\\n            POP     H ;GET ADDRESS\\n            MOV     M,C ;SAVE VALUE\\n            INX     H\\n            MOV     M,B\\n            RET\\nSV1:        JMP     QWHAT ;NO \\\"=\\\" SIGN\\n;\\nFIN:        RST     1 ;*** FIN ***\\n            DB      3BH\\n            DB      FI1-$-1\\n            POP     PSW ;\\\";\\\", PURGE RET. ADDR.\\n            JMP     RUNSML ;CONTINUE SAME LINE\\nFI1:        RST     1 ;NOT \\\";\\\", IS IT CR?\\n            DB      CR\\n            DB      FI2-$-1\\n            POP     PSW ;YES, PURGE RET. ADDR.\\n            JMP     RUNNXL ;RUN NEXT LINE\\nFI2:        RET      ;ELSE RETURN TO CALLER\\n;\\nENDCHK:     RST     5 ;*** ENDCHK ***\\n            CPI     CR ;END WITH CR?\\n            RZ       ;OK, ELSE SAY: \\\"WHAT?\\\"\\n;\\nQWHAT:      PUSH    D ;*** QWHAT ***\\nAWHAT:      LXI     D,WHAT ;*** AWHAT ***\\nERROR:      SUB     A ;*** ERROR ***\\n            CALL    PRTSTG ;PRINT \\\"WHAT?\\\", \\\"HOW?\\\"\\n            POP     D ;OR \\\"SORRY\\\"\\n            LDAX    D ;SAVE THE CHARACTER\\n            PUSH    PSW ;AT WHERE OLD DE ->\\n            SUB     A ;AND PUT A 0 THERE\\n            STAX    D\\n            LHLD    CURRNT ;GET CURRENT LINE #\\n            PUSH    H\\n            MOV     A,M ;CHECK THE VALUE\\n            INX     H\\n            ORA     M\\n            POP     D\\n            JZ      RSTART ;IF ZERO, JUST RESTART\\n            MOV     A,M ;IF NEGATIVE,\\n            ORA     A\\n            JM      INPERR ;REDO INPUT\\n            CALL    PRTLN ;ELSE PRINT THE LINE\\n            DCX     D ;UPTO WHERE THE 0 IS\\n            POP     PSW ;RESTORE THE CHARACTER\\n            STAX    D\\n            MVI     A,3FH ;PRINT A \\\"?\\\"\\n            RST     2\\n            SUB     A ;AND THE REST OF THE\\n            CALL    PRTSTG ;LINE\\n            JMP     RSTART ;THEN RESTART\\n;\\nQSORRY:     PUSH    D ;*** QSORRY ***\\nASORRY:     LXI     D,SORRY ;*** ASORRY ***\\n            JMP     ERROR\\n;\\n;*************************************************************\\n;\\n; *** GETLN *** FNDLN (& FRIENDS) ***\\n;\\n; \\\"GETLN\\\" READS A INPUT LINE INTO \\\"BUFFER\\\".  IT FIRST PROMPT\\n; THE CHARACTER IN A (GIVEN BY THE CALLER), THEN IT FILLS\\n; THE BUFFER AND ECHOS.  IT IGNORES LF'S AND NULLS, BUT STILL\\n; ECHOS THEM BACK.  RUB-OUT IS USED TO CAUSE IT TO DELETE\\n; THE LAST CHARACTER (IF THERE IS ONE), AND ALT-MOD IS USED TO\\n; CAUSE IT TO DELETE THE WHOLE LINE AND START IT ALL OVER.\\n; CR SIGNALS THE END OF A LINE, AND CAUSE \\\"GETLN\\\" TO RETURN.\\n;\\n; \\\"FNDLN\\\" FINDS A LINE WITH A GIVEN LINE # (IN HL) IN THE\\n; TEXT SAVE AREA.  DE IS USED AS THE TEXT POINTER.  IF THE\\n; LINE IS FOUND, DE WILL POINT TO THE BEGINNING OF THAT LINE\\n; (I.E., THE LOW BYTE OF THE LINE #), AND FLAGS ARE NC & Z.\\n; IF THAT LINE IS NOT THERE AND A LINE WITH A HIGHER LINE #\\n; IS FOUND, DE POINTS TO THERE AND FLAGS ARE NC & NZ.  IF\\n; WE REACHED THE END OF TEXT SAVE AREA AND CANNOT FIND THE\\n; LINE, FLAGS ARE C & NZ.\\n; \\\"FNDLN\\\" WILL INITIALIZE DE TO THE BEGINNING OF THE TEXT SAVE\\n; AREA TO START THE SEARCH.  SOME OTHER ENTRIES OF THIS\\n; ROUTINE WILL NOT INITIALIZE DE AND DO THE SEARCH.\\n; \\\"FNDLNP\\\" WILL START WITH DE AND SEARCH FOR THE LINE #.\\n; \\\"FNDNXT\\\" WILL BUMP DE BY 2, FIND A CR AND THEN START SEARCH.\\n; \\\"FNDSKP\\\" USE DE TO FIND A CR, AND THEN START SEARCH.\\n;\\nGETLN:      RST     2 ;*** GETLN ***\\n            LXI     D,BUFFER ;PROMPT AND INIT.\\nGL1:        CALL    CHKIO ;CHECK KEYBOARD\\n            JZ      GL1 ;NO INPUT, WAIT\\n            CPI     7FH ;DELETE LAST CHARACTER?\\n            JZ      GL3 ;YES\\n            RST     2 ;INPUT, ECHO BACK\\n            CPI     0AH ;IGNORE LF\\n            JZ      GL1\\n            ORA     A ;IGNORE NULL\\n            JZ      GL1\\n            CPI     7DH ;DELETE THE WHOLE LINE?\\n            JZ      GL4 ;YES\\n            STAX    D ;ELSE SAVE INPUT\\n            INX     D ;AND BUMP POINTER\\n            CPI     0DH ;WAS IT CR?\\n            RZ       ;YES, END OF LINE\\n            MOV     A,E ;ELSE MORE FREE ROOM?\\n            CPI     BUFEND & 0FFH\\n            JNZ     GL1 ;YES, GET NEXT INPUT\\nGL3:        MOV     A,E ;DELETE LAST CHARACTER\\n            CPI     BUFFER & 0FFH ;BUT DO WE HAVE ANY?\\n            JZ      GL4 ;NO, REDO WHOLE LINE\\n            DCX     D ;YES, BACKUP POINTER\\n            MVI     A,08H ;AND ERASE IT ON THE SCREEN\\n            RST     2\\n            MVI     A,20H\\n            RST     2\\n            MVI     A,08H\\n            RST     2\\n            JMP     GL1 ;GO GET NEXT INPUT\\nGL4:        CALL    CRLF ;REDO ENTIRE LINE\\n            MVI     A,05EH ;CR, LF AND UP-ARROW\\n            JMP     GETLN\\n;\\nFNDLN:      MOV     A,H ;*** FNDLN ***\\n            ORA     A ;CHECK SIGN OF HL\\n            JM      QHOW ;IT CANNOT BE -\\n            LXI     D,TXTBGN ;INIT TEXT POINTER\\n;\\nFNDLP:               ;*** FDLNP ***\\nFL1:        PUSH    H ;SAVE LINE #\\n            LHLD    TXTUNF ;CHECK IF WE PASSED END\\n            DCX     H\\n            RST     4\\n            POP     H ;GET LINE # BACK\\n            RC       ;C,NZ PASSED END\\n            LDAX    D ;WE DID NOT, GET BYTE 1\\n            SUB     L ;IS THIS THE LINE?\\n            MOV     B,A ;COMPARE LOW ORDER\\n            INX     D\\n            LDAX    D ;GET BYTE 2\\n            SBB     H ;COMPARE HIGH ORDER\\n            JC      FL2 ;NO, NOT THERE YET\\n            DCX     D ;ELSE WE EITHER FOUND\\n            ORA     B ;IT, OR IT IS NOT THERE\\n            RET      ;NC,Z:FOUND, NC,NZ:NO\\n;\\nFNDNXT:              ;*** FNDNXT ***\\n            INX     D ;FIND NEXT LINE\\nFL2:        INX     D ;JUST PASSED BYTE 1 & 2\\n;\\nFNDSKP:     LDAX    D ;*** FNDSKP ***\\n            CPI     CR ;TRY TO FIND CR\\n            JNZ     FL2 ;KEEP LOOKING\\n            INX     D ;FOUND CR, SKIP OVER\\n            JMP     FL1 ;CHECK IF END OF TEXT\\n;\\n;*************************************************************\\n;\\n; *** PRTSTG *** QTSTG *** PRTNUM *** & PRTLN ***\\n;\\n; \\\"PRTSTG\\\" PRINTS A STRING POINTED BY DE.  IT STOPS PRINTING\\n; AND RETURNS TO CALLER WHEN EITHER A CR IS PRINTED OR WHEN\\n; THE NEXT BYTE IS THE SAME AS WHAT WAS IN A (GIVEN BY THE\\n; CALLER).  OLD A IS STORED IN B, OLD B IS LOST.\\n;\\n; \\\"QTSTG\\\" LOOKS FOR A BACK-ARROW, SINGLE QUOTE, OR DOUBLE\\n; QUOTE.  IF NONE OF THESE, RETURN TO CALLER.  IF BACK-ARROW,\\n; OUTPUT A CR WITHOUT A LF.  IF SINGLE OR DOUBLE QUOTE, PRINT\\n; THE STRING IN THE QUOTE AND DEMANDS A MATCHING UNQUOTE.\\n; AFTER THE PRINTING THE NEXT 3 BYTES OF THE CALLER IS SKIPPED\\n; OVER (USUALLY A JUMP INSTRUCTION.\\n;\\n; \\\"PRTNUM\\\" PRINTS THE NUMBER IN HL.  LEADING BLANKS ARE ADDED\\n; IF NEEDED TO PAD THE NUMBER OF SPACES TO THE NUMBER IN C.\\n; HOWEVER, IF THE NUMBER OF DIGITS IS LARGER THAN THE # IN\\n; C, ALL DIGITS ARE PRINTED ANYWAY.  NEGATIVE SIGN IS ALSO\\n; PRINTED AND COUNTED IN, POSITIVE SIGN IS NOT.\\n;\\n; \\\"PRTLN\\\" PRINTS A SAVED TEXT LINE WITH LINE # AND ALL.\\n;\\nPRTSTG:     MOV     B,A ;*** PRTSTG ***\\nPS1:        LDAX    D ;GET A CHARACTER\\n            INX     D ;BUMP POINTER\\n            CMP     B ;SAME AS OLD A?\\n            RZ       ;YES, RETURN\\n            RST     2 ;ELSE PRINT IT\\n            CPI     CR ;WAS IT A CR?\\n            JNZ     PS1 ;NO, NEXT\\n            RET      ;YES, RETURN\\n;\\nQTSTG:      RST     1 ;*** QTSTG ***\\n            DB      022h ;\\\"\\\"\\\"\\n            DB      QT3-$-1\\n            MVI     A,22H ;IT IS A \\\"\\nQT1:        CALL    PRTSTG ;PRINT UNTIL ANOTHER\\n            CPI     CR ;WAS LAST ONE A CR?\\n            POP     H ;RETURN ADDRESS\\n            JZ      RUNNXL ;WAS CR, RUN NEXT LINE\\nQT2:        INX     H ;SKIP 3 BYTES ON RETURN\\n            INX     H\\n            INX     H\\n            PCHL     ;RETURN\\nQT3:        RST     1 ;IS IT A '?\\n            DB      27H\\n            DB      QT4-$-1\\n            MVI     A,27H ;YES, DO THE SAME\\n            JMP     QT1 ;AS IN \\\"\\nQT4:        RST     1 ;IS IT BACK-ARROW?\\n            DB      5FH\\n            DB      QT5-$-1\\n            MVI     A,08DH ;YES, CR WITHOUT LF\\n            RST     2 ;DO IT TWICE TO GIVE\\n            RST     2 ;TTY ENOUGH TIME\\n            POP     H ;RETURN ADDRESS\\n            JMP     QT2\\nQT5:        RET      ;NONE OF ABOVE\\n;\\nPRTNUM:     MVI     B,0 ;*** PRTNUM ***\\n            CALL    CHKSGN ;CHECK SIGN\\n            JP      PN1 ;NO SIGN\\n            MVI     B,\\\"-\\\" ;B=SIGN\\n            DCR     C ;\\\"-\\\" TAKES SPACE\\nPN1:        PUSH    D ;SAVE\\n            LXI     D,0AH ;DECIMAL\\n            PUSH    D ;SAVE AS A FLAG\\n            DCR     C ;C=SPACES\\n            PUSH    B ;SAVE SIGN & SPACE\\nPN2:        CALL    DIVIDE ;DIVIDE HL BY 10\\n            MOV     A,B ;RESULT 0?\\n            ORA     C\\n            JZ      PN3 ;YES, WE GOT ALL\\n            XTHL     ;NO, SAVE REMAINDER\\n            DCR     L ;AND COUNT SPACE\\n            PUSH    H ;HL IS OLD BC\\n            MOV     H,B ;MOVE RESULT TO BC\\n            MOV     L,C\\n            JMP     PN2 ;AND DIVIDE BY 10\\nPN3:        POP     B ;WE GOT ALL DIGITS IN\\nPN4:        DCR     C ;THE STACK\\n            MOV     A,C ;LOOK AT SPACE COUNT\\n            ORA     A\\n            JM      PN5 ;NO LEADING BLANKS\\n            MVI     A,20H ;LEADING BLANKS\\n            RST     2\\n            JMP     PN4 ;MORE?\\nPN5:        MOV     A,B ;PRINT SIGN\\n            ORA     A\\n            CNZ     10H\\n            MOV     E,L ;LAST REMAINDER IN E\\nPN6:        MOV     A,E ;CHECK DIGIT IN E\\n            CPI     0AH ;10 IS FLAG FOR NO MORE\\n            POP     D\\n            RZ       ;IF SO, RETURN\\n            ADI     30H ;ELSE CONVERT TO ASCII\\n            RST     2 ;AND PRINT THE DIGIT\\n            JMP     PN6 ;GO BACK FOR MORE\\n;\\nPRTLN:      LDAX    D ;*** PRTLN ***\\n            MOV     L,A ;LOW ORDER LINE #\\n            INX     D\\n            LDAX    D ;HIGH ORDER\\n            MOV     H,A\\n            INX     D\\n            MVI     C,4H ;PRINT 4 DIGIT LINE #\\n            CALL    PRTNUM\\n            MVI     A,20H ;FOLLOWED BY A BLANK\\n            RST     2\\n            SUB     A ;AND THEN THE NEXT\\n            CALL    PRTSTG\\n            RET\\n;\\n;*************************************************************\\n;\\n; *** MVUP *** MVDOWN *** POPA *** & PUSHA ***\\n;\\n; \\\"MVUP\\\" MOVES A BLOCK UP FROM WHERE DE-> TO WHERE BC-> UNTIL\\n; DE = HL\\n;\\n; \\\"MVDOWN\\\" MOVES A BLOCK DOWN FROM WHERE DE-> TO WHERE HL->\\n; UNTIL DE = BC\\n;\\n; \\\"POPA\\\" RESTORES THE \\\"FOR\\\" LOOP VARIABLE SAVE AREA FROM THE\\n; STACK\\n;\\n; \\\"PUSHA\\\" STACKS THE \\\"FOR\\\" LOOP VARIABLE SAVE AREA INTO THE\\n; STACK\\n;\\nMVUP:       RST     4 ;*** MVUP ***\\n            RZ       ;DE = HL, RETURN\\n            LDAX    D ;GET ONE BYTE\\n            STAX    B ;MOVE IT\\n            INX     D ;INCREASE BOTH POINTERS\\n            INX     B\\n            JMP     MVUP ;UNTIL DONE\\n;\\nMVDOWN:     MOV     A,B ;*** MVDOWN ***\\n            SUB     D ;TEST IF DE = BC\\n            JNZ     MD1 ;NO, GO MOVE\\n            MOV     A,C ;MAYBE, OTHER BYTE?\\n            SUB     E\\n            RZ       ;YES, RETURN\\nMD1:        DCX     D ;ELSE MOVE A BYTE\\n            DCX     H ;BUT FIRST DECREASE\\n            LDAX    D ;BOTH POINTERS AND\\n            MOV     M,A ;THEN DO IT\\n            JMP     MVDOWN ;LOOP BACK\\n;\\nPOPA:       POP     B ;BC = RETURN ADDR.\\n            POP     H ;RESTORE LOPVAR, BUT\\n            SHLD    LOPVAR ;=0 MEANS NO MORE\\n            MOV     A,H\\n            ORA     L\\n            JZ      PP1 ;YEP, GO RETURN\\n            POP     H ;NOP, RESTORE OTHERS\\n            SHLD    LOPINC\\n            POP     H\\n            SHLD    LOPLMT\\n            POP     H\\n            SHLD    LOPLN\\n            POP     H\\n            SHLD    LOPPT\\nPP1:        PUSH    B ;BC = RETURN ADDR.\\n            RET\\n;\\nPUSHA:      LXI     H,STKLMT ;*** PUSHA ***\\n            CALL    CHGSGN\\n            POP     B ;BC=RETURN ADDRESS\\n            DAD     SP ;IS STACK NEAR THE TOP?\\n            JNC     QSORRY ;YES, SORRY FOR THAT\\n            LHLD    LOPVAR ;ELSE SAVE LOOP VAR'S\\n            MOV     A,H ;BUT IF LOPVAR IS 0\\n            ORA     L ;THAT WILL BE ALL\\n            JZ      PU1\\n            LHLD    LOPPT ;ELSE, MORE TO SAVE\\n            PUSH    H\\n            LHLD    LOPLN\\n            PUSH    H\\n            LHLD    LOPLMT\\n            PUSH    H\\n            LHLD    LOPINC\\n            PUSH    H\\n            LHLD    LOPVAR\\nPU1:        PUSH    H\\n            PUSH    B ;BC = RETURN ADDR.\\n            RET\\n;\\n;*************************************************************\\n;\\n; *** OUTC *** & CHKIO ***\\n;\\n; THESE ARE THE ONLY I\/O ROUTINES IN TBI.\\n; \\\"OUTC\\\" IS CONTROLLED BY A SOFTWARE SWITCH \\\"OCSW\\\".  IF OCSW=0\\n; \\\"OUTC\\\" WILL JUST RETURN TO THE CALLER.  IF OCSW IS NOT 0,\\n; IT WILL OUTPUT THE BYTE IN A.  IF THAT IS A CR, A LF IS ALSO\\n; SEND OUT.  ONLY THE FLAGS MAY BE CHANGED AT RETURN. ALL REG.\\n; ARE RESTORED.\\n;\\n; \\\"CHKIO\\\" CHECKS THE INPUT.  IF NO INPUT, IT WILL RETURN TO\\n; THE CALLER WITH THE Z FLAG SET.  IF THERE IS INPUT, Z FLAG\\n; IS CLEARED AND THE INPUT BYTE IS IN A.  HOWEVER, IF THE\\n; INPUT IS A CONTROL-O, THE \\\"OCSW\\\" SWITCH IS COMPLIMENTED, AND\\n; Z FLAG IS RETURNED.  IF A CONTROL-C IS READ, \\\"CHKIO\\\" WILL\\n; RESTART TBI AND DO NOT RETURN TO THE CALLER.\\n;\\n;OUTC:  PUSH PSW                        ;THIS IS AT LOC. 10\\n;       LDA  OCSW                       ;CHECK SOFTWARE SWITCH\\n;       ORA  A\\nINIT:       STA     OCSW\\n            MVI     A,3 ;RESET ACIA\\n            OUT     ACIA_CTRL\\n            MVI     A,15H ;15H FOR 8N1, 11H FOR 8N2\\n            OUT     ACIA_CTRL\\n            MVI     D,19H\\nPATLOP:\\n            CALL    CRLF\\n            DCR     D\\n            JNZ     PATLOP\\n            SUB     A\\n            LXI     D,MSG1\\n            CALL    PRTSTG\\n            LXI     H,START\\n            SHLD    RANPNT\\n            LXI     H,TXTBGN\\n            SHLD    TXTUNF\\n            JMP     RSTART\\nOC2:        JNZ     OC3 ;IT IS ON\\n            POP     PSW ;IT IS OFF\\n            RET      ;RESTORE AF AND RETURN\\nOC3:        IN      ACIA_CTRL ;COME HERE TO DO OUTPUT\\n            ANI     2H ;STATUS BIT\\n            JZ      OC3 ;NOT READY, WAIT\\n            POP     PSW ;READY, GET OLD A BACK\\n            OUT     ACIA_DATA ;AND SEND IT OUT\\n            CPI     CR ;WAS IT CR?\\n            RNZ      ;NO, FINISHED\\n            MVI     A,LF ;YES, WE SEND LF TOO\\n            RST     2 ;THIS IS RECURSIVE\\n            MVI     A,CR ;GET CR BACK IN A\\n            RET\\n;\\nCHKIO:      IN      ACIA_CTRL ;*** CHKIO ***\\n            NOP      ;STATUS BIT FLIPPED?\\n            ANI     1H ;MASK STATUS BIT\\n            RZ       ;NOT READY, RETURN \\\"Z\\\"\\n            IN      ACIA_DATA ;READY, READ DATA\\n            ANI     7FH ;MASK BIT 7 OFF\\n            CPI     08H ;MM: IS IT A BACKSPACE?\\n            JZ      CIRUB\\n            CPI     1BH ;MM: IS IT AN ESCAPE?\\n            JZ      CIESC\\n            CPI     0FH ;IS IT CONTROL-O?\\n            JNZ     CI1 ;NO, MORE CHECKING\\n            LDA     OCSW ;CONTROL-O FLIPS OCSW\\n            CMA      ;ON TO OFF, OFF TO ON\\n            STA     OCSW\\n            JMP     CHKIO ;GET ANOTHER INPUT\\nCI1:        CPI     3H ;IS IT CONTROL-C?\\n            RNZ      ;NO, RETURN \\\"NZ\\\"\\n            JMP     RSTART ;YES, RESTART TBI\\nCIRUB:      MVI     A,7Fh ;RUBOUT\\n            JMP     CI1\\nCIESC:      MVI     A,7Dh ;DELETE LINE\\n            JMP     CI1\\nMSG1:       DB      \\\"PALO ALTO TINY BASIC 2.0\\\"\\n            DB      CR\\n;\\n;*************************************************************\\n;\\n; *** TABLES *** DIRECT *** & EXEC ***\\n;\\n; THIS SECTION OF THE CODE TESTS A STRING AGAINST A TABLE.\\n; WHEN A MATCH IS FOUND, CONTROL IS TRANSFERED TO THE SECTION\\n; OF CODE ACCORDING TO THE TABLE.\\n;\\n; AT \\\"EXEC\\\", DE SHOULD POINT TO THE STRING AND HL SHOULD POINT\\n; TO THE TABLE-1.  AT \\\"DIRECT\\\", DE SHOULD POINT TO THE STRING.\\n; HL WILL BE SET UP TO POINT TO TAB1-1, WHICH IS THE TABLE OF\\n; ALL DIRECT AND STATEMENT COMMANDS.\\n;\\n; A \\\".\\\" IN THE STRING WILL TERMINATE THE TEST AND THE PARTIAL\\n; MATCH WILL BE CONSIDERED AS A MATCH.  E.G., \\\"P.\\\", \\\"PR.\\\",\\n; \\\"PRI.\\\", \\\"PRIN.\\\", OR \\\"PRINT\\\" WILL ALL MATCH \\\"PRINT\\\".\\n;\\n; THE TABLE CONSISTS OF ANY NUMBER OF ITEMS.  EACH ITEM\\n; IS A STRING OF CHARACTERS WITH BIT 7 SET TO 0 AND\\n; A JUMP ADDRESS STORED HI-LOW WITH BIT 7 OF THE HIGH\\n; BYTE SET TO 1.\\n;\\n; END OF TABLE IS AN ITEM WITH A JUMP ADDRESS ONLY.  IF THE\\n; STRING DOES NOT MATCH ANY OF THE OTHER ITEMS, IT WILL\\n; MATCH THIS NULL ITEM AS DEFAULT.\\n;\\nTAB1:                ;DIRECT COMMANDS\\n            DB      \\\"LIST\\\"\\n            DB      (LIST \/ 256) + 128   ; DWA LIST\\n            DB      LIST & 0FFH\\n            DB      \\\"RUN\\\"\\n            DB      (RUN \/ 256) + 128   ; DWA RUN\\n            DB      RUN & 0FFH\\n            DB      \\\"NEW\\\"\\n            DB      (NEW \/ 256) + 128   ; DWA NEW\\n            DB      NEW & 0FFH\\n;\\nTAB2:                ;DIRECT\/STATEMENT\\n            DB      \\\"NEXT\\\"\\n            DB      (NEXT \/ 256) + 128   ; DWA NEXT\\n            DB      NEXT & 0FFH\\n            DB      \\\"LET\\\"\\n            DB      (LET \/ 256) + 128   ; DWA LET\\n            DB      LET & 0FFH\\n            DB      \\\"IF\\\"\\n            DB      (IFF \/ 256) + 128   ; DWA IFF\\n            DB      IFF & 0FFH\\n            DB      \\\"GOTO\\\"\\n            DB      (GOTO \/ 256) + 128   ; DWA GOTO\\n            DB      GOTO & 0FFH\\n            DB      \\\"GOSUB\\\"\\n            DB      (GOSUB \/ 256) + 128   ; DWA GOSUB\\n            DB      GOSUB & 0FFH\\n            DB      \\\"RETURN\\\"\\n            DB      (RETURN \/ 256) + 128   ; DWA RETURN\\n            DB      RETURN & 0FFH\\n            DB      \\\"REM\\\"\\n            DB      (REM \/ 256) + 128   ; DWA REM\\n            DB      REM & 0FFH\\n            DB      \\\"FOR\\\"\\n            DB      (FOR \/ 256) + 128   ; DWA FOR\\n            DB      FOR & 0FFH\\n            DB      \\\"INPUT\\\"\\n            DB      (INPUT \/ 256) + 128   ; DWA INPUT\\n            DB      INPUT & 0FFH\\n            DB      \\\"PRINT\\\"\\n            DB      (PRINT \/ 256) + 128   ; DWA PRINT\\n            DB      PRINT & 0FFH\\n            DB      \\\"STOP\\\"\\n            DB      (STOP \/ 256) + 128   ; DWA STOP\\n            DB      STOP & 0FFH\\n            DB      (DEFLT \/ 256) + 128   ; DWA DEFLT\\n            DB      DEFLT & 0FFH\\n;\\nTAB4:                ;FUNCTIONS\\n            DB      \\\"RND\\\"\\n            DB      (RND \/ 256) + 128   ; DWA RND\\n            DB      RND & 0FFH\\n            DB      \\\"ABS\\\"\\n            DB      (ABS \/ 256) + 128   ; DWA ABS\\n            DB      ABS & 0FFH\\n            DB      \\\"SIZE\\\"\\n            DB      (SIZE \/ 256) + 128   ; DWA SIZE\\n            DB      SIZE & 0FFH\\n            DB      (XP40 \/ 256) + 128   ; DWA XP40\\n            DB      XP40 & 0FFH\\n;\\nTAB5:                ;\\\"TO\\\" IN \\\"FOR\\\"\\n            DB      \\\"TO\\\"\\n            DB      (FR1 \/ 256) + 128   ; DWA FR1\\n            DB      FR1 & 0FFH\\n            DB      (QWHAT \/ 256) + 128   ; DWA QWHAT\\n            DB      QWHAT & 0FFH\\n;\\nTAB6:                ;\\\"STEP\\\" IN \\\"FOR\\\"\\n            DB      \\\"STEP\\\"\\n            DB      (FR2 \/ 256) + 128   ; DWA FR2\\n            DB      FR2 & 0FFH\\n            DB      (FR3 \/ 256) + 128   ; DWA FR3\\n            DB      FR3 & 0FFH\\n;\\nTAB8:                ;RELATION OPERATORS\\n            DB      \\\">=\\\"\\n            DB      (XP11 \/ 256) + 128   ; DWA XP11\\n            DB      XP11 & 0FFH\\n            DB      \\\"#\\\"\\n            DB      (XP12 \/ 256) + 128   ; DWA XP12\\n            DB      XP12 & 0FFH\\n            DB      \\\">\\\"\\n            DB      (XP13 \/ 256) + 128   ; DWA XP13\\n            DB      XP13 & 0FFH\\n            DB      \\\"=\\\"\\n            DB      (XP15 \/ 256) + 128   ; DWA XP15\\n            DB      XP15 & 0FFH\\n            DB      \\\"<=\\\"\\n            DB      (XP14 \/ 256) + 128   ; DWA XP14\\n            DB      XP14 & 0FFH\\n            DB      \\\"<\\\"\\n            DB      (XP16 \/ 256) + 128   ; DWA XP16\\n            DB      XP16 & 0FFH\\n            DB      (XP17 \/ 256) + 128   ; DWA XP17\\n            DB      XP17 & 0FFH\\n;\\nDIRECT:     LXI     H,TAB1-1 ;*** DIRECT ***\\n;\\nEXEC:                ;*** EXEC ***\\nEX0:        RST     5 ;IGNORE LEADING BLANKS\\n            PUSH    D ;SAVE POINTER\\nEX1:        LDAX    D ;IF FOUND \\\".\\\" IN STRING\\n            INX     D ;BEFORE ANY MISMATCH\\n            CPI     2EH ;WE DECLARE A MATCH\\n            JZ      EX3\\n            INX     H ;HL->TABLE\\n            CMP     M ;IF MATCH, TEST NEXT\\n            JZ      EX1\\n            MVI     A,07FH ;ELSE SEE IF BIT 7\\n            DCX     D ;OF TABLE IS SET, WHICH\\n            CMP     M ;IS THE JUMP ADDR. (HI)\\n            JC      EX5 ;C:YES, MATCHED\\nEX2:        INX     H ;NC:NO, FIND JUMP ADDR.\\n            CMP     M\\n            JNC     EX2\\n            INX     H ;BUMP TO NEXT TAB. ITEM\\n            POP     D ;RESTORE STRING POINTER\\n            JMP     EX0 ;TEST AGAINST NEXT ITEM\\nEX3:        MVI     A,07FH ;PARTIAL MATCH, FIND\\nEX4:        INX     H ;JUMP ADDR., WHICH IS\\n            CMP     M ;FLAGGED BY BIT 7\\n            JNC     EX4\\nEX5:        MOV     A,M ;LOAD HL WITH THE JUMP\\n            INX     H ;ADDRESS FROM THE TABLE\\n            MOV     L,M\\n            ANI     7FH ;MASK OFF BIT 7\\n            MOV     H,A\\n            POP     PSW ;CLEAN UP THE GABAGE\\n            PCHL     ;AND WE GO DO IT\\n;\\nLSTROM:              ;ALL ABOVE CAN BE ROM\\n;       ORG  1000H                      ;HERE DOWN MUST BE RAM\\n            ORG     0800H\\nOCSW:       DS      1 ;SWITCH FOR OUTPUT\\nCURRNT:     DS      2 ;POINTS TO CURRENT LINE\\nSTKGOS:     DS      2 ;SAVES SP IN \\\"GOSUB\\\"\\nVARNXT:     DS      2 ;TEMP STORAGE\\nSTKINP:     DS      2 ;SAVES SP IN \\\"INPUT\\\"\\nLOPVAR:     DS      2 ;\\\"FOR\\\" LOOP SAVE AREA\\nLOPINC:     DS      2 ;INCREMENT\\nLOPLMT:     DS      2 ;LIMIT\\nLOPLN:      DS      2 ;LINE NUMBER\\nLOPPT:      DS      2 ;TEXT POINTER\\nRANPNT:     DS      2 ;RANDOM NUMBER POINTER\\nTXTUNF:     DS      2 ;->UNFILLED TEXT AREA\\nTXTBGN:     DS      2 ;TEXT SAVE AREA BEGINS\\n;       ORG  1366H\\n            ORG     7F00H\\nTXTEND:     DS      0 ;TEXT SAVE AREA ENDS\\nVARBGN:     DS      55 ;VARIABLE @(0)\\nBUFFER:     DS      64 ;INPUT BUFFER\\nBUFEND:     DS      1 ;BUFFER ENDS\\nSTKLMT:     DS      1 ;TOP LIMIT FOR STACK\\n;       ORG  1400H\\n            ORG     8000H\\nSTACK:      DS      0 ;STACK STARTS HERE\\n;\\nCR          EQU     0DH\\nLF          EQU     0AH\\n;\\n            END\\n\"\n},\n{\n  id: 'somma',\n  name: { it: 'Somma due numeri (dal manuale MITS)', en: 'Add two numbers (from the MITS manual)' },\n  desc: {\n    it: 'Il primo programma del manuale dell\\'Altair: somma i byte agli indirizzi 80h e 81h (200 e 201 in ottale) e scrive il risultato in 82h (202). Sono 12 byte da inserire con le levette: la guida qui sotto ti accompagna passo passo.',\n    en: 'The first program in the Altair manual: it adds the bytes at addresses 80h and 81h (200 and 201 octal) and writes the result to 82h (202). Twelve bytes to toggle in: the guide below walks you through it.'\n  },\n  src: [\n    '; SOMMA DUE NUMERI - il primo programma del manuale dell\\'Altair 8800',\n    '; indirizzi in esadecimale; nel manuale sono in ottale (200, 201, 202)',\n    '        ORG 0',\n    'INIZIO: LDA 80H        ; primo numero',\n    '        MOV B,A',\n    '        LDA 81H        ; secondo numero',\n    '        ADD B          ; A = A + B',\n    '        STA 82H        ; risultato',\n    '        JMP INIZIO',\n    '',\n    '        ORG 80H',\n    '        DB 5           ; 80h: primo numero',\n    '        DB 3           ; 81h: secondo numero',\n    '        END INIZIO'\n  ]\n},\n{\n  id: 'contatore',\n  name: { it: 'Contatore sui LED di indirizzo', en: 'Counter on the address LEDs' },\n  desc: {\n    it: 'Conta in binario sui LED di indirizzo A15-A8. Le levette A15-A8 (SENSE SW.) regolano la velocit\u00e0: alzane qualcuna per rallentare.',\n    en: 'Counts in binary on address LEDs A15-A8. Switches A15-A8 (SENSE SW.) set the speed: raise some of them to slow it down.'\n  },\n  src: [\n    '; CONTATORE sui LED A15-A8: LDAX D legge dall\\'indirizzo D00h,',\n    '; cosi i LED alti del bus indirizzi mostrano il valore di D',\n    '        ORG 0',\n    'INIZIO: LXI D,0        ; D = contatore',\n    'CICLO:  IN 0FFH        ; levette A15-A8 = durata della pausa',\n    '        ORI 08H        ; mai troppo veloce',\n    '        MOV B,A',\n    '        MVI C,0',\n    'PAUSA:  LDAX D         ; mostra D sul bus indirizzi',\n    '        DCX B',\n    '        MOV A,B',\n    '        ORA C',\n    '        JNZ PAUSA',\n    '        INR D          ; 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 di indirizzo A15-A8, con una subroutine e lo stack.',\n    en: 'A light running back and forth on address LEDs A15-A8, using a subroutine and the stack.'\n  },\n  src: [\n    '; LUCE CHE SCORRE avanti e indietro sui LED A15-A8',\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 A15-A8 per un po\\'',\n    'MOSTRA: PUSH PSW',\n    '        MOV D,A',\n    '        MVI E,0',\n    '        LXI B,0C00H',\n    'PAUSA:  LDAX D         ; il bus indirizzi mostra D',\n    '        DCX B',\n    '        MOV A,B',\n    '        ORA C',\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), scritto proprio per l\\'Altair: un bit gira sui LED di indirizzo A15-A8. Alza e riabbassa la levetta sotto il bit quando passa per \"ucciderlo\"; se sbagli ne compaiono altri.',\n    en: 'Dean McDaniel\\'s 1975 game, written for 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, cosi i LED alti dell\\'indirizzo lo mostrano.',\n    '        ORG 0',\n    'INIZIO: LXI H,0        ; contatore per la velocita',\n    '        MVI D,080H     ; il bit da colpire',\n    '        LXI B,0EH      ; velocita (piu alto = piu 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       ; finche non trabocca',\n    '        IN 0FFH        ; legge le levette A15-A8',\n    '        XRA D          ; una levetta alzata 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 attraverso la scheda seriale 88-2SIO (porte 10h e 11h), aspettando che la seriale sia pronta prima di ogni carattere.',\n    en: 'Writes a message on the terminal through the 88-2SIO serial board (ports 10h and 11h), waiting for the port to be ready before each character.'\n  },\n  terminal: true,\n  src: [\n    '; CIAO DAL TERMINALE - MITS 88-2SIO: 10h stato, 11h dati',\n    'STATO   EQU 10H',\n    'DATI    EQU 11H',\n    '        ORG 0',\n    'INIZIO: LXI SP,0100H'\n  ].concat(ACIA_INIT, [\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  ], IO2SIO, [\n    '',\n    'MSG:    DB 1AH         ; ADM-3A: cancella lo schermo',\n    '        DB \\'CIAO! SONO UN ALTAIR 8800 DEL 1975.\\',13,10',\n    '        DB \\'CPU INTEL 8080 A 2 MHZ, MITS ALBUQUERQUE.\\',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. Mentre aspetta un tasto, guarda i LED: la CPU gira in un ciclo che legge lo stato della seriale (LED INP).',\n    en: 'Type on the terminal: every key is echoed on screen. While it waits for a key, watch the LEDs: the CPU loops reading the serial status (INP LED).'\n  },\n  terminal: true,\n  src: [\n    '; ECO: tastiera -> schermo',\n    'STATO   EQU 10H',\n    'DATI    EQU 11H',\n    '        ORG 0',\n    'INIZIO: LXI SP,0100H'\n  ].concat(ACIA_INIT, [\n    '        LXI H,MSG',\n    '        CALL PRINT',\n    'CICLO:  CALL GETC      ; aspetta un tasto',\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    'PRINT:  MOV A,M',\n    '        ORA A',\n    '        RZ',\n    '        CALL PUTC',\n    '        INX H',\n    '        JMP PRINT',\n    ''\n  ], IO2SIO, [\n    '',\n    'MSG:    DB 1AH,\\'SCRIVI QUALCOSA:\\',13,10,0',\n    '        END INIZIO'\n  ])\n}\n];\n\n\/\/ caricatore di avvio della 88-2SIO per i nastri di Altair BASIC 4.x (28 byte, dal manuale MITS)\nRF.PROGRAMS.push({\n  id: 'boot2sio',\n  name: { it: 'Bootstrap 2SIO per Altair BASIC (nastro)', en: '2SIO bootstrap for Altair BASIC (tape)' },\n  desc: {\n    it: 'Il caricatore di 28 byte che si inseriva con le levette per leggere Altair BASIC 4.x dal nastro. Abbassa tutte le levette A15-A8 (console e caricamento sulla 2SIO), premi <b>Carica ed esegui<\\\/b>, poi metti il nastro .tap nel lettore (sezione \"Salva e carica\"). Per il 4K o l\\'Extended cambia <code>VERS<\\\/code>.',\n    en: 'The 28-byte loader that was toggled in to read Altair BASIC 4.x from tape. Put all switches A15-A8 down (console and load on the 2SIO), press <b>Load and run<\\\/b>, then put the .tap tape in the reader (\"Save and load\" section). For 4K or Extended change <code>VERS<\\\/code>.'\n  },\n  terminal: true,\n  src: [\n    '; BOOTSTRAP 88-2SIO per Altair BASIC 4.x (dal manuale MITS)',\n    '; legge dal nastro il caricatore con checksum e lo mette in memoria al contrario,',\n    '; da xxC1h in giu, saltando il preambolo; poi lo esegue da xx00h.',\n    'VERS    EQU 37Q        ; pagina del caricatore: 17Q = 4K, 37Q = 8K, 57Q = Extended',\n    '        ORG 0',\n    '        MVI A,03H      ; reset della 2SIO',\n    '        OUT 10H',\n    '        MVI A,11H      ; 8 bit, 2 stop (15H = 1 stop)',\n    '        OUT 10H',\n    '        LXI H,(VERS SHL 8) + 0C2H',\n    'LEGGI:  LXI SP,STACK   ; ogni RET torna qui: lo stack contiene LEGGI',\n    '        IN 10H',\n    '        RRC',\n    '        RNC            ; nessun carattere: torna a LEGGI',\n    '        IN 11H',\n    '        CMP L          ; byte del preambolo?',\n    '        RZ',\n    '        DCR L',\n    '        MOV M,A        ; un byte del caricatore',\n    '        RNZ',\n    '        PCHL           ; finito: esegue il caricatore',\n    'STACK:  DW LEGGI',\n    '        END'\n  ]\n});\n\n\/\/ il mini monitor della pagina IMSAI, con la console sulla 88-2SIO\nRF.PROGRAMS.push({\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: \"; MINI MONITOR 8080 - RetroFixer (console sulla MITS 88-2SIO)\\n; D aaaa  mostra 128 byte     E aaaa  modifica     G aaaa  esegue\\nCR      EQU 13\\nLF      EQU 10\\nDATI    EQU 11H\\nSTATO   EQU 10H\\n        ORG 0\\nINIZIO: LXI SP,0       ; lo stack parte dalla cima della memoria\\n        LXI H,TITOLO\\n        CALL PRINT\\nPROMPT: 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\\nDUMP:   CALL GETADR\\n        JC ERRORE\\n        MVI C,8\\nDRIGA:  CALL CRLF\\n        CALL PADDR\\n        MVI A,':'\\n        CALL PUTC\\n        MVI B,16\\n        PUSH H\\nDBYTE:  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\\nDASC:   MOV A,M\\n        CPI ' '\\n        JC DPUNTO      ; caratteri di controllo -> \\\".\\\"\\n        CPI 7FH\\n        JC DCAR\\nDPUNTO: MVI A,'.'\\nDCAR:   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\\nENTER:  CALL GETADR\\n        JC ERRORE\\nELOOP:  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\\nESALTA: INX H\\n        JMP ELOOP\\n\\n; ---- G aaaa: chiama il programma; il suo RET torna al prompt\\nGO:     CALL GETADR\\n        JC ERRORE\\n        CALL CRLF\\n        LXI D,PROMPT\\n        PUSH D\\n        PCHL\\n\\nERRORE: LXI H,ERRMSG\\n        CALL PRINT\\n        JMP PROMPT\\n\\n; legge un indirizzo esadecimale in HL (carry = errore)\\nGETADR: LXI H,0\\n        MVI B,0        ; cifre lette\\nGA1:    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\\nGA2:    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\\nGAFINE: MOV A,B\\n        ORA A\\n        JZ GAERR\\nGAOK:   ORA A          ; carry a 0\\n        RET\\nGAERR:  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\\nGETBYTE: MVI E,0\\n        MVI D,0\\nGB1:    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\\nGBCR:   MOV A,E\\n        ORA A\\n        JZ GBFINE      ; Invio senza cifre: fine\\nGBOK:   MOV A,D\\n        ORA A          ; carry a 0\\n        RET\\nGBFINE: STC\\n        RET\\n\\n; carattere ASCII in A -> valore 0-15 (carry = non esadecimale)\\nHEXVAL: 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\\nPADDR:  MOV A,H\\n        CALL PHEX\\n        MOV A,L\\n; stampa A in esadecimale (il trucco classico con DAA)\\nPHEX:   PUSH PSW\\n        RRC\\n        RRC\\n        RRC\\n        RRC\\n        CALL PNIB\\n        POP PSW\\nPNIB:   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\\nGETCE:  IN STATO       ; bit 0 = carattere ricevuto\\n        ANI 01H\\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\\nGCE1:   CALL PUTC\\n        RET\\n\\nPUTC:   PUSH PSW\\nPUTC1:  IN STATO       ; bit 1 = pronta a trasmettere\\n        ANI 02H\\n        JZ PUTC1\\n        POP PSW\\n        OUT DATI\\n        RET\\nCRLF:   MVI A,CR\\n        CALL PUTC\\n        MVI A,LF\\n        JMP PUTC\\nPRINT:  MOV A,M\\n        ORA A\\n        RZ\\n        CALL PUTC\\n        INX H\\n        JMP PRINT\\n\\nTITOLO: DB 1AH,'MINI MONITOR 8080 - RETROFIXER',CR,LF\\n        DB 'D=MOSTRA  E=MODIFICA  G=ESEGUI  ?=AIUTO',CR,LF,0\\nPRMSG:  DB CR,LF,'>',0\\nAIUTO:  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\\nERRMSG: DB ' ?',0\\n        END INIZIO\\n\"\n});\n\nRF.PROGRAMS.forEach(function (p) { if (Array.isArray(p.src)) p.src = p.src.join('\\n') + '\\n'; });\n})(window.RFAltair = window.RFAltair || {});\n\n\/* RetroFixer \u00b7 Altair 8800 \u00b7 testi (italiano e inglese), foto, progetti\n * Fatti e link dalla ricerca del 5 novembre 2026 (fonti\/ricerca-altair.md).\n *\/\n(function (RF) {\n'use strict';\n\n\/\/ Foto da Wikimedia Commons (licenze libere, attribuzione obbligatoria), ridotte a 1280 px (cartella foto\/) e caricate nella libreria media (11603, 11604)\nRF.PHOTOS = [\n  {\n    src: 'https:\/\/retrofixer.it\/wp-content\/uploads\/2026\/11\/altair-8800-smithsonian-ed-uthman-cc-by-sa-2.jpg',\n    w: 1280, h: 673,\n    page: 'https:\/\/commons.wikimedia.org\/wiki\/File:Altair_8800,_Smithsonian_Museum.jpg', author: 'Ed Uthman', license: 'CC BY-SA 2.0',\n    alt: { it: 'Altair 8800 con la cornice azzurra, il pannello grigio con i LED rossi e le levette, e la scritta ALTAIR 8800 COMPUTER', en: 'Altair 8800 with its blue frame, grey panel with red LEDs and toggle switches, and the ALTAIR 8800 COMPUTER label' },\n    cap: { it: 'Un Altair 8800 allo Smithsonian.', en: 'An Altair 8800 at the Smithsonian.' }\n  },\n  {\n    src: 'https:\/\/retrofixer.it\/wp-content\/uploads\/2026\/11\/altair-8800-pannello-cromemco-cc-by-sa-4.jpg',\n    w: 1280, h: 543,\n    page: 'https:\/\/commons.wikimedia.org\/wiki\/File:MITS_Altair_8800_Front_Panel.jpg', author: 'Cromemco', license: 'CC BY-SA 4.0',\n    alt: { it: 'Il pannello frontale dell\\'Altair 8800 visto di fronte, con tutte le scritte leggibili', en: 'The Altair 8800 front panel seen straight on, with every label readable' },\n    cap: { it: 'Il pannello frontale: in questo esemplare le levette hanno cappucci colorati.', en: 'The front panel: on this unit the switches have coloured caps.' }\n  }\n];\n\nvar KIT = { it: 'Replica', en: 'Replica' }, DIY = { it: 'Fai da te', en: 'DIY' }, HW = { it: 'Hardware S-100', en: 'S-100 hardware' },\n  SW = { it: 'Emulatore', en: 'Emulator' }, DOC = { it: 'Documentazione', en: 'Documentation' }, SRC = { it: 'Ricerca', en: 'Search' };\n\nRF.PROJECTS = [\n  {\n    title: { it: 'Repliche pronte o in kit', en: 'Ready-made and kit replicas' },\n    items: [\n      { tag: KIT, name: 'Altair 8800 Clone (Mike Douglas)', url: 'https:\/\/altairclone.com\/',\n        desc: { it: 'Una replica a grandezza naturale: dentro un microcontrollore PIC24 emula l\\'8080 ciclo per ciclo, cos\u00ec i LED si comportano come sull\\'originale. 64 KB di RAM, due seriali, dischi da 8\" e Minidisk emulati. Costava 621 dollari, come un Altair montato nel 1975.',\n          en: 'A full-size replica: inside, a PIC24 microcontroller emulates the 8080 cycle by cycle, so the LEDs behave like the original\\'s. 64 KB RAM, two serial ports, emulated 8\" and Minidisk drives. It cost USD 621, like an assembled Altair in 1975.' } },\n      { tag: KIT, name: 'Altair-Duino Pro (Adwater & Stir)', url: 'https:\/\/adwaterandstir.com\/shop\/',\n        desc: { it: 'Kit di Chris Davis basati sul simulatore di David Hansel per Arduino Due: fa girare il software originale e il CP\/M. Il Pro ha il mobile a grandezza naturale (circa 475 dollari), l\\'Experimenter \u00e8 una scatola pi\u00f9 piccola (circa 240).',\n          en: 'Chris Davis\\'s kits based on David Hansel\\'s Arduino Due simulator: they run original software and CP\/M. The Pro has a full-size case (about USD 475), the Experimenter is a smaller box (about 240).' } },\n      { tag: HW, name: 'Altair 8800c', url: 'https:\/\/funwithretrocomputers.blogspot.com\/2025\/03\/altair-8800c-build.html',\n        desc: { it: 'Un Altair \"vero\": un Intel 8080 autentico su schede S-100, con una riproduzione del pannello progettata da Mike Douglas. Qui il diario di montaggio di un appassionato.',\n          en: 'A \"real\" Altair: a genuine Intel 8080 on S-100 boards, with a reproduction front panel designed by Mike Douglas. Here a hobbyist\\'s build log.' } }\n    ]\n  },\n  {\n    title: { it: 'Fai da te con Arduino, Raspberry Pi ed ESP32', en: 'DIY with Arduino, Raspberry Pi and ESP32' },\n    items: [\n      { tag: DIY, name: 'Altair 8800 simulator (David Hansel)', url: 'https:\/\/github.com\/dhansel\/Altair8800',\n        desc: { it: 'Il progetto open source (GPL-3) alla base dell\\'Altair-Duino: gira su Arduino Due, Mega, Teensy 3.6 o PC, emula 8080 o Z80, dischi, video e seriali. Con un pannello di LED e levette costruito da te diventa un Altair da scrivania.',\n          en: 'The open-source project (GPL-3) behind the Altair-Duino: it runs on Arduino Due, Mega, Teensy 3.6 or a PC and emulates 8080 or Z80, disks, video and serial ports. Add a home-made panel of LEDs and switches and you have a desk Altair.' } },\n      { tag: DIY, name: 'Altair 8800 su Raspberry Pi (gloveboxes)', url: 'https:\/\/github.com\/gloveboxes\/Altair8800.Emulator.UN-X',\n        desc: { it: 'Emulatore in C per Raspberry Pi e Linux con un terminale web; il \"pannello\" pu\u00f2 essere la matrice di LED 8\u00d78 del Sense HAT.',\n          en: 'A C emulator for Raspberry Pi and Linux with a web terminal; the \"panel\" can be the Sense HAT 8\u00d78 LED matrix.' } },\n      { tag: DIY, name: 'Blinkencard \u2013 pocket Altair 8800', url: 'https:\/\/hackaday.io\/project\/169140-blinkencard-pocket-altair-8800',\n        desc: { it: 'Un Altair tascabile: un ESP32 e una FPGA Lattice che esegue l\\'8080, con una matrice di LED in omaggio al pannello.',\n          en: 'A pocket Altair: an ESP32 and a Lattice FPGA running the 8080, with an LED matrix as a tribute to the panel.' } },\n      { tag: SRC, name: 'Thingiverse: \"Altair 8800\"', url: 'https:\/\/www.thingiverse.com\/search?q=altair+8800',\n        desc: { it: 'Modelli da stampare in 3D della comunit\u00e0 (mobili in scala, contenitori per Raspberry Pi in stile Altair). Controlla licenza e misure di ogni modello.',\n          en: 'Community 3D-printable models (scale cabinets, Altair-style Raspberry Pi cases). Check each model\\'s licence and dimensions.' } }\n    ]\n  },\n  {\n    title: { it: 'Hardware S-100 per costruirne uno vero', en: 'S-100 hardware to build a real one' },\n    items: [\n      { tag: HW, name: 's100computers.com (John Monahan)', url: 'https:\/\/www.s100computers.com\/',\n        desc: { it: 'Schede S-100 nuove da montare: CPU 8080, pannello frontale, piastre madri e RAM. I circuiti stampati si ordinano attraverso il gruppo Google del sito.',\n          en: 'New S-100 boards to build: 8080 CPU, front panel, motherboards and RAM. The bare PCBs are ordered through the site\\'s Google group.' } },\n      { tag: HW, name: 'Replica della scheda CPU MITS 8080', url: 'https:\/\/www.tindie.com\/products\/gekaufman\/altair-mits-8800-cpu-card-8080a-s-100-s100-replica',\n        desc: { it: 'Circuito stampato che riproduce la scheda CPU originale dell\\'Altair (circa 40 dollari, componenti esclusi).',\n          en: 'A PCB reproducing the original Altair CPU board (about USD 40, parts not included).' } },\n      { tag: HW, name: 'deramp.com', url: 'https:\/\/deramp.com\/',\n        desc: { it: 'Il sito di Mike Douglas: il controller per floppy FDC+ e un enorme archivio di manuali, schemi e software MITS.',\n          en: 'Mike Douglas\\'s site: the FDC+ floppy controller and a huge archive of MITS manuals, schematics and software.' } }\n    ]\n  },\n  {\n    title: { it: 'Emulatori e documentazione', en: 'Emulators and documentation' },\n    items: [\n      { tag: SW, name: 'SIMH AltairZ80 (Peter Schorn)', url: 'https:\/\/schorn.ch\/altair.html',\n        desc: { it: 'Il simulatore pi\u00f9 completo per PC: CPU 8080\/Z80, dischi, CP\/M e molte schede di terze parti.',\n          en: 'The most complete PC simulator: 8080\/Z80 CPU, disks, CP\/M and many third-party boards.' } },\n      { tag: SW, name: 'z80pack (altairsim) \u2013 Udo Munk', url: 'https:\/\/github.com\/udo-munk\/z80pack',\n        desc: { it: 'Emulatore open source (licenza MIT) con la macchina altairsim e un pannello frontale grafico.',\n          en: 'Open-source emulator (MIT licence) with the altairsim machine and a graphical front panel.' } },\n      { tag: SW, name: 's2js Altair 8800', url: 'https:\/\/s2js.com\/altair\/',\n        desc: { it: 'Un altro emulatore nel browser, con telescrivente ASR-33 e il BASIC 4K su nastro.',\n          en: 'Another browser emulator, with an ASR-33 Teletype and the 4K BASIC paper tape.' } },\n      { tag: DOC, name: 'Manuale operativo dell\\'Altair 8800', url: 'https:\/\/ubuntourist.codeberg.page\/Altair-8800\/part-3.html',\n        desc: { it: 'La trascrizione di Kevin Cole del manuale MITS, con il pannello spiegato e il programma della somma usato nella guida qui sotto.',\n          en: 'Kevin Cole\\'s transcription of the MITS manual, with the panel explained and the addition program used in the guide below.' } },\n      { tag: DOC, name: 'Archivio software Altair (deramp.com)', url: 'https:\/\/deramp.com\/downloads\/altair\/software\/papertape_cassette\/',\n        desc: { it: 'Immagini dei nastri originali (BASIC 4K, 8K, Extended, caricatori) e documenti sulle versioni: da qui si prende il nastro per il lettore dell\\'emulatore.',\n          en: 'Original paper-tape images (4K, 8K, Extended BASIC, loaders) and version notes: get the tape for the emulator\\'s reader here.' } },\n      { tag: DOC, name: 'Kill the Bit (1975)', url: 'https:\/\/altairclone.com\/downloads\/killbits.pdf',\n        desc: { it: 'Il listato originale di Dean McDaniel, lo stesso caricato nell\\'emulatore.',\n          en: 'Dean McDaniel\\'s original listing, the same one loaded in the emulator.' } },\n      { tag: DOC, name: 'Palo Alto Tiny BASIC (sorgente)', url: 'https:\/\/github.com\/Geddd\/PATB4ASM80',\n        desc: { it: 'Il sorgente del Tiny BASIC di Li-Chen Wang (versione 2.0 di Roger Rauskolb, adattata da Martin Mal\u00fd, CC0) da cui viene quello dell\\'emulatore.',\n          en: 'The source of Li-Chen Wang\\'s Tiny BASIC (Roger Rauskolb\\'s version 2.0, adapted by Martin Mal\u00fd, CC0) used by the emulator.' } }\n    ]\n  }\n];\n\nvar A = function (u, t) { return '<a href=\"' + u + '\" target=\"_blank\" rel=\"noopener\">' + t + '<\\\/a>'; };\n\nRF.TEXTS = {\nit: {\n  kicker: '> retrocomputer del 1975',\n  lead: 'Il computer in kit che nel gennaio 1975 fin\u00ec sulla copertina di <b>Popular Electronics<\\\/b> e fece nascere l\\'informatica personale, e anche <b>Microsoft<\\\/b>. Qui trovi la sua storia, come averne uno oggi (repliche, kit e fai da te) e un <b>emulatore completo<\\\/b>: pannello frontale funzionante, terminale video con il <b>BASIC<\\\/b>, assembler e lettore di nastro perforato.',\n  navStoria: 'Storia', navCostruire: 'Averne uno oggi', navEmu: 'Emulatore', navBasic: 'Il BASIC', navGuida: 'Come si usa',\n  storiaT: 'Che cos\\'\u00e8',\n  storia: '<p>L\\'<b>Altair 8800<\\\/b> lo produceva la <b>MITS<\\\/b> (Micro Instrumentation and Telemetry Systems) di Albuquerque, nel New Mexico, fondata nel 1969 da <b>Ed Roberts<\\\/b> e Forrest Mims per vendere moduli di telemetria per modellini di razzi e poi calcolatrici in kit. Nel 1974 la guerra dei prezzi delle calcolatrici aveva riempito la MITS di debiti, e Roberts punt\u00f2 tutto su un computer in kit con il nuovo <b>Intel 8080<\\\/b>.<\\\/p>' +\n    '<p>Il numero di <b>gennaio 1975<\\\/b> di <b>Popular Electronics<\\\/b>, in edicola da met\u00e0 dicembre 1974, lo mise in copertina. Costava <b>439 dollari in kit<\\\/b> e <b>621 montato<\\\/b>: un prezzo incredibile per un computer. Gli ordini travolsero la MITS: migliaia di kit spediti gi\u00e0 nel 1975. Il nome, secondo il direttore Les Solomon, lo propose sua figlia.<\\\/p>' +\n    '<p>Di serie aveva <b>256 byte di RAM<\\\/b>, niente tastiera e niente schermo: si programmava con le <b>levette del pannello<\\\/b>, un byte alla volta in binario, e si leggevano i risultati sui LED rossi. Con le schede di espansione arrivarono la memoria, la seriale per una telescrivente o un terminale, il registratore a cassette e i dischi.<\\\/p>' +\n    '<p>Per l\\'Altair <b>Bill Gates<\\\/b> e <b>Paul Allen<\\\/b>, con Monte Davidoff, scrissero il <b>BASIC<\\\/b>, senza avere un Altair: lo provarono su un simulatore dell\\'8080 scritto da Allen su un PDP-10 di Harvard. Allen vol\u00f2 ad Albuquerque con il nastro, scrisse il caricatore in aereo e al primo vero avvio <code>PRINT 2+2<\\\/code> rispose <b>4<\\\/b>. Da l\u00ec nacque <b>Micro-Soft<\\\/b> (1975).<\\\/p>' +\n    '<p>L\\'Altair diede il via all\\'<b>Homebrew Computer Club<\\\/b> (dove Steve Dompier fece \"suonare\" un Altair attraverso una radio) e il suo bus a 100 contatti divent\u00f2 lo standard <b>S-100<\\\/b>, usato da decine di aziende, tra cui l\\'IMSAI 8080. Seguirono l\\'8800a, l\\'8800b e l\\'Altair 680. Nel 1977 la MITS fu venduta alla Pertec, che chiuse la linea entro la fine del decennio. Gli esemplari venduti furono probabilmente circa 25.000.<\\\/p>',\n  schedaT: 'Scheda tecnica',\n  scheda: '<tr><th>Produttore<\\\/th><td>MITS, Albuquerque (New Mexico)<\\\/td><\\\/tr>' +\n    '<tr><th>Uscita<\\\/th><td>gennaio 1975 (copertina di Popular Electronics)<\\\/td><\\\/tr>' +\n    '<tr><th>CPU<\\\/th><td>Intel 8080 a 2 MHz, 8 bit, 64 KB indirizzabili<\\\/td><\\\/tr>' +\n    '<tr><th>Memoria<\\\/th><td>256 byte di serie; fino a 64 KB con le schede di memoria<\\\/td><\\\/tr>' +\n    '<tr><th>Bus<\\\/th><td>\"Altair bus\" a 100 contatti, poi chiamato S-100 (IEEE 696)<\\\/td><\\\/tr>' +\n    '<tr><th>Pannello<\\\/th><td>36 LED e 25 levette: indirizzo, dati, stato e comandi<\\\/td><\\\/tr>' +\n    '<tr><th>Alimentatore<\\\/th><td>lineare: +8 V e \u00b116 V non stabilizzati, ogni scheda ha i suoi regolatori<\\\/td><\\\/tr>' +\n    '<tr><th>Prezzo<\\\/th><td>439 $ in kit, 621 $ montato<\\\/td><\\\/tr>' +\n    '<tr><th>Espansioni<\\\/th><td>seriali 88-SIO e 88-2SIO, parallela 88-PIO, cassette 88-ACR, dischi da 8\" e Minidisk<\\\/td><\\\/tr>' +\n    '<tr><th>Esemplari<\\\/th><td>circa 25.000 (stima)<\\\/td><\\\/tr>',\n  pannelloSpiega: '<p><b>Come si legge il pannello.<\\\/b> In alto a sinistra <b>INTE<\\\/b> (interrupt abilitati), <b>PROT<\\\/b> (memoria protetta) e gli 8 LED di <b>STATUS<\\\/b>, che dicono che cosa sta facendo la CPU: legge la memoria (MEMR), fa input (INP), legge un\\'istruzione (M1), fa output (OUT), \u00e8 ferma su HLT (HLTA), usa lo stack (STACK), <i>non<\\\/i> scrive (W\u0304O), risponde a un interrupt (INT). A destra i LED <b>D7-D0<\\\/b> del bus dati. Sotto <b>WAIT<\\\/b> (CPU ferma), <b>HLDA<\\\/b> e i 16 LED <b>A15-A0<\\\/b> del bus indirizzi.<\\\/p>' +\n    '<p>Le 16 levette scrivono indirizzi e dati in binario: <b>su = 1<\\\/b>, <b>gi\u00f9 = 0<\\\/b>. Sono raggruppate a tre perch\u00e9 il manuale MITS usava l\\'<b>ottale<\\\/b>. Le 8 di sinistra sono anche le <b>SENSE SW.<\\\/b>: un programma le legge con <code>IN 0FFH<\\\/code>. Sotto ci sono i comandi, che tornano al centro da soli, e l\\'interruttore <b>OFF\/ON<\\\/b>.<\\\/p>',\n\n  costruireT: 'Averne uno oggi',\n  costruireIntro: 'Gli originali sono rari e costano migliaia di euro. Ma oggi si pu\u00f2 avere un Altair sulla scrivania in molti modi: repliche fedeli con un microcontrollore che emula l\\'8080, kit da saldare, progetti fai da te con Arduino o Raspberry Pi, oppure un Altair \"vero\" con un 8080 autentico e schede S-100 nuove.',\n  fotoDi: 'Foto:',\n\n  emuT: 'Emulatore Altair 8800',\n  emuIntro: 'Un Altair 8800 con 64 KB di RAM, le seriali MITS 88-SIO e 88-2SIO collegate a un terminale video e un lettore di nastro perforato. La CPU 8080 \u00e8 emulata istruzione per istruzione, alla velocit\u00e0 originale di 2 MHz. Parte con il <b>Tiny BASIC<\\\/b>: premi <b>Carica ed esegui<\\\/b> e scrivi nel terminale. Oppure usa le levette come nel 1975: la guida passo passo \u00e8 <a href=\"#rfalGuida\">pi\u00f9 sotto<\\\/a>.',\n  prog: 'Programma', 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 levetta per alzarla e sulla <b>met\u00e0 bassa<\\\/b> per abbassarla (anche con Tab e le frecce su\/gi\u00f9). Le levette dei comandi tornano al centro da sole; l\\'interruttore di alimentazione ha OFF in alto e ON in basso, come sull\\'originale. Mentre il programma gira i LED brillano in proporzione al tempo in cui il segnale \u00e8 a 1.',\n  termName: 'TERMINALE VIDEO 80\u00d724 \u00b7 88-2SIO',\n  termClick: 'clicca qui per scrivere',\n  caps: 'MAIUSCOLE', termClr: 'Pulisci', termCopy: 'Copia il testo',\n  termBoot: '\\x1aMITS ALTAIR 8800 - 64K RAM - TERMINALE PRONTO\\r\\n\\r\\nPREMI \"CARICA ED ESEGUI\" PER AVVIARE IL TINY BASIC,\\r\\nOPPURE USA LE LEVETTE 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>IN 0FFH<\\\/code> levette A15-A8, <code>10H<\\\/code>\/<code>11H<\\\/code> terminale (88-2SIO), <code>00H<\\\/code>\/<code>01H<\\\/code> terminale (88-SIO).',\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: abbassa RUN sul pannello (o SINGLE STEP per un\\'istruzione alla volta).',\n  fileT: 'Salva e carica, lettore di nastro',\n  fileNote: 'Salva un pezzo di memoria in un file <b>Intel HEX<\\\/b> o <b>binario<\\\/b>, oppure carica file <code>.hex<\\\/code>, <code>.bin<\\\/code>, <code>.com<\\\/code> (all\\'indirizzo \"Da\", 0100h per i .com) e sorgenti <code>.asm<\\\/code>. Il <b>lettore di nastro<\\\/b> invece manda un file <code>.tap<\\\/code> alla seriale, byte per byte, quando il programma lo legge: serve per caricare Altair BASIC con il bootstrap (vedi la sezione sul BASIC). Con \"Salva nel browser\" conservi tutto lo stato della macchina.',\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  tapeBtn: 'Metti un nastro nel lettore\u2026', tapeIn: 'Nastro \"{f}\" nel lettore ({n} byte): ora il programma in esecuzione lo legge dalla seriale.', tapePos: 'nastro letto: {p}%',\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}: abbassa RUN.', loadedAsm: 'Sorgente caricato e assemblato.', loadedBin: 'Caricati {n} byte da {a}: abbassa 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 levette: {v} = {b}',\n\n  basicT: 'Il BASIC sull\\'Altair',\n  basic: '<p><b>Palo Alto Tiny BASIC.<\\\/b> L\\'emulatore parte con il BASIC che Li-Chen Wang pubblic\u00f2 gratis su Dr. Dobb\\'s nel 1976, scritto proprio per l\\'Altair con la seriale 88-2SIO e firmato con lo scherzoso \"@COPYLEFT ALL WRONGS RESERVED\". Sta in meno di 2 KB. Scegli <b>Palo Alto Tiny BASIC<\\\/b>, premi <b>Carica ed esegui<\\\/b>, clicca sul terminale e scrivi:<\\\/p>' +\n    '<pre class=\"snip\">PRINT 2+2\\n10 FOR I=1 TO 10\\n20 PRINT I, I*I\\n30 NEXT I\\nRUN<\\\/pre>' +\n    '<ul><li>Comandi: <code>LIST<\\\/code>, <code>RUN<\\\/code>, <code>NEW<\\\/code>. Istruzioni: <code>PRINT<\\\/code>, <code>INPUT<\\\/code>, <code>LET<\\\/code>, <code>IF<\\\/code>, <code>GOTO<\\\/code>, <code>GOSUB<\\\/code>\/<code>RETURN<\\\/code>, <code>FOR<\\\/code>\/<code>NEXT<\\\/code>\/<code>STEP<\\\/code>, <code>REM<\\\/code>, <code>STOP<\\\/code>; pi\u00f9 istruzioni sulla stessa riga separate da <code>;<\\\/code>.<\\\/li>' +\n    '<li>Funzioni: <code>ABS(x)<\\\/code>, <code>RND(x)<\\\/code> (numero da 1 a x), <code>SIZE<\\\/code> (byte liberi, qui circa 30.000). Numeri interi da -32767 a 32767, variabili <code>A<\\\/code>-<code>Z<\\\/code> e la matrice <code>@(i)<\\\/code>.<\\\/li>' +\n    '<li>Backspace cancella l\\'ultimo carattere, Esc cancella la riga, <b>Ctrl-C<\\\/b> ferma il programma. Puoi incollare un intero programma nel terminale.<\\\/li><\\\/ul>' +\n    '<p><b>Altair BASIC di Microsoft.<\\\/b> Il BASIC di Gates, Allen e Davidoff (4K, 8K, Extended) \u00e8 ancora protetto da copyright: nel 2025 Bill Gates ha pubblicato la scansione del listato originale come documento storico, ma senza una licenza che permetta di ridistribuirlo. Per questo non \u00e8 incluso. Se hai un\\'immagine del nastro (gli appassionati la conservano, per esempio nell\\'' + A('https:\/\/deramp.com\/downloads\/altair\/software\/papertape_cassette\/', 'archivio di deramp.com') + '), puoi caricarla come nel 1975:<\\\/p>' +\n    '<ol><li>Scegli <b>Bootstrap 2SIO per Altair BASIC<\\\/b> (per il 4K o l\\'Extended cambia <code>VERS<\\\/code> nell\\'editor).<\\\/li>' +\n    '<li>Abbassa tutte le levette A15-A8: per il BASIC 4.x vuol dire terminale e caricamento sulla 2SIO.<\\\/li>' +\n    '<li>Premi <b>Carica ed esegui<\\\/b>, poi <b>Metti un nastro nel lettore\u2026<\\\/b> e scegli il file <code>.tap<\\\/code> della versione 4.x.<\\\/li>' +\n    '<li>Quando il nastro \u00e8 letto, il BASIC fa le sue domande iniziali (come <code>MEMORY SIZE?<\\\/code>): premi Invio per i valori predefiniti.<\\\/li><\\\/ol>',\n\n  guidaT: 'Come si usa: il primo programma del manuale',\n  guidaIntro: '<p>Quando si accendeva l\\'Altair la memoria era vuota: il programma si inseriva <b>un byte alla volta con le levette<\\\/b>. Proviamo il primo esempio del manuale MITS, che somma due numeri. Il manuale lo scrive in ottale; qui accanto c\\'\u00e8 l\\'esadecimale:<\\\/p>' +\n    '<p><code>0000 3A 80 00<\\\/code> LDA 80H \u00b7 <code>0003 47<\\\/code> MOV B,A \u00b7 <code>0004 3A 81 00<\\\/code> LDA 81H \u00b7 <code>0007 80<\\\/code> ADD B \u00b7 <code>0008 32 82 00<\\\/code> STA 82H \u00b7 <code>000B C3 00 00<\\\/code> JMP 0<\\\/p>' +\n    '<p>In ottale: 072 200 000 \u00b7 107 \u00b7 072 201 000 \u00b7 200 \u00b7 062 202 000 \u00b7 303 000 000. Ogni pulsante \"imposta le levette\" mette le levette nella posizione giusta, cos\u00ec vedi il numero binario; poi tocca a te azionare il comando.<\\\/p>',\n  steps: [\n    ['L\\'interruttore <b>OFF\/ON<\\\/b> in basso a sinistra deve essere abbassato (ON). Se lo alzi la RAM si cancella, come sull\\'originale.'],\n    ['Alza <b>RESET<\\\/b>: la CPU torna all\\'indirizzo 0000 e i LED A15-A0 si spengono.'],\n    ['Primo byte, <b>3A<\\\/b> (LDA). Imposta le levette e alza <b>DEPOSIT<\\\/b>: il valore appare sui LED D7-D0.', 0x3A],\n    ['<b>80<\\\/b> (parte bassa dell\\'indirizzo 0080h): abbassa <b>DEPOSIT NEXT<\\\/b>, che passa all\\'indirizzo successivo e ci scrive il byte.', 0x80],\n    ['<b>00<\\\/b>: DEPOSIT NEXT.', 0x00],\n    ['<b>47<\\\/b> (MOV B,A): DEPOSIT NEXT.', 0x47],\n    ['<b>3A<\\\/b> (LDA): DEPOSIT NEXT.', 0x3A],\n    ['<b>81<\\\/b>: DEPOSIT NEXT.', 0x81],\n    ['<b>00<\\\/b>: DEPOSIT NEXT.', 0x00],\n    ['<b>80<\\\/b> (ADD B): DEPOSIT NEXT.', 0x80],\n    ['<b>32<\\\/b> (STA): DEPOSIT NEXT.', 0x32],\n    ['<b>82<\\\/b>: DEPOSIT NEXT.', 0x82],\n    ['<b>00<\\\/b>: DEPOSIT NEXT.', 0x00],\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 da 0000 a 000D.', 0x00],\n    ['Ora i numeri da sommare. Imposta tutte le 16 levette sull\\'indirizzo <b>0080<\\\/b> e alza <b>EXAMINE<\\\/b>.', 0x0080, true],\n    ['Primo numero, per esempio <b>5<\\\/b>: alza <b>DEPOSIT<\\\/b>.', 0x05],\n    ['Secondo numero, <b>3<\\\/b>: abbassa <b>DEPOSIT NEXT<\\\/b> (va in 0081).', 0x03],\n    ['Alza <b>RESET<\\\/b>, abbassa <b>RUN<\\\/b>: il LED WAIT si spegne e il programma gira. Dopo un istante alza <b>STOP<\\\/b>.'],\n    ['Per leggere il risultato imposta le levette su <b>0082<\\\/b> e alza <b>EXAMINE<\\\/b>: i LED D7-D0 mostrano 00001000, cio\u00e8 <b>8<\\\/b>.', 0x0082, true]\n  ],\n  guidaDopo: '<p>Sbagliato un byte? Imposta l\\'indirizzo con le 16 levette, alza <b>EXAMINE<\\\/b> e 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 nato sull\\'Altair: un bit gira sui LED A15-A8 e per \"ucciderlo\" devi alzare e riabbassare la levetta sotto di lui proprio mentre passa. Per i programmi pi\u00f9 lunghi c\\'\u00e8 il <b>mini monitor<\\\/b> sul terminale, oppure il BASIC.<\\\/p>',\n  leveT: 'Levette e LED del pannello',\n  leve: '<tr><td>A15-A8<\\\/td><td>Parte alta dell\\'indirizzo per EXAMINE; come <b>SENSE SW.<\\\/b> un programma le legge con <code>IN 0FFH<\\\/code> (il BASIC le usa per sapere quale scheda \u00e8 il terminale).<\\\/td><\\\/tr>' +\n    '<tr><td>A7-A0<\\\/td><td>Parte bassa dell\\'indirizzo e byte da scrivere con DEPOSIT.<\\\/td><\\\/tr>' +\n    '<tr><td>STOP \/ RUN<\\\/td><td>Ferma la CPU (su) \/ la avvia dall\\'indirizzo attuale (gi\u00f9).<\\\/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>EXAMINE<\\\/td><td>Va all\\'indirizzo impostato sulle 16 levette e ne mostra il contenuto. <b>EXAMINE NEXT<\\\/b> (gi\u00f9): indirizzo successivo.<\\\/td><\\\/tr>' +\n    '<tr><td>DEPOSIT<\\\/td><td>Scrive le levette A7-A0 all\\'indirizzo attuale. <b>DEPOSIT NEXT<\\\/b> (gi\u00f9): passa all\\'indirizzo successivo e scrive l\u00ec.<\\\/td><\\\/tr>' +\n    '<tr><td>RESET \/ CLR<\\\/td><td>Azzera la CPU: il programma ripartir\u00e0 da 0000. <b>CLR<\\\/b> (gi\u00f9) azzera le schede di I\/O: qui svuota la seriale e toglie il nastro.<\\\/td><\\\/tr>' +\n    '<tr><td>PROTECT<\\\/td><td>Protegge dalla scrittura la pagina di 256 byte all\\'indirizzo attuale (LED PROT); <b>UNPROTECT<\\\/b> la sblocca.<\\\/td><\\\/tr>' +\n    '<tr><td>AUX<\\\/td><td>Due levette di riserva, non collegate nell\\'Altair di base.<\\\/td><\\\/tr>' +\n    '<tr><td>OFF \/ ON<\\\/td><td>Spegne (su) e accende (gi\u00f9). Spegnendo si perde il contenuto della RAM.<\\\/td><\\\/tr>',\n  porteT: 'Porte di I\/O dell\\'emulatore',\n  porte: '<tr><td>IN FFh<\\\/td><td>Le levette A15-A8 (sense switches).<\\\/td><\\\/tr>' +\n    '<tr><td>10h \/ 11h<\\\/td><td>MITS <b>88-2SIO<\\\/b> (ACIA 6850), porta A: 10h stato (bit 0 = carattere ricevuto, bit 1 = pronta a trasmettere) e controllo, 11h dati. \u00c8 la console del Tiny BASIC e dei programmi di esempio.<\\\/td><\\\/tr>' +\n    '<tr><td>00h \/ 01h<\\\/td><td>MITS <b>88-SIO<\\\/b>: 00h stato con bit attivi bassi (bit 0 = 0 carattere ricevuto, bit 7 = 0 pronta a trasmettere), 01h dati. Per il software scritto per la prima seriale MITS.<\\\/td><\\\/tr>' +\n    '<tr><td>Nastro<\\\/td><td>Il lettore manda il file alla stessa seriale: il programma lo legge come se fosse il terminale, come con una telescrivente ASR-33.<\\\/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. A 300 baud si vede scrivere un carattere alla volta, come su una telescrivente.<\\\/td><\\\/tr>',\n  fontiT: 'Fonti',\n  fonti: '<li>Wikipedia: ' + A('https:\/\/en.wikipedia.org\/wiki\/Altair_8800', 'Altair 8800') + ', ' + A('https:\/\/en.wikipedia.org\/wiki\/Micro_Instrumentation_and_Telemetry_Systems', 'MITS') + ', ' + A('https:\/\/en.wikipedia.org\/wiki\/Altair_BASIC', 'Altair BASIC') + ', ' + A('https:\/\/en.wikipedia.org\/wiki\/S-100_bus', 'S-100 bus') + '<\\\/li>' +\n    '<li>' + A('https:\/\/ubuntourist.codeberg.page\/Altair-8800\/part-3.html', 'Manuale operativo dell\\'Altair 8800') + ' (trascrizione di Kevin Cole) e ' + A('https:\/\/www.oldcomputers.net\/altair-8800.html', 'oldcomputers.net') + '<\\\/li>' +\n    '<li>' + A('https:\/\/www.i-programmer.info\/news\/82-heritage\/17946-bill-gates-shares-the-code-that-launched-microsoft.html', 'I Programmer') + ' sul sorgente del BASIC pubblicato da Bill Gates nel 2025<\\\/li>' +\n    '<li>Caricatori e levette di senso: ' + A('https:\/\/archive.decromancer.ca\/deramp.com\/downloads\/mfe_archive\/010-S100%20Computers%20and%20Boards\/00-MITS\/40-Software\/BASIC\/Loaders\/Boot%20Loaders.txt', 'Boot Loaders (archivio deramp)') + '<\\\/li>' +\n    '<li>Palo Alto Tiny BASIC: ' + A('https:\/\/en.wikipedia.org\/wiki\/Li-Chen_Wang', 'Li-Chen Wang') + ', ' + A('https:\/\/github.com\/Geddd\/PATB4ASM80', 'sorgente PATB4ASM80') + '<\\\/li>'\n},\n\nen: {\n  kicker: '> a 1975 retrocomputer',\n  lead: 'The kit computer that made the cover of <b>Popular Electronics<\\\/b> in January 1975 and started personal computing, and <b>Microsoft<\\\/b> too. Here you find its history, how to own one today (replicas, kits and DIY) and a <b>complete emulator<\\\/b>: working front panel, video terminal with <b>BASIC<\\\/b>, assembler and paper-tape reader.',\n  navStoria: 'History', navCostruire: 'Own one today', navEmu: 'Emulator', navBasic: 'BASIC', navGuida: 'How to use it',\n  storiaT: 'What it is',\n  storia: '<p>The <b>Altair 8800<\\\/b> was made by <b>MITS<\\\/b> (Micro Instrumentation and Telemetry Systems) of Albuquerque, New Mexico, founded in 1969 by <b>Ed Roberts<\\\/b> and Forrest Mims to sell telemetry modules for model rockets, and later calculator kits. By 1974 the calculator price war had left MITS deep in debt, and Roberts bet everything on a computer kit built around the new <b>Intel 8080<\\\/b>.<\\\/p>' +\n    '<p>The <b>January 1975<\\\/b> issue of <b>Popular Electronics<\\\/b>, on newsstands from mid-December 1974, put it on the cover. It cost <b>USD 439 as a kit<\\\/b> and <b>621 assembled<\\\/b>: an unbelievable price for a computer. Orders swamped MITS: thousands of kits shipped in 1975 alone. The name, according to editor Les Solomon, was suggested by his daughter.<\\\/p>' +\n    '<p>As shipped it had <b>256 bytes of RAM<\\\/b>, no keyboard and no screen: you programmed it with the <b>front-panel switches<\\\/b>, one byte at a time in binary, and read the results on the red LEDs. Expansion boards brought memory, a serial port for a Teletype or a terminal, a cassette interface and disks.<\\\/p>' +\n    '<p>For the Altair, <b>Bill Gates<\\\/b> and <b>Paul Allen<\\\/b>, with Monte Davidoff, wrote <b>BASIC<\\\/b> without owning an Altair: they tested it on an 8080 simulator Allen wrote on a Harvard PDP-10. Allen flew to Albuquerque with the tape, wrote the loader on the plane, and on the first real run <code>PRINT 2+2<\\\/code> answered <b>4<\\\/b>. That was the start of <b>Micro-Soft<\\\/b> (1975).<\\\/p>' +\n    '<p>The Altair kicked off the <b>Homebrew Computer Club<\\\/b> (where Steve Dompier made an Altair \"play\" music through a radio), and its 100-pin bus became the <b>S-100<\\\/b> standard used by dozens of companies, the IMSAI 8080 among them. The 8800a, 8800b and Altair 680 followed. In 1977 MITS was sold to Pertec, which phased the line out by the end of the decade. Probably about 25,000 were sold.<\\\/p>',\n  schedaT: 'Specifications',\n  scheda: '<tr><th>Maker<\\\/th><td>MITS, Albuquerque (New Mexico)<\\\/td><\\\/tr>' +\n    '<tr><th>Released<\\\/th><td>January 1975 (Popular Electronics cover)<\\\/td><\\\/tr>' +\n    '<tr><th>CPU<\\\/th><td>Intel 8080 at 2 MHz, 8-bit, 64 KB addressable<\\\/td><\\\/tr>' +\n    '<tr><th>Memory<\\\/th><td>256 bytes as shipped; up to 64 KB with memory boards<\\\/td><\\\/tr>' +\n    '<tr><th>Bus<\\\/th><td>100-pin \"Altair bus\", later called S-100 (IEEE 696)<\\\/td><\\\/tr>' +\n    '<tr><th>Panel<\\\/th><td>36 LEDs and 25 switches: address, data, status and controls<\\\/td><\\\/tr>' +\n    '<tr><th>Power supply<\\\/th><td>linear: unregulated +8 V and \u00b116 V, each board has its own regulators<\\\/td><\\\/tr>' +\n    '<tr><th>Price<\\\/th><td>USD 439 kit, 621 assembled<\\\/td><\\\/tr>' +\n    '<tr><th>Expansion<\\\/th><td>88-SIO and 88-2SIO serial, 88-PIO parallel, 88-ACR cassette, 8\" and Minidisk drives<\\\/td><\\\/tr>' +\n    '<tr><th>Units<\\\/th><td>about 25,000 (estimate)<\\\/td><\\\/tr>',\n  pannelloSpiega: '<p><b>Reading the panel.<\\\/b> Top left are <b>INTE<\\\/b> (interrupts enabled), <b>PROT<\\\/b> (memory protected) and the 8 <b>STATUS<\\\/b> LEDs, which tell what the CPU is doing: reading memory (MEMR), input (INP), fetching an instruction (M1), output (OUT), halted on HLT (HLTA), using the stack (STACK), <i>not<\\\/i> writing (W\u0304O), acknowledging an interrupt (INT). On the right are the <b>D7-D0<\\\/b> data bus LEDs. Below are <b>WAIT<\\\/b> (CPU stopped), <b>HLDA<\\\/b> and the 16 <b>A15-A0<\\\/b> address bus LEDs.<\\\/p>' +\n    '<p>The 16 switches enter addresses and data in binary: <b>up = 1<\\\/b>, <b>down = 0<\\\/b>. They are grouped in threes because the MITS manual used <b>octal<\\\/b>. The left 8 are also the <b>SENSE SW.<\\\/b>: a program reads them with <code>IN 0FFH<\\\/code>. Below are the controls, which spring back to the centre, and the <b>OFF\/ON<\\\/b> switch.<\\\/p>',\n\n  costruireT: 'Own one today',\n  costruireIntro: 'Originals are rare and cost thousands. But today there are many ways to have an Altair on your desk: faithful replicas with a microcontroller emulating the 8080, solder-it-yourself kits, DIY projects with Arduino or Raspberry Pi, or a \"real\" Altair with a genuine 8080 and new S-100 boards.',\n  fotoDi: 'Photo:',\n\n  emuT: 'Altair 8800 emulator',\n  emuIntro: 'An Altair 8800 with 64 KB of RAM, MITS 88-SIO and 88-2SIO serial boards wired to a video terminal, and a paper-tape reader. The 8080 CPU is emulated instruction by instruction at the original 2 MHz. It starts with <b>Tiny BASIC<\\\/b>: press <b>Load and run<\\\/b> and type in the terminal. Or use the switches as in 1975: the step-by-step guide is <a href=\"#rfalGuida\">further down<\\\/a>.',\n  prog: '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>top half<\\\/b> of a switch to raise it and the <b>bottom half<\\\/b> to lower it (also with Tab and the up\/down arrows). The control switches spring back to the centre; the power switch has OFF up and ON down, like the original. While a program runs, the LEDs glow in proportion to the time the signal is 1.',\n  termName: 'VIDEO TERMINAL 80\u00d724 \u00b7 88-2SIO',\n  termClick: 'click here to type',\n  caps: 'CAPS', termClr: 'Clear', termCopy: 'Copy text',\n  termBoot: '\\x1aMITS ALTAIR 8800 - 64K RAM - TERMINAL READY\\r\\n\\r\\nPRESS \"LOAD AND RUN\" TO START TINY BASIC,\\r\\nOR USE THE FRONT 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>IN 0FFH<\\\/code> switches A15-A8, <code>10H<\\\/code>\/<code>11H<\\\/code> terminal (88-2SIO), <code>00H<\\\/code>\/<code>01H<\\\/code> terminal (88-SIO).',\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: push RUN down on the panel (or SINGLE STEP for one instruction at a time).',\n  fileT: 'Save and load, tape reader',\n  fileNote: 'Save a block of memory as an <b>Intel HEX<\\\/b> or <b>binary<\\\/b> file, or load <code>.hex<\\\/code>, <code>.bin<\\\/code>, <code>.com<\\\/code> (at the \"From\" address, 0100h for .com) and <code>.asm<\\\/code> files. The <b>tape reader<\\\/b> instead sends a <code>.tap<\\\/code> file to the serial port, byte by byte, as the program reads it: use it to load Altair BASIC with the bootstrap (see the BASIC section). \"Save in browser\" keeps the whole machine state.',\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  tapeBtn: 'Put a tape in the reader\u2026', tapeIn: 'Tape \"{f}\" in the reader ({n} bytes): the running program now reads it from the serial port.', tapePos: 'tape read: {p}%',\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 address {a}).',\n  tooBig: 'File too big for 64 KB of memory.', emptyFile: 'The file has no data.',\n  loadedHex: 'Loaded {n} bytes ({a}-{b}), start at {s}: push RUN down.', loadedAsm: 'Source loaded and assembled.', loadedBin: 'Loaded {n} bytes at {a}: push RUN down.',\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 the switches: {v} = {b}',\n\n  basicT: 'BASIC on the Altair',\n  basic: '<p><b>Palo Alto Tiny BASIC.<\\\/b> The emulator starts with the BASIC Li-Chen Wang published for free in Dr. Dobb\\'s in 1976, written for the Altair with the 88-2SIO serial board and signed with the tongue-in-cheek \"@COPYLEFT ALL WRONGS RESERVED\". It fits in under 2 KB. Pick <b>Palo Alto Tiny BASIC<\\\/b>, press <b>Load and run<\\\/b>, click the terminal and type:<\\\/p>' +\n    '<pre class=\"snip\">PRINT 2+2\\n10 FOR I=1 TO 10\\n20 PRINT I, I*I\\n30 NEXT I\\nRUN<\\\/pre>' +\n    '<ul><li>Commands: <code>LIST<\\\/code>, <code>RUN<\\\/code>, <code>NEW<\\\/code>. Statements: <code>PRINT<\\\/code>, <code>INPUT<\\\/code>, <code>LET<\\\/code>, <code>IF<\\\/code>, <code>GOTO<\\\/code>, <code>GOSUB<\\\/code>\/<code>RETURN<\\\/code>, <code>FOR<\\\/code>\/<code>NEXT<\\\/code>\/<code>STEP<\\\/code>, <code>REM<\\\/code>, <code>STOP<\\\/code>; several statements on one line separated by <code>;<\\\/code>.<\\\/li>' +\n    '<li>Functions: <code>ABS(x)<\\\/code>, <code>RND(x)<\\\/code> (a number from 1 to x), <code>SIZE<\\\/code> (free bytes, about 30,000 here). Integers from -32767 to 32767, variables <code>A<\\\/code>-<code>Z<\\\/code> and the array <code>@(i)<\\\/code>.<\\\/li>' +\n    '<li>Backspace deletes the last character, Esc deletes the line, <b>Ctrl-C<\\\/b> stops the program. You can paste a whole program into the terminal.<\\\/li><\\\/ul>' +\n    '<p><b>Microsoft Altair BASIC.<\\\/b> Gates, Allen and Davidoff\\'s BASIC (4K, 8K, Extended) is still under copyright: in 2025 Bill Gates published a scan of the original listing as a historical document, but with no licence allowing redistribution. That is why it is not included. If you have a tape image (hobbyists preserve them, for example in the ' + A('https:\/\/deramp.com\/downloads\/altair\/software\/papertape_cassette\/', 'deramp.com archive') + '), you can load it as in 1975:<\\\/p>' +\n    '<ol><li>Pick <b>2SIO bootstrap for Altair BASIC<\\\/b> (for 4K or Extended change <code>VERS<\\\/code> in the editor).<\\\/li>' +\n    '<li>Put all switches A15-A8 down: for BASIC 4.x that means terminal and loading on the 2SIO.<\\\/li>' +\n    '<li>Press <b>Load and run<\\\/b>, then <b>Put a tape in the reader\u2026<\\\/b> and choose the <code>.tap<\\\/code> file of version 4.x.<\\\/li>' +\n    '<li>When the tape is read, BASIC asks its start-up questions (like <code>MEMORY SIZE?<\\\/code>): press Enter for the defaults.<\\\/li><\\\/ol>',\n\n  guidaT: 'How to use it: the first program in the manual',\n  guidaIntro: '<p>When you switched on an Altair the memory was empty: programs went in <b>one byte at a time with the switches<\\\/b>. Let\\'s try the first example in the MITS manual, which adds two numbers. The manual writes it in octal; here it is in hex:<\\\/p>' +\n    '<p><code>0000 3A 80 00<\\\/code> LDA 80H \u00b7 <code>0003 47<\\\/code> MOV B,A \u00b7 <code>0004 3A 81 00<\\\/code> LDA 81H \u00b7 <code>0007 80<\\\/code> ADD B \u00b7 <code>0008 32 82 00<\\\/code> STA 82H \u00b7 <code>000B C3 00 00<\\\/code> JMP 0<\\\/p>' +\n    '<p>In octal: 072 200 000 \u00b7 107 \u00b7 072 201 000 \u00b7 200 \u00b7 062 202 000 \u00b7 303 000 000. Each \"set the switches\" button puts the switches in the right position, so you see the binary number; then it\\'s your turn to flip the control.<\\\/p>',\n  steps: [\n    ['The <b>OFF\/ON<\\\/b> switch at the bottom left must be down (ON). If you raise it the RAM is wiped, as on the original.'],\n    ['Raise <b>RESET<\\\/b>: the CPU goes back to address 0000 and LEDs A15-A0 go out.'],\n    ['First byte, <b>3A<\\\/b> (LDA). Set the switches and raise <b>DEPOSIT<\\\/b>: the value shows on LEDs D7-D0.', 0x3A],\n    ['<b>80<\\\/b> (low half of address 0080h): push <b>DEPOSIT NEXT<\\\/b> down, which moves to the next address and writes the byte there.', 0x80],\n    ['<b>00<\\\/b>: DEPOSIT NEXT.', 0x00],\n    ['<b>47<\\\/b> (MOV B,A): DEPOSIT NEXT.', 0x47],\n    ['<b>3A<\\\/b> (LDA): DEPOSIT NEXT.', 0x3A],\n    ['<b>81<\\\/b>: DEPOSIT NEXT.', 0x81],\n    ['<b>00<\\\/b>: DEPOSIT NEXT.', 0x00],\n    ['<b>80<\\\/b> (ADD B): DEPOSIT NEXT.', 0x80],\n    ['<b>32<\\\/b> (STA): DEPOSIT NEXT.', 0x32],\n    ['<b>82<\\\/b>: DEPOSIT NEXT.', 0x82],\n    ['<b>00<\\\/b>: DEPOSIT NEXT.', 0x00],\n    ['<b>C3<\\\/b> (JMP): DEPOSIT NEXT.', 0xC3],\n    ['<b>00<\\\/b>: DEPOSIT NEXT.', 0x00],\n    ['<b>00<\\\/b>: DEPOSIT NEXT. The program is in memory from 0000 to 000D.', 0x00],\n    ['Now the numbers to add. Set all 16 switches to address <b>0080<\\\/b> and raise <b>EXAMINE<\\\/b>.', 0x0080, true],\n    ['First number, for example <b>5<\\\/b>: raise <b>DEPOSIT<\\\/b>.', 0x05],\n    ['Second number, <b>3<\\\/b>: push <b>DEPOSIT NEXT<\\\/b> down (it goes to 0081).', 0x03],\n    ['Raise <b>RESET<\\\/b>, push <b>RUN<\\\/b> down: the WAIT LED goes out and the program runs. After a moment raise <b>STOP<\\\/b>.'],\n    ['To read the result set the switches to <b>0082<\\\/b> and raise <b>EXAMINE<\\\/b>: LEDs D7-D0 show 00001000, that is <b>8<\\\/b>.', 0x0082, true]\n  ],\n  guidaDopo: '<p>Got a byte wrong? Set the address with the 16 switches, raise <b>EXAMINE<\\\/b> and 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 born on the Altair: a bit rotates on LEDs A15-A8 and to \"kill\" it you flip the switch under it up and down just as it passes. For longer programs there is the <b>mini monitor<\\\/b> on the terminal, or BASIC.<\\\/p>',\n  leveT: 'Panel switches and LEDs',\n  leve: '<tr><td>A15-A8<\\\/td><td>High half of the address for EXAMINE; as <b>SENSE SW.<\\\/b> a program reads them with <code>IN 0FFH<\\\/code> (BASIC uses them to know which board is the terminal).<\\\/td><\\\/tr>' +\n    '<tr><td>A7-A0<\\\/td><td>Low half of the address and the byte to write with DEPOSIT.<\\\/td><\\\/tr>' +\n    '<tr><td>STOP \/ RUN<\\\/td><td>Stops the CPU (up) \/ starts it from the current address (down).<\\\/td><\\\/tr>' +\n    '<tr><td>SINGLE STEP<\\\/td><td>With the CPU stopped, runs one instruction (in this emulator always a whole instruction).<\\\/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 A7-A0 at the current address. <b>DEPOSIT NEXT<\\\/b> (down): moves to the next address and writes there.<\\\/td><\\\/tr>' +\n    '<tr><td>RESET \/ CLR<\\\/td><td>Resets the CPU: the program restarts from 0000. <b>CLR<\\\/b> (down) resets the I\/O boards: here it empties the serial port and removes the tape.<\\\/td><\\\/tr>' +\n    '<tr><td>PROTECT<\\\/td><td>Write-protects the 256-byte page at the current address (PROT LED); <b>UNPROTECT<\\\/b> releases it.<\\\/td><\\\/tr>' +\n    '<tr><td>AUX<\\\/td><td>Two spare switches, not connected on the basic Altair.<\\\/td><\\\/tr>' +\n    '<tr><td>OFF \/ ON<\\\/td><td>Switches off (up) and on (down). Switching off wipes the RAM.<\\\/td><\\\/tr>',\n  porteT: 'Emulator I\/O ports',\n  porte: '<tr><td>IN FFh<\\\/td><td>Switches A15-A8 (sense switches).<\\\/td><\\\/tr>' +\n    '<tr><td>10h \/ 11h<\\\/td><td>MITS <b>88-2SIO<\\\/b> (6850 ACIA), port A: 10h status (bit 0 = character received, bit 1 = ready to send) and control, 11h data. Console of Tiny BASIC and the sample programs.<\\\/td><\\\/tr>' +\n    '<tr><td>00h \/ 01h<\\\/td><td>MITS <b>88-SIO<\\\/b>: 00h status with active-low bits (bit 0 = 0 character received, bit 7 = 0 ready to send), 01h data. For software written for the first MITS serial board.<\\\/td><\\\/tr>' +\n    '<tr><td>Tape<\\\/td><td>The reader sends the file to the same serial port: the program reads it as if it came from the terminal, as with an ASR-33 Teletype.<\\\/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 the cursor, <code>07h<\\\/code> bell. At 300 baud you see one character at a time, like on a Teletype.<\\\/td><\\\/tr>',\n  fontiT: 'Sources',\n  fonti: '<li>Wikipedia: ' + A('https:\/\/en.wikipedia.org\/wiki\/Altair_8800', 'Altair 8800') + ', ' + A('https:\/\/en.wikipedia.org\/wiki\/Micro_Instrumentation_and_Telemetry_Systems', 'MITS') + ', ' + A('https:\/\/en.wikipedia.org\/wiki\/Altair_BASIC', 'Altair BASIC') + ', ' + A('https:\/\/en.wikipedia.org\/wiki\/S-100_bus', 'S-100 bus') + '<\\\/li>' +\n    '<li>' + A('https:\/\/ubuntourist.codeberg.page\/Altair-8800\/part-3.html', 'Altair 8800 operator\\'s manual') + ' (Kevin Cole\\'s transcription) and ' + A('https:\/\/www.oldcomputers.net\/altair-8800.html', 'oldcomputers.net') + '<\\\/li>' +\n    '<li>' + A('https:\/\/www.i-programmer.info\/news\/82-heritage\/17946-bill-gates-shares-the-code-that-launched-microsoft.html', 'I Programmer') + ' on the BASIC source Bill Gates published in 2025<\\\/li>' +\n    '<li>Loaders and sense switches: ' + A('https:\/\/archive.decromancer.ca\/deramp.com\/downloads\/mfe_archive\/010-S100%20Computers%20and%20Boards\/00-MITS\/40-Software\/BASIC\/Loaders\/Boot%20Loaders.txt', 'Boot Loaders (deramp archive)') + '<\\\/li>' +\n    '<li>Palo Alto Tiny BASIC: ' + A('https:\/\/en.wikipedia.org\/wiki\/Li-Chen_Wang', 'Li-Chen Wang') + ', ' + A('https:\/\/github.com\/Geddd\/PATB4ASM80', 'PATB4ASM80 source') + '<\\\/li>'\n}\n};\n})(window.RFAltair = window.RFAltair || {});\n\n\/* RetroFixer \u00b7 Altair 8800 \u00b7 interfaccia della pagina *\/\n(function (RF) {\n'use strict';\nvar root = document.querySelector('.rfal');\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 = $('rfalFoto' + n), cap = $('rfalFoto' + 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 = $('rfalCards');\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.Altair();\nvar term = new RF.Terminal($('rfalTerm'), 80, 24);\nm.onTx = function (c) { term.put(c); };\nvar panel = new RF.Panel($('rfalPanel'), 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 = $('rfalPDesc'); 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 = $('rfalProg');\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() { $('rfalPDesc').innerHTML = prog().desc[L]; delete $('rfalPDesc').dataset.old; }\nsel.addEventListener('change', function () { showDesc(); $('rfalSrc').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.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  $('rfalFrom').value = H(r.min, 4); $('rfalTo').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 = $('rfalSrc').value, r, out = $('rfalAsmOut');\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$('rfalLoadRun').addEventListener('click', function () { $('rfalSrc').value = prog().src; saveSrc(); var r = assembleEditor(true); if (r && prog().terminal) focusTerm(); });\n$('rfalLoadOnly').addEventListener('click', function () { $('rfalSrc').value = prog().src; saveSrc(); assembleEditor(false); flash(tr('loaded')); });\n$('rfalAsmRun').addEventListener('click', function () { assembleEditor(true); });\n$('rfalAsm').addEventListener('click', function () { if (assembleEditor(false)) flash(tr('loaded')); });\n$('rfalAsmSave').addEventListener('click', function () { download('programma.asm', new Blob([$('rfalSrc').value], { type: 'text\/plain' })); });\nfunction saveSrc() { try { localStorage.setItem('rfalSrc', $('rfalSrc').value); } catch (e) {} }\n$('rfalSrc').addEventListener('input', saveSrc);\n$('rfalSrc').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$('rfalSpeed').addEventListener('change', function () { m.speed = +this.value; });\n$('rfalBaud').addEventListener('change', function () { m.baud = +this.value; });\n\n\/\/ ---- tastiera del terminale ----------------------------------------------------------------------\nvar kbd = $('rfalKbd'), 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 ($('rfalCaps').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$('rfalTermClr').addEventListener('click', function () { term.clear(); });\n$('rfalTermCopy').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($('rfalFrom').value, 16), b = parseInt($('rfalTo').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 = $('rfalFileMsg'); e.textContent = s; e.className = 'msg' + (err ? ' err' : ''); }\n$('rfalSaveHex').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('altair-' + 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$('rfalSaveBin').addEventListener('click', function () {\n  var r = range(); if (!r) return;\n  download('altair-' + 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$('rfalOpen').addEventListener('click', function () { $('rfalFile').click(); });\n$('rfalFile').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        $('rfalSrc').value = rd.result; saveSrc(); $('rfalSrc').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($('rfalFrom').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\/\/ lettore di nastro perforato: il file entra nella seriale 88-2SIO (e 88-SIO) un byte alla volta, quando il programma lo legge\n$('rfalTape').addEventListener('click', function () { $('rfalTapeFile').click(); });\n$('rfalTapeFile').addEventListener('change', function () {\n  var f = this.files[0]; this.value = ''; if (!f) return;\n  if (f.size > 65536 * 2) { fileMsg(tr('tooBig'), true); return; }\n  var rd = new FileReader();\n  rd.onload = function () {\n    if (!m.power) { m.setPower(true); panel.syncSwitches(); afterCmd('powerOn'); }\n    m.loadTape(new Uint8Array(rd.result));\n    fileMsg(tr('tapeIn', { n: m.tape.length, f: f.name }));\n  };\n  rd.readAsArrayBuffer(f);\n});\nvar tapeEl = $('rfalTapeState'), tapeShown = -1;\nfunction showTape() {\n  var left = m.tapeLeft(), v = m.tape ? Math.round(100 * (m.tape.length - left) \/ m.tape.length) : -1;\n  if (v === tapeShown) return;\n  tapeShown = v;\n  tapeEl.textContent = v < 0 ? '' : tr('tapePos', { p: v });\n}\n\n\/\/ stato completo nel browser (memoria, registri, interruttori)\n$('rfalSnapSave').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, prot: Array.from(c.prot), cur: current, src: $('rfalSrc').value, t: Date.now() };\n  try { localStorage.setItem('rfalSnap', JSON.stringify(st)); fileMsg(tr('snapSaved')); }\n  catch (e) { fileMsg(tr('snapErr'), true); }\n});\n$('rfalSnapLoad').addEventListener('click', function () {\n  var st; try { st = JSON.parse(localStorage.getItem('rfalSnap') || '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;\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; current = st.cur; panel.syncSwitches();\n  if (st.src) { $('rfalSrc').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 = $('rfalSteps');\nT.steps.forEach(function (st) {\n  var li = document.createElement('li'); li.innerHTML = st[0];\n  if (st[1] !== undefined) {\n    var full = st[1] > 255 || st[2];   \/\/ st[2] = true: imposta tutte le 16 levette (un indirizzo)\n    var b = document.createElement('button'); b.type = 'button'; b.className = 'btn sm setsw';\n    b.innerHTML = tr('setSw', { v: H(st[1], full ? 4 : 2), b: bin(st[1], full ? 16 : 8) });\n    b.addEventListener('click', function () {\n      m.switches = full ? st[1] : (m.switches & 0xFF00) | st[1]; panel.syncSwitches();\n      $('rfalPanel').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 = $('rfalRegs'), flagsEl = $('rfalFlags'), disEl = $('rfalDis'), cycEl = $('rfalCyc');\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  $('rfalTermMsg').hidden = document.activeElement === kbd || !m.power;\n  if (dbgDirty || (m.running && now - lastDbg > 120)) { dbgDirty = false; lastDbg = now; updateDbg(now); showTape(); }\n  requestAnimationFrame(frame);\n}\n\n\/\/ ---- avvio ---------------------------------------------------------------------------------------\nvar saved = null; try { saved = localStorage.getItem('rfalSrc'); } catch (e) {}\nsel.value = '0'; showDesc();\n$('rfalSrc').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.RFAltair = window.RFAltair || {});\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>Altair 8800 \u25b6 30012009600 \u25b6 \u2b07 \u2b07 \u2b07 \ud83d\udcc2 \ud83d\udcfc<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-11605","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/pages\/11605","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=11605"}],"version-history":[{"count":3,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/pages\/11605\/revisions"}],"predecessor-version":[{"id":11608,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/pages\/11605\/revisions\/11608"}],"wp:attachment":[{"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/media?parent=11605"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/categories?post=11605"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/tags?post=11605"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}