Search code examples
stringassemblymemorysubroutinelc3

(LC3) Using characters within a string to point to the address of different subroutines?


I am attempting to write a program in LC3 Assembly that will essentially take 1-4 hexadecimal values that are input by a user, store them to a respective label, then identify what each of those values are in order to inform the program what subroutine to jump to (there will be a subroutine hex characters x0-xf). I already have the user input section completely finished.

I had an idea for optimizing the identification process--using a .STRINGZ of x0-xf as a character index of sorts--but I am struggling to implement it. The idea is to use a loop with counter = #16, such that each iteration will load the corresponding character from the index string, then this value is compared to the user input, if the values are equal, then the program will jump to the subroutine corresponding to the now known identity of the user input. If they are not equal, the loop will restart. My question is: After the loop is complete and the value is known, how can I then use the specific character in the string to point to it's corresponding corresponding label or memory location? Conceivably, you could also use the loop counter to point to an address or label, but conceptually those are fairly similar.

.ORIG x3000
;-----------------------------------
;other code
;-----------------------------------
;values in these labels are for the sake of example.
CHAR1       .FILL x30
CHAR2       .FILL x31
CHAR3       .FILL x32
CHAR4       .FILL x33
SUB_RET     .FILL x0000 
;-----------------------------------
;**Question relevant section**
;-----------------------------------
CHAR_FIND   ST R7, SUB_RET
LEA R0, H_INDEX
;CHAR ID LOOP   
            RET
H_INDEX .STRINGZ "0123456789abcdef"         
;-----------------------------------
;CHARACTER SUBROUTINES (REMOVED SEVERAL FOR BREVITY)
;-----------------------------------
D_CHAR_0    ST R7, SUB_RET  
            ;REMOVED SUBROUTINE CONTENTS FOR BREVITY

            
D_CHAR_1    ST R7, SUB_RET          
            ;REMOVED SUBROUTINE CONTENTS FOR BREVITY


D_CHAR_2    ST R7, SUB_RET  
            ;REMOVED SUBROUTINE CONTENTS FOR BREVITY

D_CHAR_3    ST R7, SUB_RET  
            ;REMOVED SUBROUTINE CONTENTS FOR BREVITY

Solution

  • A character in a string, once extracted as a character, is just a value.  So, can we use that value to index into an array?  Yes, of course.

    Can we have an array of pointers to code?  Also yes, that's a data array whose element values are pointers (to labels in code, which may be functions).

    DTBL, .FILL hello         ; data pointer to code
          .FILL world         ; data pointer to code
          .FILL there         ; data pointer to code
          ...
    

    where hello is a function label and world is a function label.  We would use this as follows (let's imagine your index is in R1, and it is already known to be in the restricted range of the size of the table):

          LEA R0, DTBL        ; point to start of data table / array
          ADD R0, R0, R1      ; points to proper element in table
          LDR R0, R0, #0      ; load data pointer element from table
          JSRR R0             ; indirect function call thru pointer
    

    or if the code labels are not functions, but rather just labels in code all in the same function, then JMP instead of JSR

          LEA R0, DTBL        ; point to start of data table / array
          ADD R0, R0, R1      ; points to proper element in table
          LDR R0, R0, #0      ; load data pointer element from table
          JMP R0              ; indirect jump thru pointer
    

    Further, you can create a "code array", if you will, a table where the array elements are executable instructions, usually BR instructions. So, instead of indexing into a data array to perform an indirect branch, we can index into a code array -- to jump into the code array by index.

    CTBL, BR hello            ; code instruction element
          BR world            ; code instruction element
          BR there            ; code instruction element
          ...
    

    Which would be used more directly as the branch target instead of loading the elements as pointers:

          LEA R0, CTBL        ; points to start of code table / array
          ADD R0, R0, R1      ; points to proper element from table
          JSRR R0 ; or JMP R0 ; indirect function call (or jmp) directly into the table
    

    You see here we actually branch into the table as each element is an executable instruction.


    These are some of the constructs that a compiler uses for switch statements, in particular when the case values are dense and contiguous.  When they aren't the compiler will try to identify distinct groups of such ranges, using ordinary if-then-else constructs to separate the groups.