assemblyx86x86-16tasmunresolved-external

Turbo Assembler (TASM) Undefined symbol error for extrn procedure


I am working on some Turbo Assembler which I was introduced to in my architecture class. The problem I am working on is using the extrn and include keywords to access a function/procedure from one ASM file and use it in another one. I included the respective files below and also ran the following commands:

tasm file1.asm  
tasm file2.asm  
tlink file1.obj+tlink2.obj

After tlink I get the error about MyProcedure being an undefined symbol in file2.asm.

file1.asm:

.MODEL SMALL
.DATA
    ; Data declarations go here

.CODE
    ; Code segment

    ; Define the procedure
    MyProcedure PROC
        ; Procedure code goes here
        ; For example:
        MOV AH, 9     ; DOS interrupt to print string
        LEA DX, Msg   ; Load the address of the message
        INT 21h       ; Call DOS interrupt
        RET           ; Return from the procedure
    MyProcedure ENDP

    ; Data section
    Msg DB "Hello, World!$"

END

file1.inc:

; Declare external procedure MyProcedure
EXTRN MyProcedure:NEAR

file2.asm:

.MODEL SMALL
.DATA
    ; Data declarations go here

.CODE
    ; Code segment

    ; Include external file
    INCLUDE file1.inc

    ; Main program
    START:
        ; Call the procedure defined in file1.inc
        CALL MyProcedure

        ; Other code goes here

    ; End of main program

END START

Solution

  • I was able to figure out what I was doing. This answer is considered using Borland's 16-bit TASM 5.0 so keep that in mind. I will include two different answers and just remember to use accordingly.

    First way: Using a reference from an external ASM file.
    If you want to reference an external file with a specific procedure, here is an example with instructions:

    file1.asm (main file)

    ; Simple program that will use an external call to file external.asm
    ; The procedure will call a system interrupt
    ; External procedure that will execute the system interrupt in the program
    ; Declare at the top here to let the assembler know that this will be referenced outside this file when creating the assembler object
    
    EXTRN myProcedure:PROC
                     
    .model small
    .stack 100h
    
    .data
        ; Data declaration goes here
    .code
        ; Code declaration goes here
    _start:
        CALL myProcedure   ; calls the myProcedure that will execute the system interrupt
    
    end _start
    

    This first file is the main file that is using the externally called procedure myProcedure. Seems best practice is to place the EXTRN command at the top so the assembler knows.

    file2.asm (externally referenced file)

    ; Program that contains the system interrupt command procedure called myProcedure
    ; Will be externally referenced in main.asm
    ; 
    
    .MODEL small
    .CODE
    
    ; This public declaration works like an interface and lets the assembler know that this procedure 
    ; can be externally referenced and executed by the files that are tlinked with this program respectively
    
    PUBLIC myProcedure
    myProcedure PROC NEAR ; Procedure declaration with procedure code for declaration
        ; Procedure code here
        MOV AH, 4Ch
        INT 21h
        RET
    myProcedure ENDP
    
    END
    

    This file is the external file that will contain the defined procedure myProcedure. The NEAR keyword just lets the program know to jump inside the segment. In this example, when called in the main.asm, it will stay inside the Start segment. If you find the program begins to get larger, you may need to switch to FAR and experiment with that keyword instead.

    Instructions:

    tasm file1.asm  
    tasm file2.asm  
    tlink file1.obj file2.obj  
    main.exe (or whatever executable that is spit out)
    

    Second way: Using a .INC file that will hold the procedure that will be included at run time in the main .ASM file.

    file1.asm (main file)

    ; This program is example on how to use `INCLUDE` keyword in TASM 16-bit program to reference an external procedure, struct, obj, macro, etc
    ; All it does is call a system interrupt from the respective .INC file. In this case it is main2.inc
    
    .MODEL small
    .STACK 100h
    .DATA
        ; Data section here
    
    .CODE
            INCLUDE file1.inc ; Contains the procedure I will reference
    Start:
    
            Call myProcedure ; Call the desired procedure.
    
    END Start
    

    This main file just has the INCLUDE statement that is placed in the .CODE segment. Now that the assembler knows to include the file you can see below at the Start label where I will call the procedure that is being referenced from file1.inc.

    file1.inc (file to be included that contains the procedure)

    ; .INC file that will contain the system interrupt procedure 
    
    PUBLIC myProcedure ; Interface declaration that allows assembler to know that the procedure can be used publicly in the respective file
    myProcedure PROC NEAR
        ; Procedure code here
        MOV AH, 4Ch
        INT 21h
        RET
    myProcedure ENDP
    

    You can see that the .INC works the same way like a hoarder file in C or C++. You just put whatever needs to be referenced there so it can be called in any file at anytime as long as the file that wants to reference included it with the INCLUDE statement. In this case as long as file1.asm has the INCLUDE file1.inc we can use the procedure myProcedure.

    Instructions:

    tasm file1.asm
    tlink file1.obj
    main.exe (or whatever executable that is spit out)
    

    You can see this second set of instructions differs a little bit considering we do not need to tasm or tlink a second file. You can run the program pretty straightforward.


    I hope this helps anyone else on this assembler journey. Please comment and ask questions. I will do my best to get back or I hope someone is able to help.