How to do port I/O (IN/OUT) from OS/2: Difference between revisions
m →Tools |
mNo edit summary |
||
Line 4: | Line 4: | ||
====Assembler source==== | ====Assembler source==== | ||
IDEAL ;Enter Borland's IDEAL mode for TASM | ; io386.mac | ||
P486N ;Assume we have an 80486 processor | ; | ||
MODEL OS2 FLAT ;flat (OS/2) memory model | ; This file was originally downloaded from the internet. | ||
; It is now being used at the Ames Laboratory, Condensed Matter Physics | |||
SEGMENT R2SEG BYTE PUBLIC USE16 'CODE' | ; | ||
; Stefan Zollner, 12 March 1996 | |||
; History: | |||
; 03/12/96 STZO Added lots of comments for PHY 232. | |||
; | |||
; This module contains 4 C or CPP callable procedures that allow access to the | |||
; hardware ports. They must be defined in the calling program as follows: | |||
; | |||
; Note the qualifiers: | |||
; _Far16 indicates a Far 16-bit procedure. | |||
; Far means that the address to the routine is a segment:offset pointer. | |||
; 16-bit means that parameters are either 16-bit constants (2 bytes) | |||
; or far pointers (with segment:offset, needs 4 bytes on the stack). | |||
; The _Pascal qualifier indicates that the PASCAL calling convention is | |||
; used. Among other things, this reverses the order the parameters are | |||
; passed on the stack. | |||
; | |||
; extern "C" void _Far16 _Pascal OUTP8(unsigned short, unsigned char); | |||
; The first parameter is a port address, the second one is a byte. | |||
; The byte is sent to the specified port using the OUT DX,AL instruction | |||
; | |||
; extern "C" void _Far16 _Pascal OUTP16(unsigned short, unsigned short); | |||
; same as above, but a word is sent to the port using the OUT DX,AX instruction. | |||
; | |||
; extern "C" unsigned char _Far16 _Pascal INP8(unsigned short); | |||
; The parameter is the port address. A byte is read using the IN AL,DX | |||
; instruction and returned as the function result. | |||
; | |||
; extern "C" unsigned short _Far16 _Pascal INP16(unsigned short); | |||
; Same as above, but a word is read using the IN AX,DX instruction. | |||
; The word is returned as the function result. | |||
; | |||
; This module will compile under TASM (Borland assembler version 4.1 for OS/2) | |||
; Use the /oi switch when using TASM and the IBM OS/2 linker | |||
; | |||
IDEAL ;Enter Borland's IDEAL mode for TASM | |||
P486N ;Assume we have an 80486 processor | |||
MODEL OS2 FLAT ;flat (OS/2) memory model | |||
SEGMENT R2SEG BYTE PUBLIC USE16 'CODE' | |||
ASSUME CS:R2SEG, DS:NOTHING, ES:NOTHING | ASSUME CS:R2SEG, DS:NOTHING, ES:NOTHING | ||
; This segment is defined as IOPL in the module definition (DEF) file. | ; This segment is defined as IOPL in the module definition (DEF) file. | ||
; | ; | ||
; Detailed description: | ; Detailed description: | ||
; | ; | ||
; Port I/O is a priviledged instruction in the INTEL processor. | ; Port I/O is a priviledged instruction in the INTEL processor. | ||
; Therefore, port I/O is not allowed in user programs (running in ring 3) | ; Therefore, port I/O is not allowed in user programs (running in ring 3) | ||
; We therefore need to place the routines containing port I/O statements | ; We therefore need to place the routines containing port I/O statements | ||
; and other priviledged instructions (such as disable/enable interrupts) | ; and other priviledged instructions (such as disable/enable interrupts) | ||
; into a special segment. We later tell the linker that this segment is | ; into a special segment. We later tell the linker that this segment is | ||
; supposed to run in ring 2 (see DEF file). In the design of the INTEL | ; supposed to run in ring 2 (see DEF file). In the design of the INTEL | ||
; processor, every ring has its own stack. Therefore, some thunking needs | ; processor, every ring has its own stack. Therefore, some thunking needs | ||
; to be done if you call ring 2 (16-bit) from ring 3 (32-bit). Therefore, | ; to be done if you call ring 2 (16-bit) from ring 3 (32-bit). Therefore, | ||
; calling these routines is rather slow. I have not tested the speed, but | ; calling these routines is rather slow. I have not tested the speed, but | ||
; 400-kHz data acquisition is certainly not possible. (See IOAD.ZIP). | ; 400-kHz data acquisition is certainly not possible. (See IOAD.ZIP). | ||
; | ; | ||
; R2SEG is simply the name of the SEGMENT. You can replace R2SEG with any | ; R2SEG is simply the name of the SEGMENT. You can replace R2SEG with any | ||
; valid identifier you like. | ; valid identifier you like. | ||
; | ; | ||
; The segment qualifiers mean the following: The segment is aligned on a | ; The segment qualifiers mean the following: The segment is aligned on a | ||
; BYTE boundary. The segment is PUBLIC (readable by other programs). The | ; BYTE boundary. The segment is PUBLIC (readable by other programs). The | ||
; segment uses 16-bit assembly language instructions by default: Whenever | ; segment uses 16-bit assembly language instructions by default: Whenever | ||
; there is an ambiguity, the 16-bit instructions will be put here by the | ; there is an ambiguity, the 16-bit instructions will be put here by the | ||
; assembler. However, you can override this with an operand size of address | ; assembler. However, you can override this with an operand size of address | ||
; size instruction prefix (DB 66H or DB 67H). It is perfectly OK to include | ; size instruction prefix (DB 66H or DB 67H). It is perfectly OK to include | ||
; 32-bit instructions here. (See the IOAD example for analog/digital | ; 32-bit instructions here. (See the IOAD example for analog/digital | ||
; conversion.) This segment will be placed in the 'CODE' GROUP. 'CODE' not | ; conversion.) This segment will be placed in the 'CODE' GROUP. 'CODE' not | ||
; a reserved word, but most compilers generate this GROUP for their code | ; a reserved word, but most compilers generate this GROUP for their code | ||
; segments. | ; segments. | ||
; | ; | ||
; The ASSUME statements tells the assembler to assume that the segment | ; The ASSUME statements tells the assembler to assume that the segment | ||
; register CS contains the address of the R2SEG segment. We assume nothing | ; register CS contains the address of the R2SEG segment. We assume nothing | ||
; about the contents of DS and ES (which are set by the OS/2 operating system). | ; about the contents of DS and ES (which are set by the OS/2 operating system). | ||
; We are not using variables here (which use the DS, ES segment registers), | ; We are not using variables here (which use the DS, ES segment registers), | ||
; therefore this does not matter here. We also need to assume that the CS | ; therefore this does not matter here. We also need to assume that the CS | ||
; register points to the correct stack segment. This is set up by the operating | ; register points to the correct stack segment. This is set up by the operating | ||
; system or the INTEL processor (not sure which). Remember that each ring | ; system or the INTEL processor (not sure which). Remember that each ring | ||
; has its own stack segment. | ; has its own stack segment. | ||
; | ; | ||
; References: | ; References: | ||
; | ; | ||
; 1) Borland Assembler (TASM) manual. | ; 1) Borland Assembler (TASM) manual. | ||
; 2) See my article in Computers in Physics, March/April 1994 (or is it 95). | ; 2) See my article in Computers in Physics, March/April 1994 (or is it 95). | ||
; 3) Chapter 12 of the IBM Visual Age C++ (Version 3.0) compiler manual. | ; 3) Chapter 12 of the IBM Visual Age C++ (Version 3.0) compiler manual. | ||
; This chapter deals with 23-bit to 16-bit calling issues. | ; This chapter deals with 23-bit to 16-bit calling issues. | ||
; (or the manual for your compiler). | ; (or the manual for your compiler). | ||
; 4) Chapter 3 of the IBM OS/2 Application Design Guide | ; 4) Chapter 3 of the IBM OS/2 Application Design Guide | ||
; (Mixing 16-bit and 32-bit code). | ; (Mixing 16-bit and 32-bit code). | ||
; | ; | ||
PUBLIC OUTP8, OUTP16, INP8, INP16 | PUBLIC OUTP8, OUTP16, INP8, INP16 | ||
PROC OUTP8 FAR | PROC OUTP8 FAR | ||
push bp ;save program's BP register | push bp ;save program's BP register | ||
mov bp, sp ;move 16-bit stack pointer into BP register | mov bp, sp ;move 16-bit stack pointer into BP register | ||
Line 108: | Line 108: | ||
pop bp ;restore program's BP register | pop bp ;restore program's BP register | ||
ret 4 ;remove 4 bytes (2 words) from the stack, RETURN | ret 4 ;remove 4 bytes (2 words) from the stack, RETURN | ||
ENDP OUTP8 | ENDP OUTP8 | ||
PROC OUTP16 FAR | PROC OUTP16 FAR | ||
push bp ;same as above routine OUTP8 | push bp ;same as above routine OUTP8 | ||
mov bp, sp | mov bp, sp | ||
Line 118: | Line 118: | ||
pop bp | pop bp | ||
ret 4 ;remove 4 bytes (2 words) from the stack | ret 4 ;remove 4 bytes (2 words) from the stack | ||
ENDP OUTP16 | ENDP OUTP16 | ||
PROC INP8 FAR | PROC INP8 FAR | ||
push bp ;same as OUTP8, but now we only have one parameter | push bp ;same as OUTP8, but now we only have one parameter | ||
mov bp, sp | mov bp, sp | ||
Line 128: | Line 128: | ||
pop bp | pop bp | ||
ret 2 ;remove 2 bytes (1 word) from the stack | ret 2 ;remove 2 bytes (1 word) from the stack | ||
ENDP INP8 | ENDP INP8 | ||
PROC INP16 FAR ;same as INP16, but now read 16-bit word | PROC INP16 FAR ;same as INP16, but now read 16-bit word | ||
push bp | push bp | ||
mov bp, sp | mov bp, sp | ||
Line 137: | Line 137: | ||
pop bp | pop bp | ||
ret 2 ;remove 2 bytes (1 word) from the stack | ret 2 ;remove 2 bytes (1 word) from the stack | ||
ENDP INP16 | ENDP INP16 | ||
ENDS R2SEG ;end of the ring 2 segment | ENDS R2SEG ;end of the ring 2 segment | ||
END ;end of the assembly language program | END ;end of the assembly language program | ||
====Include file for C++ main program.==== | ====Include file for C++ main program.==== | ||
/* io386.hpp */ | |||
/* external function declarations for io386.dll/lib */ | |||
/* to be used in calling routine */ | |||
/* */ | |||
/* Warning: If you include this file in a C program (not C++), */ | |||
/* then error messages may be caused by the extern "C" modifier. */ | |||
/* When including this in a C program, replace extern "C" by extern. */ | |||
extern "C" void _Far16 _Pascal OUTP8(unsigned short, unsigned char); | |||
// The first parameter is a port address, the second one is a byte. | |||
// The byte is sent to the specified port using the OUT DX,AL instruction | |||
extern "C" void _Far16 _Pascal OUTP16(unsigned short, unsigned short); | |||
// same as above, but a word is sent to the port using the OUT DX,AX instruction. | |||
extern "C" unsigned char _Far16 _Pascal INP8(unsigned short); | |||
// The parameter is the port address. A byte is read using the IN AL,DX | |||
// instruction and returned as the function result. | |||
extern "C" unsigned short _Far16 _Pascal INP16(unsigned short); | |||
// Same as above, but a word is read using the IN AX,DX instruction. | |||
// The word is returned as the function result. | |||
====Include file for C main program.==== | ====Include file for C main program.==== | ||
/* io386.hpp */ | |||
/* io386.hpp */ | /* external function declarations for io386.dll/lib */ | ||
/* external function declarations for io386.dll/lib */ | /* to be used in calling routine */ | ||
/* to be used in calling routine */ | /* */ | ||
/* */ | |||
extern void _Far16 _Pascal OUTP8(unsigned short, unsigned char); | |||
extern void _Far16 _Pascal OUTP8(unsigned short, unsigned char); | // The first parameter is a port address, the second one is a byte. | ||
// The first parameter is a port address, the second one is a byte. | // The byte is sent to the specified port using the OUT DX,AL instruction | ||
// The byte is sent to the specified port using the OUT DX,AL instruction | |||
extern void _Far16 _Pascal OUTP16(unsigned short, unsigned short); | |||
extern void _Far16 _Pascal OUTP16(unsigned short, unsigned short); | // same as above, but a word is sent to the port using the OUT DX,AX instruction. | ||
// same as above, but a word is sent to the port using the OUT DX,AX instruction. | |||
extern unsigned char _Far16 _Pascal INP8(unsigned short); | |||
extern unsigned char _Far16 _Pascal INP8(unsigned short); | // The parameter is the port address. A byte is read using the IN AL,DX | ||
// The parameter is the port address. A byte is read using the IN AL,DX | // instruction and returned as the function result. | ||
// instruction and returned as the function result. | |||
extern unsigned short _Far16 _Pascal INP16(unsigned short); | |||
extern unsigned short _Far16 _Pascal INP16(unsigned short); | // Same as above, but a word is read using the IN AX,DX instruction. | ||
// Same as above, but a word is read using the IN AX,DX instruction. | // The word is returned as the function result. | ||
// The word is returned as the function result. | |||
====Object code.==== | ====Object code.==== | ||
Line 199: | Line 194: | ||
====Linker definition file for DLL (DEF).==== | ====Linker definition file for DLL (DEF).==== | ||
LIBRARY IO386 INITINSTANCE TERMINSTANCE | |||
LIBRARY IO386 INITINSTANCE TERMINSTANCE | PROTMODE | ||
PROTMODE | DATA MULTIPLE NONSHARED READWRITE LOADONCALL | ||
DATA MULTIPLE NONSHARED READWRITE LOADONCALL | CODE LOADONCALL | ||
CODE LOADONCALL | DESCRIPTION 'Port I/O 16-bit library, Version 1.02, 02/16/96 Kurt Jensen/SZ/KM' | ||
DESCRIPTION 'Port I/O 16-bit library, Version 1.02, 02/16/96 Kurt Jensen/SZ/KM' | SEGMENTS R2SEG CLASS 'CODE' IOPL | ||
SEGMENTS R2SEG CLASS 'CODE' IOPL | STACKSIZE 65535 | ||
STACKSIZE 65535 | EXPORTS | ||
EXPORTS | ;From object file: F:\SPEA300\queues\indxlib\io386\io386.obj | ||
;From object file: F:\SPEA300\queues\indxlib\io386\io386.obj | |||
;PUBDEFs (Symbols available from object file): | ;PUBDEFs (Symbols available from object file): | ||
;The number of words passed on the stack has to added by hand. | ;The number of words passed on the stack has to added by hand. | ||
Line 216: | Line 210: | ||
OUTP16 2 | OUTP16 2 | ||
INP8 1 | INP8 1 | ||
====Resulting DLL.==== | ====Resulting DLL.==== | ||
====Resulting import library (LIB)====. | ====Resulting import library (LIB)====. | ||
====The make file which builds it all.==== | ====The make file which builds it all.==== | ||
# io386.mak | |||
# io386.mak | # Created by IBM WorkFrame/2 MakeMake at 19:07:20 on 17 Feb 1996 | ||
# Created by IBM WorkFrame/2 MakeMake at 19:07:20 on 17 Feb 1996 | # | ||
# | # The actions included in this make file are: | ||
# The actions included in this make file are: | # Assemble::Borland Assembler | ||
# Assemble::Borland Assembler | # Compile::C++ Compiler | ||
# Compile::C++ Compiler | # Link::Linker | ||
# Link::Linker | # Package::Zip | ||
# Package::Zip | # Lib::Copy DLL to LIBPATH | ||
# Lib::Copy DLL to LIBPATH | # Lib::Import Lib | ||
# Lib::Import Lib | |||
.SUFFIXES: .LIB .cpp .dll .jnk .mac .obj | |||
.SUFFIXES: .LIB .cpp .dll .jnk .mac .obj | |||
.all: \ | |||
.all: \ | |||
.\io386.zip \ | .\io386.zip \ | ||
.\io386.jnk \ | .\io386.jnk \ | ||
.\io386.LIB | .\io386.LIB | ||
.mac.obj: | .mac.obj: | ||
@echo " Assemble::Borland Assembler " | @echo " Assemble::Borland Assembler " | ||
F:\BCOS2\BIN\TASM.EXE %s /oi | F:\BCOS2\BIN\TASM.EXE %s /oi | ||
{F:\os2utils\tcpip\wwwfemto\os2\io386}.mac.obj: | {F:\os2utils\tcpip\wwwfemto\os2\io386}.mac.obj: | ||
@echo " Assemble::Borland Assembler " | @echo " Assemble::Borland Assembler " | ||
F:\BCOS2\BIN\TASM.EXE %s /oi | F:\BCOS2\BIN\TASM.EXE %s /oi | ||
.cpp.obj: | .cpp.obj: | ||
@echo " Compile::C++ Compiler " | @echo " Compile::C++ Compiler " | ||
icc.exe /Q /Wall /Gh /Ti /Gm /Gd /Ge- /G4 /Ft- /C %s | icc.exe /Q /Wall /Gh /Ti /Gm /Gd /Ge- /G4 /Ft- /C %s | ||
{F:\os2utils\tcpip\wwwfemto\os2\io386}.cpp.obj: | {F:\os2utils\tcpip\wwwfemto\os2\io386}.cpp.obj: | ||
@echo " Compile::C++ Compiler " | @echo " Compile::C++ Compiler " | ||
icc.exe /Q /Wall /Gh /Ti /Gm /Gd /Ge- /G4 /Ft- /C %s | icc.exe /Q /Wall /Gh /Ti /Gm /Gd /Ge- /G4 /Ft- /C %s | ||
.dll.jnk: | .dll.jnk: | ||
@echo " Lib::Copy DLL to LIBPATH " | @echo " Lib::Copy DLL to LIBPATH " | ||
CMD.EXE /C copy %|fF.dll f:\myos2\dll | CMD.EXE /C copy %|fF.dll f:\myos2\dll | ||
{F:\os2utils\tcpip\wwwfemto\os2\io386}.dll.jnk: | {F:\os2utils\tcpip\wwwfemto\os2\io386}.dll.jnk: | ||
@echo " Lib::Copy DLL to LIBPATH " | @echo " Lib::Copy DLL to LIBPATH " | ||
CMD.EXE /C copy %|fF.dll f:\myos2\dll | CMD.EXE /C copy %|fF.dll f:\myos2\dll | ||
.dll.LIB: | .dll.LIB: | ||
@echo " Lib::Import Lib " | @echo " Lib::Import Lib " | ||
implib.exe /nologo %|dpfF.LIB %s | implib.exe /nologo %|dpfF.LIB %s | ||
{F:\os2utils\tcpip\wwwfemto\os2\io386}.dll.LIB: | {F:\os2utils\tcpip\wwwfemto\os2\io386}.dll.LIB: | ||
@echo " Lib::Import Lib " | @echo " Lib::Import Lib " | ||
implib.exe /nologo %|dpfF.LIB %s | implib.exe /nologo %|dpfF.LIB %s | ||
.\io386.dll: \ | .\io386.dll: \ | ||
.\IO386.obj \ | .\IO386.obj \ | ||
.\dllinit.obj \ | .\dllinit.obj \ | ||
Line 286: | Line 278: | ||
.\IO386.obj | .\IO386.obj | ||
.\dllinit.obj | .\dllinit.obj | ||
<< | << | ||
.\io386.zip: \ | .\io386.zip: \ | ||
.\io386.dll \ | .\io386.dll \ | ||
io386.mak | io386.mak | ||
@echo " Package::Zip " | @echo " Package::Zip " | ||
cmd.exe /C f:\os2utils\unz50x32\zip -9 io386 * -x *.zip | cmd.exe /C f:\os2utils\unz50x32\zip -9 io386 * -x *.zip | ||
.\IO386.obj: \ | .\IO386.obj: \ | ||
F:\os2utils\tcpip\wwwfemto\os2\io386\IO386.mac \ | F:\os2utils\tcpip\wwwfemto\os2\io386\IO386.mac \ | ||
io386.mak | io386.mak | ||
.\dllinit.obj: \ | .\dllinit.obj: \ | ||
F:\os2utils\tcpip\wwwfemto\os2\io386\dllinit.cpp \ | F:\os2utils\tcpip\wwwfemto\os2\io386\dllinit.cpp \ | ||
io386.mak | io386.mak | ||
.\io386.jnk: \ | .\io386.jnk: \ | ||
.\io386.dll \ | .\io386.dll \ | ||
io386.mak | io386.mak | ||
.\io386.LIB: \ | .\io386.LIB: \ | ||
.\io386.dll \ | .\io386.dll \ | ||
io386.mak | io386.mak | ||
====A zip file containing all of the above, plus additional files you need.==== | ====A zip file containing all of the above, plus additional files you need.==== |
Revision as of 05:15, 19 March 2012
A question often asked in the comp.os.os2.programming.misc newsgroup is the following: How can I do port I/O (using the Intel IN/OUT commands) from within OS/2? There are several possibilities, but I have found the following most useful: Port I/O in OS/2 should be done from a 16-bit segment running at ring 2. Therefore, you need a 16-bit assembler (or compiler) and you have to tell the linker (in the DEF file) that the segment should have I/O priviledge. Once you have the source code, you need to assemble (or compile) the source code. The resulting OBJ file can either be linked directly to your main program, or you can link the OBJ file into a DLL. (That's what I do.) Then, your programs can do port I/O by simply linking to your DLL.
Sample files:
Assembler source
; io386.mac ; ; This file was originally downloaded from the internet. ; It is now being used at the Ames Laboratory, Condensed Matter Physics ; ; Stefan Zollner, 12 March 1996 ; History: ; 03/12/96 STZO Added lots of comments for PHY 232. ; ; This module contains 4 C or CPP callable procedures that allow access to the ; hardware ports. They must be defined in the calling program as follows: ; ; Note the qualifiers: ; _Far16 indicates a Far 16-bit procedure. ; Far means that the address to the routine is a segment:offset pointer. ; 16-bit means that parameters are either 16-bit constants (2 bytes) ; or far pointers (with segment:offset, needs 4 bytes on the stack). ; The _Pascal qualifier indicates that the PASCAL calling convention is ; used. Among other things, this reverses the order the parameters are ; passed on the stack. ; ; extern "C" void _Far16 _Pascal OUTP8(unsigned short, unsigned char); ; The first parameter is a port address, the second one is a byte. ; The byte is sent to the specified port using the OUT DX,AL instruction ; ; extern "C" void _Far16 _Pascal OUTP16(unsigned short, unsigned short); ; same as above, but a word is sent to the port using the OUT DX,AX instruction. ; ; extern "C" unsigned char _Far16 _Pascal INP8(unsigned short); ; The parameter is the port address. A byte is read using the IN AL,DX ; instruction and returned as the function result. ; ; extern "C" unsigned short _Far16 _Pascal INP16(unsigned short); ; Same as above, but a word is read using the IN AX,DX instruction. ; The word is returned as the function result. ; ; This module will compile under TASM (Borland assembler version 4.1 for OS/2) ; Use the /oi switch when using TASM and the IBM OS/2 linker ; IDEAL ;Enter Borland's IDEAL mode for TASM P486N ;Assume we have an 80486 processor MODEL OS2 FLAT ;flat (OS/2) memory model SEGMENT R2SEG BYTE PUBLIC USE16 'CODE' ASSUME CS:R2SEG, DS:NOTHING, ES:NOTHING ; This segment is defined as IOPL in the module definition (DEF) file. ; ; Detailed description: ; ; Port I/O is a priviledged instruction in the INTEL processor. ; Therefore, port I/O is not allowed in user programs (running in ring 3) ; We therefore need to place the routines containing port I/O statements ; and other priviledged instructions (such as disable/enable interrupts) ; into a special segment. We later tell the linker that this segment is ; supposed to run in ring 2 (see DEF file). In the design of the INTEL ; processor, every ring has its own stack. Therefore, some thunking needs ; to be done if you call ring 2 (16-bit) from ring 3 (32-bit). Therefore, ; calling these routines is rather slow. I have not tested the speed, but ; 400-kHz data acquisition is certainly not possible. (See IOAD.ZIP). ; ; R2SEG is simply the name of the SEGMENT. You can replace R2SEG with any ; valid identifier you like. ; ; The segment qualifiers mean the following: The segment is aligned on a ; BYTE boundary. The segment is PUBLIC (readable by other programs). The ; segment uses 16-bit assembly language instructions by default: Whenever ; there is an ambiguity, the 16-bit instructions will be put here by the ; assembler. However, you can override this with an operand size of address ; size instruction prefix (DB 66H or DB 67H). It is perfectly OK to include ; 32-bit instructions here. (See the IOAD example for analog/digital ; conversion.) This segment will be placed in the 'CODE' GROUP. 'CODE' not ; a reserved word, but most compilers generate this GROUP for their code ; segments. ; ; The ASSUME statements tells the assembler to assume that the segment ; register CS contains the address of the R2SEG segment. We assume nothing ; about the contents of DS and ES (which are set by the OS/2 operating system). ; We are not using variables here (which use the DS, ES segment registers), ; therefore this does not matter here. We also need to assume that the CS ; register points to the correct stack segment. This is set up by the operating ; system or the INTEL processor (not sure which). Remember that each ring ; has its own stack segment. ; ; References: ; ; 1) Borland Assembler (TASM) manual. ; 2) See my article in Computers in Physics, March/April 1994 (or is it 95). ; 3) Chapter 12 of the IBM Visual Age C++ (Version 3.0) compiler manual. ; This chapter deals with 23-bit to 16-bit calling issues. ; (or the manual for your compiler). ; 4) Chapter 3 of the IBM OS/2 Application Design Guide ; (Mixing 16-bit and 32-bit code). ; PUBLIC OUTP8, OUTP16, INP8, INP16 PROC OUTP8 FAR push bp ;save program's BP register mov bp, sp ;move 16-bit stack pointer into BP register mov dx, [bp+8] ;get port address (16-bit) from stack mov al, [bp+6] ;get data byte (8 bits) from stack out dx, al ;write data byte to port address in DX pop bp ;restore program's BP register ret 4 ;remove 4 bytes (2 words) from the stack, RETURN ENDP OUTP8 PROC OUTP16 FAR push bp ;same as above routine OUTP8 mov bp, sp mov dx, [bp+8] mov ax, [bp+6] ;but now we load a data word (16 bits) into AX out dx, ax ;write data word in AX to port address in DX pop bp ret 4 ;remove 4 bytes (2 words) from the stack ENDP OUTP16 PROC INP8 FAR push bp ;same as OUTP8, but now we only have one parameter mov bp, sp mov dx, [bp+6] ;get port address and store it in DX in al, dx ;read a byte from the port in DX and store it in AL sub ah, ah ;set the high byte AH to zero pop bp ret 2 ;remove 2 bytes (1 word) from the stack ENDP INP8 PROC INP16 FAR ;same as INP16, but now read 16-bit word push bp mov bp, sp mov dx, [bp+6] in ax, dx ;read a 16-bit word from port in DX and store it in AX pop bp ret 2 ;remove 2 bytes (1 word) from the stack ENDP INP16 ENDS R2SEG ;end of the ring 2 segment END ;end of the assembly language program
Include file for C++ main program.
/* io386.hpp */ /* external function declarations for io386.dll/lib */ /* to be used in calling routine */ /* */ /* Warning: If you include this file in a C program (not C++), */ /* then error messages may be caused by the extern "C" modifier. */ /* When including this in a C program, replace extern "C" by extern. */ extern "C" void _Far16 _Pascal OUTP8(unsigned short, unsigned char); // The first parameter is a port address, the second one is a byte. // The byte is sent to the specified port using the OUT DX,AL instruction extern "C" void _Far16 _Pascal OUTP16(unsigned short, unsigned short); // same as above, but a word is sent to the port using the OUT DX,AX instruction. extern "C" unsigned char _Far16 _Pascal INP8(unsigned short); // The parameter is the port address. A byte is read using the IN AL,DX // instruction and returned as the function result. extern "C" unsigned short _Far16 _Pascal INP16(unsigned short); // Same as above, but a word is read using the IN AX,DX instruction. // The word is returned as the function result.
Include file for C main program.
/* io386.hpp */ /* external function declarations for io386.dll/lib */ /* to be used in calling routine */ /* */ extern void _Far16 _Pascal OUTP8(unsigned short, unsigned char); // The first parameter is a port address, the second one is a byte. // The byte is sent to the specified port using the OUT DX,AL instruction extern void _Far16 _Pascal OUTP16(unsigned short, unsigned short); // same as above, but a word is sent to the port using the OUT DX,AX instruction. extern unsigned char _Far16 _Pascal INP8(unsigned short); // The parameter is the port address. A byte is read using the IN AL,DX // instruction and returned as the function result. extern unsigned short _Far16 _Pascal INP16(unsigned short); // Same as above, but a word is read using the IN AX,DX instruction. // The word is returned as the function result.
Object code.
Linker definition file for DLL (DEF).
LIBRARY IO386 INITINSTANCE TERMINSTANCE PROTMODE DATA MULTIPLE NONSHARED READWRITE LOADONCALL CODE LOADONCALL DESCRIPTION 'Port I/O 16-bit library, Version 1.02, 02/16/96 Kurt Jensen/SZ/KM' SEGMENTS R2SEG CLASS 'CODE' IOPL STACKSIZE 65535 EXPORTS ;From object file: F:\SPEA300\queues\indxlib\io386\io386.obj ;PUBDEFs (Symbols available from object file): ;The number of words passed on the stack has to added by hand. ;This is NOT added by CPPFILT INP16 1 OUTP8 2 OUTP16 2 INP8 1
Resulting DLL.
====Resulting import library (LIB)====.
The make file which builds it all.
# io386.mak # Created by IBM WorkFrame/2 MakeMake at 19:07:20 on 17 Feb 1996 # # The actions included in this make file are: # Assemble::Borland Assembler # Compile::C++ Compiler # Link::Linker # Package::Zip # Lib::Copy DLL to LIBPATH # Lib::Import Lib .SUFFIXES: .LIB .cpp .dll .jnk .mac .obj .all: \ .\io386.zip \ .\io386.jnk \ .\io386.LIB .mac.obj: @echo " Assemble::Borland Assembler " F:\BCOS2\BIN\TASM.EXE %s /oi {F:\os2utils\tcpip\wwwfemto\os2\io386}.mac.obj: @echo " Assemble::Borland Assembler " F:\BCOS2\BIN\TASM.EXE %s /oi .cpp.obj: @echo " Compile::C++ Compiler " icc.exe /Q /Wall /Gh /Ti /Gm /Gd /Ge- /G4 /Ft- /C %s {F:\os2utils\tcpip\wwwfemto\os2\io386}.cpp.obj: @echo " Compile::C++ Compiler " icc.exe /Q /Wall /Gh /Ti /Gm /Gd /Ge- /G4 /Ft- /C %s .dll.jnk: @echo " Lib::Copy DLL to LIBPATH " CMD.EXE /C copy %|fF.dll f:\myos2\dll {F:\os2utils\tcpip\wwwfemto\os2\io386}.dll.jnk: @echo " Lib::Copy DLL to LIBPATH " CMD.EXE /C copy %|fF.dll f:\myos2\dll .dll.LIB: @echo " Lib::Import Lib " implib.exe /nologo %|dpfF.LIB %s {F:\os2utils\tcpip\wwwfemto\os2\io386}.dll.LIB: @echo " Lib::Import Lib " implib.exe /nologo %|dpfF.LIB %s .\io386.dll: \ .\IO386.obj \ .\dllinit.obj \ {$(LIB)}cppopa3.obj \ {$(LIB)}io386.def \ io386.mak @echo " Link::Linker " icc.exe @<< /B" /de /nologo /noe /m /l" /Feio386.dll cppopa3.obj io386.def .\IO386.obj .\dllinit.obj << .\io386.zip: \ .\io386.dll \ io386.mak @echo " Package::Zip " cmd.exe /C f:\os2utils\unz50x32\zip -9 io386 * -x *.zip .\IO386.obj: \ F:\os2utils\tcpip\wwwfemto\os2\io386\IO386.mac \ io386.mak .\dllinit.obj: \ F:\os2utils\tcpip\wwwfemto\os2\io386\dllinit.cpp \ io386.mak .\io386.jnk: \ .\io386.dll \ io386.mak .\io386.LIB: \ .\io386.dll \ io386.mak
A zip file containing all of the above, plus additional files you need.
Current version: 1.02, dated 02/16/1996.
Tools
Tools you need: I use the following programming tools for doing port I/O:
- VisualAge C++ for OS/2 (version 3.0) with CSDs.
- Borland Assembler TASM (version 4.1), included in Borland C++ for OS/2, version 1.5.
DLLINIT.CPP
Remarks: I was unable to create a DLL consisting ENTIRELY of routines written in assembly language. Therefore, I created a dummy C++ file (DLLINIT.CPP) which is supposed to initialize the C++ runtime environment.
/* This dummy routine is needed to initialized the C/C++ runtime environment */ /* in the DLL. Without this dummy function, the NMAKE will fail with an */ /* error message LNK4038: No starting address. */ int dummy;
Please note: Dr.-Ing. Holger Veit has pointed out to me that there is a different way to do port I/O without requiring ring switching and going to 32-bit. He recommended to use device driver for port I/O. I have not tried this and do not intend to make information about this method available. I merely want to disperse Dr. Veit's opinion that a 16-bit segment is NOT required for port I/O (although I find it convenient for my own purposes). Dr. Veit suggests to download xf86s363.zip from HOBBES and use the FASTIO$ device driver for port I/O. Also see Dr. Veit's article on 32-bit port I/O in the EDM/2 magazine (volume 4, number 1; January 1996).
Also, Eberhard Mattes from Stuttgart has recommended to me that I should stay away from calls to ring 2 DLLs in order to get better performance. He suggests that I should use EMXIO.DLL, or, for even better performance, xf86.sys (sp?).
I respect the opinions of these gentlemen (they are probably right), but what I describe here works just fine for me, therefore I keep using my ring 2 DLLs.
Acknowledgment: IO386.ZIP was originally downloaded from the internet. The original version was written by Keith Murray (Oregon State) with help from Joel Armengaud (IBM). We have modified the file and added some of our own routines. We also added documentation. This is a link to the original documentation which we downloaded from hobbes.
Disclaimer: This server is an experimental offering. Please send comments or complaints to Dr. Stefan Zollner, Department of Physics and Astronomy, A205 Physics Hall. zollner@iastate.edu
DISCLAIMER: This sample code is for your information only. Do not attempt to use this code on your equipment without first inspecting the source code and making sure it does what you want. This code is supplied as is without warranty or liability of any kind. Most of the brand names on this page are registered trademarks.