In-line Assembler in Lazarus on the Raspberry Pi

[First Demo][Shifts and Loop][Local Variable][Simple Array][2D Array][String][Subroutine][Stack]

See in our Translators tutorial an introduction to ARM assembler and also how we have converted Jack Crenshaw's TINY compiler to generate ARM assembly code. Using version 0.9.30.4-6 of Lazarus on Debian Linux on the Raspberry Pi, we developed these introductory demonstrations (with copious comments).

When arguments are passed to procedures, registers r0 to r3 hold the addresses of the first four arguments.

Demonstration of Addition, Subtraction, Multiplication and Branching

program AsmLazPiDemo1;
var
  MyResult: Integer;
// An assembler function returns the last value of r0
function asmfn: integer; assembler;
label equality;
asm
  mov r0, #0     // initialises r0 to 0
  add r0, #5     // adds immediate operand 5 to r0
  add r0, #2     // adds immediate operand 2 to r0
  sub r0, #4     // subtracts immediate operand 4 from r0
  mov r1, r0     // copies value in r0 (3) to r1
  add r0, r1     // adds r1 to r0, storing sum (6) in r0
  mov r2, r0     // copies contents of r0 (6) to r2
  cmp r0, #3     // compares r0 with immediate operand 3 ...
  beq equality   // and branches to label "equality" if equal (which it is not)
  mul r0, r1, r2 // multiplies r1 (3) by r2 (6) and stores product in r0
  equality:
end;

begin
  MyResult := asmfn;
  writeln(MyResult);
end.

Output: 18

Demonstration of Shifts and Loop

program AsmLazPiDemo2;
var
  ShiftResult, LoopResult: Integer;

function ShiftLeft(x, s: integer): integer; assembler;
asm
  lsl r0, r1   // x logical shift left by s places
end;

function ShiftRight(x, s: integer): integer; assembler;
asm
  lsr r0, r1   // x logical shift right by s places
end;

function LoopTest(iterations: integer): integer; assembler;
// http://www.davespace.co.uk/arm/introduction-to-arm/conditional.html
label loop;
asm
  mov r1, r0         // initialises r1 to iterations
  mov r0, #0         // initialises r0 to 0
  loop:
  add r0, r1         // adds r1 to r0, storing result in r0
  subs r1, #1        // subtracts 1 from r1 and sets flags
  bne loop           // loops if result is not zero
end;

begin
  ShiftResult := ShiftLeft(4, 1);
  writeln('Result of shifting binary 100 left by one place: ', ShiftResult);
  ShiftResult := ShiftRight(64, 2);
  writeln('Result of shifting binary 1000000 right by two places: ', ShiftResult);
  LoopResult := LoopTest(10);
  writeln('Addition of numbers from 10 down to 1: ', LoopResult);
end.    

Output:

/home/pi/laz/asm/AsmLazPiDemo2
Result of shifting binary 100 left by one place: 8
Result of shifting binary 1000000 right by two places: 16
Addition of numbers from 10 down to 1: 55

Demonstration of Local Variable

program AsmLazPiDemo3;
var
  LocalVarResult: Integer;

function LocalVar: integer; assembler;
var
  a: integer;
asm
  mov r1, #13    // initialises r1 with 13
  str r1, a      // stores r1 contents (13) in local variable a
  ldr r0, a      // loads contents of a into r0
end;

begin
  LocalVarResult := LocalVar;
  writeln('Contents of local variable a: ', LocalVarResult);
end.

Output:

/home/pi/laz/asm/AsmLazPiDemo3
Contents of local variable a: 13
--------------------------------------------------
Press enter

Demonstrations of Array Processing

program AsmLazPiDemo4;
type
  TTwoInts = array[0..1] of integer;
var
  MyTwoInts: TTwoInts = (4, 1);
  Int0, Int1: integer;

function ReadArray0(x: TTwoInts): integer; assembler;
asm
  ldr r0, [r0]     // loads first integer of array into r0
end;

function ReadArray1(x: TTwoInts): integer; assembler;
asm
  ldr r0, [r0, #4] // loads second integer of array into r0
end;

procedure WriteArray(x: TTwoInts); assembler;
asm
  mov r1, #42
  str r1, [r0]     // stores 42 as first integer of array
  mov r1, #84
  str r1, [r0, #4] // stores 84 as second integer of array
end;

begin
  Int0 :=  ReadArray0(MyTwoInts);
  writeln('First integer in array: ', Int0);
  Int1 :=  ReadArray1(MyTwoInts);
  writeln('Second integer in array: ', Int1);
  WriteArray(MyTwoInts);
  writeln('After writing to the array:');
  Int0 :=  ReadArray0(MyTwoInts);
  writeln('First integer in array: ', Int0);
  Int1 :=  ReadArray1(MyTwoInts); 
  writeln('Second integer in array: ', Int1);
end.
             

Output:

/home/pi/laz/asm/AsmLazPiDemo4
First integer in array: 4
Second integer in array: 1
After writing to the array:
First integer in array: 42
Second integer in array: 84


program AsmLazPiDemo5;

type
  TTable = array[1..4, 1..5] of byte;
var
  Table: TTable;
  Row, Col: integer;

procedure PopulateArray(arr: TTable); assembler;
label
  StartRow, StartCol;
asm
  mov r2, #0  // r2 to contain current byte
  mov r3, #0  // zero based row count
  StartRow:
    add r2, #10 // Left digit is row number (see output)
    mov r1, #0  // zero based col count
    StartCol:
      add r2, #1 //Right digit is column number.
      // Use indirect indexed addressing to put cell value in table
      str r2, [r0, r1]
      add r1, #1
      cmp r1, #5
    bne StartCol
      add r3, #1
      sub r2, #5 // Right digit becomes 0
      add r0, #5  //Change the address in the base register r0
      cmp r3, #4
  bne StartRow
end;

begin
  PopulateArray(Table);
  for row := 1 to 4 do
    begin
      for col := 1 to 5 do
        write(Table[Row,Col], ' ');
      writeln;
    end;
end.
    

Output:

/home/pi/laz/asm/AsmLazPiDemo5
11 12 13 14 15 
21 22 23 24 25 
31 32 33 34 35 
41 42 43 44 45

Demonstration of String Processing

program AsmLazPiDemo6;
var
  MyString: ShortString = 'abcdefghij';
  MyStringLength: byte;

procedure ProcessString(str: ShortString); assembler;
asm
  ldr r1, [r0, #1] // loads first ASCII code into r1
  sub r1, #32      // converts first letter to upper case
  str r1, [r0, #1] // and stores it
end;

function StringLength(str: ShortString): integer; assembler;
asm
  ldr r0, [r0] // loads first byte (string length) into r0
end;

begin
  ProcessString(MyString);
  writeln(MyString);
  MyStringLength :=  StringLength(MyString);
  writeln('Length of string: ', MyStringLength);
end.    

Output:

/home/pi/laz/asm/AsmLazPiDemo6
Abcdefghij
Length of string: 10

Demonstration of Subroutine

program AsmLazPiDemo7;
var
  Sum: integer;

function AddPowers(num1, exp1, num2, exp2: integer): integer; assembler;
label
  pow, finish;
asm
  mov r4, r0  // copies num1 into r4
  mov r7, r0  // copies num1 into r7
  mov r5, r1  // copies exp1 into r5
  bl pow      // calls subroutine
  mov r0, r4  // saves result in r0
  // repeat for num2 and exp2
  mov r4, r2
  mov r7, r2
  mov r5, exp2
  bl pow
  add r0, r4    // adds current result
  b finish      // then ends

  pow:
    mul r4, r7  // multiplies r7 by r4, storing result in r4
    sub r5, #1  // subtracts 1 from r5
    cmp r5, #1  // compares r5 with 1
    bne pow     // loops if r5 is not equal to 1
  bx lr         // returns from subroutine

  finish:
end;

begin
  Sum := AddPowers(2, 3, 3, 4); // adds 2^3 to 3^4
  writeln('2^3 + 3^4 = ', Sum);
end.
    

Output:

/home/pi/laz/asm/AsmLazPiDemo7
2^3 + 3^4 = 89

Stack Demonstration

This uses store multiple (STM) and load multiple (LDM) instructions to push and pop a single register. (The push and pop instructions are unavailable in the current in-line assembler).

program AsmLazPiDemo8;
type
  TIntArray5 = array[1..5] of integer;
var
  MyInts: TIntArray5 = (2, 4, 6, 8, 10);
  i: integer;

procedure Reverse(arr: TIntArray5); assembler;
label
  StartLoopPush, StartLoopPop;
asm
  mov r1, #0
  StartLoopPush:
    ldr r12, [r0, r1]  // load integer from array into r12
    STMFD r13!, {r12}  // push r12
    add r1, #4
    cmp r1, #20
  bne StartLoopPush
  mov r1, #0
  StartLoopPop:
    LDMFD r13!, {r12}  // pop r12
    str r12, [r0, r1]  //  store r12 in array
    add r1, #4
    cmp r1, #20
  bne StartLoopPop
end;

begin
  Reverse(MyInts);
  for i := 1 to 5 do
    write(MyInts[i] : 3);
  writeln;
end.    

Output:

/home/pi/laz/asm/AsmLazPiDemo8
 10  8  6  4  2
Programming - a skill for life!

In-line assembler with Intel and AT&T syntax, addressing methods and encryption