Gordons Projects

--> Projects Top-Level GIT

Initial GIT creation
[lmc] / lmc.rtb
1
2 // LMC: The Little Man Computer
3 //  It's a combined assembler and interpreter for the LMC
4 //  computer system.
5
6 // This implementation copyright (c) 2014 Gordon Henderson
7 //  and features a JSR/RET extension, as well as Raspberry
8 //  Pi GPIO. (simplified!)
9
10 // The LMC memory
11
12   dim memory (100)
13
14 // Opcodes for the disassembler
15
16   dim opcodeDef$ (10)
17
18 // Storage for the assembler
19
20   dim labels$   (100)
21   dim opcodes$  (100)
22   dim operands$ (100)
23   dim labels    (999) // Bigger to reduce ptential hash clashes
24
25 // LMC modes
26
27   lmcRunning  = FALSE
28   lmcError    = FALSE
29   lmcInput    = FALSE
30
31 // Initialise and get going
32
33   proc setup
34   proc drawLmc
35
36   proc updateModeIndicators
37   proc displayLmcMemory
38   proc displayLmcRegisters
39   update
40
41 //======================================================================
42 // Main input loop:
43 //  Read the keyboard and do what's needed.
44 //======================================================================
45
46   cycle
47
48 // Update the screen
49
50     proc updateModeIndicators
51     proc displayLmcMemory
52     proc displayLmcRegisters
53
54 // disassemble the current instruction
55
56     value = memory (pc)
57
58     proc disAsm (value)
59
60     update
61
62     cycle
63       key = inkey
64       if key <> -1 then wait (0.01)
65     repeat until key <> -1
66
67     lmcError = FALSE
68
69     switch (key)
70
71       case keyF1
72         proc lmcHelp
73       endcase
74
75       case keyF2
76         proc lmcInstructionSet
77       endcase
78
79 // Load?
80
81       case 76, 108
82         proc loadFile
83         proc drawLmc
84       endcase
85
86 // Assemble
87
88       case 65, 97
89         proc assembleFile
90         proc drawLmc
91       endcase
92
93 // Print (Really debugging)
94
95       case 80, 112
96         proc printFile
97         proc drawLmc
98       endcase
99
100 // Arrow keys
101
102       case keyUp
103         pc = pc - 1
104         if (pc mod 10) = 9 then pc = pc + 10
105       endcase
106
107       case keyDown
108         pc = pc + 1
109         if (pc mod 10) = 0 then pc = pc - 10
110       endcase
111
112       case keyLeft
113         pc = pc - 10
114         if pc < 0 then pc = pc + 100
115       endcase
116
117       case keyRight
118         pc = pc + 10
119         if pc > 99 then pc = pc - 100
120       endcase
121
122 // Enter moves to the next location
123
124       case 10, 13
125         proc incPc
126       endcase
127
128 // Negative key
129
130       case 45
131         if value <> 0 then value = -value
132         memory (pc) = value
133       endcase
134
135 // Number key
136
137       case 48, 49, 50, 51, 52, 53, 54, 55, 56, 57
138         key = key - 48 // Convert to decimal digit
139         tmp = abs (value) * 10 + key
140         if tmp > 999 then tmp = tmp MOD 1000
141         memory (pc) = tmp * sgn (value) + 0 // The +0 copes with negative 0 issues.
142       endcase
143
144 // r for Run (Lower case r to run slowly with screen updates)
145
146       case 114
147         pc = 0
148         proc lmcRunMode (TRUE)
149         nextCycle = TIME + 100  // Run at 10 instructions/second
150         cycle
151           proc displayLmcMemory
152           proc disAsm (memory (pc))
153           proc displayLmcRegisters
154           update
155           proc runLmcStep
156
157           if TIME > nextCycle then nextCycle = TIME // Probably an INP instruction
158           while TIME < nextCycle cycle
159             wait (0.01)
160           repeat
161           nextCycle = nextCycle + 100
162           if inkey = 32 then lmcRunning = FALSE
163         repeat until NOT lmcRunning
164       endcase
165
166 // R for Run (Upper case R to run fast!)
167
168       case 82
169         pc = 0
170         proc lmcRunMode (TRUE)
171         cycle
172           proc runLmcStep
173           if inkey = 32 then lmcRunning = FALSE
174         repeat until NOT lmcRunning
175       endcase
176
177 // S or s for a Single Step from the current PC location
178
179       case 83, 115
180         proc lmcRunMode (TRUE)
181         proc runLmcStep
182 //        proc displayLmcMemory
183         proc lmcRunMode (FALSE)
184       endcase
185
186 // Capital Z to zero memory
187
188       case 90
189         proc resetLmc
190         proc updateModeIndicators
191         proc displayLmcMemory
192         update
193       endcase
194
195     endswitch
196
197   repeat
198 end
199
200
201 // incPc:
202 //  Increment the Program Counter and wrap at 1000
203 //======================================================================
204
205 def proc incPc
206   if pc = 999 then
207     pc = 0
208   else
209     pc = pc + 1
210   endif
211 endproc
212
213
214 // runLmcStep:
215 //  Run a single step of the computer
216 //======================================================================
217
218 def proc runLmcStep
219
220   local value, opcode, operand
221
222   value   = memory (pc)
223   opcode  = value DIV 100
224   operand = value MOD 100
225
226 // Executing data?
227
228   if value < 0 then
229     lmcRunning = FALSE
230     lmcError   = TRUE
231     endproc
232   endif
233
234 // Check opcode
235
236   switch (opcode)
237
238     case 0  // Halt - time for coffee
239       lmcRunning = FALSE
240       if operand <> 0 then lmcError = TRUE
241     endcase
242
243     case 1  // Add
244       acc = acc + memory (operand)
245       proc incPc
246     endcase
247
248     case 2  // Sub
249       acc = acc - memory (operand)
250       proc incPc
251     endcase
252
253     case 3  // STA - Store
254       memory (operand) = acc
255       proc incPc
256     endcase
257
258     case 4  // JSR - Jump to Subroutine - GH Extension
259       proc incPc
260       sPc = pc
261       pc  = operand
262     endcase
263
264     case 5  // LDA - Load
265       acc = memory (operand)
266       proc incPc
267     endcase
268
269     case 6  // BRA - Unconitional Branch
270       pc = operand
271     endcase
272
273     case 7  // BRZ - Branch if Acc is zero
274       if acc = 0 then
275         pc = operand
276       else
277         proc incPc
278       endif
279     endcase
280
281     case 8  // BRP - Branch if the Acc is positive
282       if acc >= 0 then
283         pc = operand
284       else
285         proc incPc
286       endif
287     endcase
288
289     case 9  // Multi-Use instruction
290       switch (operand)
291         case 1
292           lmcInput = TRUE
293           proc updateModeIndicators
294           proc inputAcc
295           if lmcRunning then proc incPc // We could have halted & want to re-start this instruction
296           lmcInput = FALSE
297           proc updateModeIndicators
298         endcase
299
300         case 2
301           proc outputAcc
302           proc incPc
303         endcase
304
305         case 3    // RET
306           pc = sPc
307         endcase
308
309 // GPIO
310         case 10   // GIN - GPIO Input
311           pinMode (acc, pinInput)
312           acc = digitalRead (acc)
313           proc incPc
314         endcase
315
316         case 11   // GHI - GPIO Output High
317           pinMode (acc, pinOutput)
318           digitalWrite (acc, 1)
319           proc incPc
320         endcase
321
322         case 12   // GLO - GPIO Output Low
323           pinMode (acc, pinOutput)
324           digitalWrite (acc, 0)
325           proc incPc
326         endcase
327
328         default
329           lmcRunning = FALSE
330           lmcError   = TRUE
331         endcase
332       endswitch
333
334     endcase
335
336   endswitch
337
338 endproc
339
340
341 // pData:
342 //  Convenience procedure to print Data: NNN
343 //======================================================================
344
345 def proc pData (v)
346   print "Data: ";
347   proc pNum (v)
348 endproc
349
350
351 // disAsm
352 //  Disassemble the instruction at the PC and print it on the screem
353 //======================================================================
354
355 def proc disAsm (value)
356
357   local operator, operand
358
359   paper = Black
360   if lmcRunning then
361     ink = Pink
362   else
363     ink = Yellow
364   endif
365
366   hvTab (xOffset + 10, yOffset + 14)
367
368   operator = value DIV 100
369   operand  = value MOD 100
370
371   if value < 0 then
372     proc pData (value)
373   else
374     switch (operator)
375
376       case 0
377         if operand = 0 then
378           print "HLT";
379         else
380           proc pData (value)
381         endif
382         endcase
383
384       case 1, 2, 3, 4, 5, 6, 7, 8
385         print opcodeDef$ (operator); "     ";
386         if operand < 10 then print "0";
387         print operand;
388       endcase
389
390       case 9
391         switch (operand)
392           case 1
393             print "INP";
394           endcase
395           case 2
396             print "OUT";
397           endcase
398           case 3
399             print "RET";
400           endcase
401           case 10
402             print "GIN";
403           endcase
404           case 11
405             print "GHI";
406           endcase
407           case 12
408             print "GLO";
409           endcase
410           default
411             proc pData (value)
412           endcase
413         endswitch
414       endcase
415
416       default
417         proc pData (value)
418       endcase
419
420     endswitch
421   endif
422   print "       ";
423 endproc
424
425
426 // lmcRunMode:
427 //  Just update the run-mode indicator as required
428 //======================================================================
429
430 def proc lmcRunMode (running)
431   lmcRunning = running
432   proc updateModeIndicators
433   update
434 endproc
435
436
437 // updateModeIndicators
438 //  Update the Run/Step/Stop mode indicators
439 //======================================================================
440
441 def proc updateModeIndicators
442
443   ink = Black
444
445 // Run Lamp (Green)
446
447   colour = Green
448     ellipse ((xOffset + 62) * 8 + 2, gHeight - (yOffset * 30 +  0), 26, 20, TRUE)
449
450   if lmcRunning then
451     colour = Lime
452     ellipse ((xOffset + 62) * 8 + 2, gHeight - (yOffset * 30 +  0), 24, 18, TRUE)
453     paper = Lime
454     hvTab (xOffset + 61, yOffset + 2)
455     print "RUN";
456   endif
457
458 // Error Lamp (Red)
459
460   colour = Maroon
461     ellipse ((xOffset + 62) * 8 + 2, gHeight - (yOffset * 30 +  100), 26, 20, TRUE)
462
463   if lmcError then
464     colour = Red
465     ellipse ((xOffset + 62) * 8 + 2, gHeight - (yOffset * 30 + 100), 24, 18, TRUE)
466     paper = Red
467     hvTab (xOffset + 61, yOffset + 7)
468     print "ERR";
469   endif
470
471 // Input Lamp (Yellow)
472
473   colour = Olive
474     ellipse ((xOffset + 62) * 8 + 2, gHeight - (yOffset * 30 +  200), 26, 20, TRUE)
475
476   if lmcInput then
477     colour = Yellow
478     ellipse ((xOffset + 62) * 8 + 2, gHeight - (yOffset * 30 + 200), 24, 18, TRUE)
479     paper = Yellow
480     hvTab (xOffset + 61, yOffset + 12)
481     print "INP";
482   endif
483
484
485 endproc
486
487
488 // pNum:
489 //  Procedure to print a nuber in the fixed field-width used in the LMC
490 //  The field width is 4 characters, the first being a space or - sign,
491 //  then the number as 3 digits with leading zeros
492 //======================================================================
493
494 def proc pNum (n)
495
496   if n < 0 then
497     print "-";
498     n = abs (n)
499   else
500     print " ";
501   endif
502
503   if n > 999 then
504     print "***";
505   else
506     if n < 100 then print "0";
507     if n <  10 then print "0";
508     print n;
509   endif
510 endproc
511
512
513 // displayMemCell
514 //  Display one location of the LMCs memory on-screen
515 //======================================================================
516
517 def proc displayMemCell (loc)
518
519   local x,y
520
521   x = loc div 10 * 5
522   y = loc mod 10
523
524   hvtab (x + xOffset + 6, y + yOffset + 3)
525   proc pNum (memory (loc))
526 endproc
527
528
529 // displayLmcRegisters
530 //  Display the program counter and acc registers
531 //======================================================================
532
533 def proc displayLmcRegisters
534   ink = Pink
535   hvTab (xOffset +  6, yOffset + 16)
536   proc pNum (pc)
537   hvTab (xOffset + 11, yOffset + 16)
538   proc pNum (sPc)
539
540   ink = Lime
541   hvTab (xOffset + 25, yOffset + 16)
542   proc pNum (acc)
543 endproc
544
545
546 // outputAcc
547 //  Output the Acc in the output register box
548 //======================================================================
549
550 def proc outputAcc
551   paper = black
552   ink   = Aqua
553   hvTab (xOffset + 48, yOffset + 16)
554   proc pNum (acc)
555   update
556 endproc
557
558
559 // inputAcc
560 //  Read user input
561 //======================================================================
562
563 def proc inputAcc
564
565   local key, value
566
567   value = 0
568   paper = Black
569   ink   = Silver
570
571 // Update the display
572
573   proc displayLmcMemory
574   proc disAsm (memory (pc))
575   proc displayLmcRegisters
576
577   cycle
578
579     hvTab (xOffset + 37, yOffset + 16)
580     proc pNum (value)
581     update
582
583     cycle
584       key = inkey
585       if key = -1 then wait (0.01)
586     repeat until key <> -1
587
588     switch (key)
589
590 // Spacebar terminatest entry and aborts the run
591
592       case 32
593         lmcRunning = FALSE
594         break
595       endcase
596
597 // Enter/Return to exit
598
599       case 10, 13
600         acc = value
601         break
602       endcase
603
604 // Negative key
605
606     case 45
607       if value <> 0 then value = -value
608     endcase
609
610 // Number key
611
612     case 48, 49, 50, 51, 52, 53, 54, 55, 56, 57
613       key = key - 48 // Convert to decimal digit
614       tmp = abs (value) * 10 + key
615       if tmp > 999 then tmp = tmp MOD 1000
616       value = tmp * sgn (value) + 0 // The +0 copes with negative 0 issues.
617     endcase
618
619   endswitch
620
621   repeat
622
623 endproc
624
625
626 // displayLmcMemory
627 //  Display the LMC memory on the screen
628 //======================================================================
629 ////////////////////////////////////////////////////////////////////////
630
631 def proc displayLmcMemory
632   local i, col
633
634   if lmcRunning then
635     col = Pink
636   else
637     col = Yellow
638   endif
639
640   paper = Black
641   for i = 0 to 99 cycle
642     if i = pc then
643       ink = col
644     else
645       ink = Green
646     endif
647     proc displayMemCell (i)
648   repeat
649 endproc
650
651
652 // resetLmc:
653 //  Zero memory and reset registers
654 //======================================================================
655 ////////////////////////////////////////////////////////////////////////
656
657 def proc resetLmc
658   local i
659
660   for i = 0 to 99 cycle
661     memory (i) = 0
662   repeat
663
664   acc = 0
665   pc  = 0
666   sPc = 0
667 endproc
668
669
670 // waitForSpace:
671 //  Output a prompt (or a default one) and wait
672 //======================================================================
673 ////////////////////////////////////////////////////////////////////////
674
675 def proc waitForSpace (prompt$)
676
677   if prompt$ = "" then
678     print "Press the spacebar to continue ";
679   else
680     print prompt$;
681   endif
682   update
683
684   while get <> 32 cycle
685     wait (0.01)
686   repeat
687
688 endproc
689
690
691 // splitLine:
692 //    Split a line into its 3 components:
693 //      label, opcode, operand
694 //======================================================================
695
696 def proc splitLine (progl$)
697   local x$, sPtr
698
699   label$   = ""
700   opcode$  = ""
701   operand$ = ""
702
703 // Check for a label
704
705   sPtr = 0
706   x$ = left$ (progl$, 1)
707   if  NOT (x$ = " " or x$ = chr$ (9)) then  // No space, so it's a Label
708     for sPtr = 0 TO len (progl$) - 1 cycle
709       x$ = mid$ (progl$, sPtr, 1)
710       if x$ = " " OR x$ = chr$ (9) then break
711       label$ = label$ + x$
712     repeat
713   endif
714
715 // Check for an opcode
716
717   progl$ = fN trim (right$ (progl$, len (progl$) - sPtr - 1))
718   if len (progl$) = 0 then endproc
719   if left$ (progl$, 1) = "#" then endproc
720
721   for sPtr = 0 TO len (progl$) - 1 cycle
722     x$ = mid$ (progl$, sPtr, 1)
723     if x$ = " " OR x$ = chr$ (9) then break
724     opcode$ = opcode$ + x$
725   repeat
726
727 // Check for operand
728
729   progl$ = fn trim (right$ (progl$, len (progl$) - sPtr - 1))
730   if len (progl$) = 0 then endproc
731   if left$ (progl$, 2) = "//" then endproc  // Ignoring the comment
732   for sPtr = 0 TO len (progl$) - 1 cycle
733     x$ = mid$ (progl$, sPtr, 1)
734     if x$ = " " or x$ = chr$ (9) then break
735     operand$ = operand$ + x$
736   repeat
737
738 endproc
739
740
741 // trim:
742 //    Remove leading spaces (recursively)
743 //======================================================================
744
745 def fn trim(ln$)
746   if len (ln$) = 0 then  = ln$
747   if left$ (ln$, 1) = " " OR left$ (ln$, 1) = chr$ (9) then
748      = fn trim (right$ (ln$, len (ln$) - 1))
749   else
750      = ln$
751   endif
752
753
754 // upperCase:
755 //    Translate a line of text into upper case
756 //======================================================================
757
758 def fn upperCase (mixed$)
759   local t$, i
760   if len (mixed$) = 0 then  = mixed$
761   t$ = ""
762   for i = 0 TO len (mixed$) - 1 cycle
763     x$ = mid$ (mixed$, i, 1)
764     if asc (x$) >= 96 then x$ = chr$ (asc (x$) - 32)
765     t$ = t$ + x$
766   repeat
767  = t$
768
769
770 // loadFile:
771 //  Load and assemble a file from disk into our memory.
772 //======================================================================
773
774 def proc loadFile
775   local handle
776   local file$, in$
777
778   numLines = 0
779   error$   = ""
780
781   proc lmcHeader
782   vTab = yOffset + 4
783   input "File to load: ", file$
784
785   if len (file$) = 0 then endproc
786
787   handle = openIn (file$)
788   if handle = -1 then
789     print "Unable to open file"
790     proc waitForSpace ("")
791     endproc
792   endif
793
794   while NOT EOF (handle) cycle
795     INPUT# handle, in$
796
797     in$ = fn upperCase (in$)
798
799     if len (in$) = 0 OR left$ (in$, 2) = "//" then
800       labels$   (numLines) = ""
801       opcodes$  (numLines) = ""
802       operands$ (numLines) = ""
803       numLines = numLines + 1
804       continue
805     endif
806
807     proc splitLine (in$)
808     if error$ <> "" then
809       print "File LOAD failed at line "; numLines; ": "; error$
810       print "["; numLines; "] "; in$
811       proc waitForspace ("")
812       break
813     endif
814
815     labels$   (numLines) = label$
816     opcodes$  (numLines) = opcode$
817     operands$ (numLines) = operand$
818     numLines = numLines + 1
819   repeat
820
821   close (handle)
822 endproc
823
824
825 // printfile
826 //  Output our loaded program data
827 //======================================================================
828
829 def proc printFile
830   local i, n
831
832   proc lmcHeader
833   vTab = yOffset + 4
834
835   for i = 0 to numLines - 1 cycle
836     n = i + 1
837     print " ";
838     if n < 100 then print " ";
839     if n <  10 then print " ";
840     print n; ": ";
841     print labels$   (i); space$ (10 - len (labels$  (i)));
842     print opcodes$  (i); space$ ( 6 - len (opcodes$ (i)));
843     print operands$ (i)
844   repeat
845   print
846   proc waitForSpace ("")
847 endproc
848
849
850 // findOpcode
851 //  Return the numeric opcode for the string supplied.
852 //  Maybe not the most efficient, but it's easy to do.
853 //======================================================================
854
855 def fn findOpcode (op$)
856
857 // Non instruction codes
858
859   if op$ = "DAT" then = 1000
860   if op$ = "ORG" then = 1001
861
862 // Halt! (Or Coffee Break time)
863
864   if op$ = "HLT" then = 0
865   if op$ = "COB" then = 0
866
867 // Check for the 9xx specials
868
869   if op$ = "INP" then = 901
870   if op$ = "OUT" then = 902
871   if op$ = "RET" then = 903
872   if op$ = "GIN" then = 910
873   if op$ = "GHI" then = 911
874   if op$ = "GLO" then = 912
875
876   if op$ = "JSR" then = 400
877
878 // Arithmetic
879
880   if op$ = "ADD" then = 100
881   if op$ = "SUB" then = 200
882
883 // Load/Store
884
885   if op$ = "LDA" then = 500
886   if op$ = "STA" then = 300
887
888 // Jumps
889
890   if op$ = "BRA" then = 600
891   if op$ = "BRZ" then = 700
892   if op$ = "BRP" then = 800
893
894 // Unknown
895
896   = -1
897
898
899 // getOperandNum:
900 //  Turn the supplied operand into a number.
901 //======================================================================
902
903 def fn getOperandNum (op$)
904
905   local num
906   local first$
907
908   if len (op$) = 0 then
909     = -1
910   endif
911
912   first$ = left$ (op$, 1)
913
914   if (first$ < "0" OR first$ > "9") AND first$ <> "-" then
915     = -1
916   endif
917
918   num = val (op$)
919   if num < -999 OR num > 999 then
920     = -1
921   else
922     = num
923
924
925
926 // assembleFile
927 //  Run the assembler on the loaded file and store it in memory
928 //======================================================================
929
930 def proc assembleFile
931   local i, lNum, opcode, err, num
932   local dCount, iCount, pass
933
934   for i = 0 to 999 cycle
935     labels (i) = -1
936   repeat
937
938   proc lmcHeader
939   vTab = yOffset + 4
940
941   if numLines = 0 then
942     print "No program has been loaded"
943     proc waitForSpace ("")
944     endproc
945   endif
946
947   print "Assembling loaded file into memory ..."
948
949   err = FALSE
950
951   for pass = 1 to 2 cycle // 2 pass assembler, inefficient this way, but..
952
953     if err then break
954
955     pc  = 0
956     iCount = 0
957     dCount = 0
958
959     lNum = 0
960     while lNum < numLines cycle
961
962       label$   = labels$   (lNum)
963       opcode$  = opcodes$  (lNum)
964       operand$ = operands$ (lNum)
965
966       lNum = lNum + 1
967
968       if pass = 1 then
969         if label$ <> "" then labels (label$) = pc // Build up the label <-> location "symbol table"
970       endif
971
972       if opcode$ = "" then
973         continue       // Empty opcode is ok.
974       endif
975
976       opcode = fn findOpcode (opcode$)
977       if opcode = -1 then
978         print "Invalid opcode ("; opcode$; ") at line: "; lNum
979         err = TRUE
980         break
981       endif
982
983       switch (opcode)
984
985 // Handle pseudo opcodes
986
987         case 1000               // DAT
988           if operand$ = "" then
989             num = 0
990           else
991             num = fn getOperandNum (operand$)
992             if num = -1 then
993               print "Invalid operand: Number expected (got "; operand$; ") at line "; lNum
994               err = TRUE
995               break
996             endif
997           endif
998           memory (pc) = num
999           dCount = dCount + 1
1000           proc incPc
1001         endcase
1002
1003         case 1001               // ORG
1004           num = fn getOperandNum (operand$)
1005           if num = -1 then
1006             print "Invalid operand: Number expected (got "; operand$; ") at line "; lNum
1007             err = TRUE
1008             break
1009           endif
1010           if num < 0 OR num > 99 then
1011             print "Invalid ORG - 0-99 expected (got "; num$; ") at line "; lNum
1012             err = TRUE
1013             break
1014           endif
1015           pc = num
1016         endcase
1017
1018 // Simple opcodes with no operands
1019
1020         case 0, 901, 902, 903, 910, 911, 912
1021           if operand$ <> "" then
1022             print "Unexpected operand at line "; lNum
1023             err = TRUE
1024             break
1025           endif
1026           memory (pc) = opcode
1027           iCount = iCount + 1
1028           proc incPc
1029         endcase
1030
1031 // All the rest need a label
1032
1033         default
1034           if pass = 1 then  // Just stuff the opcodes
1035             memory (pc) = opcode
1036           else
1037             num = labels (operand$)
1038             if num = -1 then
1039               print "Label required at line "; lNum
1040               err = TRUE
1041               break
1042             endif
1043             opcode = opcode + num
1044             memory (pc) = opcode
1045             iCount = iCount + 1
1046           endif
1047           proc incPc
1048         endcase
1049
1050       endswitch
1051
1052     repeat
1053
1054     if err then break
1055
1056   repeat
1057
1058   if NOT err then
1059     print
1060     print "Assembly into memory OK:"
1061     print "  Instructions: "; iCount
1062     print "          Data: "; dCount
1063   endif
1064
1065   print
1066   proc waitForSpace ("")
1067
1068   pc  = 0
1069   sPc = 0
1070
1071 endproc
1072
1073
1074 // lmcHeader
1075 //  Draw the header for the LMC as a common thing for
1076 //  all pages - e.g. help, run, etc.
1077 //======================================================================
1078
1079 def proc lmcHeader
1080
1081   paper = Black
1082   ink   = White
1083   cls2
1084   fontScale (1,2)
1085
1086   print " +-----------------------------------------------------------------------+"
1087   print " |    Dr. Stuart E. Madnicks ";
1088   ink = Lime
1089   print "  Little Man Computer";
1090   ink = White
1091   print "                       |"
1092   print " |                                                                       |"
1093   fontScale (1,1)
1094   print
1095   print
1096   print " |  This implementation by Gordon Henderson; http://drogon.net/lmc       |"
1097   print " +-----------------------------------------------------------------------+"
1098 endproc
1099
1100
1101 // lmcHelp
1102 //  Quick help page
1103 //======================================================================
1104
1105 def proc lmcHelp
1106
1107   proc lmcHeader
1108   vTab = yOffset + 4
1109
1110   print " The LMC is controlled by single-key entrys and is either in Run or Stop mode."
1111   print " In Stop mode, you can use the arrow keys to move the current  program-counter"
1112   print " location through memory array to enable editing of the value stored there. The"
1113   print " Green indicator to the right of the memory display indicates Run or Stop mode."
1114   print
1115   print " You can stop a running program at any time by prssing the Space-Bar."
1116   print
1117   print " You can use the arrow keys to move through the memory array and enter/change"
1118   print " the data at that location just by typing in a new three-digit number. Use the"
1119   print " minus (-) key to change the sign of the numbers. The Enter key will move the"
1120   print " program counter to the next location."
1121   print
1122   print " When the program requests input with the INP instruction, the input indicator"
1123   print " will light up Yellow on the right of the screen and you can enter a value in"
1124   print " the INP box. Terminate data entry with the Enter key. The Space-bar will stop"
1125   print " the program at that point."
1126   print
1127   print " Should the program encouter an error (an invalid instruction) then execution"
1128   print " will halt and the Red Error indicator will light up on the right. The error"
1129   print " condition will be cleared as soon as you press any key."
1130
1131   print
1132   print "Press the space-bar to continue to the next page ";
1133   cycle
1134   repeat until get$ = " "
1135
1136   proc lmcHeader
1137   vTab = yOffset + 4
1138
1139   print " Commands:"
1140   print
1141   print "   Z - Zero memory, PC and the Calculator."
1142   print
1143   print "   R or r: Run a program from address zero. A lower-case 'r' will run the"
1144   print "   program at a rate of 10 instructions per second with all the registers"
1145   print "   and memory being updated on-screen as changes happen. Starting with an"
1146   print "   upper-case 'R', will run the program as fast as possible with changes"
1147   print "   only visible at INP, OUT or HLT instructions."
1148   print
1149   print "   S/s - Single Step from the current Program Counter location."
1150   print
1151   print "   L/l - Load a file into the computer."
1152   print "   A/a - Assemble previously loaded file into memory"
1153
1154   print
1155   print "Press the space-bar to continue ";
1156   cycle
1157   repeat until get$ = " "
1158
1159   proc drawLmc
1160 endproc
1161
1162
1163 // lmcInstructionSet
1164 //  Put a description of the instruction set on-screen
1165 //======================================================================
1166
1167 def proc lmcInstructionSet
1168   cls2
1169   proc lmcHeader
1170
1171   fontScale (1, 2)
1172   vTab = yOffset
1173
1174   print "  Opcode Instruction  "
1175   print "   000     HLT   Stops execution"
1176   print "   1xx     ADD   Add data to the calculator"
1177   print "   2xx     SUB   Subtract data from the calculator"
1178   print "   3xx     STA   Store the calculator value in memory"
1179   print "   4xx     JSR   Jump to subroutine (Extension)"
1180   print "   5xx     LDA   Load the calculator from memory"
1181   print "   6xx     BRA   Branch to the given address"
1182   print "   7xx     BRZ   Branch if the calculator is zero"
1183   print "   8xx     BRP   Branch is the calculator is positive"
1184   print "   901     INP   Input a value from the keyboard"
1185   print "   902     OUT   Output the calculator value"
1186   print "   903     RTS   Return from Subroutine (Extension"
1187   print "   910     GIN   GPIO Input. (Extension)"
1188   print "   911     GHI   GPIO Output - set pin to 1/Hi. (Extension)"
1189   print "   912     GLO   GPIO Output - set pin to 0/Lo. (Extension)"
1190   print
1191   print "Press the space-bar to continue ";
1192   cycle
1193   repeat until get$ = " "
1194
1195   cls2
1196   proc drawLmc
1197 endproc
1198
1199
1200
1201 // drawLMC
1202 //  Draw the entire LMC on the screen
1203 //======================================================================
1204
1205 def proc drawLmc
1206   local s$
1207
1208   proc lmcHeader
1209
1210   s$ = space$ (xOffset)
1211
1212   vtab  = tHeight - 1
1213   ink   = Lime
1214   paper = Black
1215
1216 // the outside border
1217
1218   fontScale (1,2)
1219   hvTab (0, yOffset)
1220
1221   ink = Silver
1222   print s$; "+----+----+----+----+----+----+----+----+----+----+----+"
1223   print s$; "|    |   0|  10|  20|  30|  40|  50|  60|  70|  80|  90|"
1224   print s$; "+----+----+----+----+----+----+----+----+----+----+----+"
1225   print s$; "|   0|    |    |    |    |    |    |    |    |    |    |"
1226   print s$; "|   1|    |    |    |    |    |    |    |    |    |    |"
1227   print s$; "|   2|    |    |    |    |    |    |    |    |    |    |"
1228   print s$; "|   3|    |    |    |    |    |    |    |    |    |    |"
1229   print s$; "|   4|    |    |    |    |    |    |    |    |    |    |"
1230   print s$; "|   5|    |    |    |    |    |    |    |    |    |    |"
1231   print s$; "|   6|    |    |    |    |    |    |    |    |    |    |"
1232   print s$; "|   7|    |    |    |    |    |    |    |    |    |    |"
1233   print s$; "|   8|    |    |    |    |    |    |    |    |    |    |"
1234   print s$; "|   9|    |    |    |    |    |    |    |    |    |    |"
1235   print s$; "+----+----+----+----+----+----+----+----+----+----+----+"
1236   print
1237   print s$; "  +--+----+----+   +----+----+   +--+----+ +---+----+"
1238   print s$; "  |PC|    |    |   |Calc|    |   |In|    | |Out|    |"
1239   print s$; "  +--+----+----+   +----+----+   +--+----+ +---+----+"
1240   ink = Red
1241   print s$; "          F1 - Help    F2 - Instruction set";
1242
1243 endproc
1244
1245
1246 // setup:
1247 //  Initialise our computer, etc.
1248 //======================================================================
1249
1250 def proc setup
1251   local i
1252
1253   updateMode = 0 // Fast - No auto update on print statements
1254
1255 // Check screen size, etc.
1256
1257   if (tWidth < 80) then
1258     print
1259     print "The screen needs to be at least 80 columns wide"
1260     print "Make sure you start rtb with -x 640 or more."
1261     print
1262     stop
1263   endif
1264
1265   if tHeight < 48 then
1266     print
1267     print "The screen needs to be at least 48 lines high"
1268     print "Make sure you start rtb with -y 480 or more."
1269     print
1270     stop
1271   endif
1272
1273   xOffset = 5
1274   yOffset = 5
1275
1276   for i = 0 to 9 cycle
1277     read opcodeDef$ (i)
1278   repeat
1279
1280 // Start with random garbage!
1281
1282   for i = 0 to 99 cycle
1283     memory (i) = rnd (1999) - 999
1284   repeat
1285
1286   acc = rnd (1999) - 999
1287   pc  = 0
1288   sPc = 0
1289
1290   numLines = 0
1291
1292 endproc
1293
1294
1295 // Data for the op-codes
1296 //======================================================================
1297
1298 data "HLT", "ADD", "SUB", "STA", "JSR",
1299 data "LDA", "BRA", "BRZ", "BRP", "ZZZ"
1300