Gordons Projects

--> Projects Top-Level GIT

Initial GIT creation master
authorGordon Henderson <projects@drogon.net>
Thu, 15 May 2014 21:10:46 +0000 (22:10 +0100)
committerGordon Henderson <projects@drogon.net>
Thu, 15 May 2014 21:10:46 +0000 (22:10 +0100)
blink.lmc [new file with mode: 0644]
blink8.lmc [new file with mode: 0644]
countd.lmc [new file with mode: 0644]
divide.lmc [new file with mode: 0644]
lmc.rtb [new file with mode: 0644]
multiply.lmc [new file with mode: 0644]
pibrella.lmc [new file with mode: 0644]
square.lmc [new file with mode: 0644]

diff --git a/blink.lmc b/blink.lmc
new file mode 100644 (file)
index 0000000..59024bc
--- /dev/null
+++ b/blink.lmc
@@ -0,0 +1,11 @@
+
+// blink.lmc
+
+       lda     zero
+loop   ghi
+       glo
+       bra     loop
+
+// data
+
+zero   dat     0
diff --git a/blink8.lmc b/blink8.lmc
new file mode 100644 (file)
index 0000000..da32ccd
--- /dev/null
@@ -0,0 +1,28 @@
+
+// blink8.lmc:
+//     Sequence through 8 LEDs
+
+start  lda     zero    // Start on LED 0
+       sta     led
+
+loop   lda     led     // Light up LED "led"
+       out
+       ghi
+       ghi
+       ghi
+       ghi
+       ghi
+       ghi             // Crude delay in slow mode, else it just blinks
+       glo
+       add     one     // Add one to the led counter & store back.
+       sta     led
+       sub     eight   // Have we reached 8 yet?
+       brp     start   //   Yes - restart.
+       bra     loop
+
+       org     90
+
+zero   dat     0
+one    dat     1
+eight  dat     8
+led    dat
diff --git a/countd.lmc b/countd.lmc
new file mode 100644 (file)
index 0000000..afcfd3d
--- /dev/null
@@ -0,0 +1,14 @@
+// Countdown:
+//     LMC Example from wikipedia
+//
+// Program asks for a number, then subtracts one from it until it reaches zero.
+
+     INP
+LOOP SUB ONE   // Label this memory address as LOOP
+     OUT       // Calculator to the output register
+     BRZ QUIT  // If the calculator value is 0, jump to the memory address labeled QUIT
+     BRA LOOP  // If the calculator value is not 0, jump to the memory address labeled LOOP
+
+QUIT HLT       // Label this memory address as QUIT
+
+ONE  DAT 1     // Store the value 1 in this memory address, and label it ONE (variable declaration)
diff --git a/divide.lmc b/divide.lmc
new file mode 100644 (file)
index 0000000..d9a2132
--- /dev/null
@@ -0,0 +1,41 @@
+
+// divide.lmc:
+//     Program to divide 2 numbers. 2 values are input
+//     from the user then the first is divided by the second.
+
+       inp
+       sta     top
+       inp
+       sta     bottom
+
+// Zero the answer
+
+       lda     zero
+loop   sta     answer
+
+// Load the 'top' number and subtract the bottom
+
+       lda     top
+       sub     bottom
+       sta     top     // Storing it back for the next time
+       brp     add1
+
+// We're now negative - end of division
+
+       lda     answer
+       out
+       hlt
+
+add1   lda     answer
+       add     one
+       bra     loop
+
+// Data
+
+       org     90
+
+zero   dat     0
+one    dat     1
+top    dat
+bottom dat
+answer dat
diff --git a/lmc.rtb b/lmc.rtb
new file mode 100644 (file)
index 0000000..eca6d78
--- /dev/null
+++ b/lmc.rtb
@@ -0,0 +1,1300 @@
+
+// LMC: The Little Man Computer
+//  It's a combined assembler and interpreter for the LMC
+//  computer system.
+
+// This implementation copyright (c) 2014 Gordon Henderson
+//  and features a JSR/RET extension, as well as Raspberry
+//  Pi GPIO. (simplified!)
+
+// The LMC memory
+
+  dim memory (100)
+
+// Opcodes for the disassembler
+
+  dim opcodeDef$ (10)
+
+// Storage for the assembler
+
+  dim labels$   (100)
+  dim opcodes$  (100)
+  dim operands$ (100)
+  dim labels    (999) // Bigger to reduce ptential hash clashes
+
+// LMC modes
+
+  lmcRunning  = FALSE
+  lmcError    = FALSE
+  lmcInput    = FALSE
+
+// Initialise and get going
+
+  proc setup
+  proc drawLmc
+
+  proc updateModeIndicators
+  proc displayLmcMemory
+  proc displayLmcRegisters
+  update
+
+//======================================================================
+// Main input loop:
+//  Read the keyboard and do what's needed.
+//======================================================================
+
+  cycle
+
+// Update the screen
+
+    proc updateModeIndicators
+    proc displayLmcMemory
+    proc displayLmcRegisters
+
+// disassemble the current instruction
+
+    value = memory (pc)
+
+    proc disAsm (value)
+
+    update
+
+    cycle
+      key = inkey
+      if key <> -1 then wait (0.01)
+    repeat until key <> -1
+
+    lmcError = FALSE
+
+    switch (key)
+
+      case keyF1
+        proc lmcHelp
+      endcase
+
+      case keyF2
+        proc lmcInstructionSet
+      endcase
+
+// Load?
+
+      case 76, 108
+        proc loadFile
+        proc drawLmc
+      endcase
+
+// Assemble
+
+      case 65, 97
+        proc assembleFile
+        proc drawLmc
+      endcase
+
+// Print (Really debugging)
+
+      case 80, 112
+        proc printFile
+        proc drawLmc
+      endcase
+
+// Arrow keys
+
+      case keyUp
+        pc = pc - 1
+        if (pc mod 10) = 9 then pc = pc + 10
+      endcase
+
+      case keyDown
+        pc = pc + 1
+        if (pc mod 10) = 0 then pc = pc - 10
+      endcase
+
+      case keyLeft
+        pc = pc - 10
+        if pc < 0 then pc = pc + 100
+      endcase
+
+      case keyRight
+        pc = pc + 10
+        if pc > 99 then pc = pc - 100
+      endcase
+
+// Enter moves to the next location
+
+      case 10, 13
+        proc incPc
+      endcase
+
+// Negative key
+
+      case 45
+        if value <> 0 then value = -value
+        memory (pc) = value
+      endcase
+
+// Number key
+
+      case 48, 49, 50, 51, 52, 53, 54, 55, 56, 57
+        key = key - 48 // Convert to decimal digit
+        tmp = abs (value) * 10 + key
+        if tmp > 999 then tmp = tmp MOD 1000
+        memory (pc) = tmp * sgn (value) + 0 // The +0 copes with negative 0 issues.
+      endcase
+
+// r for Run (Lower case r to run slowly with screen updates)
+
+      case 114
+        pc = 0
+        proc lmcRunMode (TRUE)
+        nextCycle = TIME + 100  // Run at 10 instructions/second
+        cycle
+          proc displayLmcMemory
+          proc disAsm (memory (pc))
+          proc displayLmcRegisters
+          update
+          proc runLmcStep
+
+          if TIME > nextCycle then nextCycle = TIME // Probably an INP instruction
+          while TIME < nextCycle cycle
+            wait (0.01)
+          repeat
+          nextCycle = nextCycle + 100
+          if inkey = 32 then lmcRunning = FALSE
+        repeat until NOT lmcRunning
+      endcase
+
+// R for Run (Upper case R to run fast!)
+
+      case 82
+        pc = 0
+        proc lmcRunMode (TRUE)
+        cycle
+          proc runLmcStep
+          if inkey = 32 then lmcRunning = FALSE
+        repeat until NOT lmcRunning
+      endcase
+
+// S or s for a Single Step from the current PC location
+
+      case 83, 115
+        proc lmcRunMode (TRUE)
+        proc runLmcStep
+//        proc displayLmcMemory
+        proc lmcRunMode (FALSE)
+      endcase
+
+// Capital Z to zero memory
+
+      case 90
+        proc resetLmc
+        proc updateModeIndicators
+        proc displayLmcMemory
+        update
+      endcase
+
+    endswitch
+
+  repeat
+end
+
+
+// incPc:
+//  Increment the Program Counter and wrap at 1000
+//======================================================================
+
+def proc incPc
+  if pc = 999 then
+    pc = 0
+  else
+    pc = pc + 1
+  endif
+endproc
+
+
+// runLmcStep:
+//  Run a single step of the computer
+//======================================================================
+
+def proc runLmcStep
+
+  local value, opcode, operand
+
+  value   = memory (pc)
+  opcode  = value DIV 100
+  operand = value MOD 100
+
+// Executing data?
+
+  if value < 0 then
+    lmcRunning = FALSE
+    lmcError   = TRUE
+    endproc
+  endif
+
+// Check opcode
+
+  switch (opcode)
+
+    case 0  // Halt - time for coffee
+      lmcRunning = FALSE
+      if operand <> 0 then lmcError = TRUE
+    endcase
+
+    case 1  // Add
+      acc = acc + memory (operand)
+      proc incPc
+    endcase
+
+    case 2  // Sub
+      acc = acc - memory (operand)
+      proc incPc
+    endcase
+
+    case 3  // STA - Store
+      memory (operand) = acc
+      proc incPc
+    endcase
+
+    case 4  // JSR - Jump to Subroutine - GH Extension
+      proc incPc
+      sPc = pc
+      pc  = operand
+    endcase
+
+    case 5  // LDA - Load
+      acc = memory (operand)
+      proc incPc
+    endcase
+
+    case 6  // BRA - Unconitional Branch
+      pc = operand
+    endcase
+
+    case 7  // BRZ - Branch if Acc is zero
+      if acc = 0 then
+        pc = operand
+      else
+        proc incPc
+      endif
+    endcase
+
+    case 8  // BRP - Branch if the Acc is positive
+      if acc >= 0 then
+        pc = operand
+      else
+        proc incPc
+      endif
+    endcase
+
+    case 9  // Multi-Use instruction
+      switch (operand)
+        case 1
+          lmcInput = TRUE
+          proc updateModeIndicators
+          proc inputAcc
+          if lmcRunning then proc incPc // We could have halted & want to re-start this instruction
+          lmcInput = FALSE
+          proc updateModeIndicators
+        endcase
+
+        case 2
+          proc outputAcc
+          proc incPc
+        endcase
+
+        case 3    // RET
+          pc = sPc
+        endcase
+
+// GPIO
+        case 10   // GIN - GPIO Input
+          pinMode (acc, pinInput)
+          acc = digitalRead (acc)
+          proc incPc
+        endcase
+
+        case 11   // GHI - GPIO Output High
+          pinMode (acc, pinOutput)
+          digitalWrite (acc, 1)
+          proc incPc
+        endcase
+
+        case 12   // GLO - GPIO Output Low
+          pinMode (acc, pinOutput)
+          digitalWrite (acc, 0)
+          proc incPc
+        endcase
+
+        default
+          lmcRunning = FALSE
+          lmcError   = TRUE
+        endcase
+      endswitch
+
+    endcase
+
+  endswitch
+
+endproc
+
+
+// pData:
+//  Convenience procedure to print Data: NNN
+//======================================================================
+
+def proc pData (v)
+  print "Data: ";
+  proc pNum (v)
+endproc
+
+
+// disAsm
+//  Disassemble the instruction at the PC and print it on the screem
+//======================================================================
+
+def proc disAsm (value)
+
+  local operator, operand
+
+  paper = Black
+  if lmcRunning then
+    ink = Pink
+  else
+    ink = Yellow
+  endif
+
+  hvTab (xOffset + 10, yOffset + 14)
+
+  operator = value DIV 100
+  operand  = value MOD 100
+
+  if value < 0 then
+    proc pData (value)
+  else
+    switch (operator)
+
+      case 0
+        if operand = 0 then
+          print "HLT";
+        else
+          proc pData (value)
+        endif
+        endcase
+
+      case 1, 2, 3, 4, 5, 6, 7, 8
+        print opcodeDef$ (operator); "     ";
+        if operand < 10 then print "0";
+        print operand;
+      endcase
+
+      case 9
+        switch (operand)
+          case 1
+            print "INP";
+          endcase
+          case 2
+            print "OUT";
+          endcase
+          case 3
+            print "RET";
+          endcase
+          case 10
+            print "GIN";
+          endcase
+          case 11
+            print "GHI";
+          endcase
+          case 12
+            print "GLO";
+          endcase
+          default
+            proc pData (value)
+          endcase
+        endswitch
+      endcase
+
+      default
+        proc pData (value)
+      endcase
+
+    endswitch
+  endif
+  print "       ";
+endproc
+
+
+// lmcRunMode:
+//  Just update the run-mode indicator as required
+//======================================================================
+
+def proc lmcRunMode (running)
+  lmcRunning = running
+  proc updateModeIndicators
+  update
+endproc
+
+
+// updateModeIndicators
+//  Update the Run/Step/Stop mode indicators
+//======================================================================
+
+def proc updateModeIndicators
+
+  ink = Black
+
+// Run Lamp (Green)
+
+  colour = Green
+    ellipse ((xOffset + 62) * 8 + 2, gHeight - (yOffset * 30 +  0), 26, 20, TRUE)
+
+  if lmcRunning then
+    colour = Lime
+    ellipse ((xOffset + 62) * 8 + 2, gHeight - (yOffset * 30 +  0), 24, 18, TRUE)
+    paper = Lime
+    hvTab (xOffset + 61, yOffset + 2)
+    print "RUN";
+  endif
+
+// Error Lamp (Red)
+
+  colour = Maroon
+    ellipse ((xOffset + 62) * 8 + 2, gHeight - (yOffset * 30 +  100), 26, 20, TRUE)
+
+  if lmcError then
+    colour = Red
+    ellipse ((xOffset + 62) * 8 + 2, gHeight - (yOffset * 30 + 100), 24, 18, TRUE)
+    paper = Red
+    hvTab (xOffset + 61, yOffset + 7)
+    print "ERR";
+  endif
+
+// Input Lamp (Yellow)
+
+  colour = Olive
+    ellipse ((xOffset + 62) * 8 + 2, gHeight - (yOffset * 30 +  200), 26, 20, TRUE)
+
+  if lmcInput then
+    colour = Yellow
+    ellipse ((xOffset + 62) * 8 + 2, gHeight - (yOffset * 30 + 200), 24, 18, TRUE)
+    paper = Yellow
+    hvTab (xOffset + 61, yOffset + 12)
+    print "INP";
+  endif
+
+
+endproc
+
+
+// pNum:
+//  Procedure to print a nuber in the fixed field-width used in the LMC
+//  The field width is 4 characters, the first being a space or - sign,
+//  then the number as 3 digits with leading zeros
+//======================================================================
+
+def proc pNum (n)
+
+  if n < 0 then
+    print "-";
+    n = abs (n)
+  else
+    print " ";
+  endif
+
+  if n > 999 then
+    print "***";
+  else
+    if n < 100 then print "0";
+    if n <  10 then print "0";
+    print n;
+  endif
+endproc
+
+
+// displayMemCell
+//  Display one location of the LMCs memory on-screen
+//======================================================================
+
+def proc displayMemCell (loc)
+
+  local x,y
+
+  x = loc div 10 * 5
+  y = loc mod 10
+
+  hvtab (x + xOffset + 6, y + yOffset + 3)
+  proc pNum (memory (loc))
+endproc
+
+
+// displayLmcRegisters
+//  Display the program counter and acc registers
+//======================================================================
+
+def proc displayLmcRegisters
+  ink = Pink
+  hvTab (xOffset +  6, yOffset + 16)
+  proc pNum (pc)
+  hvTab (xOffset + 11, yOffset + 16)
+  proc pNum (sPc)
+
+  ink = Lime
+  hvTab (xOffset + 25, yOffset + 16)
+  proc pNum (acc)
+endproc
+
+
+// outputAcc
+//  Output the Acc in the output register box
+//======================================================================
+
+def proc outputAcc
+  paper = black
+  ink   = Aqua
+  hvTab (xOffset + 48, yOffset + 16)
+  proc pNum (acc)
+  update
+endproc
+
+
+// inputAcc
+//  Read user input
+//======================================================================
+
+def proc inputAcc
+
+  local key, value
+
+  value = 0
+  paper = Black
+  ink   = Silver
+
+// Update the display
+
+  proc displayLmcMemory
+  proc disAsm (memory (pc))
+  proc displayLmcRegisters
+
+  cycle
+
+    hvTab (xOffset + 37, yOffset + 16)
+    proc pNum (value)
+    update
+
+    cycle
+      key = inkey
+      if key = -1 then wait (0.01)
+    repeat until key <> -1
+
+    switch (key)
+
+// Spacebar terminatest entry and aborts the run
+
+      case 32
+        lmcRunning = FALSE
+        break
+      endcase
+
+// Enter/Return to exit
+
+      case 10, 13
+        acc = value
+        break
+      endcase
+
+// Negative key
+
+    case 45
+      if value <> 0 then value = -value
+    endcase
+
+// Number key
+
+    case 48, 49, 50, 51, 52, 53, 54, 55, 56, 57
+      key = key - 48 // Convert to decimal digit
+      tmp = abs (value) * 10 + key
+      if tmp > 999 then tmp = tmp MOD 1000
+      value = tmp * sgn (value) + 0 // The +0 copes with negative 0 issues.
+    endcase
+
+  endswitch
+
+  repeat
+
+endproc
+
+
+// displayLmcMemory
+//  Display the LMC memory on the screen
+//======================================================================
+////////////////////////////////////////////////////////////////////////
+
+def proc displayLmcMemory
+  local i, col
+
+  if lmcRunning then
+    col = Pink
+  else
+    col = Yellow
+  endif
+
+  paper = Black
+  for i = 0 to 99 cycle
+    if i = pc then
+      ink = col
+    else
+      ink = Green
+    endif
+    proc displayMemCell (i)
+  repeat
+endproc
+
+
+// resetLmc:
+//  Zero memory and reset registers
+//======================================================================
+////////////////////////////////////////////////////////////////////////
+
+def proc resetLmc
+  local i
+
+  for i = 0 to 99 cycle
+    memory (i) = 0
+  repeat
+
+  acc = 0
+  pc  = 0
+  sPc = 0
+endproc
+
+
+// waitForSpace:
+//  Output a prompt (or a default one) and wait
+//======================================================================
+////////////////////////////////////////////////////////////////////////
+
+def proc waitForSpace (prompt$)
+
+  if prompt$ = "" then
+    print "Press the spacebar to continue ";
+  else
+    print prompt$;
+  endif
+  update
+
+  while get <> 32 cycle
+    wait (0.01)
+  repeat
+
+endproc
+
+
+// splitLine:
+//    Split a line into its 3 components:
+//      label, opcode, operand
+//======================================================================
+
+def proc splitLine (progl$)
+  local x$, sPtr
+
+  label$   = ""
+  opcode$  = ""
+  operand$ = ""
+
+// Check for a label
+
+  sPtr = 0
+  x$ = left$ (progl$, 1)
+  if  NOT (x$ = " " or x$ = chr$ (9)) then  // No space, so it's a Label
+    for sPtr = 0 TO len (progl$) - 1 cycle
+      x$ = mid$ (progl$, sPtr, 1)
+      if x$ = " " OR x$ = chr$ (9) then break
+      label$ = label$ + x$
+    repeat
+  endif
+
+// Check for an opcode
+
+  progl$ = fN trim (right$ (progl$, len (progl$) - sPtr - 1))
+  if len (progl$) = 0 then endproc
+  if left$ (progl$, 1) = "#" then endproc
+
+  for sPtr = 0 TO len (progl$) - 1 cycle
+    x$ = mid$ (progl$, sPtr, 1)
+    if x$ = " " OR x$ = chr$ (9) then break
+    opcode$ = opcode$ + x$
+  repeat
+
+// Check for operand
+
+  progl$ = fn trim (right$ (progl$, len (progl$) - sPtr - 1))
+  if len (progl$) = 0 then endproc
+  if left$ (progl$, 2) = "//" then endproc  // Ignoring the comment
+  for sPtr = 0 TO len (progl$) - 1 cycle
+    x$ = mid$ (progl$, sPtr, 1)
+    if x$ = " " or x$ = chr$ (9) then break
+    operand$ = operand$ + x$
+  repeat
+
+endproc
+
+
+// trim:
+//    Remove leading spaces (recursively)
+//======================================================================
+
+def fn trim(ln$)
+  if len (ln$) = 0 then  = ln$
+  if left$ (ln$, 1) = " " OR left$ (ln$, 1) = chr$ (9) then
+     = fn trim (right$ (ln$, len (ln$) - 1))
+  else
+     = ln$
+  endif
+
+
+// upperCase:
+//    Translate a line of text into upper case
+//======================================================================
+
+def fn upperCase (mixed$)
+  local t$, i
+  if len (mixed$) = 0 then  = mixed$
+  t$ = ""
+  for i = 0 TO len (mixed$) - 1 cycle
+    x$ = mid$ (mixed$, i, 1)
+    if asc (x$) >= 96 then x$ = chr$ (asc (x$) - 32)
+    t$ = t$ + x$
+  repeat
+ = t$
+
+
+// loadFile:
+//  Load and assemble a file from disk into our memory.
+//======================================================================
+
+def proc loadFile
+  local handle
+  local file$, in$
+
+  numLines = 0
+  error$   = ""
+
+  proc lmcHeader
+  vTab = yOffset + 4
+  input "File to load: ", file$
+
+  if len (file$) = 0 then endproc
+
+  handle = openIn (file$)
+  if handle = -1 then
+    print "Unable to open file"
+    proc waitForSpace ("")
+    endproc
+  endif
+
+  while NOT EOF (handle) cycle
+    INPUT# handle, in$
+
+    in$ = fn upperCase (in$)
+
+    if len (in$) = 0 OR left$ (in$, 2) = "//" then
+      labels$   (numLines) = ""
+      opcodes$  (numLines) = ""
+      operands$ (numLines) = ""
+      numLines = numLines + 1
+      continue
+    endif
+
+    proc splitLine (in$)
+    if error$ <> "" then
+      print "File LOAD failed at line "; numLines; ": "; error$
+      print "["; numLines; "] "; in$
+      proc waitForspace ("")
+      break
+    endif
+
+    labels$   (numLines) = label$
+    opcodes$  (numLines) = opcode$
+    operands$ (numLines) = operand$
+    numLines = numLines + 1
+  repeat
+
+  close (handle)
+endproc
+
+
+// printfile
+//  Output our loaded program data
+//======================================================================
+
+def proc printFile
+  local i, n
+
+  proc lmcHeader
+  vTab = yOffset + 4
+
+  for i = 0 to numLines - 1 cycle
+    n = i + 1
+    print " ";
+    if n < 100 then print " ";
+    if n <  10 then print " ";
+    print n; ": ";
+    print labels$   (i); space$ (10 - len (labels$  (i)));
+    print opcodes$  (i); space$ ( 6 - len (opcodes$ (i)));
+    print operands$ (i)
+  repeat
+  print
+  proc waitForSpace ("")
+endproc
+
+
+// findOpcode
+//  Return the numeric opcode for the string supplied.
+//  Maybe not the most efficient, but it's easy to do.
+//======================================================================
+
+def fn findOpcode (op$)
+
+// Non instruction codes
+
+  if op$ = "DAT" then = 1000
+  if op$ = "ORG" then = 1001
+
+// Halt! (Or Coffee Break time)
+
+  if op$ = "HLT" then = 0
+  if op$ = "COB" then = 0
+
+// Check for the 9xx specials
+
+  if op$ = "INP" then = 901
+  if op$ = "OUT" then = 902
+  if op$ = "RET" then = 903
+  if op$ = "GIN" then = 910
+  if op$ = "GHI" then = 911
+  if op$ = "GLO" then = 912
+
+  if op$ = "JSR" then = 400
+
+// Arithmetic
+
+  if op$ = "ADD" then = 100
+  if op$ = "SUB" then = 200
+
+// Load/Store
+
+  if op$ = "LDA" then = 500
+  if op$ = "STA" then = 300
+
+// Jumps
+
+  if op$ = "BRA" then = 600
+  if op$ = "BRZ" then = 700
+  if op$ = "BRP" then = 800
+
+// Unknown
+
+  = -1
+
+
+// getOperandNum:
+//  Turn the supplied operand into a number.
+//======================================================================
+
+def fn getOperandNum (op$)
+
+  local num
+  local first$
+
+  if len (op$) = 0 then
+    = -1
+  endif
+
+  first$ = left$ (op$, 1)
+
+  if (first$ < "0" OR first$ > "9") AND first$ <> "-" then
+    = -1
+  endif
+
+  num = val (op$)
+  if num < -999 OR num > 999 then
+    = -1
+  else
+    = num
+
+
+
+// assembleFile
+//  Run the assembler on the loaded file and store it in memory
+//======================================================================
+
+def proc assembleFile
+  local i, lNum, opcode, err, num
+  local dCount, iCount, pass
+
+  for i = 0 to 999 cycle
+    labels (i) = -1
+  repeat
+
+  proc lmcHeader
+  vTab = yOffset + 4
+
+  if numLines = 0 then
+    print "No program has been loaded"
+    proc waitForSpace ("")
+    endproc
+  endif
+
+  print "Assembling loaded file into memory ..."
+
+  err = FALSE
+
+  for pass = 1 to 2 cycle // 2 pass assembler, inefficient this way, but..
+
+    if err then break
+
+    pc  = 0
+    iCount = 0
+    dCount = 0
+
+    lNum = 0
+    while lNum < numLines cycle
+
+      label$   = labels$   (lNum)
+      opcode$  = opcodes$  (lNum)
+      operand$ = operands$ (lNum)
+
+      lNum = lNum + 1
+
+      if pass = 1 then
+        if label$ <> "" then labels (label$) = pc // Build up the label <-> location "symbol table"
+      endif
+
+      if opcode$ = "" then
+        continue       // Empty opcode is ok.
+      endif
+
+      opcode = fn findOpcode (opcode$)
+      if opcode = -1 then
+        print "Invalid opcode ("; opcode$; ") at line: "; lNum
+        err = TRUE
+        break
+      endif
+
+      switch (opcode)
+
+// Handle pseudo opcodes
+
+        case 1000               // DAT
+          if operand$ = "" then
+            num = 0
+          else
+            num = fn getOperandNum (operand$)
+            if num = -1 then
+              print "Invalid operand: Number expected (got "; operand$; ") at line "; lNum
+              err = TRUE
+              break
+            endif
+          endif
+          memory (pc) = num
+          dCount = dCount + 1
+          proc incPc
+        endcase
+
+        case 1001               // ORG
+          num = fn getOperandNum (operand$)
+          if num = -1 then
+            print "Invalid operand: Number expected (got "; operand$; ") at line "; lNum
+            err = TRUE
+            break
+          endif
+          if num < 0 OR num > 99 then
+            print "Invalid ORG - 0-99 expected (got "; num$; ") at line "; lNum
+            err = TRUE
+            break
+          endif
+          pc = num
+        endcase
+
+// Simple opcodes with no operands
+
+        case 0, 901, 902, 903, 910, 911, 912
+          if operand$ <> "" then
+            print "Unexpected operand at line "; lNum
+            err = TRUE
+            break
+          endif
+          memory (pc) = opcode
+          iCount = iCount + 1
+          proc incPc
+        endcase
+
+// All the rest need a label
+
+        default
+          if pass = 1 then  // Just stuff the opcodes
+            memory (pc) = opcode
+          else
+            num = labels (operand$)
+            if num = -1 then
+              print "Label required at line "; lNum
+              err = TRUE
+              break
+            endif
+            opcode = opcode + num
+            memory (pc) = opcode
+            iCount = iCount + 1
+          endif
+          proc incPc
+        endcase
+
+      endswitch
+
+    repeat
+
+    if err then break
+
+  repeat
+
+  if NOT err then
+    print
+    print "Assembly into memory OK:"
+    print "  Instructions: "; iCount
+    print "          Data: "; dCount
+  endif
+
+  print
+  proc waitForSpace ("")
+
+  pc  = 0
+  sPc = 0
+
+endproc
+
+
+// lmcHeader
+//  Draw the header for the LMC as a common thing for
+//  all pages - e.g. help, run, etc.
+//======================================================================
+
+def proc lmcHeader
+
+  paper = Black
+  ink   = White
+  cls2
+  fontScale (1,2)
+
+  print " +-----------------------------------------------------------------------+"
+  print " |    Dr. Stuart E. Madnicks ";
+  ink = Lime
+  print "  Little Man Computer";
+  ink = White
+  print "                       |"
+  print " |                                                                       |"
+  fontScale (1,1)
+  print
+  print
+  print " |  This implementation by Gordon Henderson; http://drogon.net/lmc       |"
+  print " +-----------------------------------------------------------------------+"
+endproc
+
+
+// lmcHelp
+//  Quick help page
+//======================================================================
+
+def proc lmcHelp
+
+  proc lmcHeader
+  vTab = yOffset + 4
+
+  print " The LMC is controlled by single-key entrys and is either in Run or Stop mode."
+  print " In Stop mode, you can use the arrow keys to move the current  program-counter"
+  print " location through memory array to enable editing of the value stored there. The"
+  print " Green indicator to the right of the memory display indicates Run or Stop mode."
+  print
+  print " You can stop a running program at any time by prssing the Space-Bar."
+  print
+  print " You can use the arrow keys to move through the memory array and enter/change"
+  print " the data at that location just by typing in a new three-digit number. Use the"
+  print " minus (-) key to change the sign of the numbers. The Enter key will move the"
+  print " program counter to the next location."
+  print
+  print " When the program requests input with the INP instruction, the input indicator"
+  print " will light up Yellow on the right of the screen and you can enter a value in"
+  print " the INP box. Terminate data entry with the Enter key. The Space-bar will stop"
+  print " the program at that point."
+  print
+  print " Should the program encouter an error (an invalid instruction) then execution"
+  print " will halt and the Red Error indicator will light up on the right. The error"
+  print " condition will be cleared as soon as you press any key."
+
+  print
+  print "Press the space-bar to continue to the next page ";
+  cycle
+  repeat until get$ = " "
+
+  proc lmcHeader
+  vTab = yOffset + 4
+
+  print " Commands:"
+  print
+  print "   Z - Zero memory, PC and the Calculator."
+  print
+  print "   R or r: Run a program from address zero. A lower-case 'r' will run the"
+  print "   program at a rate of 10 instructions per second with all the registers"
+  print "   and memory being updated on-screen as changes happen. Starting with an"
+  print "   upper-case 'R', will run the program as fast as possible with changes"
+  print "   only visible at INP, OUT or HLT instructions."
+  print
+  print "   S/s - Single Step from the current Program Counter location."
+  print
+  print "   L/l - Load a file into the computer."
+  print "   A/a - Assemble previously loaded file into memory"
+
+  print
+  print "Press the space-bar to continue ";
+  cycle
+  repeat until get$ = " "
+
+  proc drawLmc
+endproc
+
+
+// lmcInstructionSet
+//  Put a description of the instruction set on-screen
+//======================================================================
+
+def proc lmcInstructionSet
+  cls2
+  proc lmcHeader
+
+  fontScale (1, 2)
+  vTab = yOffset
+
+  print "  Opcode Instruction  "
+  print "   000     HLT   Stops execution"
+  print "   1xx     ADD   Add data to the calculator"
+  print "   2xx     SUB   Subtract data from the calculator"
+  print "   3xx     STA   Store the calculator value in memory"
+  print "   4xx     JSR   Jump to subroutine (Extension)"
+  print "   5xx     LDA   Load the calculator from memory"
+  print "   6xx     BRA   Branch to the given address"
+  print "   7xx     BRZ   Branch if the calculator is zero"
+  print "   8xx     BRP   Branch is the calculator is positive"
+  print "   901     INP   Input a value from the keyboard"
+  print "   902     OUT   Output the calculator value"
+  print "   903     RTS   Return from Subroutine (Extension"
+  print "   910     GIN   GPIO Input. (Extension)"
+  print "   911     GHI   GPIO Output - set pin to 1/Hi. (Extension)"
+  print "   912     GLO   GPIO Output - set pin to 0/Lo. (Extension)"
+  print
+  print "Press the space-bar to continue ";
+  cycle
+  repeat until get$ = " "
+
+  cls2
+  proc drawLmc
+endproc
+
+
+
+// drawLMC
+//  Draw the entire LMC on the screen
+//======================================================================
+
+def proc drawLmc
+  local s$
+
+  proc lmcHeader
+
+  s$ = space$ (xOffset)
+
+  vtab  = tHeight - 1
+  ink   = Lime
+  paper = Black
+
+// the outside border
+
+  fontScale (1,2)
+  hvTab (0, yOffset)
+
+  ink = Silver
+  print s$; "+----+----+----+----+----+----+----+----+----+----+----+"
+  print s$; "|    |   0|  10|  20|  30|  40|  50|  60|  70|  80|  90|"
+  print s$; "+----+----+----+----+----+----+----+----+----+----+----+"
+  print s$; "|   0|    |    |    |    |    |    |    |    |    |    |"
+  print s$; "|   1|    |    |    |    |    |    |    |    |    |    |"
+  print s$; "|   2|    |    |    |    |    |    |    |    |    |    |"
+  print s$; "|   3|    |    |    |    |    |    |    |    |    |    |"
+  print s$; "|   4|    |    |    |    |    |    |    |    |    |    |"
+  print s$; "|   5|    |    |    |    |    |    |    |    |    |    |"
+  print s$; "|   6|    |    |    |    |    |    |    |    |    |    |"
+  print s$; "|   7|    |    |    |    |    |    |    |    |    |    |"
+  print s$; "|   8|    |    |    |    |    |    |    |    |    |    |"
+  print s$; "|   9|    |    |    |    |    |    |    |    |    |    |"
+  print s$; "+----+----+----+----+----+----+----+----+----+----+----+"
+  print
+  print s$; "  +--+----+----+   +----+----+   +--+----+ +---+----+"
+  print s$; "  |PC|    |    |   |Calc|    |   |In|    | |Out|    |"
+  print s$; "  +--+----+----+   +----+----+   +--+----+ +---+----+"
+  ink = Red
+  print s$; "          F1 - Help    F2 - Instruction set";
+
+endproc
+
+
+// setup:
+//  Initialise our computer, etc.
+//======================================================================
+
+def proc setup
+  local i
+
+  updateMode = 0 // Fast - No auto update on print statements
+
+// Check screen size, etc.
+
+  if (tWidth < 80) then
+    print
+    print "The screen needs to be at least 80 columns wide"
+    print "Make sure you start rtb with -x 640 or more."
+    print
+    stop
+  endif
+
+  if tHeight < 48 then
+    print
+    print "The screen needs to be at least 48 lines high"
+    print "Make sure you start rtb with -y 480 or more."
+    print
+    stop
+  endif
+
+  xOffset = 5
+  yOffset = 5
+
+  for i = 0 to 9 cycle
+    read opcodeDef$ (i)
+  repeat
+
+// Start with random garbage!
+
+  for i = 0 to 99 cycle
+    memory (i) = rnd (1999) - 999
+  repeat
+
+  acc = rnd (1999) - 999
+  pc  = 0
+  sPc = 0
+
+  numLines = 0
+
+endproc
+
+
+// Data for the op-codes
+//======================================================================
+
+data "HLT", "ADD", "SUB", "STA", "JSR",
+data "LDA", "BRA", "BRZ", "BRP", "ZZZ"
+
diff --git a/multiply.lmc b/multiply.lmc
new file mode 100644 (file)
index 0000000..e52d682
--- /dev/null
@@ -0,0 +1,40 @@
+
+// multiply.lmc:
+//     Program to multiply 2 numbers. 2 values are input
+//     from the user then they are multiplied
+
+       inp
+       sta     n1
+       inp
+       sta     n2
+
+// Zero the answer
+
+       lda     zero
+loop   sta     answer
+
+// Subtract one from n2, if negative, then we're done.
+
+       lda     n2
+       sub     one
+       sta     n2
+       brp     adder
+       lda     answer
+       out
+       hlt
+
+// Load n1, add it into the answer
+
+adder  lda     answer
+       add     n1
+       bra     loop
+
+// Data
+
+       org     90
+
+zero   dat     0
+one    dat     1
+answer dat
+n1     dat
+n2     dat
diff --git a/pibrella.lmc b/pibrella.lmc
new file mode 100644 (file)
index 0000000..92d7494
--- /dev/null
@@ -0,0 +1,93 @@
+
+// pibrella.lmc
+//     Example to use the PiBrella board from Pimoroni
+
+
+       jsr     setup
+
+loop   jsr     wButton
+       lda     yellow
+       ghi
+       lda     green
+       glo
+       lda     one
+       jsr     delay
+       lda     red
+       ghi
+       lda     yellow
+       glo
+       lda     five
+       jsr     delay
+
+// Back to Green
+
+       lda     yellow
+       ghi
+       lda     red
+       glo
+       lda     one
+       jsr     delay
+       lda     green
+       ghi
+       lda     yellow
+       glo
+
+       bra     loop
+
+// delay:
+//     Delay for acc seconds. (approx)
+
+delay  sta     timer
+       lda     zero    // 2
+       lda     zero
+       lda     zero
+       lda     zero
+       lda     zero
+       lda     zero    // 7 10ths.
+       lda     timer
+       sub     one
+       brp     delay
+       ret
+
+// wButton:
+//     Subroutine to wait for the button to be pressed
+
+wButton
+       lda     button
+       gin
+       brz     wButton
+       ret
+
+// Sub to initialise the Pibrella in TUXX mode
+
+setup  lda     red
+       glo
+       lda     yellow
+       glo
+       lda     green
+       ghi
+       ret
+
+// Constants for the Pibrella LEDs
+
+       org     80
+
+red    dat     2
+yellow dat     0
+green  dat     7
+button dat     14
+
+led0   dat     3
+led1   dat     4
+led2   dat     5
+led3   dat     6
+
+// Program data
+
+       org     90
+
+zero   dat     0
+one    dat     1
+five   dat     5
+timer  dat
+
diff --git a/square.lmc b/square.lmc
new file mode 100644 (file)
index 0000000..45b07de
--- /dev/null
@@ -0,0 +1,34 @@
+// Square program from the wikipedia entry
+//    Some changes for the RTB version of LMC by GH
+//    See ORG below.
+
+START   LDA ZERO     // Initialize for multiple program run
+        STA RESULT
+        STA COUNT
+        INP          // User provided input
+        BRZ END      // Branch to program END if input = 0
+        STA VALUE    // Store input as VALUE
+LOOP    LDA RESULT   // Load the RESULT
+        ADD VALUE    // Add VALUE, the user provided input, to RESULT
+        STA RESULT   // Store the new RESULT
+        LDA COUNT    // Load the COUNT
+        ADD ONE      // Add ONE to the COUNT
+        STA COUNT    // Store the new COUNT
+        SUB VALUE    // Subtract the user provided input VALUE from COUNT
+        BRZ ENDLOOP  // If zero (VALUE has been added to RESULT by VALUE times), branch to ENDLOOP
+        BRA LOOP     // Branch to LOOP to continue adding VALUE to RESULT
+ENDLOOP LDA RESULT   // Load RESULT
+        OUT          // Output RESULT
+        BRA START    // Branch to the START to initialize and get another input VALUE
+END     HLT          // HALT - a zero was entered so done!
+
+// Start data at 90 for visual effect in the RTB version
+
+       ORG 90
+
+ZERO    DAT          // Constant, value of 0 (defaults to 0)
+ONE     DAT 1        // Constant, value of 1
+
+RESULT  DAT          // Computed result (defaults to 0)
+COUNT   DAT          // Counter (defaults to 0)
+VALUE   DAT          // User provided input, the value to be squared (defaults to 0)