Removed bit flip buttons from interface.
[CS-101.git] / digital_logic_simulator.html
blobdf1aa475b2de46562445b7c5d1c25c0b4df0c575
1 <html>
2 <title>Digital Logic Simulator</title>
3 <script>
4 /* DEFAULT COLOR SETTINGS */
5 var color_on = "#fe0"; /* color of objects in high state */
6 var color_on_hlt = "#f90";
8 var color_off = "#bbb"; /* color of objects in low state */
9 var color_off_hlt = "#ddd";
11 /* GENERAL DATA STRUCTRURES */
12 var stage = new Array(); /* status of stage - all details stored here */
13 var stage_rows = 0; /* number of rows on stage */
14 var stage_cols = 0; /* number of columns on stage */
15 var grid_status = 1; /* boolean status of stage grid lines */
16 var grid_size = 50; /* this is stored here for reference */
17 var canvas; /* object id of canvas tag */
18 var ctx; /* context of canvas */
19 var item_selected = "gate_and" /* only one type of object can be drawn at a time */
21 var out = new Array(); /* output bits */
22 var input = new Array(); /* intput bits */
24 /* each square on grid has an associated block of data tied to it */
25 function grid(type, value)
27 this.type = type; /* type of entity @ location */
28 this.value = value; /* many entities have a value associated */
29 this.name = ''; /* used for inputs and outputs */
32 /* flip bit of specified input and redraw on screen */
33 function input_flip(v)
35 if(v == 'undefined') { alert("Cannot flip unknown value"); }
36 if(input[v].val == 0) { input[v].val = 1; } else { input[v].val = 0; }
37 // redraw output on screen
38 draw_input_simple(v);
41 /* flip bit of specified output and redraw on screen */
42 function output_flip(v)
44 if(v == 'undefined') { alert("Cannot flip unknown value"); }
45 if(out[v].val == 0) { out[v].val = 1; } else { out[v].val = 0; }
46 // redraw output on screen
47 draw_output_simple(v);
50 /* reset all outputs to zero and redraw on screen */
51 function output_reset()
53 for(var i = 0; i < 9; i++) {
54 out[i].val = 0;
55 // redraw outputs on screen
56 draw_output_simple(i);
60 /* converts decimal degrees to radians */
61 function deg_to_rad(deg)
63 return (Math.PI/180)*deg;
66 /* create all output data structures and draw inital values on screen */
67 function init_stage()
69 var x; var y;
71 /* create blank grid data structure */
72 for(x = 0; x < stage_cols; x++) {
73 stage[x] = new Array();
74 for(y = 0; y < stage_rows; y++) {
75 stage[x][y] = new grid('', '');
79 /* setup clocks */
80 stage[0][0].type = "clock";
81 stage[0][1].type = "clock_";
83 /* setup inputs */
84 for(x = 0; x < 5; x++) {
85 stage[15][x+3].type = "input";
86 stage[15][x+3].value = 0;
87 stage[15][x+3].name = x;
90 /* setup outputs */
91 for(y = "A", x = 0; x < 5; x++, y = String.fromCharCode(y.charCodeAt() + 1)) {
92 stage[0][x+3].type = "output";
93 stage[0][x+3].value = 0;
94 stage[0][x+3].name = y;
97 /* reset all command buttons */
98 item_selected = "gate_and";
99 document.getElementById("gate_and").disabled = true;
100 document.getElementById("gate_nand").disabled = false;
101 document.getElementById("gate_nor").disabled = false;
102 document.getElementById("gate_not").disabled = false;
103 document.getElementById("gate_or").disabled = false;
104 document.getElementById("gate_xor").disabled = false;
105 document.getElementById("delete").disabled = false;
109 move through each grid in stage and draw contents.
110 this function can be used to refresh the screen at any time.
112 function draw_stage()
114 var x; var y;
115 /* loop through all grids on stage, drawing contents */
116 for(x=0; x < stage_cols; x++) {
117 for(y = 0; y < stage_rows; y++) {
118 switch(stage[x][y].type) {
119 case "clock":
120 draw_clock(x,y,1);
121 break;
122 case "clock_":
123 draw_clock(x,y,0);
124 break;
125 case "input":
126 draw_input(x,y,x);
127 break;
128 case "output":
129 draw_output(x,y,x);
130 break;
135 /* draw lines (wires) connecting gates */
137 /* update inputs */
139 /* update outputs */
142 /* intial setup of page - must be called on page load */
143 function init_form()
145 var x; var y;
146 /* initalize canvas element for use */
147 canvas = document.getElementById("stage");
148 ctx = canvas.getContext("2d");
149 /* get width and height of window and set stage (canvas) with it. */
150 canvas.height = window.innerHeight-125;
151 canvas.width = window.innerWidth - 45;
152 if(grid_status) {draw_grid(); }
153 init_stage();
154 draw_stage();
157 function draw_clock(x,y,phase)
159 if(x == 'undefined' || y == 'undefined') { alert("Coordinates Not Sent to Function"); return; }
160 if(x < 0 || y < 0) { alert("Coordinates Cannot Be Negative"); return ; }
161 if(phase > 1 || phase < 0) { alert("Clock phase not sent"); return ; }
162 x = (x*grid_size) + (grid_size/2); /* offset from conrner of grid */
163 y = (y*grid_size) + (grid_size/2);
164 ctx.strokeStyle = "#f33";
165 ctx.lineWidth = 2;
166 ctx.moveTo(x,y);
167 ctx.beginPath();
168 ctx.arc(x,y,15, deg_to_rad(360), deg_to_rad(0), false);
169 ctx.stroke();
170 ctx.font = "10pt Arial";
171 ctx.textAlign = "center";
172 ctx.textBaseline = "middle";
173 ctx.fillText("CLK", x, y);
174 /* draw line over text */
175 if(phase == 0) {
176 ctx.strokeStyle = "#000";
177 ctx.moveTo(x,y);
178 ctx.beginPath();
179 ctx.lineTo(x-8,y-8);
180 ctx.lineTo(x+8,y-8);
181 ctx.stroke();
185 /* inputs have are represented by a character with a parameter v */
186 function draw_input(x,y)
188 if(x == 'undefined' || y == 'undefined') { alert("Coordinates Not Sent to Function"); return; }
189 if(x < 0 || y < 0) { alert("Coordinates Cannot Be Negative"); return ; }
190 var s = stage[x][y].value;
191 var name = stage[x][y].name;
192 x = (x*grid_size) + (grid_size/2); /* offset from corner of grid */
193 y = (y*grid_size) + (grid_size/2);
194 if(s) { ctx.strokeStyle = color_on_hlt } else { ctx.strokeStyle = color_off; }
195 if(s) { ctx.fillStyle = color_on } else { ctx.fillStyle = color_off_hlt; }
196 ctx.lineWidth = 2;
197 ctx.moveTo(x,y);
198 /* draw background circle */
199 ctx.beginPath();
200 ctx.arc(x,y,15, deg_to_rad(360), deg_to_rad(0), false);
201 ctx.fill();
203 ctx.beginPath();
204 ctx.arc(x,y,15, deg_to_rad(360), deg_to_rad(0), false);
205 ctx.stroke();
207 if(s) { ctx.fillStyle = color_on_hlt } else { ctx.fillStyle = color_off; }
208 /* draw character on top */
209 ctx.font = "15pt Arial";
210 ctx.textAlign = "center";
211 ctx.textBaseline = "middle";
212 ctx.fillText(name, x, y);
215 /* simple version with auto lookup to data structure */
216 function draw_input_simple(v)
218 var x;
219 if(v == 'undefined') { alert("Cannot modify unknown output"); }
220 // TODO: Make this work with lib function instead...
221 switch(v) {
222 case 1:
223 x = "A";
224 break;
225 case 2:
226 x = "B";
227 break;
228 case 3:
229 x = "C";
230 break;
231 case 4:
232 x = "D";
233 break;
234 case 5:
235 x = "E";
236 break;
237 case 6:
238 x = "F";
239 break;
240 case 7:
241 x = "G";
242 break;
243 case 8:
244 x = "H";
245 break;
247 draw_input(input[v].xpos, input[v].ypos, x, input[v].val);
250 /* draw output button.
252 x = grid position on x axis.
253 y = grid position on y axis.
255 function draw_output(x,y)
257 var w = 30; /* width of box */
258 var tx = x; var ty = y;
259 if(x == 'undefined' || y == 'undefined') { alert("Coordinates Not Sent to Function"); return; }
260 if(x < 0 || y < 0) { alert("Coordinates Cannot Be Negative"); return ; }
261 var s = stage[x][y].value;
262 var name = stage[x][y].name;
263 tx = x * grid_size + (grid_size / 2);
264 ty = y * grid_size + (grid_size / 2);
267 x = (x * grid_size) + (grid_size / 5); /* offset from corner of grid */
268 y = (y * grid_size) + (grid_size / 5);
270 /* draw filled box around char */
271 if(s) { ctx.fillStyle = color_on } else { ctx.fillStyle = color_off_hlt; }
272 ctx.lineWidth = 2;
273 ctx.moveTo(x,y);
274 ctx.beginPath();
275 ctx.lineTo(x+w,y);
276 ctx.lineTo(x+w,y+w);
277 ctx.lineTo(x,y+w);
278 ctx.lineTo(x,y);
279 ctx.lineTo(x+w,y);
280 ctx.fill();
282 /* draw outline box around char */
283 if(s) { ctx.strokeStyle = color_on_hlt } else { ctx.strokeStyle = color_off; }
284 ctx.lineWidth = 2;
285 ctx.moveTo(x,y);
286 ctx.beginPath();
287 ctx.lineTo(x+w,y);
288 ctx.lineTo(x+w,y+w);
289 ctx.lineTo(x,y+w);
290 ctx.lineTo(x,y);
291 ctx.lineTo(x+w,y);
292 ctx.stroke();
294 /* draw char inside box */
295 if(s) { ctx.fillStyle = color_on_hlt; } else { ctx.fillStyle = color_off; }
296 ctx.font = "15pt Arial";
297 ctx.textAlign = "center";
298 ctx.textBaseline = "middle";
299 ctx.fillText(name, tx, ty);
302 /* simple version with auto lookup to data structure */
303 function draw_output_simple(v)
305 if(v == 'undefined') { alert("Cannot modify unknown output"); }
306 draw_output(out[v].xpos, out[v].ypos, v, out[v].val);
309 /* draw faint gridlines on stage - used as a guide for the user */
310 function draw_grid()
312 var x, y; /* current x and y position */
313 var offset = 10; /* x and y maximum offset (far bottom or side of the window) */
314 ctx.strokeStyle = "#ccc";
315 ctx.lineWidth = 1;
316 /* draw vertical lines */
317 for(x = grid_size, y = 0, offset = window.innerWidth; x < window.innerWidth; x = x + grid_size)
319 ctx.beginPath();
320 ctx.moveTo(x,y);
321 ctx.lineTo(x,y+offset);
322 ctx.stroke();
323 stage_cols++;
325 /* draw horizontal lines */
326 for(x = 0, y = grid_size, offset = window.innerWidth; y < window.innerWidth; y = y + grid_size)
328 ctx.beginPath();
329 ctx.moveTo(x,y);
330 ctx.lineTo(x+offset,y);
331 ctx.stroke();
332 stage_rows++;
336 function draw_and_gate(x,y)
338 var h = 50; /* height of square of gate */
339 var w = 30; /* width of square area of gate */
340 if(x == 'undefined' || y == 'undefined') { alert("Coordinates Not Sent to Function"); return; }
341 if(x < 0 || y < 0) { alert("Coordinates Cannot Be Negative"); return ; }
342 ctx.strokeStyle = "#11f";
343 ctx.lineWidth = 3;
344 x = 10 + (grid_size*x); /* offset from conrner of grid */
345 y = 10 + (grid_size*y);
346 ctx.beginPath();
347 ctx.moveTo(x,y);
348 ctx.lineTo(x+w, y);
349 ctx.arc( x+w, y+(h/2), (h/2), deg_to_rad(-90), deg_to_rad(90), false);
350 ctx.lineTo(x+w, y+h);
351 ctx.lineTo(x,y+h);
352 ctx.lineTo(x,y);
353 ctx.stroke();
356 function draw_nand_gate(x,y)
358 var h = 50; /* height of square of gate */
359 var w = 30; /* width of square area of gate */
360 if(x == 'undefined' || y == 'undefined') { alert("Coordinates Not Sent to Function"); return; }
361 if(x < 0 || y < 0) { alert("Coordinates Cannot Be Negative"); return ; }
362 ctx.strokeStyle = "#11f";
363 ctx.lineWidth = 3;
364 x = 10 + (grid_size*x); /* offset from conrner of grid */
365 y = 10 + (grid_size*y);
366 ctx.beginPath();
367 ctx.moveTo(x,y);
368 ctx.lineTo(x+w, y);
369 ctx.arc( x+w, y+(h/2), (h/2), deg_to_rad(-90), deg_to_rad(90), false);
370 ctx.lineTo(x+w, y+h);
371 ctx.lineTo(x,y+h);
372 ctx.lineTo(x,y);
373 ctx.stroke();
374 /* nose */
375 ctx.beginPath();
376 ctx.arc( x+(w*2), y+(h/2),5, deg_to_rad(0), deg_to_rad(360), false);
377 ctx.stroke();
380 function draw_not_gate(x,y)
382 var h = 50; /* height of square of gate */
383 var w = 40; /* width of square area of gate */
384 if(x == 'undefined' || y == 'undefined') { alert("Coordinates Not Sent to Function"); return; }
385 if(x < 0 || y < 0) { alert("Coordinates Cannot Be Negative"); return ; }
386 ctx.strokeStyle = "#11f";
387 ctx.lineWidth = 3;
388 ctx.lineJoin = 'bevel';
389 x = 10 + (grid_size*x); /* offset from conrner of grid */
390 y = 10 + (grid_size*y);
392 /* triangle */
393 ctx.beginPath();
394 ctx.moveTo(x,y);
395 ctx.lineTo(x+w,y+(h/2));
396 ctx.lineTo(x,y+h);
397 ctx.lineTo(x,y);
398 ctx.stroke();
400 /* nose */
401 ctx.beginPath();
402 ctx.arc( x+w+6, y+(h/2),5, deg_to_rad(0), deg_to_rad(360), false);
403 ctx.stroke();
406 function draw_or_gate(x,y)
408 var h = 50; /* height of square of gate */
409 var w = 21; /* width of square area of gate */
410 if(x == 'undefined' || y == 'undefined') { alert("Coordinates Not Sent to Function"); return; }
411 if(x < 0 || y < 0) { alert("Coordinates Cannot Be Negative"); return ; }
412 ctx.strokeStyle = "#11f";
413 ctx.lineWidth = 3;
414 ctx.lineJoin = 'bevel';
415 x = 10 + (grid_size*x); /* offset from conrner of grid */
416 y = 10 + (grid_size*y);
417 /* back curve - inputs */
418 ctx.beginPath();
419 ctx.arc(x-(2*w), y +(h/2), h, deg_to_rad(-30), deg_to_rad(30), false);
420 ctx.stroke();
421 /* top line */
422 ctx.beginPath();
423 ctx.moveTo(x,y);
424 ctx.lineTo(x+w,y);
425 ctx.stroke();
426 /* top curve */
427 ctx.beginPath();
428 ctx.arc(x+w, y+h, h, deg_to_rad(-90), deg_to_rad(-30), false);
429 ctx.stroke();
430 /* bottom line */
431 ctx.beginPath();
432 ctx.moveTo(x,y+h);
433 ctx.lineTo(x+w,y+h);
434 ctx.stroke();
435 /* bottom curve */
436 ctx.beginPath();
437 ctx.arc(x+w, y, h, deg_to_rad(30), deg_to_rad(90), false);
438 ctx.stroke();
441 function draw_nor_gate(x,y)
443 var h = 50; /* height of square of gate */
444 var w = 21; /* width of square area of gate */
445 if(x == 'undefined' || y == 'undefined') { alert("Coordinates Not Sent to Function"); return; }
446 if(x < 0 || y < 0) { alert("Coordinates Cannot Be Negative"); return ; }
447 ctx.strokeStyle = "#11f";
448 ctx.lineWidth = 3;
449 ctx.lineJoin = 'bevel';
450 x = 10 + (grid_size*x); /* offset from conrner of grid */
451 y = 10 + (grid_size*y);
452 /* back curve - inputs */
453 ctx.beginPath();
454 ctx.arc(x-(2*w), y +(h/2), h, deg_to_rad(-30), deg_to_rad(30), false);
455 ctx.stroke();
456 /* top line */
457 ctx.beginPath();
458 ctx.moveTo(x,y);
459 ctx.lineTo(x+w,y);
460 ctx.stroke();
461 /* top curve */
462 ctx.beginPath();
463 ctx.arc(x+w, y+h, h, deg_to_rad(-90), deg_to_rad(-30), false);
464 ctx.stroke();
465 /* bottom line */
466 ctx.beginPath();
467 ctx.moveTo(x,y+h);
468 ctx.lineTo(x+w,y+h);
469 ctx.stroke();
470 /* bottom curve */
471 ctx.beginPath();
472 ctx.arc(x+w, y, h, deg_to_rad(30), deg_to_rad(90), false);
473 ctx.stroke();
474 /* nose */
475 ctx.beginPath();
476 ctx.arc(x+(w*3)+6, y+(h/2),5, deg_to_rad(0), deg_to_rad(360), false);
477 ctx.stroke();
480 function draw_xor_gate(x,y)
482 var h = 50; /* height of square of gate */
483 var w = 21; /* width of square area of gate */
484 if(x == 'undefined' || y == 'undefined') { alert("Coordinates Not Sent to Function"); return; }
485 if(x < 0 || y < 0) { alert("Coordinates Cannot Be Negative"); return ; }
486 ctx.strokeStyle = "#11f";
487 ctx.lineWidth = 3;
488 ctx.lineJoin = 'bevel';
489 x = 10 + (grid_size*x); /* offset from conrner of grid */
490 y = 10 + (grid_size*y);
491 /* back curve - inputs */
492 ctx.beginPath();
493 ctx.arc(x-(2*w), y +(h/2), h, deg_to_rad(-30), deg_to_rad(30), false);
494 ctx.stroke();
495 /* extra back curve - inputs */
496 ctx.beginPath();
497 ctx.arc(x-(2*w)-10, y +(h/2), h, deg_to_rad(-30), deg_to_rad(30), false);
498 ctx.stroke();
499 /* top line */
500 ctx.beginPath();
501 ctx.moveTo(x,y);
502 ctx.lineTo(x+w,y);
503 ctx.stroke();
504 /* top curve */
505 ctx.beginPath();
506 ctx.arc(x+w, y+h, h, deg_to_rad(-90), deg_to_rad(-30), false);
507 ctx.stroke();
508 /* bottom line */
509 ctx.beginPath();
510 ctx.moveTo(x,y+h);
511 ctx.lineTo(x+w,y+h);
512 ctx.stroke();
513 /* bottom curve */
514 ctx.beginPath();
515 ctx.arc(x+w, y, h, deg_to_rad(30), deg_to_rad(90), false);
516 ctx.stroke();
519 /* user changes what type of gate will be drawn next */
520 function change_selection(sel)
522 var button = document.getElementById(sel);
523 if(!button) { alert("Cannot Find Selected Button"); return; }
524 /* enable old button */
525 document.getElementById(item_selected).disabled = false;
526 //alert("enabling " + item_selected);
527 /* disable new button */
528 button.disabled = true;
529 /* set variable */
530 item_selected = sel;
533 /* returns coordinates of canvas in pixels */
534 function cnvs_get_coordinates(e)
536 var x_offset = canvas.offsetLeft;
537 var y_offset = canvas.offsetTop;
538 if(canvas == 'undefined') { alert("Canvas parameter is undefined"); }
539 x_offset = e.clientX - x_offset;
540 y_offset = e.clientY - y_offset;
541 document.getElementById("xycoordinates").innerHTML="Coordinates: (" + x_offset + "," + y_offset + ")";
542 return [x_offset,y_offset];
545 /* canvas has been clicked find out which grid and make correct change to square if needed. */
546 function cnvs_clicked(e)
548 var coords = cnvs_get_coordinates(e);
549 var x_pos = Math.floor(coords[0] / grid_size);
550 var y_pos = Math.floor(coords[1] / grid_size);
551 // if delete command, remove contents of grid
552 if(item_selected == "delete") {
553 // cannot allow inputs and outputs to be deleted
554 if(stage[x_pos][y_pos].type == "input" || stage[x_pos][y_pos].type == "output") { return; }
555 stage[x_pos][y_pos].type = "";
556 draw_stage();
559 // if an input or output, flip value
561 // ...otherwise perform whatever the current selected function is
562 if(stage[x_pos][y_pos].type != "") { return; } // check for presence of an item
563 switch(item_selected) {
564 case "gate_and":
565 stage[x_pos][y_pos].type = "and";
566 draw_and_gate(x_pos,y_pos);
567 break;
568 case "gate_nand":
569 stage[x_pos][y_pos].type = "nand";
570 draw_nand_gate(x_pos,y_pos);
571 break;
572 case "gate_nor":
573 stage[x_pos][y_pos].type = "nor";
574 draw_nor_gate(x_pos,y_pos);
575 break;
576 case "gate_not":
577 stage[x_pos][y_pos].type = "not";
578 draw_not_gate(x_pos,y_pos);
579 break;
580 case "gate_or":
581 stage[x_pos][y_pos].type = "or";
582 draw_or_gate(x_pos,y_pos);
583 break;
584 case "gate_xor":
585 stage[x_pos][y_pos].type = "xor";
586 draw_xor_gate(x_pos,y_pos);
587 break;
591 </script>
593 <style type="text/css">
595 #stage {
596 border: solid 1px #000;
599 </style>
600 <body onLoad="init_form();">
602 <center><h2>Digital Logic Simulator</h2></center>
603 <center>
604 <button type="button" id="gate_and" onClick='change_selection("gate_and");' disabled="disabled">AND</button>
605 &nbsp;&nbsp;
606 <button type="button" id="gate_nand" onClick='change_selection("gate_nand");'>NAND</button>
607 &nbsp;&nbsp;
608 <button type="button" id="gate_nor" onClick='change_selection("gate_nor");'>NOR</button>
609 &nbsp;&nbsp;
610 <button type="button" id="gate_not" onClick='change_selection("gate_not");'>NOT</button>
611 &nbsp;&nbsp;
612 <button type="button" id="gate_or" onClick='change_selection("gate_or");'>OR</button>
613 &nbsp;&nbsp;
614 <button type="button" id="gate_xor" onClick='change_selection("gate_xor");'>XOR</button>
615 &nbsp;&nbsp;
616 <button type="button" id="delete" onClick='change_selection("delete");'>Delete</button>
617 &nbsp;&nbsp;
618 <button type="button" id="gate_connect" onClick='change_selection("gate_connect");'>Connect</button>
619 </center>
621 <div id=xycoordinates>&nbsp;</div>
623 <canvas id="stage" width="200" height="200" onMouseMove="cnvs_get_coordinates(event)" onClick="cnvs_clicked(event);">
624 Your browser does not support HTML5 Canvas.
625 </canvas>
627 </body>
628 </html>