Inline Assembler


External Modules

Most high-level languages (3rd-generation) allow the programmer to include object code compiled or assembled in other languages. A good example of this is Turbo Pascal's ability to use object code modules created from Turbo Assembler. There are various technical issues regarding the linkage of assembler to pascal code but, if the standard assembler directives are used there ought not to be too many glitches.

Of the multitude of items that need to be checked when linking assembler to pascal, the following are most important:

Although some languages (eg. C) allow the programmer to write modules in high-level language and link these to a main program in assembler, this is not possible with Turbo Pascal. It is only possible to write procedures in assembler and link these to pascal, which must contain the main program body.

In order to keep the following examples simple, all parameters and return values are assumed to be of type either byte or word. Turbo Pascal has some advanced features for linking to assembler modules but lots of these are very specific to Turbo Pascal. For further information, consult the Turbo Pascal Reference Manual.

Sample Program #17

{ program to illustrate simple linking to an ASM file from pascal }

program ExternalDeclarations;

procedure readsint; external; { define readsint as being in another module }
procedure writesint; external;{ define writesint as being in another module }

{$L ioasm.obj} { include ioasm module object code }

begin
readsint; { read in a value into AX }
writesint; { output the value in AX }
end.

Sample Program #18 - assembler part

; sample function to return a value

DOSSEG
.MODEL SMALL

.DATA

.CODE

PUBLIC samplefunc ; declare that samplefunc is to be used in other
; modules

samplefunc PROC ; stores a specific integer value into AX
mov ax,1234
ret
samplefunc ENDP

END

Sample Program #18 - pascal part

{ program to illustrate linking to a function in an ASM file from pascal }

program ExternalDeclarations;

function samplefunc : word; external;
{ define samplefunc as being in another module }

{$L sample18.obj} { include sample18 module object code }

begin
writeln ('The value is : ', samplefunc);
{ output return value from ASM module }
end.

Machine Code

Another option to programmers was to include machine code directly into the pascal program. By means of the INLINE command, the programmer could specify a string of machine code commands to be integrated with the compiled code at that point. This is not the option of choice since it means that the programmer has to convert the assembly language commands into machine code manually and enter the binary data into the pascal source file. This method is almost never used nowadays.

Inline assembler

The more recent versions of the Turbo Pascal compiler introduced a built-in assembler in the IDE. This means that a programmer can write parts of the pascal program directly in assembly language. The inline assembler, as it is called, can access all variables used in the program. Procedures can even be declared as strictly assembler, thereby eliminating the redundant startup and cleanup code that pascal might normally add.

Just as a block of pascal code is defined by a begin and end, a block of inline assembler code is defined by an asm and end. Strict assembly language procedures are declared to be assembler at the end of the procedure/function declaration. There are of course certain points that must be noted:

  • most of the directives do not work - primarily because they are not needed
  • in procedures, no stack setup is required and no RET is needed
  • comments are in standard pascal-style {}
  • the data segment is already set up and must not be redirected
  • function return values must be set in AX

Sample Program #19

{ program to illustrate inline assembler functionality }

program InlineDemo;

var
Equip : word;

begin
asm { assembler statements }
int 11h { get equipment list }
mov Equip,ax { save in variable Equip }
end;
Writeln ('The equipment word for this computer is : ', Equip);
end.

Sample Program #20

{ program to illustrate memory access functionality }

program MemoryDemo;

var
Equip : word;

begin
Equip:=MemW[$0040:$0010]; { get equipment list from BIOS Data Area }
Writeln ('The equipment word for this computer is : ', Equip);
end.

Sample Program #21

{ program to illustrate inline assembler procedures functionality }

program InlineDemo;

const
VideoSegment = $B800; { assume C/E/VGA mode }

procedure ClrScr; assembler;
{ procedure to clear the screen }
asm
mov ax,VideoSegment { set the video segment }
mov es,ax
mov cx,25*80 { get number of characters on screen }
xor bx,bx { set start position on screen }
@@1:
mov byte ptr es:[bx],0 { clear character }
add bx,2 { move to next position }
loop @@1
end;

procedure GotoXY ( x, y : byte ); assembler;
{ procedure to move the cursor to the specified coordinates }
asm
mov dh,y { set DH=y-1 }
dec dh
mov dl,x { set DL=x-1 }
dec dl
mov bh,0 { set page number=0 }
mov ah,2 { set service number }
int 10h { call video interrupt }
end;

function GetCharacter ( x, y : word ) : byte; assembler;
{ procedure to return the character at a certain position on the screen }
asm
mov ax,y { y }
dec ax { y-1 }
mov dx,160
mul dx { 160*(y-1) }
mov bx,x { x }
dec bx { x-1 }
shl bx,1 { 2*(x-1) }
add bx,ax { BX=160*(y-1) + 2*(x-1) }
mov ax,VideoSegment { set ES to video segment }
mov es,ax
mov ax,es:[bx] { get character }
end;

begin
ClrScr; { clear screen }
GotoXY (10, 10); { move cursor }
Writeln ('X'); { output X at 10,10 }
Writeln (Chr (GetCharacter (10, 10)));
{ get/output the character at 10,10 }
end.