├── media
├── switch.mp3
├── toggle.mp3
└── beepbeep.mp3
├── design
├── led-on.afdesign
├── panel.afdesign
├── led-off.afdesign
├── switch-up.afdesign
├── switch-down.afdesign
├── switch-mid.afdesign
├── altair-8800-panel.afdesign
├── led-on.svg
├── led-off.svg
├── switch-mid.svg
├── switch-up.svg
├── switch-down.svg
└── panel.svg
├── screenshots
├── sim-debug.png
├── sim-panel.png
└── sim-mobile.png
├── .gitignore
├── doc
├── altair_8800_operator_manual.pdf
└── 8080_instructions.txt
├── asm
├── adder.asm
└── tiny_programs.md
├── README.md
├── css
└── style.css
├── js
├── l10n.js
├── sim8800.js
└── panel.js
├── text-mode.html
├── LICENSE
└── index.html
/media/switch.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wixette/8800-simulator/HEAD/media/switch.mp3
--------------------------------------------------------------------------------
/media/toggle.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wixette/8800-simulator/HEAD/media/toggle.mp3
--------------------------------------------------------------------------------
/media/beepbeep.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wixette/8800-simulator/HEAD/media/beepbeep.mp3
--------------------------------------------------------------------------------
/design/led-on.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wixette/8800-simulator/HEAD/design/led-on.afdesign
--------------------------------------------------------------------------------
/design/panel.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wixette/8800-simulator/HEAD/design/panel.afdesign
--------------------------------------------------------------------------------
/design/led-off.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wixette/8800-simulator/HEAD/design/led-off.afdesign
--------------------------------------------------------------------------------
/design/switch-up.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wixette/8800-simulator/HEAD/design/switch-up.afdesign
--------------------------------------------------------------------------------
/screenshots/sim-debug.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wixette/8800-simulator/HEAD/screenshots/sim-debug.png
--------------------------------------------------------------------------------
/screenshots/sim-panel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wixette/8800-simulator/HEAD/screenshots/sim-panel.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | __pycache__
3 | *~
4 | .DS_Store
5 | build/*
6 | dist/*
7 | .coverage
8 | .tern-port
9 |
--------------------------------------------------------------------------------
/design/switch-down.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wixette/8800-simulator/HEAD/design/switch-down.afdesign
--------------------------------------------------------------------------------
/design/switch-mid.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wixette/8800-simulator/HEAD/design/switch-mid.afdesign
--------------------------------------------------------------------------------
/screenshots/sim-mobile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wixette/8800-simulator/HEAD/screenshots/sim-mobile.png
--------------------------------------------------------------------------------
/design/altair-8800-panel.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wixette/8800-simulator/HEAD/design/altair-8800-panel.afdesign
--------------------------------------------------------------------------------
/doc/altair_8800_operator_manual.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wixette/8800-simulator/HEAD/doc/altair_8800_operator_manual.pdf
--------------------------------------------------------------------------------
/asm/adder.asm:
--------------------------------------------------------------------------------
1 | ;;;--------------------------------------------------------------------------------
2 | ;;; Simple 8080 addition program.
3 | ;;;--------------------------------------------------------------------------------
4 | LDA 0080H ; 00 111 010 10 000 000 00 000 000
5 | MOV B,A ; 01 000 111
6 | LDA 0081H ; 00 111 010 10 000 001 00 000 000
7 | ADD B ; 10 000 000
8 | STA 0082H ; 00 110 010 10 000 010 00 000 000
9 | JMP 0000H ; 11 000 011 00 000 000 00 000 000
10 | ;;;--------------------------------------------------------------------------------
11 |
--------------------------------------------------------------------------------
/asm/tiny_programs.md:
--------------------------------------------------------------------------------
1 | # Hex dump of tiny 8080 programs.
2 |
3 | Each program can be loaded into the simulator via the
4 | loadDataAsHexString() interface or the "LOAD DATA" input box.
5 |
6 | The HEX bytes in the strings are the binary sequence of 8080 machine
7 | instructions.
8 |
9 | ## Simple adder
10 |
11 | Adds the two values stored at 0080H and 0081H, then writes the result
12 | to 0082H.
13 |
14 | ```
15 | 3a 80 00 47 3a 81 00 80 32 82 00 c3 00 00
16 | ```
17 |
18 | ## Pattern shift
19 |
20 | Keep right shifting the value of register A.
21 |
22 | ```
23 | 3e 8c d3 ff 0f c3 02 00
24 | ```
25 |
26 | ## I/O test
27 |
28 | Echos IN to OUT. Reads from SENSE SW. switches, then outputs the
29 | value.
30 |
31 | ```
32 | db ff d3 ff c3 00 00
33 | ```
34 |
35 | ## More I/O test
36 |
37 | ```
38 | 0e ff 16 01 7a fe 80 ca 0f 00 fe 01 c2 12 00 79 2f 4f 79 fe 00 c2 1e 00 7a 17 57 c3 21 00 7a 1f 57 7a 2f d3 ff db ff 3c 06 02 1e ff 1d c2 2c 00 05 c2 2a 00 3d c2 28 00 c3 04 00
39 | ```
40 |
--------------------------------------------------------------------------------
/design/led-on.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/design/led-off.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/design/switch-mid.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/design/switch-up.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/design/switch-down.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Altair 8800 simulator.
2 |
3 | A JavaScript simulator to demonstrate the front panel operations of Altair 8800.
4 |
5 | ## Usage
6 |
7 | Simply open index.html in browser, or copy the entire dir to your web server's root dir.
8 |
9 | The simulator UI supports English and Chinese for now. In a desktop browser, you may use mouse to toggle or click the switches on the panel directly.
10 |
11 | 
12 |
13 | There is a Debugger tab where you can check the internal status of the simulated 8080 CPU, or the contents of the simulator's memory.
14 |
15 | 
16 |
17 | The simulator works fine with modern mobile browsers, except that it is a bit challenging to touch a single switch on the panel on a mobile screen. Although, the helper switch buttons below the panel can be used as an alternative solution.
18 |
19 | 
20 |
21 | ## A Quick Tutorial
22 |
23 | With a running Altair 8800 simulator, how to input and run the following program to calculate 1 + 2 = 3:
24 |
25 | ```
26 | LDA 0080H ; 00 111 010
27 | ; 10 000 000
28 | ; 00 000 000
29 | MOV B,A ; 01 000 111
30 | LDA 0081H ; 00 111 010
31 | ; 10 000 001
32 | ; 00 000 000
33 | ADD B ; 10 000 000
34 | STA 0082H ; 00 110 010
35 | ; 10 000 010
36 | ; 00 000 000
37 | JMP 0000H ; 11 000 011
38 | ; 00 000 000
39 | ; 00 000 000
40 | ```
41 |
42 | 1. Turn on Altair 8800 by clicking OFF/ON switch.
43 | 1. Set switches A7-A0 to 00 111 010 (up for 1, down for 0).
44 | 1. Click "DEPOSIT".
45 | 1. Set switches A7-A0 to 10 000 000.
46 | 1. Click "DEPOSIT NEXT".
47 | 1. Repeat step 4-5 to input the following bytes one by one: 00 000 000, 01 000 111, 00 111 010, 10 000 001, 00 000 000, 10 000 000, 00 110 010, 10 000 010, 00 000 000, 11 000 011, 00 000 000, 00 000 000.
48 | 1. Set switches A7-A0 to 10 000 000.
49 | 1. Click "EXAMINE".
50 | 1. Set switches A7-A0 to 00 000 001 (the first number to be added, or 1 in decimal).
51 | 1. Click "DEPOSIT".
52 | 1. Set switches A7-A0 to 00 000 010 (the second number to be added, or 2 in decimal).
53 | 1. Click "DEPOSIT NEXT".
54 | 1. Click "RESET".
55 | 1. Click "RUN" and wait for a few seconds.
56 | 1. Click "STOP".
57 | 1. Set switches A7-A0 to 10 000 010 (the address that holds the sum).
58 | 1. Click "EXAMINE".
59 | 1. The LEDs D7-D0 show the result 00 000 011 (3 in decimal).
60 | 1. Turn off Altair 8800.
61 |
62 | ## References
63 |
64 | - [Wikipedia: Altair 8800](https://en.wikipedia.org/wiki/Altair_8800)
65 | - [Wikipedia: Intel 8080 CPU](https://en.wikipedia.org/wiki/Intel_8080)
66 | - [Intel 8080 instruction set](http://www.classiccmp.org/dunfield/r/8080.txt)
67 | - [Original Altair 8800 manuals](https://altairclone.com/altair_manuals.html)
68 | - [Altair 8800 Operator's Manual](https://altairclone.com/downloads/manuals/Altair%208800%20Operator's%20Manual.pdf)
69 | - [Intel 8080 Assembly Language Programming Manual](http://www.classiccmp.org/dunfield/r/8080asm.pdf)
70 | - [Another Altair 8800 simulator](https://s2js.com/altair/)
71 |
72 | ## Acknowledgements
73 |
74 | I use https://github.com/maly/8080js to execute Intel 8080 instruments.
75 |
76 | The Quick Tutoral in the simulator UI refers to an example program got from the original [Altair 8800 Operator's Manual](https://altairclone.com/downloads/manuals/Altair%208800%20Operator's%20Manual.pdf).
77 |
78 | The interaction design took [another Altair 8800 simulator](https://s2js.com/altair/) as a reference.
79 |
--------------------------------------------------------------------------------
/doc/8080_instructions.txt:
--------------------------------------------------------------------------------
1 | ;; http://www.classiccmp.org/dunfield/r/8080.txt
2 |
3 | 8080 instruction encoding:
4 |
5 | Conventions in instruction source:
6 | D = Destination register (8 bit)
7 | S = Source register (8 bit)
8 | RP = Register pair (16 bit)
9 | # = 8 or 16 bit immediate operand
10 | a = 16 bit Memory address
11 | p = 8 bit port address
12 | ccc = Conditional
13 |
14 | Conventions in instruction encoding:
15 | db = Data byte (8 bit)
16 | lb = Low byte of 16 bit value
17 | hb = High byte of 16 bit value
18 | pa = Port address (8 bit)
19 |
20 | Dest and Source reg fields:
21 | 111=A (Accumulator)
22 | 000=B
23 | 001=C
24 | 010=D
25 | 011=E
26 | 100=H
27 | 101=L
28 | 110=M (Memory reference through address in H:L)
29 |
30 | Register pair 'RP' fields:
31 | 00=BC (B:C as 16 bit register)
32 | 01=DE (D:E as 16 bit register)
33 | 10=HL (H:L as 16 bit register)
34 | 11=SP (Stack pointer, refers to PSW (FLAGS:A) for PUSH/POP)
35 |
36 | Condition code 'CCC' fields: (FLAGS: S Z x A x P x C)
37 | 000=NZ ('Z'ero flag not set)
38 | 001=Z ('Z'ero flag set)
39 | 010=NC ('C'arry flag not set)
40 | 011=C ('C'arry flag set)
41 | 100=PO ('P'arity flag not set - ODD)
42 | 101=PE ('P'arity flag set - EVEN)
43 | 110=P ('S'ign flag not set - POSITIVE)
44 | 111=M ('S'ign flag set - MINUS)
45 |
46 | Inst Encoding Flags Description
47 | ----------------------------------------------------------------------
48 | MOV D,S 01DDDSSS - Move register to register
49 | MVI D,# 00DDD110 db - Move immediate to register
50 | LXI RP,# 00RP0001 lb hb - Load register pair immediate
51 | LDA a 00111010 lb hb - Load A from memory
52 | STA a 00110010 lb hb - Store A to memory
53 | LHLD a 00101010 lb hb - Load H:L from memory
54 | SHLD a 00100010 lb hb - Store H:L to memory
55 | LDAX RP 00RP1010 *1 - Load indirect through BC or DE
56 | STAX RP 00RP0010 *1 - Store indirect through BC or DE
57 | XCHG 11101011 - Exchange DE and HL content
58 | ADD S 10000SSS ZSPCA Add register to A
59 | ADI # 11000110 db ZSCPA Add immediate to A
60 | ADC S 10001SSS ZSCPA Add register to A with carry
61 | ACI # 11001110 db ZSCPA Add immediate to A with carry
62 | SUB S 10010SSS ZSCPA Subtract register from A
63 | SUI # 11010110 db ZSCPA Subtract immediate from A
64 | SBB S 10011SSS ZSCPA Subtract register from A with borrow
65 | SBI # 11011110 db ZSCPA Subtract immediate from A with borrow
66 | INR D 00DDD100 ZSPA Increment register
67 | DCR D 00DDD101 ZSPA Decrement register
68 | INX RP 00RP0011 - Increment register pair
69 | DCX RP 00RP1011 - Decrement register pair
70 | DAD RP 00RP1001 C Add register pair to HL (16 bit add)
71 | DAA 00100111 ZSPCA Decimal Adjust accumulator
72 | ANA S 10100SSS ZSCPA AND register with A
73 | ANI # 11100110 db ZSPCA AND immediate with A
74 | ORA S 10110SSS ZSPCA OR register with A
75 | ORI # 11110110 ZSPCA OR immediate with A
76 | XRA S 10101SSS ZSPCA ExclusiveOR register with A
77 | XRI # 11101110 db ZSPCA ExclusiveOR immediate with A
78 | CMP S 10111SSS ZSPCA Compare register with A
79 | CPI # 11111110 ZSPCA Compare immediate with A
80 | RLC 00000111 C Rotate A left
81 | RRC 00001111 C Rotate A right
82 | RAL 00010111 C Rotate A left through carry
83 | RAR 00011111 C Rotate A right through carry
84 | CMA 00101111 - Compliment A
85 | CMC 00111111 C Compliment Carry flag
86 | STC 00110111 C Set Carry flag
87 | JMP a 11000011 lb hb - Unconditional jump
88 | Jccc a 11CCC010 lb hb - Conditional jump
89 | CALL a 11001101 lb hb - Unconditional subroutine call
90 | Cccc a 11CCC100 lb hb - Conditional subroutine call
91 | RET 11001001 - Unconditional return from subroutine
92 | Rccc 11CCC000 - Conditional return from subroutine
93 | RST n 11NNN111 - Restart (Call n*8)
94 | PCHL 11101001 - Jump to address in H:L
95 | PUSH RP 11RP0101 *2 - Push register pair on the stack
96 | POP RP 11RP0001 *2 *2 Pop register pair from the stack
97 | XTHL 11100011 - Swap H:L with top word on stack
98 | SPHL 11111001 - Set SP to content of H:L
99 | IN p 11011011 pa - Read input port into A
100 | OUT p 11010011 pa - Write A to output port
101 | EI 11111011 - Enable interrupts
102 | DI 11110011 - Disable interrupts
103 | HLT 01110110 - Halt processor
104 | NOP 00000000 - No operation
105 |
106 | *1 = Only RP=00(BC) and 01(DE) are allowed for LDAX/STAX
107 |
108 | *2 = RP=11 refers to PSW for PUSH/POP (cannot push/pop SP).
109 | When PSW is POP'd, ALL flags are affected.
110 |
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #f0f0f0;
3 | color: #eee;
4 | font-family: 'Consolas', 'Monaco', 'Menlo', 'Ubuntu Mono', 'Liberation Mono', 'DejaVu Sans Mono', 'Courier New', monospace;
5 | font-size: 16px;
6 | margin: 15px;
7 | padding: 0;
8 | }
9 |
10 | input {
11 | font-family: 'Consolas', 'Monaco', 'Menlo', 'Ubuntu Mono', 'Liberation Mono', 'DejaVu Sans Mono', 'Courier New', monospace;
12 | }
13 |
14 | div, img, pre {
15 | border: 0;
16 | margin: 0;
17 | padding: 0;
18 | }
19 |
20 | a:link {
21 | color: #6cf;
22 | }
23 | a:visited {
24 | color: #6cf;
25 | }
26 | a:hover {
27 | color: #6cf;
28 | }
29 | a:active {
30 | color: #6cf;
31 | }
32 |
33 | .h-box {
34 | display: flex;
35 | flex-direction: row;
36 | flex-wrap: wrap;
37 | }
38 |
39 | .v-box {
40 | display: flex;
41 | flex-direction: column;
42 | flex-wrap: nowrap;
43 | }
44 |
45 | .justify-start {
46 | justify-content: start;
47 | }
48 |
49 | .justify-end {
50 | justify-content: end;
51 | }
52 |
53 | .justify-center {
54 | justify-content: center;
55 | }
56 |
57 | .justify-space-between {
58 | justify-content: space-between;
59 | }
60 |
61 | .justify-space-around {
62 | justify-content: space-around;
63 | }
64 |
65 | .justify-space-evenly {
66 | justify-content: space-evenly;
67 | }
68 |
69 | .align-items-center {
70 | align-items: center;
71 | }
72 |
73 | .m-8 {
74 | margin: 8px;
75 | }
76 |
77 | .mt-8 {
78 | margin-top: 8px;
79 | }
80 |
81 | .ml-8 {
82 | margin-left: 8px;
83 | }
84 |
85 | .mr-8 {
86 | margin-right: 8px;
87 | }
88 |
89 | .mb-8 {
90 | margin-bottom: 8px;
91 | }
92 |
93 | .flex-grow-1 {
94 | flex-grow: 1;
95 | }
96 |
97 | .flex-grow-2 {
98 | flex-grow: 2;
99 | }
100 |
101 | .flex-grow-4 {
102 | flex-grow: 4;
103 | }
104 |
105 | main {
106 | background-color: #222;
107 | border-radius: 10px;
108 | margin: 0 auto;
109 | max-width: 1200px;
110 | padding: 10px;
111 | }
112 |
113 | .header-bar {
114 | align-items: center;
115 | color: #eee;
116 | display: flex;
117 | flex-direction: row;
118 | flex-wrap: wrap;
119 | justify-content: center;
120 | margin: 10px 0;
121 | min-height: 64px;
122 | }
123 |
124 | #header-title {
125 | font-size: 18px;
126 | font-weight: bold;
127 | }
128 |
129 | #switch-locale {
130 | background-color: #369;
131 | border-radius: 10px;
132 | color: #6cf;
133 | cursor: pointer;
134 | font-size: 12px;
135 | margin: 20px;
136 | padding: 5px 10px;
137 | user-select: none;
138 | -moz-user-select: none;
139 | -ms-user-select: none;
140 | -webkit-user-select: none;
141 | }
142 |
143 | .subheader-bar {
144 | background-color: #666;
145 | border-radius: 10px;
146 | color: #eee;
147 | display: flex;
148 | flex-direction: row;
149 | flex-wrap: nowrap;
150 | justify-content: center;
151 | line-height: 3em;
152 | margin: 10px 0;
153 | padding: 0;
154 | }
155 |
156 | nav {
157 | color: #222;
158 | display: flex;
159 | flex-direction: row;
160 | flex-wrap: nowrap;
161 | justify-content: space-around;
162 | min-height: 40px;
163 | padding: 0;
164 | }
165 |
166 | .nav-item {
167 | background-color: #eee;
168 | border-radius: 30px;
169 | cursor: pointer;
170 | flex-grow: 1;
171 | line-height: 40px;
172 | margin: 0 5px;
173 | padding: 0 5px;
174 | text-align: center;
175 | user-select: none;
176 | -moz-user-select: none;
177 | -ms-user-select: none;
178 | -webkit-user-select: none;
179 | }
180 |
181 | nav .selected {
182 | background-color: #f90;
183 | cursor: auto;
184 | font-weight: bold;
185 | }
186 |
187 | #panel {
188 | user-select: none;
189 | -moz-user-select: none;
190 | -ms-user-select: none;
191 | -webkit-user-select: none;
192 | margin: 10px 0;
193 | }
194 |
195 | footer {
196 | color: #999;
197 | font-size: 14px;
198 | margin: 40px auto 0 auto;
199 | max-width: 960px;
200 | text-align: center;
201 | }
202 |
203 | .comments {
204 | color: #999;
205 | font-size: 14px;
206 | line-height: 1.5em;
207 | padding: 0 10px;
208 | }
209 |
210 | #debug-data-input {
211 | background-color: #eee;
212 | border-radius: 10px;
213 | line-height: 24px;
214 | padding: 5px 10px;
215 | }
216 |
217 | #debug-load-data {
218 | border: #ccc 1px solid;
219 | }
220 |
221 | #cpu-dump {
222 | background-color: #ccc;
223 | border-radius: 10px;
224 | color: #222;
225 | font-size: 14px;
226 | line-height: 24px;
227 | min-height: 24px;
228 | overflow-x: hidden;
229 | padding: 5px 10px;
230 | }
231 |
232 | #mem-dump {
233 | background-color: #ccc;
234 | border-radius: 10px;
235 | color: #222;
236 | font-size: 14px;
237 | line-height: 24px;
238 | min-height: 24px;
239 | overflow-x: hidden;
240 | padding: 5px 10px;
241 | }
242 |
243 | .tutorial {
244 | background-color: #ccc;
245 | border-radius: 10px;
246 | color: #222;
247 | font-size: 14px;
248 | line-height: 1.5em;
249 | min-height: 20px;
250 | overflow-x: hidden;
251 | padding: 5px 10px;
252 | }
253 |
254 | .button {
255 | background-color: #eee;
256 | border: 1px solid #bbb;
257 | border-radius: 10px;
258 | color: #222;
259 | cursor: pointer;
260 | font-size: 14px;
261 | line-height: 2em;
262 | padding: 2px 10px;
263 | text-align: center;
264 | user-select: none;
265 | -moz-user-select: none;
266 | -ms-user-select: none;
267 | -webkit-user-select: none;
268 | }
269 |
270 | #tab-sim {
271 | margin: 10px 0;
272 | }
273 |
274 | #tab-debug {
275 | display: none;
276 | margin: 10px 0;
277 | }
278 |
279 | #tab-ref {
280 | display: none;
281 | margin: 10px 0;
282 | }
283 |
--------------------------------------------------------------------------------
/js/l10n.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2020 wixette@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @fileoverview Localization utilities and message translations.
17 | */
18 |
19 | /**
20 | * Simple namespace.
21 | * @type {Object}
22 | */
23 | l10n = {};
24 |
25 | /**
26 | * Pre-defined locales.
27 | * @type {Array}
28 | */
29 | l10n.LOCALES = [
30 | 'en',
31 | 'zh',
32 | ];
33 |
34 | /**
35 | * Localized messages.
36 | */
37 | l10n.MESSAGES = {
38 | 'title': {
39 | 'en': 'Sim-8800: Altair 8800 Simulator',
40 | 'zh': 'Sim-8800: Altair 8800 模拟器',
41 | },
42 |
43 | 'header-title': {
44 | 'en': 'Altair 8800 Simulator',
45 | 'zh': 'Altair 8800 模拟器',
46 | },
47 |
48 | 'nav-sim': {
49 | 'en': 'Simulator',
50 | 'zh': '模拟器',
51 | },
52 |
53 | 'nav-debug': {
54 | 'en': 'Debugger',
55 | 'zh': '调试器',
56 | },
57 |
58 | 'nav-ref': {
59 | 'en': 'Tutorial',
60 | 'zh': '使用手册',
61 | },
62 |
63 | 'switchboard-helper': {
64 | 'en': 'Switch Board Helper',
65 | 'zh': '辅助开关面板',
66 | },
67 |
68 | 'back-home': {
69 | 'en': 'Source Code',
70 | 'zh': '源代码',
71 | },
72 |
73 | 'source-code': {
74 | 'en': 'Source code',
75 | 'zh': '源代码',
76 | },
77 |
78 | 'debug-load-data-title': {
79 | 'en': 'Load Data to Addr #0',
80 | 'zh': '从地址0开始加载数据',
81 | },
82 |
83 | 'debug-load-data': {
84 | 'en': 'Load Data',
85 | 'zh': '加载数据',
86 | },
87 |
88 | 'debug-data-sample': {
89 | 'en': 'Bytes in HEX string, such as \'c3 00 00\'',
90 | 'zh': '十六进制字节序列,如 c3 00 00',
91 | },
92 |
93 | 'debug-cpu-dump-title': {
94 | 'en': '8080 CPU Status Dump',
95 | 'zh': '8080 CPU 的状态信息',
96 | },
97 |
98 | 'debug-mem-dump-title': {
99 | 'en': 'Memory Dump',
100 | 'zh': '内存信息',
101 | },
102 |
103 | 'tutorial-title': {
104 | 'en': 'Quick Tutorial',
105 | 'zh': '快速教程',
106 | },
107 |
108 | 'tutorial-desc': {
109 | 'en': 'How to input and run the following program to calculate 1 + 2 = 3:',
110 | 'zh': '如何输入并运行以下加法程序,并计算 1 + 2 = 3:',
111 | },
112 |
113 | 'tutorial-1': {
114 | 'en': 'Turn on Altair 8800 by clicking OFF/ON switch.',
115 | 'zh': '点击 OFF/ON 开关,打开 Altair 8800',
116 | },
117 |
118 | 'tutorial-2': {
119 | 'en': 'Set switches A7-A0 to 00 111 010 (up for 1, down for 0).',
120 | 'zh': '将开关 A7-A0 依次设置为 00 111 010 (开关朝上为 1,开关朝下为 0)',
121 | },
122 |
123 | 'tutorial-3': {
124 | 'en': 'Click "DEPOSIT".',
125 | 'zh': '点击 DEPOSIT',
126 | },
127 |
128 | 'tutorial-4': {
129 | 'en': 'Set switches A7-A0 to 10 000 000.',
130 | 'zh': '将开关 A7-A0 依次设置为 10 000 000',
131 | },
132 |
133 | 'tutorial-5': {
134 | 'en': 'Click "DEPOSIT NEXT".',
135 | 'zh': '点击 DEPOSIT NEXT',
136 | },
137 |
138 | 'tutorial-6': {
139 | 'en': 'Repeat step 4-5 to input the following bytes one by one: 00 000 000, 01 000 111, 00 111 010, 10 000 001, 00 000 000, 10 000 000, 00 110 010, 10 000 010, 00 000 000, 11 000 011, 00 000 000, 00 000 000.',
140 | 'zh': '重复步骤 4 到步骤 5,逐个输入以下字节:00 000 000, 01 000 111, 00 111 010, 10 000 001, 00 000 000, 10 000 000, 00 110 010, 10 000 010, 00 000 000, 11 000 011, 00 000 000, 00 000 000',
141 | },
142 |
143 | 'tutorial-7': {
144 | 'en': 'Set switches A7-A0 to 10 000 000.',
145 | 'zh': '将开关 A7-A0 依次设置为 10 000 000',
146 | },
147 |
148 | 'tutorial-8': {
149 | 'en': 'Click "EXAMINE".',
150 | 'zh': '点击 EXAMINE',
151 | },
152 |
153 | 'tutorial-9': {
154 | 'en': 'Set switches A7-A0 to 00 000 001 (the first number to be added, or 1 in decimal).',
155 | 'zh': '将开关 A7-A0 依次设置为 00 000 001(即第一个加数的值,也就是十进制的 1)',
156 | },
157 |
158 | 'tutorial-10': {
159 | 'en': 'Click "DEPOSIT".',
160 | 'zh': '点击 DEPOSIT',
161 | },
162 |
163 | 'tutorial-11': {
164 | 'en': 'Set switches A7-A0 to 00 000 010 (the second number to be added, or 2 in decimal).',
165 | 'zh': '将开关 A7-A0 依次设置为 00 000 010(即第二个加数的值,也就是十进制的 2)',
166 | },
167 |
168 | 'tutorial-12': {
169 | 'en': 'Click "DEPOSIT NEXT".',
170 | 'zh': '点击 DEPOSIT NEXT',
171 | },
172 |
173 | 'tutorial-13': {
174 | 'en': 'Click "RESET".',
175 | 'zh': '点击 RESET',
176 | },
177 |
178 | 'tutorial-14': {
179 | 'en': 'Click "RUN" and wait for a few seconds.',
180 | 'zh': '点击 RUN 并等待几秒钟',
181 | },
182 |
183 | 'tutorial-15': {
184 | 'en': 'Click "STOP".',
185 | 'zh': '点击 STOP',
186 | },
187 |
188 | 'tutorial-16': {
189 | 'en': 'Set switches A7-A0 to 10 000 010 (the address that holds the sum).',
190 | 'zh': '将开关 A7-A0 依次设置为 10 000 010(即存储计算结果的地址)',
191 | },
192 |
193 | 'tutorial-17': {
194 | 'en': 'Click "EXAMINE".',
195 | 'zh': '点击 EXAMINE',
196 | },
197 |
198 | 'tutorial-18': {
199 | 'en': 'The LEDs D7-D0 show the result 00 000 011 (3 in decimal).',
200 | 'zh': 'LED 灯 D7-D0 显示出计算结果 00 000 011(即十进制的 3)',
201 | },
202 |
203 | 'tutorial-19': {
204 | 'en': 'Turn off Altair 8800.',
205 | 'zh': '关闭 Altair 8800',
206 | },
207 |
208 | 'reference-title': {
209 | 'en': 'References',
210 | 'zh': '参考资料',
211 | },
212 | };
213 |
214 | /**
215 | * Current locale index.
216 | * @type {number}
217 | */
218 | l10n.current = 0;
219 |
220 | /**
221 | * Local storage key.
222 | */
223 | l10n.localStorageKey = 'sim8800locale';
224 |
225 | /**
226 | * Switches to the next locale.
227 | */
228 | l10n.nextLocale = function() {
229 | l10n.current++;
230 | l10n.current = l10n.current % l10n.LOCALES.length;
231 | l10n.updateMessages();
232 | localStorage.setItem(l10n.localStorageKey, l10n.current);
233 | };
234 |
235 | /**
236 | * Restores the last locale from local storage.
237 | */
238 | l10n.restoreLocale = function() {
239 | var val = localStorage.getItem(l10n.localStorageKey);
240 | if (!val) {
241 | val = '0';
242 | }
243 | var index = parseInt(val);
244 | if (!isNaN(index)) {
245 | l10n.current = index % l10n.LOCALES.length;
246 | l10n.updateMessages();
247 | }
248 | };
249 |
250 | /**
251 | * Updates UI messages to the current locale.
252 | */
253 | l10n.updateMessages = function() {
254 | elems = document.getElementsByClassName('l10n');
255 | for (let i = 0; i < elems.length; i++) {
256 | if (l10n.MESSAGES.hasOwnProperty(elems[i].id)) {
257 | var locale = l10n.LOCALES[l10n.current];
258 | var msg = '';
259 | if (l10n.MESSAGES[elems[i].id].hasOwnProperty(locale)) {
260 | msg = l10n.MESSAGES[elems[i].id][locale];
261 | } else {
262 | msg = l10n.MESSAGES[elems[i].id]['en'];
263 | }
264 | elems[i].innerHTML = msg;
265 | }
266 | }
267 | };
268 |
--------------------------------------------------------------------------------
/text-mode.html:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
56 |
57 |
58 | Text-mode Altair 8800 simulator
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | LEDs:
95 |
96 |
97 | INTE ○
98 | PROT ○
99 | MEMR ○
100 | INP ○
101 | MI ○
102 | OUT ○
103 | HLTA ○
104 | STACK ○
105 | WO ○
106 | INT ○
107 |
108 |
109 |
110 |
111 |
112 | WAIT ○
113 | HLDA ○
114 |
115 |
116 |
117 |
118 |
119 | D7 D6 D5 D4 D3 D2 D1 D0
120 | ○
121 | ○
122 | ○
123 | ○
124 | ○
125 | ○
126 | ○
127 | ○
128 |
129 |
130 |
131 |
132 | A15 A14 A13 A12 A11 A10 A9 A8 A7 A6 A5 A4 A3 A2 A1 A0
133 | ○
134 | ○
135 | ○
136 | ○
137 | ○
138 | ○
139 | ○
140 | ○
141 | ○
142 | ○
143 | ○
144 | ○
145 | ○
146 | ○
147 | ○
148 | ○
149 |
150 |
151 |
152 |
172 |
173 |
174 |
175 |
176 |
177 | CPU dump:
178 |
179 |
180 |
181 |
182 |
183 | Memory dump:
184 |
185 |
186 |
187 |
188 |
189 |
292 |
293 |
294 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/js/sim8800.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2020 wixette@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @fileoverview Altair 8800 front panel simulator.
17 | */
18 |
19 |
20 | /**
21 | * The simulator.
22 | */
23 | class Sim8800 {
24 | /**
25 | * @param {number} memSize The memory size, in bytes.
26 | * @param {number} clockRate The clock rate.
27 | * @param {function(Array)?} setAddressLedsCallback The
28 | * callback to set address LEDs.
29 | * @param {function(Array)?} setDataLedsCallback The
30 | * callback to set data LEDs.
31 | * @param {function(boolean)?} setWaitLedCallback The callback to
32 | * set the WAIT LED.
33 | * @param {function(boolean)?} setStatusLedsCallback The callback to
34 | * set the STATUS LEDs.
35 | * @param {function():number?} getInputAddressCallback The
36 | * callback to get the input word from address/data switches.
37 | * @param {function(string)?} dumpCpuCallback The callback to receive
38 | * CPU status dump, in HTML string.
39 | * @param {function(string)?} dumpMemCallback The callback to receive
40 | * memory contents dump, in HTML string.
41 | */
42 | constructor(memSize, clockRate,
43 | setAddressLedsCallback, setDataLedsCallback,
44 | setWaitLedCallback, setStatusLedsCallback,
45 | getInputAddressCallback,
46 | dumpCpuCallback, dumpMemCallback) {
47 | this.clockRate = clockRate;
48 | this.mem = new Array(memSize);
49 | this.setAddressLedsCallback = setAddressLedsCallback;
50 | this.setDataLedsCallback = setDataLedsCallback;
51 | this.setWaitLedCallback = setWaitLedCallback;
52 | this.setStatusLedsCallback = setStatusLedsCallback;
53 | this.getInputAddressCallback = getInputAddressCallback;
54 | this.dumpCpuCallback = dumpCpuCallback;
55 | this.dumpMemCallback = dumpMemCallback;
56 | this.isPoweredOn = false;
57 | this.isRunning = false;
58 | this.lastAddress = 0;
59 | this.initMem();
60 | CPU8080.init(this.getWriteByteCallback(),
61 | this.getReadByteCallback(),
62 | null, /* not used. */
63 | this.getWritePortCallback(),
64 | this.getReadPortCallback());
65 | }
66 |
67 | /**
68 | * Formats a number to fixed length hex string.
69 | * @param {number} n The number to be formatted.
70 | * @param {number} len The output length, with leading zeros.
71 | */
72 | static toHex(n, len) {
73 | var leadingZeros = (new Array(len)).fill('0').join('');
74 | return (leadingZeros + n.toString(16)).substr(-len);
75 | }
76 |
77 | /**
78 | * Parses a number into an array of binary bits.
79 | * @param {number} data The data to be parsed.
80 | * @param {number} numBits Number of bits to be parsed.
81 | * @return {Array} Sequence of 0 or 1, from the lowest bit to
82 | * the highest bit.
83 | */
84 | static parseBits(data, numBits) {
85 | var bits = [];
86 | for (let i = 0; i < numBits; i++) {
87 | bits.push(data & 1 != 0 ? 1 : 0);
88 | data >>>= 1;
89 | }
90 | return bits;
91 | }
92 |
93 | /**
94 | * Fills the memory with dummy bytes.
95 | */
96 | initMem(random = false) {
97 | if (random) {
98 | for (let i = 0; i < this.mem.length; i++) {
99 | this.mem[i] = Math.floor(Math.random() * 256);
100 | }
101 | } else {
102 | this.mem.fill(0);
103 | }
104 | }
105 |
106 | /**
107 | * Loads data into memory.
108 | * @param {number} address The start address to load the data/program.
109 | * @param {Array} data The array of data.
110 | */
111 | loadData(address, data) {
112 | if (!this.isPoweredOn)
113 | return;
114 | for (let i = 0; i < data.length && address < this.mem.length; i++) {
115 | this.mem[address++] = data[i];
116 | }
117 | this.dumpMem();
118 | }
119 |
120 | /**
121 | * Loads data into memory.
122 | * @param {number} address The start address to load the data/program.
123 | * @param {string} hexString Data encoded in hex string, like 'c3 00 00'.
124 | */
125 | loadDataAsHexString(address, hexString) {
126 | if (!this.isPoweredOn || !hexString)
127 | return;
128 | var data = hexString.split(' ');
129 | for (let i = 0; i < data.length && address < this.mem.length; i++) {
130 | var byte = parseInt('0x' + data[i]);
131 | if (!isNaN(byte)) {
132 | this.mem[address++] = byte;
133 | }
134 | }
135 | this.dumpMem();
136 | }
137 |
138 | /**
139 | * Dumps the memory to HTML, for debugging or monitoring.
140 | */
141 | dumpMem() {
142 | if (this.dumpMemCallback) {
143 | var sb = ['\n'];
144 | for (let i = 0; i < this.mem.length; i += 16) {
145 | sb.push(Sim8800.toHex(i, 4));
146 | sb.push(' ');
147 | for (let j = i;
148 | j < Math.min(this.mem.length, i + 16);
149 | j++) {
150 | sb.push(Sim8800.toHex(this.mem[j], 2));
151 | sb.push((j + 1) % 8 == 0 ? ' ' : ' ');
152 | }
153 | sb.push('\n');
154 | }
155 | sb.push(' \n');
156 | this.dumpMemCallback(sb.join(''));
157 | }
158 | }
159 |
160 | /**
161 | * Decodes the FLAGs register.
162 | * @param {number} flags The value of the FLAGs register.
163 | * @return {Object} The decoded flags.
164 | */
165 | decodeFlags(flags) {
166 | var ret = {};
167 | ret.sign = flags & 0x80 != 0;
168 | ret.zero = flags & 0x40 != 0;
169 | ret.auxiliaryCarry = flags & 0x10 != 0;
170 | ret.parity = flags & 0x04 != 0;
171 | ret.carry = flags & 0x01 != 0;
172 | return ret;
173 | }
174 |
175 | /**
176 | * Dumps the internal CPU status to HTML, for debugging or mornitoring.
177 | */
178 | dumpCpu() {
179 | if (this.dumpCpuCallback) {
180 | var cpu = CPU8080.status();
181 | var sb = ['\n'];
182 | sb.push('PC = ' + Sim8800.toHex(cpu.pc, 4) + ' ');
183 | sb.push('SP = ' + Sim8800.toHex(cpu.sp, 4) + '\n');
184 | sb.push('A = ' + Sim8800.toHex(cpu.a, 2) + ' ');
185 | sb.push('B = ' + Sim8800.toHex(cpu.b, 2) + ' ');
186 | sb.push('C = ' + Sim8800.toHex(cpu.c, 2) + ' ');
187 | sb.push('D = ' + Sim8800.toHex(cpu.d, 2) + '\n');
188 | sb.push('E = ' + Sim8800.toHex(cpu.e, 2) + ' ');
189 | sb.push('F = ' + Sim8800.toHex(cpu.f, 2) + ' ');
190 | sb.push('H = ' + Sim8800.toHex(cpu.h, 2) + ' ');
191 | sb.push('L = ' + Sim8800.toHex(cpu.l, 2) + '\n');
192 | var flags = this.decodeFlags(cpu.f);
193 | sb.push('FLAGS: ');
194 | if (flags.sign) sb.push('SIGN ');
195 | if (flags.zero) sb.push('ZERO ');
196 | if (flags.auxiliaryCarry) sb.push('AC ');
197 | if (flags.parity) sb.push('PARITY ');
198 | if (flags.carry) sb.push('CARRY ');
199 | sb.push(' \n');
200 | this.dumpCpuCallback(sb.join(''));
201 | }
202 | }
203 |
204 | /**
205 | * Returns the byteTo (write memory) callback.
206 | * @return {function(number, number)}
207 | */
208 | getWriteByteCallback() {
209 | var self = this;
210 | return function(address, value) {
211 | address = address % self.mem.length;
212 | self.mem[address] = value;
213 | };
214 | }
215 |
216 | /**
217 | * Returns the byteAt (read memory) callback.
218 | * @return {function(number): number}
219 | */
220 | getReadByteCallback() {
221 | var self = this;
222 | return function(address) {
223 | address = address % self.mem.length;
224 | var value = self.mem[address];
225 | return value;
226 | };
227 | }
228 |
229 | /**
230 | * Returns the porto (write port) callback.
231 | * @return {function(number, number)}
232 | */
233 | getWritePortCallback() {
234 | var self = this;
235 | return function(address, value) {
236 | if (address == 0xff && self.setDataLedsCallback) {
237 | var bits = Sim8800.parseBits(value, 8);
238 | self.setDataLedsCallback(bits);
239 | }
240 | };
241 | }
242 |
243 | /**
244 | * Returns the byteAt (read memory) callback.
245 | * @return {function(number): number}
246 | */
247 | getReadPortCallback() {
248 | var self = this;
249 | return function(address) {
250 | var value = 0;
251 | // We only care about the port 0xff.
252 | if (address == 0xff && self.getInputAddressCallback) {
253 | var word = self.getInputAddressCallback();
254 | return word >> 8;
255 | }
256 | return value;
257 | };
258 | }
259 |
260 | /**
261 | * Gets the clock ticker callback.
262 | * @return {function()}
263 | */
264 | getClockTickerCallback() {
265 | var self = this;
266 | return function(timestamp) {
267 | if (self.isRunning) {
268 | var cycles = self.clockRate / 1000;
269 | self.step(cycles);
270 | window.setTimeout(self.getClockTickerCallback(), 1);
271 | }
272 | };
273 | }
274 |
275 | /**
276 | * Powers on the machine.
277 | */
278 | powerOn() {
279 | this.isPoweredOn = true;
280 | this.initMem();
281 | this.reset();
282 | if (this.setStatusLedsCallback) {
283 | this.setStatusLedsCallback(true);
284 | }
285 | if (this.setWaitLedCallback) {
286 | this.setWaitLedCallback(false);
287 | }
288 | }
289 |
290 | /**
291 | * Powers off the machine.
292 | */
293 | powerOff() {
294 | if (this.setStatusLedsCallback) {
295 | this.setStatusLedsCallback(false);
296 | }
297 | if (this.setWaitLedCallback) {
298 | this.setWaitLedCallback(true);
299 | }
300 | if (this.setAddressLedsCallback) {
301 | this.setAddressLedsCallback(new Array(16).fill(0));
302 | }
303 | if (this.setDataLedsCallback) {
304 | this.setDataLedsCallback(new Array(8).fill(0));
305 | }
306 | if (this.dumpCpuCallback) {
307 | this.dumpCpuCallback('');
308 | }
309 | if (this.dumpMemCallback) {
310 | this.dumpMemCallback('');
311 | }
312 | this.isPoweredOn = false;
313 | }
314 |
315 | /**
316 | * Resets the machine.
317 | */
318 | reset() {
319 | if (!this.isPoweredOn)
320 | return;
321 | CPU8080.reset();
322 | this.stop();
323 | this.lastAddress = 0;
324 | if (this.setAddressLedsCallback) {
325 | this.setAddressLedsCallback(new Array(16).fill(1));
326 | }
327 | if (this.setDataLedsCallback) {
328 | this.setDataLedsCallback(new Array(8).fill(1));
329 | }
330 | this.dumpCpu();
331 | this.dumpMem();
332 | var self = this;
333 | window.setTimeout(function() {
334 | if (self.setAddressLedsCallback) {
335 | self.setAddressLedsCallback(new Array(16).fill(0));
336 | }
337 | if (self.setDataLedsCallback) {
338 | self.setDataLedsCallback(new Array(8).fill(0));
339 | }
340 | }, 400);
341 | }
342 |
343 | /**
344 | * Stops the CPU.
345 | */
346 | stop() {
347 | if (!this.isPoweredOn)
348 | return;
349 | this.isRunning = false;
350 | if (this.setWaitLedCallback) {
351 | this.setWaitLedCallback(this.isRunning);
352 | }
353 | }
354 |
355 | /**
356 | * Starts the CPU.
357 | */
358 | start() {
359 | if (!this.isPoweredOn)
360 | return;
361 | this.isRunning = true;
362 | if (this.setWaitLedCallback) {
363 | this.setWaitLedCallback(this.isRunning);
364 | }
365 | window.setTimeout(this.getClockTickerCallback(), 1);
366 | }
367 |
368 | /**
369 | * Runs a given number of CPU cycles.
370 | * @param {number} cycles The number of CPU cycles to step on.
371 | */
372 | step(cycles) {
373 | if (!this.isPoweredOn)
374 | return;
375 | CPU8080.steps(cycles);
376 | this.dumpCpu();
377 | this.dumpMem();
378 | if (this.setAddressLedsCallback) {
379 | let cpu = CPU8080.status();
380 | let bits = Sim8800.parseBits(cpu.pc, 16);
381 | this.setAddressLedsCallback(bits);
382 | }
383 | }
384 |
385 | /**
386 | * Shows the address and the byte at the address via LEDs.
387 | */
388 | showAddressAndData() {
389 | if (this.setAddressLedsCallback) {
390 | let bits = Sim8800.parseBits(this.lastAddress, 16);
391 | this.setAddressLedsCallback(bits);
392 | }
393 | if (this.setDataLedsCallback) {
394 | let bits = Sim8800.parseBits(this.mem[this.lastAddress], 8);
395 | this.setDataLedsCallback(bits);
396 | }
397 | }
398 |
399 | /**
400 | * Reads a byte from the given address.
401 | */
402 | examine() {
403 | if (!this.isPoweredOn)
404 | return;
405 | if (this.getInputAddressCallback) {
406 | var address = this.getInputAddressCallback();
407 | this.lastAddress = address;
408 | this.showAddressAndData();
409 | }
410 | }
411 |
412 | /**
413 | * Reads a byte from the next address.
414 | */
415 | examineNext() {
416 | if (!this.isPoweredOn)
417 | return;
418 | this.lastAddress++;
419 | this.showAddressAndData();
420 | }
421 |
422 | /**
423 | * Writes a byte to the given address.
424 | */
425 | deposit() {
426 | if (!this.isPoweredOn)
427 | return;
428 | if (this.getInputAddressCallback) {
429 | // Only 8 bits of input is considered.
430 | var value = this.getInputAddressCallback() & 0xff;
431 | this.getWriteByteCallback()(this.lastAddress, value);
432 | this.showAddressAndData();
433 | this.dumpMem();
434 | }
435 | }
436 |
437 | /**
438 | * Writes a byte to the next address.
439 | */
440 | depositNext() {
441 | if (!this.isPoweredOn)
442 | return;
443 | this.lastAddress++;
444 | this.deposit();
445 | }
446 | };
447 |
--------------------------------------------------------------------------------
/js/panel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2020 wixette@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @fileoverview The main logic to control the front panel UI.
17 | */
18 |
19 | /**
20 | * Simple namespace.
21 | * @type {Object}
22 | */
23 | panel = {};
24 |
25 | /**
26 | * When STOP switch is pressed.
27 | */
28 | panel.onStop = function() {
29 | panel.sim.stop();
30 | };
31 |
32 | /**
33 | * When RUN switch is pressed.
34 | */
35 | panel.onRun = function() {
36 | panel.sim.start();
37 | };
38 |
39 | /**
40 | * When SINGLE STEP switch is pressed.
41 | */
42 | panel.onSingle = function() {
43 | panel.sim.step(1);
44 | };
45 |
46 | /**
47 | * When EXAMINE switch is pressed.
48 | */
49 | panel.onExamine = function() {
50 | panel.sim.examine();
51 | };
52 |
53 | /**
54 | * When EXAMINE NEXT switch is pressed.
55 | */
56 | panel.onExamineNext = function() {
57 | panel.sim.examineNext();
58 | };
59 |
60 | /**
61 | * When DEPOSIT switch is pressed.
62 | */
63 | panel.onDeposit = function() {
64 | panel.sim.deposit();
65 | };
66 |
67 | /**
68 | * When DEPOSIT NEXT switch is pressed.
69 | */
70 | panel.onDepositNext = function() {
71 | panel.sim.depositNext();
72 | };
73 |
74 | /**
75 | * When RESET switch is pressed.
76 | */
77 | panel.onReset = function() {
78 | panel.sim.reset();
79 | };
80 |
81 | /**
82 | * When power is turned on.
83 | */
84 | panel.onPowerOn = function() {
85 | panel.sim.powerOn();
86 | window.setTimeout(function() {
87 | panel.playBeepbeep();
88 | }, 500);
89 | };
90 |
91 | /**
92 | * When power is turned off.
93 | */
94 | panel.onPowerOff = function() {
95 | panel.sim.powerOff();
96 | };
97 |
98 | /**
99 | * When CPU sets the address LEDs.
100 | */
101 | panel.setAddressLedsCallback = function(bits) {
102 | for (let i = 0; i < bits.length; i++) {
103 | var ledId = 'A' + i;
104 | if (bits[i]) {
105 | panel.ledOn(ledId);
106 | } else {
107 | panel.ledOff(ledId);
108 | }
109 | }
110 | };
111 |
112 | /**
113 | * When CPU sets the data LEDs.
114 | */
115 | panel.setDataLedsCallback = function(bits) {
116 | for (let i = 0; i < bits.length; i++) {
117 | var ledId = 'D' + i;
118 | if (bits[i]) {
119 | panel.ledOn(ledId);
120 | } else {
121 | panel.ledOff(ledId);
122 | }
123 | }
124 | };
125 |
126 | /**
127 | * When CPU sets the WAIT LED.
128 | */
129 | panel.setWaitLedCallback = function(isRunning) {
130 | var ledId = 'WAIT';
131 | if (!isRunning) {
132 | panel.ledOn(ledId);
133 | } else {
134 | panel.ledOff(ledId);
135 | }
136 | };
137 |
138 | /**
139 | * When CPU sets the status LEDs.
140 | */
141 | panel.setStatusLedsCallback = function(isPoweredOn) {
142 | var ledIds = ['MEMR', 'MI', 'WO'];
143 | for (let i = 0; i < ledIds.length; i++) {
144 | if (isPoweredOn) {
145 | panel.ledOn(ledIds[i]);
146 | } else {
147 | panel.ledOff(ledIds[i]);
148 | }
149 | }
150 | };
151 |
152 | /**
153 | * When CPU reads the number input by the address/data switches.
154 | */
155 | panel.getInputAddressCallback = function() {
156 | var word = 0;
157 | for (let i = 0; i < 16; i++) {
158 | if (panel.addressSwitchStates[i]) {
159 | word |= 1 << i;
160 | }
161 | }
162 | return word;
163 | };
164 |
165 | /**
166 | * When CPU dumps the CPU status for debug.
167 | */
168 | panel.dumpCpuCallback = function(dumpHtml) {
169 | var dumpCpuElem = document.getElementById('cpu-dump');
170 | dumpCpuElem.innerHTML = dumpHtml;
171 | };
172 |
173 | /**
174 | * When CPU dumps the MEM contents for debug.
175 | */
176 | panel.dumpMemCallback = function(dumpHtml) {
177 | var dumpMemElem = document.getElementById('mem-dump');
178 | dumpMemElem.innerHTML = dumpHtml;
179 | };
180 |
181 | /**
182 | * Deposites data into MEM directly in debug panel.
183 | */
184 | panel.debugLoadData = function() {
185 | var data = document.getElementById("debug-data-input").value;
186 | panel.sim.loadDataAsHexString(0, data);
187 | };
188 |
189 | /**
190 | * The info of all LEDs.
191 | */
192 | panel.LED_INFO = [
193 | {
194 | id: 'INTE',
195 | x: 194,
196 | y: 120
197 | },
198 | {
199 | id: 'PROT',
200 | x: 245,
201 | y: 120
202 | },
203 | {
204 | id: 'MEMR',
205 | x: 296,
206 | y: 120
207 | },
208 | {
209 | id: 'INP',
210 | x: 347,
211 | y: 120
212 | },
213 | {
214 | id: 'MI',
215 | x: 398,
216 | y: 120
217 | },
218 | {
219 | id: 'OUT',
220 | x: 449,
221 | y: 120
222 | },
223 | {
224 | id: 'HLTA',
225 | x: 500,
226 | y: 120
227 | },
228 | {
229 | id: 'STACK',
230 | x: 551,
231 | y: 120
232 | },
233 | {
234 | id: 'WO',
235 | x: 602,
236 | y: 120
237 | },
238 | {
239 | id: 'INT',
240 | x: 653,
241 | y: 120
242 | },
243 | {
244 | id: 'D7',
245 | x: 830,
246 | y: 120
247 | },
248 | {
249 | id: 'D6',
250 | x: 880,
251 | y: 120
252 | },
253 | {
254 | id: 'D5',
255 | x: 959,
256 | y: 120
257 | },
258 | {
259 | id: 'D4',
260 | x: 1009,
261 | y: 120
262 | },
263 | {
264 | id: 'D3',
265 | x: 1059,
266 | y: 120
267 | },
268 | {
269 | id: 'D2',
270 | x: 1138,
271 | y: 120
272 | },
273 | {
274 | id: 'D1',
275 | x: 1188,
276 | y: 120
277 | },
278 | {
279 | id: 'D0',
280 | x: 1238,
281 | y: 120
282 | },
283 | {
284 | id: 'WAIT',
285 | x: 194,
286 | y: 230
287 | },
288 | {
289 | id: 'HLDA',
290 | x: 245,
291 | y: 230
292 | },
293 | {
294 | id: 'A15',
295 | x: 346,
296 | y: 230
297 | },
298 | {
299 | id: 'A14',
300 | x: 423,
301 | y: 230
302 | },
303 | {
304 | id: 'A13',
305 | x: 473,
306 | y: 230
307 | },
308 | {
309 | id: 'A12',
310 | x: 523,
311 | y: 230
312 | },
313 | {
314 | id: 'A11',
315 | x: 602,
316 | y: 230
317 | },
318 | {
319 | id: 'A10',
320 | x: 652,
321 | y: 230
322 | },
323 | {
324 | id: 'A9',
325 | x: 702,
326 | y: 230
327 | },
328 | {
329 | id: 'A8',
330 | x: 780,
331 | y: 230
332 | },
333 | {
334 | id: 'A7',
335 | x: 830,
336 | y: 230
337 | },
338 | {
339 | id: 'A6',
340 | x: 880,
341 | y: 230
342 | },
343 | {
344 | id: 'A5',
345 | x: 959,
346 | y: 230
347 | },
348 | {
349 | id: 'A4',
350 | x: 1009,
351 | y: 230
352 | },
353 | {
354 | id: 'A3',
355 | x: 1059,
356 | y: 230
357 | },
358 | {
359 | id: 'A2',
360 | x: 1138,
361 | y: 230
362 | },
363 | {
364 | id: 'A1',
365 | x: 1188,
366 | y: 230
367 | },
368 | {
369 | id: 'A0',
370 | x: 1238,
371 | y: 230
372 | },
373 | ];
374 |
375 | /**
376 | * The info of all toggle switches.
377 | *
378 | * A toggle switch has an upper state (which means 1 for address
379 | * switches) and a lower state (which means 0 for address switches).
380 | */
381 | panel.TOGGLE_SWITCH_INFO = [
382 | {
383 | id: 'OFF-ON',
384 | x: 105,
385 | y: 439
386 | },
387 | {
388 | id: 'S15',
389 | x: 346,
390 | y: 334
391 | },
392 | {
393 | id: 'S14',
394 | x: 423,
395 | y: 334
396 | },
397 | {
398 | id: 'S13',
399 | x: 473,
400 | y: 334
401 | },
402 | {
403 | id: 'S12',
404 | x: 523,
405 | y: 334
406 | },
407 | {
408 | id: 'S11',
409 | x: 602,
410 | y: 334
411 | },
412 | {
413 | id: 'S10',
414 | x: 652,
415 | y: 334
416 | },
417 | {
418 | id: 'S9',
419 | x: 702,
420 | y: 334
421 | },
422 | {
423 | id: 'S8',
424 | x: 780,
425 | y: 334
426 | },
427 | {
428 | id: 'S7',
429 | x: 830,
430 | y: 334
431 | },
432 | {
433 | id: 'S6',
434 | x: 880,
435 | y: 334
436 | },
437 | {
438 | id: 'S5',
439 | x: 959,
440 | y: 334
441 | },
442 | {
443 | id: 'S4',
444 | x: 1009,
445 | y: 334
446 | },
447 | {
448 | id: 'S3',
449 | x: 1059,
450 | y: 334
451 | },
452 | {
453 | id: 'S2',
454 | x: 1138,
455 | y: 334
456 | },
457 | {
458 | id: 'S1',
459 | x: 1188,
460 | y: 334
461 | },
462 | {
463 | id: 'S0',
464 | x: 1238,
465 | y: 334
466 | },
467 | ];
468 |
469 | /**
470 | * The info of all stateless switches.
471 | *
472 | * A stateless switch may has a upper command and a lower
473 | * command. When a command is clicked, the switch moves up or down
474 | * then back to its middle position, without keeping upper or lower
475 | * state.
476 | */
477 | panel.STATELESS_SWITCH_INFO = [
478 | {
479 | id: 'STOP-RUN',
480 | x: 348,
481 | y: 439,
482 | upperCmd: { textId: 'SW-STOP', callback: panel.onStop },
483 | lowerCmd: { textId: 'SW-RUN', callback: panel.onRun },
484 | },
485 | {
486 | id: 'SINGLE',
487 | x: 446,
488 | y: 439,
489 | upperCmd: { textId: 'SW-SINGLE', callback: panel.onSingle },
490 | lowerCmd: null,
491 | },
492 | {
493 | id: 'EXAMINE',
494 | x: 550,
495 | y: 439,
496 | upperCmd: { textId: 'SW-EXAMINE', callback: panel.onExamine },
497 | lowerCmd: { textId: 'SW-EXAMINE-NEXT', callback: panel.onExamineNext },
498 | },
499 | {
500 | id: 'DEPOSIT',
501 | x: 650,
502 | y: 439,
503 | upperCmd: { textId: 'SW-DEPOSIT', callback: panel.onDeposit },
504 | lowerCmd: { textId: 'SW-DEPOSIT-NEXT', callback: panel.onDepositNext },
505 | },
506 | {
507 | id: 'RESET',
508 | x: 753,
509 | y: 439,
510 | upperCmd: { textId: 'SW-RESET', callback: panel.onReset },
511 | lowerCmd: null,
512 | },
513 | {
514 | id: 'PROTECT',
515 | x: 853,
516 | y: 439,
517 | upperCmd: null,
518 | lowerCmd: null,
519 | },
520 | {
521 | id: 'AUX1',
522 | x: 957,
523 | y: 439,
524 | upperCmd: null,
525 | lowerCmd: null,
526 | },
527 | {
528 | id: 'AUX2',
529 | x: 1060,
530 | y: 439,
531 | upperCmd: null,
532 | lowerCmd: null,
533 | },
534 | ];
535 |
536 | /**
537 | * The type ID of toggle switch.
538 | */
539 | panel.TOGGLE_SWITCH = 0;
540 |
541 | /**
542 | * The type ID of stateless switch.
543 | */
544 | panel.STATELESS_SWITCH = 1;
545 |
546 | /** The current state of all the address switches. */
547 | panel.addressSwitchStates = new Array(16);
548 |
549 | /** If the power is turned on. */
550 | panel.isPoweredOn = false;
551 |
552 | /** The simulator object. */
553 | panel.sim = null;
554 |
555 | /**
556 | * Initializes thie UI.
557 | */
558 | panel.init = function() {
559 | // Restores the last locale if it exists.
560 | l10n.restoreLocale();
561 |
562 | // Initializes event listener for nav buttons.
563 | var button = document.getElementById('switch-locale');
564 | button.addEventListener('click', l10n.nextLocale, false);
565 | button = document.getElementById('nav-sim');
566 | button.addEventListener('click', panel.showTabSim, false);
567 | button = document.getElementById('nav-debug');
568 | button.addEventListener('click', panel.showTabDebug, false);
569 | button = document.getElementById('nav-ref');
570 | button.addEventListener('click', panel.showTabRes, false);
571 | panel.showTabSim();
572 |
573 | // Initializes event listener for debug controls.
574 | button = document.getElementById('debug-load-data');
575 | button.addEventListener('click', panel.debugLoadData, false);
576 |
577 | // Initializes svg components for all LEDs.
578 | for (let i = 0; i < panel.LED_INFO.length; i++) {
579 | let info = panel.LED_INFO[i];
580 | let led = panel.createLed(info.id, info.x, info.y);
581 | }
582 |
583 | // Initializes svg components for all switches.
584 | for (let i = 0; i < panel.TOGGLE_SWITCH_INFO.length; i++) {
585 | let info = panel.TOGGLE_SWITCH_INFO[i];
586 | let sw = panel.createSwitch(info.id, panel.TOGGLE_SWITCH,
587 | info.x, info.y,
588 | null, null);
589 | }
590 | for (let i = 0; i < panel.STATELESS_SWITCH_INFO.length; i++) {
591 | let info = panel.STATELESS_SWITCH_INFO[i];
592 | let sw = panel.createSwitch(info.id, panel.STATELESS_SWITCH,
593 | info.x, info.y,
594 | info.upperCmd, info.lowerCmd);
595 | }
596 |
597 | // Initializes internal states.
598 | panel.isPoweredOn = false;
599 | panel.addressSwitchStates.fill(0);
600 | panel.switchUp('OFF-ON');
601 |
602 | // Initializes the simulator.
603 | panel.sim = new Sim8800(
604 | 256, /* 256B MEM */
605 | 1000000, /* 1MHz */
606 | panel.setAddressLedsCallback, panel.setDataLedsCallback,
607 | panel.setWaitLedCallback, panel.setStatusLedsCallback,
608 | panel.getInputAddressCallback,
609 | panel.dumpCpuCallback, panel.dumpMemCallback);
610 | };
611 |
612 | /**
613 | * Creates a new LED inside the panel svg.
614 | * @param {string} id The LED ID. This ID will be used as the prefix
615 | * of DOM element's ID.
616 | * @param {number} x The x position.
617 | * @param {number} y The y position.
618 | */
619 | panel.createLed = function(id, x, y) {
620 | var panelElem = document.getElementById('panel');
621 | var ledOnElem = document.getElementById('led-on');
622 | var ledOffElem = document.getElementById('led-off');
623 |
624 | ledOnElem.style.display = 'none';
625 | ledOffElem.style.display = 'none';
626 |
627 | var onElem = ledOnElem.cloneNode(true);
628 | onElem.id = id + '-on';
629 | onElem.x.baseVal.value = '' + x;
630 | onElem.y.baseVal.value = '' + y;
631 | onElem.style.display = 'none';
632 |
633 | var offElem = ledOffElem.cloneNode(true);
634 | offElem.id = id + '-off';
635 | offElem.x.baseVal.value = '' + x;
636 | offElem.y.baseVal.value = '' + y;
637 | offElem.style.display = 'inline';
638 |
639 | panelElem.appendChild(onElem);
640 | panelElem.appendChild(offElem);
641 | };
642 |
643 | /**
644 | * Turns on the specified LED.
645 | * @param {string} id The LED ID.
646 | */
647 | panel.ledOn = function(id) {
648 | document.getElementById(id + '-on').style.display = 'inline';
649 | document.getElementById(id + '-off').style.display = 'none';
650 | };
651 |
652 | /**
653 | * Turns off the specified LED.
654 | * @param {string} id The LED ID.
655 | */
656 | panel.ledOff = function(id) {
657 | document.getElementById(id + '-on').style.display = 'none';
658 | document.getElementById(id + '-off').style.display = 'inline';
659 | };
660 |
661 | /**
662 | * Creates a new toggle switch inside the panel svg.
663 | * @param {string} id The switch ID. This ID will be used as the
664 | * prefix of DOM element's ID.
665 | * @param {number} type The type of the switch.
666 | * @param {number} x The x position.
667 | * @param {number} y The y position.
668 | * @param {Object} upperCmd The upperCmd info, for STATELESS_SWITCH
669 | * only.
670 | * @param {Object} lowerCmd The lowerCmd info, for STATELESS_SWITCH
671 | * only.
672 | */
673 | panel.createSwitch = function(id, type, x, y, upperCmd, lowerCmd) {
674 | var panelElem = document.getElementById('panel');
675 | var switchMidElem = document.getElementById('switch-mid');
676 | var switchUpElem = document.getElementById('switch-up');
677 | var switchDownElem = document.getElementById('switch-down');
678 |
679 | switchMidElem.style.display = 'none';
680 | switchUpElem.style.display = 'none';
681 | switchDownElem.style.display = 'none';
682 |
683 | var midElem = switchMidElem.cloneNode(true);
684 | midElem.id = id + '-mid';
685 | midElem.x.baseVal.value = '' + x;
686 | midElem.y.baseVal.value = '' + y;
687 | midElem.style.display = (type == panel.STATELESS_SWITCH) ? 'inline' : 'none';
688 |
689 | var upElem = switchUpElem.cloneNode(true);
690 | upElem.id = id + '-up';
691 | upElem.x.baseVal.value = '' + x;
692 | upElem.y.baseVal.value = '' + y;
693 | if (type == panel.TOGGLE_SWITCH) {
694 | upElem.style.cursor = 'pointer';
695 | }
696 | upElem.style.display = 'none';
697 |
698 | var downElem = switchDownElem.cloneNode(true);
699 | downElem.id = id + '-down';
700 | downElem.x.baseVal.value = '' + x;
701 | downElem.y.baseVal.value = '' + y;
702 | if (type == panel.TOGGLE_SWITCH) {
703 | downElem.style.cursor = 'pointer';
704 | }
705 | downElem.style.display = (type == panel.TOGGLE_SWITCH) ? 'inline' : 'none';
706 |
707 | if (type == panel.TOGGLE_SWITCH) {
708 | var sourceId = id;
709 | upElem.addEventListener('click',
710 | function() {
711 | panel.onToggle(sourceId);
712 | },
713 | false);
714 | downElem.addEventListener('click',
715 | function() {
716 | panel.onToggle(sourceId);
717 | },
718 | false);
719 | // Also installs helper switch handlers.
720 | let softSwitchId = 'S-' + id;
721 | let elem = document.getElementById(softSwitchId);
722 | elem.addEventListener(
723 | 'click',
724 | function() {
725 | panel.onToggle(sourceId);
726 | },
727 | false
728 | );
729 | } else {
730 | if (upperCmd) {
731 | let cmdElem = document.getElementById(upperCmd.textId);
732 | cmdElem.style.cursor = 'pointer';
733 | cmdElem.addEventListener(
734 | 'click',
735 | function() {
736 | panel.switchUpThenBack(id);
737 | panel.playSwitch();
738 | upperCmd.callback();
739 | },
740 | false);
741 | // Also installs helper switch handlers.
742 | cmdElem = document.getElementById('S' + upperCmd.textId);
743 | cmdElem.addEventListener(
744 | 'click',
745 | function() {
746 | panel.switchUpThenBack(id);
747 | panel.playSwitch();
748 | upperCmd.callback();
749 | },
750 | false);
751 | }
752 | if (lowerCmd) {
753 | let cmdElem = document.getElementById(lowerCmd.textId);
754 | cmdElem.style.cursor = 'pointer';
755 | cmdElem.addEventListener(
756 | 'click',
757 | function() {
758 | panel.switchDownThenBack(id);
759 | panel.playSwitch();
760 | lowerCmd.callback();
761 | },
762 | false);
763 | // Also installs helper switch handlers.
764 | cmdElem = document.getElementById('S' + lowerCmd.textId);
765 | cmdElem.addEventListener(
766 | 'click',
767 | function() {
768 | panel.switchDownThenBack(id);
769 | panel.playSwitch();
770 | lowerCmd.callback();
771 | },
772 | false);
773 | }
774 | }
775 |
776 | panelElem.appendChild(midElem);
777 | panelElem.appendChild(upElem);
778 | panelElem.appendChild(downElem);
779 | };
780 |
781 | /**
782 | * Moves the switch handle up - for TOGGLE_SWITCH only.
783 | * @param {string} id The switch ID.
784 | */
785 | panel.switchUp = function(id) {
786 | var midElem = document.getElementById(id + '-mid');
787 | var upElem = document.getElementById(id + '-up');
788 | var downElem = document.getElementById(id + '-down');
789 |
790 | upElem.style.display = 'inline';
791 | midElem.style.display = 'none';
792 | downElem.style.display = 'none';
793 | };
794 |
795 | /**
796 | * Moves the switch handle down - for TOGGLE_SWITCH only.
797 | * @param {string} id The switch ID.
798 | */
799 | panel.switchDown = function(id) {
800 | var midElem = document.getElementById(id + '-mid');
801 | var upElem = document.getElementById(id + '-up');
802 | var downElem = document.getElementById(id + '-down');
803 |
804 | upElem.style.display = 'none';
805 | midElem.style.display = 'none';
806 | downElem.style.display = 'inline';
807 | };
808 |
809 | /**
810 | * Moves the switch handle up, then back to the middle position - for
811 | * STATELESS_SWITCH only.
812 | * @param {string} id The switch ID.
813 | */
814 | panel.switchUpThenBack = function(id) {
815 | var midElem = document.getElementById(id + '-mid');
816 | var upElem = document.getElementById(id + '-up');
817 | var downElem = document.getElementById(id + '-down');
818 |
819 | upElem.style.display = 'none';
820 | midElem.style.display = 'inline';
821 | downElem.style.display = 'none';
822 |
823 | window.setTimeout(function() {
824 | upElem.style.display = 'inline';
825 | midElem.style.display = 'none';
826 | downElem.style.display = 'none';
827 |
828 | window.setTimeout(function() {
829 | upElem.style.display = 'none';
830 | midElem.style.display = 'inline';
831 | downElem.style.display = 'none';
832 | }, 300);
833 | }, 300);
834 | };
835 |
836 | /**
837 | * Moves the switch handle down, then back to the middle position -
838 | * for STATELESS_SWITCH only.
839 | * @param {string} id The switch ID.
840 | */
841 | panel.switchDownThenBack = function(id) {
842 | var midElem = document.getElementById(id + '-mid');
843 | var upElem = document.getElementById(id + '-up');
844 | var downElem = document.getElementById(id + '-down');
845 |
846 | upElem.style.display = 'none';
847 | midElem.style.display = 'inline';
848 | downElem.style.display = 'none';
849 |
850 | window.setTimeout(function() {
851 | upElem.style.display = 'none';
852 | midElem.style.display = 'none';
853 | downElem.style.display = 'inline';
854 |
855 | window.setTimeout(function() {
856 | upElem.style.display = 'none';
857 | midElem.style.display = 'inline';
858 | downElem.style.display = 'none';
859 | }, 400);
860 | }, 100);
861 | };
862 |
863 | /**
864 | * Handles the click event for all TOGGLE_SWITCH controls.
865 | * @param {string} id The switch ID which has been clicked.
866 | */
867 | panel.onToggle = function(id) {
868 | panel.playToggle();
869 | if (id[0] == 'S') {
870 | var bitIndex = parseInt(id.substr(1));
871 | var state = panel.addressSwitchStates[bitIndex];
872 | if (state == 0) {
873 | panel.switchUp(id);
874 | } else {
875 | panel.switchDown(id);
876 | }
877 | panel.addressSwitchStates[bitIndex] = state ? 0 : 1;
878 | } else if (id == 'OFF-ON') {
879 | if (panel.isPoweredOn) {
880 | panel.onPowerOff();
881 | panel.switchUp(id);
882 | panel.isPoweredOn = false;
883 | } else {
884 | panel.onPowerOn();
885 | panel.switchDown(id);
886 | panel.isPoweredOn = true;
887 | }
888 | }
889 | };
890 |
891 | /**
892 | * Plays a sound audio.
893 | */
894 | panel.playSound = function(id) {
895 | var sound = document.getElementById(id);
896 | sound.currentTime = 0;
897 | sound.play();
898 | };
899 |
900 | /**
901 | * Plays beep beep.
902 | */
903 | panel.playBeepbeep = function() {
904 | panel.playSound('sound-beepbeep');
905 | };
906 |
907 | /**
908 | * Plays the sound of toggle click.
909 | */
910 | panel.playToggle = function() {
911 | panel.playSound('sound-toggle');
912 | };
913 |
914 | /**
915 | * Plays the sound of stateless switch click.
916 | */
917 | panel.playSwitch = function() {
918 | panel.playSound('sound-switch');
919 | };
920 |
921 | /**
922 | * Highlights a nav tab or removes the effect.
923 | * @param {Element} elem The DOM element of the nav tab.
924 | * @param {boolean} highlight Whether highlight the tab.
925 | */
926 | panel.highlightNavTab = function(elem, highlight) {
927 | if (highlight) {
928 | elem.classList.add('selected');
929 | } else {
930 | elem.classList.remove('selected');
931 | }
932 | };
933 |
934 | /**
935 | * Shows the simulator tab, and hides the other two.
936 | */
937 | panel.showTabSim = function() {
938 | document.getElementById('tab-sim').style.display = 'block';
939 | document.getElementById('tab-debug').style.display = 'none';
940 | document.getElementById('tab-ref').style.display = 'none';
941 | panel.highlightNavTab(document.getElementById('nav-sim'), true);
942 | panel.highlightNavTab(document.getElementById('nav-debug'), false);
943 | panel.highlightNavTab(document.getElementById('nav-ref'), false);
944 | };
945 |
946 | /**
947 | * Shows the debug tab, and hides the other two.
948 | */
949 | panel.showTabDebug = function() {
950 | document.getElementById('tab-sim').style.display = 'none';
951 | document.getElementById('tab-debug').style.display = 'block';
952 | document.getElementById('tab-ref').style.display = 'none';
953 | panel.highlightNavTab(document.getElementById('nav-sim'), false);
954 | panel.highlightNavTab(document.getElementById('nav-debug'), true);
955 | panel.highlightNavTab(document.getElementById('nav-ref'), false);
956 | };
957 |
958 | /**
959 | * Shows the resource tab, and hides the other two.
960 | */
961 | panel.showTabRes = function() {
962 | document.getElementById('tab-sim').style.display = 'none';
963 | document.getElementById('tab-debug').style.display = 'none';
964 | document.getElementById('tab-ref').style.display = 'block';
965 | panel.highlightNavTab(document.getElementById('nav-sim'), false);
966 | panel.highlightNavTab(document.getElementById('nav-debug'), false);
967 | panel.highlightNavTab(document.getElementById('nav-ref'), true);
968 | };
969 |
--------------------------------------------------------------------------------
/design/panel.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 | INTE
196 |
197 |
198 | PROT
199 |
200 |
201 | MEMR
202 |
203 |
204 | INP
205 |
206 |
207 | MI
208 |
209 |
210 | OUT
211 |
212 |
213 | HLTA
214 |
215 |
216 | STACK
217 |
218 |
219 | WO
220 |
221 |
222 | INT
223 |
224 |
225 | D7
226 |
227 |
228 | D6
229 |
230 |
231 | D5
232 |
233 |
234 | D4
235 |
236 |
237 | D3
238 |
239 |
240 | D2
241 |
242 |
243 | D1
244 |
245 |
246 | D0
247 |
248 |
249 | A7
250 |
251 |
252 | A6
253 |
254 |
255 | A5
256 |
257 |
258 | A4
259 |
260 |
261 | A3
262 |
263 |
264 | A2
265 |
266 |
267 | A1
268 |
269 |
270 | A0
271 |
272 |
273 | A15
274 |
275 |
276 | HLDA
277 |
278 |
279 | A14
280 |
281 |
282 | A13
283 |
284 |
285 | A12
286 |
287 |
288 | A11
289 |
290 |
291 | A10
292 |
293 |
294 | A9
295 |
296 |
297 | A8
298 |
299 |
300 | WAIT
301 |
302 |
303 | SENSE SW.
304 |
305 |
306 | Address
307 |
308 |
309 | Data
310 |
311 |
312 | STOP
313 |
314 |
315 | RUN
316 |
317 |
318 | EXAMINE
319 |
320 |
321 | EXAMINE
322 | NEXT
323 |
324 |
325 | SINGLE
326 | STEP
327 |
328 |
329 | DEPOSIT
330 |
331 |
332 | DEPOSIT
333 | NEXT
334 |
335 |
336 | RESET
337 |
338 |
339 | CLR
340 |
341 |
342 | PROTECT
343 |
344 |
345 | AUX
346 |
347 |
348 | UNPROTECT
349 |
350 |
351 | AUX
352 |
353 |
354 | 7
355 |
356 |
357 | 6
358 |
359 |
360 | 5
361 |
362 |
363 | 4
364 |
365 |
366 | 3
367 |
368 |
369 | 2
370 |
371 |
372 | 1
373 |
374 |
375 | 0
376 |
377 |
378 | 15
379 |
380 |
381 | 14
382 |
383 |
384 | 13
385 |
386 |
387 | 12
388 |
389 |
390 | 11
391 |
392 |
393 | 10
394 |
395 |
396 | 9
397 |
398 |
399 | 8
400 |
401 |
402 | STATUS
403 |
404 |
405 | OFF
406 |
407 |
408 | ON
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 | Sim-8800: Altair 8800 Simulator
22 |
23 |
24 |
25 |
26 |
27 |
33 |
34 |
35 | Sim
36 | Debug
37 | Ref
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 | INTE
235 |
236 |
237 | PROT
238 |
239 |
240 | MEMR
241 |
242 |
243 | INP
244 |
245 |
246 | MI
247 |
248 |
249 | OUT
250 |
251 |
252 | HLTA
253 |
254 |
255 | STACK
256 |
257 |
258 | WO
259 |
260 |
261 | INT
262 |
263 |
264 | D7
265 |
266 |
267 | D6
268 |
269 |
270 | D5
271 |
272 |
273 | D4
274 |
275 |
276 | D3
277 |
278 |
279 | D2
280 |
281 |
282 | D1
283 |
284 |
285 | D0
286 |
287 |
288 | A7
289 |
290 |
291 | A6
292 |
293 |
294 | A5
295 |
296 |
297 | A4
298 |
299 |
300 | A3
301 |
302 |
303 | A2
304 |
305 |
306 | A1
307 |
308 |
309 | A0
310 |
311 |
312 | A15
313 |
314 |
315 | HLDA
316 |
317 |
318 | A14
319 |
320 |
321 | A13
322 |
323 |
324 | A12
325 |
326 |
327 | A11
328 |
329 |
330 | A10
331 |
332 |
333 | A9
334 |
335 |
336 | A8
337 |
338 |
339 | WAIT
340 |
341 |
342 | SENSE SW.
343 |
344 |
345 | Address
346 |
347 |
348 | Data
349 |
350 |
351 | STOP
352 |
353 |
354 | RUN
355 |
356 |
357 | EXAMINE
358 |
359 |
360 | EXAMINE
361 | NEXT
362 |
363 |
364 | SINGLE
365 | STEP
366 |
367 |
368 | DEPOSIT
369 |
370 |
371 | DEPOSIT
372 | NEXT
373 |
374 |
375 | RESET
376 |
377 |
378 | CLR
379 |
380 |
381 | PROTECT
382 |
383 |
384 | AUX
385 |
386 |
387 | UNPROTECT
388 |
389 |
390 | AUX
391 |
392 |
393 | 7
394 |
395 |
396 | 6
397 |
398 |
399 | 5
400 |
401 |
402 | 4
403 |
404 |
405 | 3
406 |
407 |
408 | 2
409 |
410 |
411 | 1
412 |
413 |
414 | 0
415 |
416 |
417 | 15
418 |
419 |
420 | 14
421 |
422 |
423 | 13
424 |
425 |
426 | 12
427 |
428 |
429 | 11
430 |
431 |
432 | 10
433 |
434 |
435 | 9
436 |
437 |
438 | 8
439 |
440 |
441 | STATUS
442 |
443 |
444 | OFF
445 |
446 |
447 | ON
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
571 |
572 |
573 |
A15
574 |
A14
575 |
A13
576 |
A12
577 |
A11
578 |
A10
579 |
A09
580 |
A08
581 |
A07
582 |
A06
583 |
A05
584 |
A04
585 |
A03
586 |
A02
587 |
A01
588 |
A00
589 |
590 |
591 |
OFF/ON
592 |
STOP
593 |
RUN
594 |
SINGLE STEP
595 |
EXAMINE
596 |
EXAMINE-NEXT
597 |
DEPOSIT
598 |
DEPOSIT-NEXT
599 |
RESET
600 |
601 |
602 |
603 |
604 |
605 |
606 |
609 |
610 |
611 |
Load Data
612 |
613 |
616 |
617 |
620 |
621 |
622 |
625 |
626 |
627 |
628 |
629 |
632 |
633 |
634 |
635 | How to input and run the following program to calculate 1 + 2 = 3:
636 |
637 |
638 |
639 | LDA 0080H ; 00 111 010
640 | ; 10 000 000
641 | ; 00 000 000
642 | MOV B,A ; 01 000 111
643 | LDA 0081H ; 00 111 010
644 | ; 10 000 001
645 | ; 00 000 000
646 | ADD B ; 10 000 000
647 | STA 0082H ; 00 110 010
648 | ; 10 000 010
649 | ; 00 000 000
650 | JMP 0000H ; 11 000 011
651 | ; 00 000 000
652 | ; 00 000 000
653 |
654 |
655 | Turn on Altair 8800 by clicking OFF/ON switch.
656 | Set switches A7-A0 to 00 111 010 (up for 1, down for 0).
657 | Click "DEPOSIT".
658 | Set switches A7-A0 to 10 000 000.
659 | Click "DEPOSIT NEXT".
660 | Repeat step 4-5 to input the following bytes one by one: 00 000 000, 01 000 111, 00 111 010, 10 000 001, 00 000 000, 10 000 000, 00 110 010, 10 000 010, 00 000 000, 11 000 011, 00 000 000, 00 000 000.
661 | Set switches A7-A0 to 10 000 000.
662 | Click "EXAMINE".
663 | Set switches A7-A0 to 00 000 001 (the first number to be added, or 1 in decimal).
664 | Click "DEPOSIT".
665 | Set switches A7-A0 to 00 000 010 (the second number to be added, or 2 in decimal).
666 | Click "DEPOSIT NEXT".
667 | Click "RESET".
668 | Click "RUN" and wait for a few seconds.
669 | Click "STOP".
670 | Set switches A7-A0 to 10 000 010 (the address that holds the sum).
671 | Click "EXAMINE".
672 | The LEDs D7-D0 show the result 00 000 011 (3 in decimal).
673 | Turn off Altair 8800.
674 |
675 |
676 |
677 |
678 |
681 |
686 |
687 |
690 |
703 |
704 |
705 |
706 |
707 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
731 |
732 |
733 |
--------------------------------------------------------------------------------