LC-3 Preproccessor
Posted on 12-12-2006, by Adrian
I know that most people wish that LC-3 had a few extra commands. Subtraction, multiplication, if-else blocks, for loops . . . the list of features goes on and on. I personally cannot believe some of the features that LC-3 lacks. Some of these feaures arent even a problem with the language; for example, the fact that every label is a global label is a weakness of the LC-3 preproccessor (or lack thereof) . For example, if I wanted to copy and paste a routine to use in another program, I'm gonna have to check that I dont use variables or labels that are used anywhere else in my program. This becomes a problems especially when I have to write if-then-else blocks, or when I have very common variable names, like ASCII_ZERO.
"Grownup" assemblers often solve this problem by introducing local labels: labels that are "local" to the last non-local label. For example, if I have the code
Code:

MY_FUNC:
  . . .
ret

and want to add in a while loop, most assemblers will allow me to declare the while loop label like this:
Code:

MY_FUNC:
  .WHILE:
  . . .
ret

Notice the dot? this means that the while loop is local to the function. If I code in a jump to that while loop, like this
Code:

jump .WHILE

then it will jump to the local while label, not some other while label located somewhere else.

This feature, along with being able to use characters in a .fill, I feel should be standard for LC-3. I am currently working on a preproccessor for LC-3 that will convert LC-3 code with the added functionality to standard LC-3 code. Examples what I am currently able to do follow:

LC-3 with added functionality:
Code:

.orig x3000

MAINZOR:
  ld r1, .NUM1
  ld r2, .NUM2
  jsr DIVIDE_R1_BY_R2
  ld r4, .ASCII_ZERO
  add r0, r3, r4
  out
halt
.NUM1 .fill #18
.NUM2 .fill #6
.ASCII_ZERO .fill '0'

DIVIDE_R1_BY_R2:   ;divides r1 by r2 and returns in r3
  zero r3      ;r1 is accumulator
  .loop:      ;loop to divide
    sub r1, r1, r2   ;subtract away!
    add r3, r3, #1
  test r1
  BRp .loop
  add r3, r3, #-1
ret
.NUM1 .fill #4
.NUM2 .fill #8

NEW_FUNC:
  if r3 then
    if r2 then
      add r1, r1, #1
    else
      add r3, r3, #1
    end
  else
    add r2, r2, #1
  end
  if r5 then
    add r5, r5, #1
  end
ret

Gets converted to:
Code:

.orig x3000

MAINZOR:
  ld r1, MAINZOR_NUM1
  ld r2, MAINZOR_NUM2
  jsr DIVIDE_R1_BY_R2
  ld r4, MAINZOR_ASCII_ZERO
  add r0, r3, r4
  out
halt
MAINZOR_NUM1 .fill #18
MAINZOR_NUM2 .fill #6
MAINZOR_ASCII_ZERO .fill #48   ;'0'

DIVIDE_R1_BY_R2:      ;divides r1 by r2 and returns in r3
  and r3, r3, #0   ;r1 is accumulator
  DIVIDE_R1_BY_R2_loop:   ;loop to divide
    not r2, r2   ;negate r2
    add r2, r2, #1
    add r1, r1, r2   ;subtract away!
    not r2, r2   ;negate r2
    add r2, r2, #1

    add r3, r3, #1
  and r1, r1, #-1
  BRp DIVIDE_R1_BY_R2_loop
  add r3, r3, #-1
ret
DIVIDE_R1_BY_R2_NUM1 .fill #4
DIVIDE_R1_BY_R2_NUM2 .fill #8

NEW_FUNC:
  and r3, r3, #-1
  BRz NEW_FUNC_ellse0
    and r2, r2, #-1
    BRz NEW_FUNC_ellse1
      add r1, r1, #1
    BRnzp NEW_FUNC_end1
    NEW_FUNC_ellse1:   ;else:
      add r3, r3, #1
    NEW_FUNC_end1:
  BRnzp NEW_FUNC_end0
  NEW_FUNC_ellse0:   ;else:
    add r2, r2, #1
  NEW_FUNC_end0:
  and r5, r5, #-1
  BRz NEW_FUNC_end2
    add r5, r5, #1
  NEW_FUNC_end2:
ret

Pretty cool, huh? Hopefully I will have a working version uploaded soon.
Difficulty: Advanced - Views: 19657

User Comments
chaoscreater wrote:
hi so how's the progress on this??

also, i don't understand this part:

Code:

not r2, r2   ;negate r2
add r2, r2, #1
add r1, r1, r2   ;subtract away!
not r2, r2   ;negate r2
add r2, r2, #1


why is it done twice?

as well as:

Code:
add r3, r3, #-1


not sure what it does...


and lastly, what does the
Code:
NEW_FUNC:
part do?? It doesn't seem to be used/called....

Adrian wrote:
Chaoscreater - I'll try and tackle each of your questions, but the important thing to remember is that this code was automatically generated from the original source that includes statements that lc3 doesnt natively have - most of the code is there purely to show off what the preprocessor does.

:
why is it done twice?

I'm going to assume you are talking about the block
Code:
not r2, r2;
add r2. r2, #1
that is in there twice; to be honest, I think that is probably a mistake on my part. The only justification I could come up with for why I had written the code that way was that perhaps you would want to test the flags to see if you were subtracting by a positive, negative, or zero value. Thats really just me grasping for straws, though, and I will have to get back to you if I think of a better reason.

:
not sure what it does...

The divide routine, if you run it, actually calculates one more than the correct value. This is because the
Code:
BRp
treats zero as a positive value, so if you divide 3 by 3 it will run through the loop twice. We have to subtract one to make sure that we accord for this final loop through.

:
It doesn't seem to be used/called...

Its only purpose is to show off the control flow proprocessing that I do. You are absolutely correct, the code is never used :)

Adrian wrote:
Oh, and we are working on getting a web-accessible version of the preprocessor up soon. Hopefully this week!

Tim wrote:
The LC-3 Preprocessor is available online now!

Check it out at http://www.lc3help.com/calculators.php

Enjoy!


Copyright 2006 © LC3Help.com v1.4.7