Solved

Read/Write I/O Ports under Win32

Posted on 1998-10-26
52
245 Views
Last Modified: 2010-04-06
How do I access I/O ports under Win32 with Delphi?
The old Port[$xx]:=yy statement from dos pascal doesn't work.

Please no components without source, no .dll solutions (SLOOOOW!), or .vxd's. What I need is just a simple solution, some lines of code, or somthing like that. Maybe a Win32 API call??

Rgds,

nova666.
0
Comment
Question by:nova666
  • 23
  • 17
  • 7
  • +4
52 Comments
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1344482
This will do what you need:

http://sunsite.icm.edu.pl/delphi/ftp/d30share/ioport.zip

(Though it is shareware, I'm sure source will be available if you register...)

Cheers,

Raymond.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344483
nova666,

Nope, you can't use that statement. And you can't talk to the ports directly anymore. Certainly not in NT. They are protected by the kernel. All talking can be done through Windows API.

Here you got 2 great freeware components with source and docs :

http://www.mdlive.com/d3k/zip/free/cd320108.zip  (I use this one)
http://www.torry.ru/vcl/comms/async32.zip

Regards, Zif.



0
 
LVL 1

Author Comment

by:nova666
ID: 1344484
Hmm, thanks, but this is EXACTLY what I didn't want:

- shareware component, no source, registration $35
- bloody complicated (even needs a driver .sys for NT (ok, I see one does need a driver for NT, but I don't need NT support. Sorry, forgot to mention that!))

I have seen some solutions with asm routines for read & write (using the IN and OUT opcodes), but I'm not sure if these do work under Win95 (maybe the samples were for Win3.1/Delphi1 !?).

Here's what I've done with Delphi3, it should make some sound on PC speaker, but it just doesn't work, i.e. no sound:

----8<---- SNIP

function GetPort(Address: Word): Byte;
asm
  mov  dx,Address
  in   al,dx
end;

procedure SetPort(Address: Word; Value: Byte);
asm
  mov  dx,Address
  mov  al,Value
  out  dx,al
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Dummy: Byte;
begin
  Dummy:=GetPort($61);
  SetPort($43,182);
  SetPort($42,$30);
  SetPort($42,$8);
  SetPort($61,Dummy or 3);
  Sleep(1000);
  SetPort($61,Dummy);
end;

---->8---- SNIP

Why does this not work? Is it because this won't run under Win95, or do I have an error in the asm routines? ARG... :-(

nova666.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344485
nova666,

The components I gave you are FREEWARE. Thus no cost AND source AND Docs. Besides, correct the code you have is only possible in WIN 3.X That's because then the ports weren't that protected.

Regards, Zif.
0
 
LVL 1

Author Comment

by:nova666
ID: 1344486
Oh ****, you guys are sooo fast! :)

My reject and comment were intended for rwilson's comment, not for Zif's..... SORRY!

Please post another answer, Zif, and I'll give you the points if it meets my requirements (I'm sure it will!!!)

Sorry again!

nova666.-(
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344487
what do you to program? Sound? or serial I/O ports?
0
 
LVL 1

Author Comment

by:nova666
ID: 1344488
Oh ****, you guys are sooo fast! :)

My reject and comment were intended for rwilson's comment, not for Zif's..... SORRY!

Please post another answer, Zif, and I'll give you the points if it meets my requirements (I'm sure it will!!!)

Sorry again!

nova666.-(
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344489
nova666, wait for a moment. What do you want to program exactly. Because I don't know if the components I gave do what you want.
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1344490
The In and OUT should work ...all depends on the OS ya using...

Cheers,
Viktor
0
 
LVL 1

Author Comment

by:nova666
ID: 1344491
I'd like to code a motherboard/CPU heat/fan monitoring tool.

I know there are already much of these, but either they are too complex to use, or I don't like their UI :)

nova666.
0
 
LVL 1

Author Comment

by:nova666
ID: 1344492
I'm using Win95/98.... I already thought about the IN / OUT opcodes not working here, or do they? I don't think so.

Zif: Hmm your component is for COM port comms, but I need something more general. Or do I have misunderstood the component? (Just took a quick look at it to see if it could help me.)

nova666.
0
 
LVL 1

Author Comment

by:nova666
ID: 1344493
I'm using Win95/98.... I already thought about the IN / OUT opcodes not working here, or do they? I don't think so.

Zif: Hmm your component is for COM port comms, but I need something more general. Or do I have misunderstood the component? (Just took a quick look at it to see if it could help me.)

nova666.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344494
Hi nova666,

It's possible to get simple sound, but with other commands :

procedure Sound(Freq : Word);
var
    B : Byte;
begin
    if Freq > 18 then
        begin
            Freq := Word(1193181 div LongInt(Freq));
            B := Byte(GetPort($61));

            if (B and 3) = 0 then
               begin
                   SetPort($61, Word(B or 3));
                   SetPort($43, $B6);
               end;

            SetPort($42, Freq);
            SetPort($42, Freq shr 8);
        end;
end;

procedure NoSound;
var
  Value: Word;
begin
    Value := GetPort($61) and $FC;
    SetPort($61, Value);
end;

procedure SetPort(address, Value:Word);
var
  bValue: byte;
begin
  bValue := trunc(Value and 255);
  asm
    mov dx, address
    mov al, bValue
    out dx, al
  end;
end;

function GetPort(address:word):word;
var
  bValue: byte;
begin
  asm
    mov dx, address
    in al, dx
    mov bValue, al
  end;
  GetPort := bValue;
end;

Zif.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344495
Aha, now we're there.. you want to work on a fan monitoring tool.. mm, never heard about this. Lets look at this closer. Zif.
0
 
LVL 1

Author Comment

by:nova666
ID: 1344496
Zif,

yes I do know this code :) I already searched the last 2000 Delphi questions and found this one. This was my template :)

I modified the GetPort / SetPort routines to be pure asm routines (according to the Delphi help, my syntax is right (8 bit return values are in AL, etc.)).

This won't spit out a single tone.... WTF am I doing wrong?

nova666.

PS: posted that one comment twice by accident. Sorry.
0
 
LVL 1

Author Comment

by:nova666
ID: 1344497
I have the specs of the Winbond chip which is used on the Asus P2B (BX) board. In the specs all the I/O ports, the chip's registers, etc. are listed.

All I need is a method to read/write these I/O ports (it's not relevant exactly which ports they are, there should be a possibility to do it without any driver interference since all these monitoring tools I mentioned are doing it!).

nova666.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344498
strange, it gives me a tone here. Zif.
0
 
LVL 1

Author Comment

by:nova666
ID: 1344499
Zif,

hmm, please wait some seconds. I never tested it THAT WAY. I modified the asm routines straight away before testing it. Maybe I did something wrong with them (though I don't think so).

MOMPLS.

nova666.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344500
are you sure your functions are correctly written? Where do you assign a result to your functions (i.e. GetPort)?
0
 
LVL 1

Author Comment

by:nova666
ID: 1344501
Back again.

Cut & Pasted the code, but no tone.

Is there another thing that can be controlled by settings I/O ports (keyboard lights or something)??? Maybe my speaker is the problem!??

nova666.
0
 
LVL 1

Author Comment

by:nova666
ID: 1344502
Regarding the asm functions:

functions which begin directly with 'asm' instead of 'begin' don't have a Result variable. The function's result must be in the EAX register if the result is 32 bits, AX if 16, AL if 8 bits. Since the IN opcode does put the read value into AL, no more MOV is needed... (see Delphi help)

nova666.
0
 
LVL 1

Author Comment

by:nova666
ID: 1344503
Hmm since this evolves into a real-time conversation, couldn't we make an ICQ chat or something?

nova666.
0
 
LVL 1

Author Comment

by:nova666
ID: 1344504
My ICQ # is: 13706562

nova666.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344505
Hi all,

I've to take some sleep. Before I leave I give you 1 article as homework :-) :

http://www.warpgroup.com/delphitips/tips1887.htm

Zif.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344506
Hi nova666, my number is 16005233, but I really have to get some sleep now, sorry.
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1344507
Alter your SetPort and GetPort to this:

procedure SetPort(address, value: Word);
var
   bValue: Byte;
begin
   bValue := trunc(value and 255);
   asm
      mov DX, address
      mov AL, bValue
      out DX, AL
   end;
end;

function GetPort(address: Word): Word;
var
   bValue: Byte;
begin
   asm
      mov DX, address
      in  AL, DX
      mov bValue, AL
   end;
   result := bValue;
end;

This WILL work.
0
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 
LVL 1

Author Comment

by:nova666
ID: 1344508
Hmm, yes now it works.... if anyone could tell me why my ASM routines would not work??? And why did Zif's routines not work? They are exactly the same I think!

nova666.-?
0
 
LVL 1

Author Comment

by:nova666
ID: 1344509
Hmm, yes now it works.... if anyone could tell me why my ASM routines would not work??? And why did Zif's routines not work? They are exactly the same I think!

nova666.-?
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1344510
Its not the asm routine itself.  Its the procedure.  It is getting the value of "value" from the wrong place.

By the way, your GetPort function is fine the way you wrote it.  It was SetPort that was causing problems.

I experimented some more with your SetPort ptocedure.

If you declare it like this:

procedure SetPort(Address: Word; Value: Byte);
begin
asm
  mov  dx,Address
  mov  al,Value
  out  dx,al
end;
end;

it will work as well.  The "begin" and "end" make the difference.

Why it works...beats me!  I know it has to do something with the declaration as a procedure though.
0
 
LVL 1

Author Comment

by:nova666
ID: 1344511
Now, I'm in a dilemma. Whom to give the points?

Zif already suggested the right code, but it wouldn't work first.
Scrapdog suggested the same code again, but then it DID work!

I'll sleep over it and give one of you the points tomorrow!

Sorry for the inconveniece.

AND WhyTF DON'T THE ASM THINGS WORK?
I narrowed it down to *my* SetPort routine (I use the SetPort routine you suggested, and my GetPort, and it works. Vice versa it doesn't). PRICE QUESTION: Now what's the difference between the following? (BTW: Trunc is for real (floating-point) numbers only. I wonder why the compiler doesn't complain about that!)

-----8<----- SNIP

{ ***** MY asm routine ***** }
procedure SetPort(Address: Word; Value: Byte);
asm
  mov  dx,Address
  mov  al,Value
  out  dx,al
end;

{ ***** YOUR routine ***** }
procedure SetPort(address, Value:Word);
var
  bValue: byte;
begin
  bValue := trunc(Value and 255);
  asm
    mov dx, address
    mov al, bValue
    out dx, al
  end;
end;

----->8----- SNIP

nova666.
0
 
LVL 1

Author Comment

by:nova666
ID: 1344512
Dare! :)))) We got the answer at the same time! :)

Well, I'll got to bed now, too. Probably giving YOU the points, scrapdog, since ZifNab already has so many of them! He'll understand it... I hope! Till tomorrow! THX again to both of you!

nova666.
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1344513
I didn't even notice that ZifNab posted the same code as I did until you just mentioned it!  I wasn't looking close enough at his code to notice this.  We must have extracted it from the same source code.  I don't want you or zif to think I'm a thief!! :)

I have no clue why mine worked and his didn't...looks the same to me!

By the way, I would suggest just using this instead:

procedure SetPort(Address: Word; Value: Byte);
begin
asm
  mov  dx,Address
  mov  al,Value
  out  dx,al
end;
end;

It is faster and simpler.
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1344514
btw, if you don't want to use this

(procedure SetPort(Address: Word; Value: Byte);
begin
asm
  mov  dx,Address
  mov  al,Value
  out  dx,al
end;
end;
)

then give zif the points. :)


0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1344515
Due to the fact that I can't retract my answer it looks like I am going to be the target of some heavy flaming :)
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344516
my, my ... you guys have worked during my nap :-).

Great, it's solved! But, now we've a bigger problem, why doesn't it work with some same code?

You, I've tested mine yesterday and it worked perfectly! Since Scapdog's code is almost the same I don't understand it. Maybe it has to do with the configuration of our Delphi?

Zif.
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1344517
result := bValue;    /
 GetPort := bValue;

 
These are the only differences I see between your code and mine.  Could this make the difference?
 
I extracted the setport and getport from the TPCSpeaker component.  Is this where you got yours?
 
My guess is that nova666 probably used your sound function as well...this could have been the faulty function, not the setport and getport...

0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344518
scrapdog,

result/GetPort should be the same.

nope, I didn't got it from that component. I got it from an question/answer article on the net.

But the sound/nosound function work on my system. Everything works! Thats the strange-thing.

Zif.

0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1344519
I just tried your functions on my system, and they work.

It is possible that nova666 simply made a typo...
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344520
scrapdog, I don't know. Because he writes : I just COPY/PAST them. So I guess it isn't a typo. Zif.
0
 
LVL 1

Author Comment

by:nova666
ID: 1344521
Hmm, back again :)

The hole thing is a bit difficult: my code (the pure asm) routines do not work at all (at least the SetPort doesn't, the GetPort seams ok). Whenever I run the code with MY SetPort routine, it doesn't work. And if I run the alternative (your) SetPort routine immediately after using mine, it won't work either. Looks like my SetPort routine screws up the Speaker ports or something.... That's the reason why Zif's code didn't work the first times.... No typo! :-/

Hell, my question is answered, I'll give Zif the points since he gave the right answer first. Scrapdog, don't be disappointed, thanks for your help.

Nevertheless I'm wondering what makes the difference between:

----->8----- SNIP

{ ***** MY asm routine ***** }
procedure SetPort(Address: Word; Value: Byte);
asm
  mov  dx,Address
  mov  al,Value
  out  dx,al
end;

{ ***** alternative routine ***** }
procedure SetPort(Address: Word; Value: Byte);
begin  <---- only difference
asm
  mov  dx,Address
  mov  al,Value
  out  dx,al
end;
end;   <---- only difference

----->8----- SNIP

Maybe I should put this into another question... :)

nova666.
0
 
LVL 1

Author Comment

by:nova666
ID: 1344522
Zif, please post another pseudo-question, so I can give you your pts, and the grade :)

Scrapdog, thanks for your help.

nova666.
0
 
LVL 1

Author Comment

by:nova666
ID: 1344523
Zif, hmm I meant 'pseudo-ANSWER'... Seems like I have some problems with my language today :)

nova666.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344524
mmm isn't it the compiler which expects a begin and end for every procedure? But the strange thing is, that it doesn't gives an error when compiling... strange, I think we've to read some stuff. Zif.
0
 
LVL 8

Accepted Solution

by:
ZifNab earned 100 total points
ID: 1344525
ok here pseude-answere:

 you can access the ports through :

procedure SetPort(address, Value:Word);
       var
         bValue: byte;
       begin
         bValue := trunc(Value and 255);
         asm
           mov dx, address
           mov al, bValue
           out dx, al
         end;
       end;

       function GetPort(address:word):word;
       var
         bValue: byte;
       begin
         asm
           mov dx, address
           in al, dx
           mov bValue, al
         end;
         GetPort := bValue;
       end;

But during our thread we found a better solution. That is nova666 and scrapdog found a shorter solution, while I was asleep.... Heck, then why am I answering then?

Zif.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344526
nope, asm end; (instead of begin end;) should work too. Then what is wrong?
0
 
LVL 1

Author Comment

by:nova666
ID: 1344527
Hey, Zif, please run your ICQ now! Want to talk to you :)

nova666.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1344528
sorry nova666, I'm at work now, and I 've to go to school now ( Master of networks and multimedia :-) till 22.00h-22.30h )

Zif.
0
 

Expert Comment

by:plambrec
ID: 1344529
Hello,

Your problem in this one is :

{ ***** MY asm routine ***** }
      procedure SetPort(Address: Word; Value: Byte);
      asm
        mov  dx,Address
        mov  al,Value
        out  dx,al
      end;

if you want to use only assembler, whithout delphi code, the procedure header should be

      procedure SetPort(Address: Word; Value: Byte); ASSEMBLER;

all the rest is just fine...

regards

Pieter

0
 
LVL 1

Author Comment

by:nova666
ID: 1344530
No, read the Delphi 3 docs, it's explicitly mentioned that the ASSEMBLER reserved word is *supported for backward compatibility*, but NOT REQUIRED!!!

Besides I tested it with the ASSEMBLER word, but (as it would be expected after reading the docs) it makes no difference.

nova666.-)
0
 
LVL 2

Expert Comment

by:Hagen040798
ID: 4446810
Hi

SetPort(Value: Byte: Port: Word);
asm
  OUT  DX,AL
end;

Value is stored in AL and Port in DX.
Delphi use Registeroptimization.

SetPort(Port: Word; Value: Byte);
asm
    XCHG DX,AX
    OUT  DX,AL
end;


Regards, Hagen
0
 
LVL 1

Author Comment

by:nova666
ID: 4448146
Hi Hagen!

Thanks for your very late suggestion. I'll try the XCHG instead of the PUSH/MOV/POP sequence, though... :-)

Currently, I'm using this now:

// Reads a byte from a hardware I/O port
function GetPort(Address: Word): Byte;
asm                // AX = Address
  mov  dx,ax       // put Address into DX
  in   al,dx       // read I/O port [DX], result is in AL
end;

// Writes a byte to a hardware I/O port
procedure SetPort(Address: Word; Value: Byte);
asm                // AX = Address, DL = Value
  push ax          // save Address
  mov  al,dl       // put Value into AL
  pop  dx          // get Address into DX
  out  dx,al       // write I/O port [AX]
end;

nova666.
0
 
LVL 2

Expert Comment

by:Hagen040798
ID: 4453661
Hi

PUSH, POP Combination can be probable faster executed as XCHG. XCHG produce additional branches and locks on some new Processors. But, XCHG with Registers only should be better.
0

Featured Post

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now