News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Reading the Status Register of the Parallel Port (solved)

Started by popcalent, June 02, 2021, 08:08:13 PM

Previous topic - Next topic

popcalent

Hello everybody,

I'm trying to code a program to read the status register of the parallel port, but I can't get it to work.

I made an adapter to connect an NES controller to the parallel port using a PIC16F84A microcontroller. The adapter works fine, when I press a button on the NES controller, the right output of the microcontroller goes high. So, I know the adapter is not the problem. Here's a simple schematic of the adapter:

The outputs of the microcontroller go straight into the LPT pins as shown in the picture.

I wrote a quick asm program to read the status of the LPT, but I always read the same status no matter what buttons I press on the NES controller.
This is what I'm using to read the status register (I'm attaching the whole code at the end of this post):
CALL LPT_IN
PUT_CHAR AL, 15
...
LPT_IN  PROC
        PUSH DX
        MOV  DX, 0379H
        IN   AL, DX
        POP  DX
        RET
LPT_IN  ENDP

I'm trying this on real hardware: A 286 with MSDOS 3.31. I can output any byte on the LPT output register (0x378), that's not a problem, but I can't read the status register (0x379).


What am I doing wrong? Thanks!

Edit: I found the mistake. It's in bold printing in the code below.

;#################################################
;#
;# LPTTEST.ASM
;#
;#################################################

;#################################################
;# DEFINITIONS
;#################################################

;#################################################
;# MACROS
;#################################################
PUT_CHAR MACRO C,A  ; C=CHAR, A=COLOR
        PUSH AX
        PUSH BX
        PUSH CX
        MOV AH,09H
        MOV BL,A      ;COLOR
        MOV CX,1      ;NUMBER OF CHARS
        MOV AL,C      ;CHARACTER
        INT 10H
        POP CX
        POP BX
        POP AX
        ENDM
;#################################################
;# DATA
;#################################################
DATASG SEGMENT PARA 'Data'

DATASG ENDS
;#################################################
;# PROGRAM STARTS HERE
;#################################################
CODESG SEGMENT PARA 'Code'
READ_LPT PROC FAR
        ASSUME CS:CODESG, DS:DATASG
        MOV AX, DATASG
        MOV DS, AX

        MOV AL, 10000000B       ;BIT7=1  BECAUSE WE TAKE OUR Vcc FROM HERE
        CALL LPT_OUT

MAIN_LOOP:
        CALL LPT_IN
        PUT_CHAR AL, 15

      ;SOLUTION
        MOV  AH, 0      ;THIS BLOCKS THE PROGRAM BEFORE I CAN CHANGE THE PORT STATUS
        INT  16H           ;IF I CHANGE THE PORT STATUS AND PRESS A KEY OTHER THAN ESC, THE CHANGE
                                 ;WILL BE REFLECTED ON SCREEN IN THE NEXT ITERATION


EXIT_PROGRAM:
        CMP AH, 1
        JE EXIT_P
        JMP MAIN_LOOP
EXIT_P:
        MOV AH, 00H
        MOV AL, 3h
        MOV AH, 4CH      ; GO TO MSDOS
        INT 21H
READ_LPT ENDP

;#################################################
;# SUBROUTINE: LPT_OUT
;#################################################
LPT_OUT PROC
        PUSH DX
        MOV  DX, 0378H
        OUT  DX, AL
        POP  DX
        RET
LPT_OUT ENDP
;#################################################
;# SUBROUTINE: LPT_IN
;#################################################
LPT_IN  PROC
        PUSH DX
        MOV  DX, 0379H
        IN   AL, DX
        POP  DX
        RET
LPT_IN  ENDP
CODESG ENDS
END READ_LPT

mineiro

I can't try this, so, maybe I can't help you the way I like.
In ms-dos we have 3 ways to get these informations:
1- in and out instructions
2- direct access to memory area
3- interruptions

Sometimes bus is locked and we need wait in a loop until it changes and give to us access.
You can try to use bios interruption 17.

mov ah,2        ;service 2, read status
mov dx,0        ;0 = LPT1
int 17h         ;printer interruption
;check returned value, I suppose ah register.

Link below was writen in portuguese, can be useful if you translate it.
http://arquivo.ufv.br/dea/ambiagro/arquivos/ParallelPort/PortaParalela/
I'd rather be this ambulant metamorphosis than to have that old opinion about everything

FORTRANS

Hi,

   I don't see anything obviously wrong.  So, for debug
purposes, try the BIOS Printer Functions.

INT 17      Printer Services

INPUT  AH = 0 = Print Character, AL = character
       AH = 1 = Initialize Printer
       AH = 2 = Get Printer Status
       DX = Printer number (0 = LPT1)

OUTPUT AH = Status


   Write a small program, or just use DEBUG.

Regards,

Steve N.

P.S.
   Beaten by mineiro, hope this helps.
SRN

popcalent

Thank you for your response, mineiro and FORTRANS.  :thumbsup:

I had used INT17h before, but it didn't work. I think I forgot to move the LPT number to DX. Anyway, I tried now, and it still doesn't work.

The only thing that changes from using IN/OUT to using INT17h is the character that the program prints at the beginning. Before it printed รง (ASCII 0x87 10000111), and now it prints a dot with a concentric hole (ASCII 0x09 00001001). This is very strange. The first code makes sense to me because bit 7 is inverted, and bits 2, 1, and 0 are not physical pins and for whatever reason are high. The second code doesn't make any sense. Nevertheless, when I externally change the status register inputs, the program does nothing.

By the way, I tested the status register pins with a tester, and the logic ones and zeroes that I enter are there, but the program can't read them.

FORTRANS

#4
Hi,

   Use DEBUG to display the BIOS memory that indicates
the LPTx ports that the BIOS finds on boot up.

40:08H LPT1
40:0AH LPT2
40:0CH LPT3

  You should see 378H (in little endian) in one of these
words.

   Otherwise look at the returned status when you try to
initialize the port.

Regards,

Steve N.

Actually the standard ports are 3BCH, 378H, or 278H.

mineiro

Good point sir FORTRANS.

I don't check if code below work, need be tested. You can try to change external while this program is running.

5 inputs in default work way.
pin  11   10   12    13      15  XXX        ;XXX=not used
    busy ack paper slctout error 110
    1     0    0    0        0   111        ;87H = busy is active, others inactive
    0     0    0    0        1   001        ;09H = error is active, others inactive

;print_bin proc
PRINT_BIN MACRO C,A  ; C=CHAR, A=COLOR
        PUSH AX
        PUSH BX
        PUSH CX
        push dx
        MOV AH,09H
        MOV BL,A      ;COLOR
        MOV CX,1      ;NUMBER OF CHARS
        mov dh,8      ;print 8 chars (bits)
        MOV dl,C      ;CHARACTER
        again:
        rcl dl,1      ;rotate left most bit in dl to carry flag
        adc al,"0"    ;"0"=30h, echo symbol zero or 1
                      ;add carry (0h or 1h) plus 30h ("0")
        INT 10H       ;print one char
        dec dh        ;8,7,6,5,...
        jnz again     ;while not zero goto again
        pop dx
        POP CX
        POP BX
        POP AX
ENDM
;ret
;print_bin endp


MAIN_LOOP:
        CALL LPT_IN
        and al,80h      ;busy?
        jnz MAIN_LOOP   ;so, read again until not busy
        ...
I'd rather be this ambulant metamorphosis than to have that old opinion about everything

popcalent

Quote from: FORTRANS on June 03, 2021, 01:35:59 AM
Use DEBUG to display the BIOS memory that indicates
the LPTx ports that the BIOS finds on boot up.

40:08H LPT1

FORTRANS,

This is what I see:


I see 378H, but not 379H, or 37AH.

Also, I should mention I'm using a Iomega Zip parallel data cable, as I don't have a regular parallel cable. But it should be the same thing, since it is possible to daisy-chain a printer to a Zip drive.

popcalent

Mineiro,

Your code displays a single ASCII character. I tried to recode it, but my code below shows all zeroes...  I changed MOV BL, C because I wasn't sure if rotating DL could affect DH. Anyway, I'm still doing something wrong....

PUT_BIN MACRO C,A  ; C=CHAR, A=COLOR
        PUSH AX
        PUSH BX
        PUSH CX
        PUSH DX
        MOV AH,09H
        MOV CX,1H      ;NUMBER OF CHRS EACH ITERATION
        MOV DH,8H      ;NUMBER OF ITERATIONS
        MOV BL,C      ;CHARACTER
PRINT_BIN:
        AND BL, 80H
        JNZ PRINT_1
        PUT_CHAR '0', A
        JMP ROTATE_BYTE
PRINT_1:
        PUT_CHAR '1', A
ROTATE_BYTE:
        RCL BL, 1H
        DEC DH
        JNZ PRINT_BIN
        POP DX
        POP CX
        POP BX
        POP AX
        ENDM


FORTRANS


popcalent

Then, according to debug, I'm using the correct address.

popcalent

Quote from: mineiro on June 03, 2021, 01:54:13 AM
MAIN_LOOP:
        CALL LPT_IN
        and al,80h      ;busy?
        jnz MAIN_LOOP   ;so, read again until not busy
        ...

But I shouldn't have to wait since I'm the one who sets/clears the busy bit. Furthermore, I'm not using it as a busy flag, I'm using it as a bit of data.

mineiro

Oh yes, you're right.
I was reading "Parallel Port Complete" trying to find some tips.
http://www.fimee.ugto.mx/profesores/dohernandez/documentos/Parallel_Port_Complete_Programming,_Interfacing_and_Using_the_PCs_Parallel_Printer_Port.pdf

Found that unused bit 0 may indicate timeout (1=timeout).
EPP/ECP/SPP in bios config.
What called my attention was bit 7 inverted as you told before.

Port Design: page 111
Status line cautions: If you're using DOS interrupts or other LPT functions to access the port, tie S3 high and S5 and S7' low (unless you're using these bits for their intended purposes). The bios interrupt requires only S7' to be low.

Well, I can't think others things.
I'd rather be this ambulant metamorphosis than to have that old opinion about everything

popcalent

Quote from: mineiro on June 03, 2021, 06:48:31 AM
Port Design: page 111
Status line cautions: If you're using DOS interrupts or other LPT functions to access the port, tie S3 high and S5 and S7' low (unless you're using these bits for their intended purposes). The bios interrupt requires only S7' to be low.

By tie high / tie low do they mean pull-up/pull-down through resistors? Still S6 and S4 should work...

popcalent

I figured out what the problem was!

This part of the loop reads the parallel port, prints the ASCII code associated to the status register, then waits for a key to be pressed.
I added the last part to have the option of exiting the program (instead of having to reboot the computer).
If I press ESC, the program exits, but if I change the status register and press any key other than ESC, the loop goes one more iteration and the new ASCII code is printed!

        CALL LPT_IN
        PUT_CHAR AL, 15

        MOV  AH, 0     
        INT  16H   

By the way, is there a way to read the keyboard and, if there's nothing there, just continue instead of waiting for a key to be pressed?


Thank you mineiro and FORTRANS for your help!

mineiro

Congrats. You need do some tests, I'm writing from memory.

You can use mouse interruption and check for press and/or release information, ... . Thats Int 33

An easy way is check status of left or right shift key, numlock, capslock, ... . These keys are not stored in keyboard circular buffer.

KEYBOARD - GET SHIFT FLAGS      AH = 02h
Return:
AL = shift flags
AH destroyed by many BIOSes

Bitfields for keyboard shift flags:
Bit(s)  Description
7      Insert active
6      CapsLock active
5      NumLock active
4      ScrollLock active
3      Alt key pressed (either Alt on 101/102-key keyboards)
2      Ctrl key pressed (either Ctrl on 101/102-key keyboards)
1      left shift key pressed
0      right shift key pressed

A code should look like:
mov ah,02h
int 16h
and al,01h                  ;test al,01h
jnz right_shift_pressed      ;jz/jnz ...    <--edited: jz to jnz

You can try too:
KEYBOARD - CHECK FOR KEYSTROKE      AH = 01h
Return:
ZF set if no keystroke available
ZF clear if keystroke available
AH = BIOS scan code
AL = ASCII character

A code should look like:
mov ah,01h
int 16h
jz main_loop
I'd rather be this ambulant metamorphosis than to have that old opinion about everything