2 <title>Digital Logic Simulator
</title>
4 <b>Your browser does not support JavaScript or JavaScript is disabled.
</b>
7 /* DEFAULT COLOR SETTINGS */
8 var color_on
= "#fe0"; /* color of objects in high state */
9 var color_on_hlt
= "#f90";
11 var color_off
= "#bbb"; /* color of objects in low state */
12 var color_off_hlt
= "#ddd";
14 var gate_stroke_style
= "#11f";
15 var gate_stroke_width
= 1;
17 var con_stroke_style_low
= "#000";
18 var con_stroke_style_high
= "#f00";
20 var con_stroke_width
= 1;
22 /* GENERAL DATA STRUCTRURES */
23 var stage
= new Array(); /* status of stage - all details stored here */
24 var stage_rows
= 0; /* number of rows on stage */
25 var stage_cols
= 0; /* number of columns on stage */
26 var grid_status
= 1; /* boolean status of stage grid lines */
27 var grid_size
= 60; /* this is stored here for reference */
28 var canvas
; /* object id of canvas tag */
29 var ctx
; /* context of canvas */
30 var item_selected
= "gate_and"; /* only one type of object can be drawn at a time */
31 var connection_started
= false; /* connections require two clicks - source and dest */
32 var connection_coord
= new Array(); /* new connection x1,y1,x2,y2 */
33 var clock_speed
= 0; /* speed of clock in seconds (can be fractional) */
34 var clock_status
= 0; /* status of normal clock signal */
35 var clock_timer
= null; /* stores clock timer so it can be stopped */
37 var out
= new Array(); /* output bit value */
38 var input
= new Array(); /* intput bit value */
40 /* change speed of clock */
43 clock_speed
= document
.getElementById("clock_speed_select").value
;
46 /* start and stop clock */
49 if(clock_timer
!= null ){
50 clearInterval(clock_timer
);
52 document
.getElementById("clock").innerHTML
= "Clock Start";
54 clock_timer
= setInterval("clk_adv()", clock_speed
* 1000);
55 document
.getElementById("clock").innerHTML
= "Clock Stop";
59 /* advance clock one step */
63 if(clock_status
> 1) { clock_status
= 0; }
69 if(clock_status
) { return 0; } else { return 1; }
72 /* each square on grid has an associated block of data tied to it */
73 function grid(type
, value
)
75 this.type
= type
; /* type of entity @ location */
76 this.value
= value
; /* many entities have a value associated */
77 this.name
= ''; /* used for inputs and outputs */
80 /* flip bit of specified input and call redraw on screen */
81 function input_flip(v
)
83 if(v
== 'undefined') { alert("Cannot flip unknown value"); }
85 if(input
[v
].val
== 0) { input
[v
].val
= 1 } /* set data value */
86 /* loop through all grids on stage, flipping inputs */
87 for(x
=0; x
< stage_cols
; x
++) {
88 for(y
= 0; y
< stage_rows
; y
++) {
89 if(stage
[x
][y
].type
== "input") {
90 stage
[x
][y
].val
= input
[v
].val
;
97 /* flip bit of specified output and call redraw on screen */
98 function output_flip(v
)
100 if(v
== 'undefined') { alert("Cannot flip unknown value"); }
101 if(out
[v
].val
== 0) { out
[v
].val
= 1; } else { out
[v
].val
= 0; }
102 // redraw output on screen
103 draw_output_simple(v
);
106 /* reset all outputs to zero and redraw on screen */
107 function output_reset()
109 for(var i
= 0; i
< 9; i
++) {
111 // redraw outputs on screen
112 draw_output_simple(i
);
116 /* converts decimal degrees to radians */
117 function deg_to_rad(deg
)
119 return (Math
.PI
/180)*deg
;
122 /* create all output data structures and draw inital values on screen */
123 function init_stage()
127 /* create blank grid data structure */
128 for(x
= 0; x
< stage_cols
; x
++) {
129 stage
[x
] = new Array();
130 for(y
= 0; y
< stage_rows
; y
++) {
131 stage
[x
][y
] = new grid('', '');
136 stage
[0][0].type
= "clock";
137 stage
[0][1].type
= "clock_";
140 for(x
= 0; x
< 5; x
++) {
141 stage
[15][x
+3].type
= "output";
142 stage
[15][x
+3].value
= 0;
143 stage
[15][x
+3].name
= x
;
147 for(y
= "A", x
= 0; x
< 5; x
++, y
= String
.fromCharCode(y
.charCodeAt() + 1)) {
148 stage
[0][x
+3].type
= "input";
149 stage
[0][x
+3].value
= 0;
150 stage
[0][x
+3].name
= y
;
153 /* reset all command buttons */
154 item_selected
= "gate_and";
155 document
.getElementById("gate_and").disabled
= true;
156 document
.getElementById("gate_nand").disabled
= false;
157 document
.getElementById("gate_nor").disabled
= false;
158 document
.getElementById("gate_not").disabled
= false;
159 document
.getElementById("gate_or").disabled
= false;
160 document
.getElementById("gate_xor").disabled
= false;
161 document
.getElementById("delete").disabled
= false;
162 document
.getElementById("gate_connect").disabled
= false;
168 move through each grid in stage and draw contents.
169 this function can be used to refresh the screen at any time.
171 function draw_stage()
174 /* loop through all grids on stage, drawing contents */
175 for(x
=0; x
< stage_cols
; x
++) {
176 for(y
= 0; y
< stage_rows
; y
++) {
177 switch(stage
[x
][y
].type
) {
179 draw_clock(x
,y
,clock_status
);
182 draw_clock(x
,y
,clock_neg(clock_status
));
194 /* draw lines (wires) connecting gates */
201 /* intial setup of page - must be called on page load */
205 /* initalize canvas element for use */
206 canvas
= document
.getElementById("stage");
207 ctx
= canvas
.getContext("2d");
208 /* get width and height of window and set stage (canvas) with it. */
209 canvas
.height
= window
.innerHeight
-125;
210 canvas
.width
= window
.innerWidth
- 45;
211 if(grid_status
) {draw_grid(); }
216 function draw_clock(x
,y
,phase
)
218 if(x
== 'undefined' || y
== 'undefined') { alert("Coordinates Not Sent to Function"); return; }
219 if(x
< 0 || y
< 0) { alert("Coordinates Cannot Be Negative"); return ; }
220 if(phase
> 1 || phase
< 0) { alert("Clock phase not sent"); return ; }
221 x
= (x
*grid_size
) + (grid_size
/2); /* offset
from conrner
of grid */
222 y
= (y
*grid_size
) + (grid_size
/2);
224 ctx
.strokeStyle
= con_stroke_style_high
;
225 ctx
.fillStyle
= con_stroke_style_high
;
227 ctx
.strokeStyle
= con_stroke_style_low
;
228 ctx
.fillStyle
= con_stroke_style_low
;
233 ctx
.arc(x
,y
,15, deg_to_rad(360), deg_to_rad(0), false);
235 ctx
.font
= "10pt Arial";
236 ctx
.textAlign
= "center";
237 ctx
.textBaseline
= "middle";
238 ctx
.fillText("CLK", x
, y
);
239 /* draw line over text */
250 draw connection (wire) from one component to another.
251 connections are automatically drawn from one place to another.
252 grids have a certain number of evenly-spaced nodes where a line can be drawn.
253 connections can only be drawn through grids that are empty.
254 inputs can only connect to outputs.
256 function draw_connect(x1
, y1
, x2
, y2
)
258 if(x1
== x2
&& y1
== y2
) { return; } /* cannot connect same grid */
259 x1
= x1
* grid_size
; y1
= y1
* grid_size
;
260 x2
= x2
* grid_size
; y2
= y2
* grid_size
;
261 /* TODO: draw line color on basis of output (high or low) */
264 ctx
.strokeStyle
= con_stroke_style_low
;
265 ctx
.lineWidth
= con_stroke_width
;
274 start new connection from one entity to another.
276 function start_connect(e
)
278 connection_started
= true;
279 var coords
= cnvs_get_grid(e
);
280 connection_coord
[0] = coords
[0];
281 connection_coord
[1] = coords
[1];
284 function stop_connect(e
)
286 connection_started
= false;
287 var coords
= cnvs_get_grid(e
);
288 connection_coord
[2] = coords
[0];
289 connection_coord
[3] = coords
[1];
293 // draw line on canvas
294 draw_connect(connection_coord
[0], connection_coord
[1], connection_coord
[2], connection_coord
[3]);
297 /* inputs have are represented by a character with a parameter v */
298 function draw_input(x
,y
)
300 if(x
== 'undefined' || y
== 'undefined') { alert("Coordinates Not Sent to Function"); return; }
301 if(x
< 0 || y
< 0) { alert("Coordinates Cannot Be Negative"); return ; }
302 var s
= stage
[x
][y
].value
;
303 var name
= stage
[x
][y
].name
;
304 x
= (x
*grid_size
) + (grid_size
/2); /* offset
from corner
of grid */
305 y
= (y
*grid_size
) + (grid_size
/2);
306 if(s
) { ctx
.strokeStyle
= color_on_hlt
} else { ctx
.strokeStyle
= color_off
; }
307 if(s
) { ctx
.fillStyle
= color_on
} else { ctx
.fillStyle
= color_off_hlt
; }
310 /* draw background circle */
312 ctx
.arc(x
,y
,15, deg_to_rad(360), deg_to_rad(0), false);
316 ctx
.arc(x
,y
,15, deg_to_rad(360), deg_to_rad(0), false);
319 if(s
) { ctx
.fillStyle
= color_on_hlt
} else { ctx
.fillStyle
= color_off
; }
320 /* draw character on top */
321 ctx
.font
= "15pt Arial";
322 ctx
.textAlign
= "center";
323 ctx
.textBaseline
= "middle";
324 ctx
.fillText(name
, x
, y
);
327 /* simple version with auto lookup to data structure */
328 function draw_input_simple(v
)
331 if(v
== 'undefined') { alert("Cannot modify unknown output"); }
332 // TODO: Make this work with lib function instead...
359 // TODO: cannot get position of input from this location - must search grid.
360 draw_input(input
[v
].xpos
, input
[v
].ypos
, x
, input
[v
].val
);
363 /* draw output button.
365 x = grid position on x axis.
366 y = grid position on y axis.
368 function draw_output(x
,y
)
370 var w
= 30; /* width of box */
371 var tx
= x
; var ty
= y
;
372 if(x
== 'undefined' || y
== 'undefined') { alert("Coordinates Not Sent to Function"); return; }
373 if(x
< 0 || y
< 0) { alert("Coordinates Cannot Be Negative"); return ; }
374 var s
= stage
[x
][y
].value
;
375 var name
= stage
[x
][y
].name
;
376 tx
= x
* grid_size
+ (grid_size
/ 2);
377 ty
= y
* grid_size
+ (grid_size
/ 2);
379 x
= (x
* grid_size
) + (grid_size
/ 5); /* offset from corner of grid */
380 y
= (y
* grid_size
) + (grid_size
/ 5);
382 /* draw filled box around char */
383 if(s
) { ctx
.fillStyle
= color_on
} else { ctx
.fillStyle
= color_off_hlt
; }
394 /* draw outline box around char */
395 if(s
) { ctx
.strokeStyle
= color_on_hlt
} else { ctx
.strokeStyle
= color_off
; }
406 /* draw char inside box */
407 if(s
) { ctx
.fillStyle
= color_on_hlt
; } else { ctx
.fillStyle
= color_off
; }
408 ctx
.font
= "15pt Arial";
409 ctx
.textAlign
= "center";
410 ctx
.textBaseline
= "middle";
411 ctx
.fillText(name
, tx
, ty
);
414 /* simple version with auto lookup to data structure */
415 function draw_output_simple(v
)
417 if(v
== 'undefined') { alert("Cannot modify unknown output"); }
418 // TODO: cannot get position of input from this location - must search grid.
419 draw_output(out
[v
].xpos
, out
[v
].ypos
, v
, out
[v
].val
);
422 /* draw faint gridlines on stage - used as a guide for the user */
425 var x
, y
; /* current x and y position */
426 var offset
= 10; /* x and y maximum offset (far bottom or side of the window) */
427 ctx
.strokeStyle
= "#ccc";
429 /* draw vertical lines */
430 for(x
= grid_size
, y
= 0, offset
= window
.innerWidth
; x
< window
.innerWidth
; x
= x
+ grid_size
)
434 ctx
.lineTo(x
,y
+offset
);
438 /* draw horizontal lines */
439 for(x
= 0, y
= grid_size
, offset
= window
.innerWidth
; y
< window
.innerWidth
; y
= y
+ grid_size
)
443 ctx
.lineTo(x
+offset
,y
);
449 function draw_and_gate(x
,y
)
451 var h
= grid_size
* .5; /* height of square of gate */
452 var w
= grid_size
* .250; /* width of square area of gate */
453 if(x
== 'undefined' || y
== 'undefined') { alert("Coordinates Not Sent to Function"); return; }
454 if(x
< 0 || y
< 0) { alert("Coordinates Cannot Be Negative"); return ; }
455 ctx
.strokeStyle
= gate_stroke_style
;
456 ctx
.lineWidth
= gate_stroke_width
;
457 x
= (grid_size
*.25) + (grid_size
*x
); /* offset from conrner of grid */
458 y
= (grid_size
*.25) + (grid_size
*y
);
462 ctx
.arc( x
+w
, y
+(h
/2), (h/2), deg_to_rad(-90), deg_to_rad(90), false);
463 ctx
.lineTo(x
+w
, y
+h
);
469 function draw_nand_gate(x
,y
)
471 var h
= grid_size
* .5; /* height of square of gate */
472 var w
= grid_size
* .250; /* width of square area of gate */
473 if(x
== 'undefined' || y
== 'undefined') { alert("Coordinates Not Sent to Function"); return; }
474 if(x
< 0 || y
< 0) { alert("Coordinates Cannot Be Negative"); return ; }
475 ctx
.strokeStyle
= gate_stroke_style
;
476 ctx
.lineWidth
= gate_stroke_width
;
477 x
= (grid_size
*.25) + (grid_size
*x
); /* offset from conrner of grid */
478 y
= (grid_size
*.25) + (grid_size
*y
);
482 ctx
.arc( x
+w
, y
+(h
/2), (h/2), deg_to_rad(-90), deg_to_rad(90), false);
483 ctx
.lineTo(x
+w
, y
+h
);
489 ctx
.arc( x
+(w
*2)+(grid_size
*.07), y
+(h
/2), grid_size
*.08, deg_to_rad(0), deg_to_rad(360), false);
493 function draw_not_gate(x
,y
)
495 var h
= grid_size
* .5; /* height of square of gate */
496 var w
= grid_size
* .375; /* width of square area of gate */
497 if(x
== 'undefined' || y
== 'undefined') { alert("Coordinates Not Sent to Function"); return; }
498 if(x
< 0 || y
< 0) { alert("Coordinates Cannot Be Negative"); return ; }
499 ctx
.strokeStyle
= gate_stroke_style
;
500 ctx
.lineWidth
= gate_stroke_width
;
501 ctx
.lineJoin
= 'bevel';
502 x
= (grid_size
*.25) + (grid_size
*x
); /* offset from conrner of grid */
503 y
= (grid_size
*.25) + (grid_size
*y
);
508 ctx
.lineTo(x
+w
,y
+(h
/2));
515 ctx
.arc( x
+w
+6, y
+(h
/2),grid_size
*.08, deg_to_rad(0), deg_to_rad(360), false);
519 function draw_or_gate(x
,y
)
521 var h
= grid_size
* .5; /* height of square of gate */
522 var w
= grid_size
* .20; /* width of square area of gate */
523 if(x
== 'undefined' || y
== 'undefined') { alert("Coordinates Not Sent to Function"); return; }
524 if(x
< 0 || y
< 0) { alert("Coordinates Cannot Be Negative"); return ; }
525 ctx
.strokeStyle
= gate_stroke_style
;
526 ctx
.lineWidth
= gate_stroke_width
;
527 ctx
.lineJoin
= 'bevel';
528 x
= (grid_size
*.25) + (grid_size
*x
); /* offset from conrner of grid */
529 y
= (grid_size
*.25) + (grid_size
*y
);
530 /* back curve - inputs */
532 ctx
.arc(x
-(2*w
), y
+(h
/2), h
, deg_to_rad(-30), deg_to_rad(30), false);
541 ctx
.arc(x
+w
, y
+h
, h
, deg_to_rad(-90), deg_to_rad(-30), false);
550 ctx
.arc(x
+w
, y
, h
, deg_to_rad(30), deg_to_rad(90), false);
554 function draw_nor_gate(x
,y
)
556 var h
= grid_size
* .5; /* height of square of gate */
557 var w
= grid_size
* .10; /* width of square area of gate */
558 if(x
== 'undefined' || y
== 'undefined') { alert("Coordinates Not Sent to Function"); return; }
559 if(x
< 0 || y
< 0) { alert("Coordinates Cannot Be Negative"); return ; }
560 ctx
.strokeStyle
= gate_stroke_style
;
561 ctx
.lineWidth
= gate_stroke_width
;
562 ctx
.lineJoin
= 'bevel';
563 x
= (grid_size
*.25) + (grid_size
*x
); /* offset from conrner of grid */
564 y
= (grid_size
*.25) + (grid_size
*y
);
565 /* back curve - inputs */
567 ctx
.arc(x
-(4*w
), y
+(h
/2), h
, deg_to_rad(-30), deg_to_rad(30), false);
576 ctx
.arc(x
+w
, y
+h
, h
, deg_to_rad(-90), deg_to_rad(-30), false);
585 ctx
.arc(x
+w
, y
, h
, deg_to_rad(30), deg_to_rad(90), false);
589 ctx
.arc(x
+(w
*3)+(grid_size
*.30), y
+(h
/2),grid_size
*.08, deg_to_rad(0), deg_to_rad(360), false);
593 function draw_xor_gate(x
,y
)
595 var h
= grid_size
* .5; /* height of square of gate */
596 var w
= grid_size
* .10; /* width of square area of gate */
597 if(x
== 'undefined' || y
== 'undefined') { alert("Coordinates Not Sent to Function"); return; }
598 if(x
< 0 || y
< 0) { alert("Coordinates Cannot Be Negative"); return ; }
599 ctx
.strokeStyle
= gate_stroke_style
;
600 ctx
.lineWidth
= gate_stroke_width
;
601 ctx
.lineJoin
= 'bevel';
602 x
= (grid_size
*.25) + (grid_size
*x
); /* offset from conrner of grid */
603 y
= (grid_size
*.25) + (grid_size
*y
);
604 /* back curve - inputs */
606 ctx
.arc(x
-(2*w
)-(grid_size
*.20), y
+(h
/2), h
, deg_to_rad(-30), deg_to_rad(30), false);
608 /* extra back curve - inputs */
610 ctx
.arc(x
-(2*w
)-(grid_size
*.315), y
+(h
/2), h
, deg_to_rad(-30), deg_to_rad(30), false);
619 ctx
.arc(x
+w
, y
+h
, h
, deg_to_rad(-90), deg_to_rad(-30), false);
628 ctx
.arc(x
+w
, y
, h
, deg_to_rad(30), deg_to_rad(90), false);
632 /* user changes what type of gate will be drawn next */
633 function change_selection(sel
)
635 var button
= document
.getElementById(sel
);
636 if(!button
) { alert("Cannot Find Selected Button"); return; }
637 /* enable old button */
638 document
.getElementById(item_selected
).disabled
= false;
639 //alert("enabling " + item_selected);
640 /* disable new button */
641 button
.disabled
= true;
646 /* returns coordinates of canvas in pixels */
647 function cnvs_get_coordinates(e
)
649 var x_offset
= canvas
.offsetLeft
;
650 var y_offset
= canvas
.offsetTop
;
651 if(canvas
== 'undefined') { alert("Canvas parameter is undefined"); }
652 x_offset
= e
.clientX
- x_offset
;
653 y_offset
= e
.clientY
- y_offset
;
654 document
.getElementById("xycoordinates").innerHTML
="Coordinates: (" + x_offset
+ "," + y_offset
+ ")";
655 return [x_offset
,y_offset
];
658 /* returns coordinates of canvas in grids 0,0 is top left corner */
659 function cnvs_get_grid(e
)
661 var coords
= cnvs_get_coordinates(e
);
662 return [Math
.floor(coords
[0] / grid_size
), Math
.floor(coords
[1] / grid_size
)];
665 /* canvas has been clicked find out which grid and make correct change to square if needed. */
666 function cnvs_clicked(e
)
668 var coords
= cnvs_get_coordinates(e
);
669 var x_pos
= Math
.floor(coords
[0] / grid_size
);
670 var y_pos
= Math
.floor(coords
[1] / grid_size
);
671 // TODO: create case starement for each type of entity
672 // if delete command, remove contents of grid
673 if(item_selected
== "delete") {
674 // cannot allow inputs and outputs to be deleted
675 if(stage
[x_pos
][y_pos
].type
== "input" || stage
[x_pos
][y_pos
].type
== "output") { return; }
676 stage
[x_pos
][y_pos
].type
= "";
680 // if an input or output, flip value
681 if(stage
[x_pos
][y_pos
].type
== "input") {
682 input_flip(stage
[x_pos
][y_pos
].value
);
686 if(stage
[x_pos
][y_pos
].type
== "output") {
690 // if connection started, begin connection process
691 if(item_selected
== "gate_connect"){
692 if(connection_started
== false){
699 // ...otherwise perform whatever the current selected function is
700 if(stage
[x_pos
][y_pos
].type
!= "") { return; } // check for presence of an item
701 switch(item_selected
) {
703 stage
[x_pos
][y_pos
].type
= "and";
704 draw_and_gate(x_pos
,y_pos
);
707 stage
[x_pos
][y_pos
].type
= "nand";
708 draw_nand_gate(x_pos
,y_pos
);
711 stage
[x_pos
][y_pos
].type
= "nor";
712 draw_nor_gate(x_pos
,y_pos
);
715 stage
[x_pos
][y_pos
].type
= "not";
716 draw_not_gate(x_pos
,y_pos
);
719 stage
[x_pos
][y_pos
].type
= "or";
720 draw_or_gate(x_pos
,y_pos
);
723 stage
[x_pos
][y_pos
].type
= "xor";
724 draw_xor_gate(x_pos
,y_pos
);
731 <style type=
"text/css">
734 border: solid
1px #000;
738 <body onLoad=
"init_form();">
740 <center><h2>Digital Logic Simulator
</h2></center>
742 <button type=
"button" id=
"gate_and" onClick='change_selection(
"gate_and");'
disabled=
"disabled">AND
</button>
744 <button type=
"button" id=
"gate_nand" onClick='change_selection(
"gate_nand");'
>NAND
</button>
746 <button type=
"button" id=
"gate_nor" onClick='change_selection(
"gate_nor");'
>NOR
</button>
748 <button type=
"button" id=
"gate_not" onClick='change_selection(
"gate_not");'
>NOT
</button>
750 <button type=
"button" id=
"gate_or" onClick='change_selection(
"gate_or");'
>OR
</button>
752 <button type=
"button" id=
"gate_xor" onClick='change_selection(
"gate_xor");'
>XOR
</button>
754 <button type=
"button" id=
"delete" onClick='change_selection(
"delete");'
>Delete
</button>
756 <button type=
"button" id=
"gate_connect" onClick='change_selection(
"gate_connect");'
>Connect
</button>
758 <button type=
"button" id=
"move" onClick='change_selection(
"move");'
>Move
</button>
760 <button type=
"button" id=
"pan" onClick='change_selection(
"pan");'
>Pan
</button>
762 <button type=
"button" id=
"zoom" onClick='change_selection(
"zoom");'
>Zoom
</button>
764 <button type=
"button" id=
"clock" onClick='clk_toggle();'
>Clock Start
</button>
766 <select id=
"clock_speed_select" onChange='clk_change();'
>
767 <option value=
"5">Slow
</option>
768 <option value=
"1">Medium
</option>
769 <option value=
".5">Fast
</option>
770 <option value=
"-1">Unlimited
</option>
774 <div id=xycoordinates
> </div>
776 <canvas id=
"stage" width=
"200" height=
"200" onMouseMove=
"cnvs_get_coordinates(event)" onClick=
"cnvs_clicked(event);">
777 Your browser does not support HTML5 Canvas.