Podés utilizar la instrucción IN de assembler. Para eso, hacé lo siguiente:
1. Hacé un pequeño procedimiento en assembler que lea un dato del puerto requerido. Ej:
mov dx, 0378h ;Puerto paralelo
xor eax, eax ;Ponemos el eax a 0. Si sos principiante, usá <mov eax, 0>
in al, dx ;Leemos el puerto y lo almacenamos en al
ret 16 ;Volvemos
2. Compilá el programa y después fijate el código binario del mismo. En el caso anterior sería:
66BA7803 MOV DX,0378h
33C0 XOR EAX,EAX
EC IN AL,DX
C21000 RET 16
3. Poné el código binario en una matriz de byte. Así:
Temp = "66BA780333C0ECC21000"
ReDim Matriz(Len(Temp) / 2 - 1)
For i = 1 To Len(Temp) Step 2
Matriz(i \ 2) = Val("&H" + Mid$(Temp, i, 2))
Next
'Ahora la variable Matriz tiene el código máquina listo para ejecutarse.
4. Utilizando la API CallWindowProc, llamamos a la matriz recién creada. Así:
Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
...
Lectura = CallWindowProc(byval VarPtr(Matriz(0)), 0, 0, 0, 0)
Listo. La variable Lectura tiene el valor leído del puerto paralelo (en este caso). Acá tenés el programa completo:
Option Explicit
Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam