Port Access Libraries
From QB64 Wiki
INP and OUT are often used to access port registers, but QB64 does not have this capability yet so here are some DLL Libraries:
- The following download links have the DLL and sample BAS file to show how it works with an LPT (parallel) port at &H378(888):
- Example 1: Reading the Parallel Port Data, Status and Control register settings before and after reversing it.
DECLARE DYNAMIC LIBRARY "inpout32" FUNCTION Inp32% (BYVAL PortAddress AS INTEGER) SUB Out32 (BYVAL PortAddress AS INTEGER, BYVAL Value AS INTEGER) END DECLARE baseadd = 888 'base address for most LPT printers is &H378 LPTport (baseadd) _DELAY 2 Out32 baseadd + 2, 32 'set control bit 6(C5) high to reverse port to read incoming data _DELAY 2 'sets Data port to 0. may not work on all parallel ports LPTport (baseadd) _DELAY 2 Out32 baseadd + 2, 12 'control port is normally set to 12 Out32 baseadd, 227 'any ASCII code value from 0 to 255. Code 227 prints p character LPTport (baseadd) SUB LPTport (address) basedata% = Inp32(address) PRINT "Data:"; basedata%; CHR$(basedata%) 'read or write PRINT "Status:"; Inp32(address + 1) 'base + 1 read only status from device! PRINT "Control:"; Inp32(address + 2) 'base + 2 read or write. 32 or over reverses port PRINT END SUB
Data: 255 Status: 120 Control: 12 Data: 0 Status: 120 Control: 32 Data: 227 π Status: 120 Control: 12
- DO NOT RUN HIGH CURRENT DEVICES with the 5 volt DC output! Use opto-isolators! with a separate power supply.
- Use appropriate grounding between devices with the 8 ground pins(green in Pinout diagram) provided!
Parallel Port Registers
- Read or Write in normal operation. Read only in reverse mode set by C5 bit on. D0 to D7 data byte values from 0 to 255 can be converted to ASCII characters in a device. Setting the byte value can turn certain pins on or off to control switching devices.
- Pin Bit values on: D0(+ 1), D1(+ 2), D2(+ 4), D3(+ 8), D4(+ 16), D5(+ 32), D6(+ 64), D7(+ 128)
- Read only! Register normally used to monitor the status of a printer or other device that is connected to the parallel port. Pins S3 to S6 are held high by resistors so that it will read 120(or 127) when nothing is connected to the pins. A device can manipulate the read only status port value by grounding certain pins. When 5 volts is fed to a pin, bit will stay on. S7 is inverted so that it adds 128 to the port value when it is grounded. Otherwise S7 is 0 by default. S0, the timeout bit, can be reset off by software when set by EPP mode. Bits S1 and S2 are not used. If the port reads 255 the parallel port is not installed.
- S0 Low can be set in EPP mode timeout only(normally off). INP32(889) AND 1 = 1 when bit is on
- S3 Low for error, offline or paper end(normally on). INP32(889) AND 8 = 8 when bit is on, pin is high
- S4 High for printer selected(normally on). INP32(889) AND 16 = 16 when bit is on, pin is high
- S5 High for out of paper(normally on). INP32(889) AND 32 = 32 when bit is on, pin is high
- S6 Low for Acknowledge(normally on). INP32(889) AND 64 = 64 when bit is on, pin is high
- S7 High for busy, offline, error.(normally off) INP32(889) AND 128 = 128 when bit is on, pin is pulled low
- Read or Write. Register is used to send messages to a printer or other device that is connected to the port. Usually the port sets itself to read 12(C2 and C3 on) after about 30 seconds. The C0, C1, and C3 pins are inverted while C2 is not. Inverted pins are set on when a bit is set low(off). C4 and C5 have no pins, but can be set internally by a program. Setting C4(16) can turn off the IRQ interrupt. When C5 is on(32) the Data port is reversed to read data sent from a device.
- C0 Low(bit on) pulse strobe data sent.(normally off) OUT32 890, INP32(890) OR 1 turns bit on, pin low
- C1 Low(bit on) linefeed or auto feed.(normally off) OUT32 890, INP32(890) OR 2 turns bit on, pin low
- C2 Low(bit off) pulse Initiate.(normally on) OUT32 890, INP32(890) OR 4 turns bit on, pin high
- C3 Low(bit on) printer select in.(normally on) OUT32 890, INP32(890) OR 8 turns bit on, pin low
- C4 High(bit on) IRQ interrupt off(normally off). OUT32 890, INP32(890) OR 16 turns bit on
- C5 High(bit on) reverse data port for input read only(normally off). OUT32 890, INP32(890) OR 32 turns bit on
- Use OUT32 with OR to turn a bit on or XOR to change the current bit status. INP32 with AND can determine if a bit is on or off(0).
- Example 2: A Teletype program that can communicate between the COM ports on two computers using a Null modem cable.
DECLARE DYNAMIC LIBRARY "inpout32" FUNCTION Inp32% (BYVAL PortAddress AS INTEGER) SUB Out32 (BYVAL PortAddress AS INTEGER, BYVAL Value AS INTEGER) END DECLARE Baseadd% = &H3F8 'any valid COM port address (see the Windows Component Settings) Out32 Baseadd% + 3, &H80 'set Divisor Latch Access Bit using 128 Hex (LCR) 'Out32 Baseadd%, &H60 ' 1200 baud 96 Write DL Low Byte 'Out32 Baseadd%, &H30 ' 2400 48 'Out32 Baseadd%, &H18 ' 4800 24 Out32 Baseadd%, &HC ' 9600 12 Maximum baud rate in QB! Out32 Baseadd% + 1, &H0 'Write DL High Byte 'Set Base and IER back to normal registers, and set Data requirements Out32 Baseadd% + 3, &H3 'set bits 0 and 1 = 8 bit word, 1 Stop Bit, No Parity 'COM should now be set as N, 8, 1 by the LCR. Like an OPEN statement. Out32 Baseadd% + 2, &H3 'Flush receiver FIFO buffer, enable FIFO (FCR) DO: x$ = INKEY$ IF x$ = CHR$(27) THEN EXIT DO 'Escape key quits IF x$ > "" THEN Out32 Baseadd%, ASC(x$) 'Write Transmit buffer data IF (Inp32(Baseadd% + 5) AND 1) THEN 'Data ready = 1 (LSR) PRINT CHR$(Inp32(Baseadd%)); 'Display Receive buffer data" END IF LOOP
- Possible port addresses are &H3F8(1016) = COM1, &H2F8(760) = COM2, &H3E8(1000) = COM3 and &H2E8(744) = COM 4.
- Connect each COM port transmit line to the opposing receive line on the other PC. Connect both COM grounds together.
Serial Communication Registers
A Serial port can use extra Registers due to the use of a UART chip. When the Base Address is Read, the chip supplies the data from the FIFO(First In First Out) Receive Buffer. When Written to, it sends data to the FIFO Transmit Buffer. If the DLA Bit is On in the LCR register, then the UART base register accepts the Divisor Latch Low Byte data.
Address DLAB INP/OUT Abb. Register Name Base + 0 Off Write - Transmitter Hold Buffer Off Read - Receiver Buffer On Read/Write - Divisor Latch Low Byte Base + 1 Off Read/Write IER Interrupt Enable Register On Read/Write - Divisor Latch High Byte Base + 2 -- Read Only IIR Interrupt ID Register -- Write Only FCR FIFO Control Register Base + 3 Set Read/Write LCR Line Control Register(DLAB On/Off) Base + 4 -- Read/Write MCR Modem Control Register Base + 5 -- Read Only LSR Line Status Register Base + 6 -- Read Only MSR Modem Status Register" Base + 7 -- Read/Write - Scratch Register (Unused)"
Also there are two Base + 1 Registers. The IER and the DL High Byte. The two Base + 2 Registers (IIR & FCR) act similar to the Base Buffers. The UART chips also use FIFO buffers to receive and send data at the same time. The most common UARTS used today are the 16550 series chips. You can add the bit values to determine the bits you want Written or Read in each register!
Like the bi-directional Parallel port, the COM Base Address can send data to a device and also can receive data at the same time. It accomplishes this by using two FIFO (First In First Out) buffers. To send Data you can use PRINT # or OUT a value to the Base Address. To read data you can use INP(baseadd) or use INPUT$ (bytes, COMnumber). LOC(COMnumber) checks for data in the receive buffer. The Base can also set the Divisor Latch Low Byte, which can also help set the baud rate of the port.
This register is fairly simple to use. The only bits used are 0 to 3. You simply set a bit high to enable the appropriate Interrupt.
Bit # Bit Value Interrupt Description 3 8 Enable Modem Status Interrupt 2 4 Enable Receiver Line Status Interrupt 1 2 Enable Transmit Hold Register Empty Interrupt 0 1 Enable Received Data Available Interrupt
Once the Interrupts are set, you can monitor the port Interrupts in the Read only IIR Interrupt Identification Register in a COM program routine. You just add the bit on values to use more than one Interrupt Enable and use OUT BaseAdd + 1, bitstotal. The IIR will tell if an Interrupt occurs! The IER register also can be read to find the present settings with INP. Like the Base, the IER is used in DLAB Mode for the Divisor High Byte."
This register is a Read Only Register! It monitors the FIFO status and Interrupts sent to the CPU. The Interrupts are enabled in the IER. You can use INP to read it using INP(BaseAddress + 2) AND 2 ^ bitnumber to see if a bit is on or off. The FIFO and Interrupt Status read 2 bits as below:
Bit # INP Value Description 7 and 6 FIFO status 1 1 192 FIFO Enabled 0 1 64 FIFO Enabled but Unusable 0 0 < 64 No FIFO Available 5 32 64 Byte Fifo Enabled (16750 UART) 4 0 Reserved (not used) 3 0 Reserved on 8250 and 16450 UART's 8 16550 UART(or higher) Time-out Pending 2 and 1 Interrupt Status (Bit value Priority) 1 1 6 Receiver Line Status Interrupt (Highest) 1 0 4 Received Data Available Interrupt 0 1 2 Transmitter Hold Register Empty Inter. 0 0 0 Modem Status Interrupt (Lowest) 0 1 1 = No Interrupt Pending. 0 = pending < denotes a byte value less than designated INP value
For FIFO status, just compare the INP status with 192 using AND as below:
This Register also uses the BaseAddress + 2 address, but it is Write Only! You can NOT try to Read it! That just returns data from the IIR Register ONLY! The FCR register is just used to set Interrupt triggers and clear the data from the Recieve or Transmit buffers of 16550 or higher UARTS.
Bit # OUT Value Description 7 and 6 Interrupt Trigger Level 1 1 192 14 Byte Trigger 1 0 128 8 Byte Trigger 0 1 64 4 Byte Trigger 0 0 < 64 1 Byte Trigger 5 32 Enable 64 Byte FIFO (16750 UART only) 4 0 Reserved (not used) 3 8 DMA Mode Select (mode 1 or 2) 2 4 Clear Transmit FIFO 1 2 Clear Receive FIFO 0 1 Enable FIFO's. 0 = Disabled FIFO" < denotes a byte value less than designated OUT value
If the trigger bytes are found in the FIFO buffer, then the Data Receive Available Interrupt in the IIR will trigger. Bits 1 and 2 clear the buffer when set hi. They will automatically reset to 0 when done. Data is lost when cleared or if bit 0 is set to 0. Be aware of this and use carefully! This register is only normally used to clear the buffers and enable them. The 0 bit must be set to 1 or both FIFO buffers are disabled! Reset it!
This Register can determine the Word Length, Stop Bits, Parity and help set the Baud Rate of the Serial Port. For instance, OUT (BaseAddress + 3), 3 will set the Word Length to 8 with 1 Stop Bit and No Parity. Similar to OPEN COM, but you can also open COM ports other than COM1 or COM2 in this register. Be sure to Write a Register value less than 128 after the DLAB baud is set!
Bit # Bit Value Description 7 128 Divisor Latch Access Bit (set baud rate) < 128 Access Receive & Transmit buffers & IER 6 0 Set Break Enable. 64 = Break in Receiver TD" 5, 4, 3 Parity Select X X 0 0 No Parity (bit 3 off only)" 0 0 1 8 Odd Parity 0 1 1 24 Even Parity 1 0 1 40 High Parity (Sticky) 1 1 1 56 Low Parity (Sticky) 2 0 One Stop Bit (normal) 4 2 Stop bits(wordlength 6,7,8) or 1.5 (5)" 1 and 0 Set Word Length 1 1 3 8 Bit Word (normal) 1 0 2 7 Bit Word 0 1 1 6 Bit Word 0 0 0 5 Bit Word < denotes a byte value less than designated OUT value
You can set the Baud Rate first by sending 128 to the LCR with OUT. This forces the Base and IER Registers to accept the Divisor Latch Low and High Byte values respectively. In DLAB mode both registers can be written to or read to find the current Divisor byte settings! The Divisor value is the number that 115200 must be divided by to attain the baud rate:
Speed (BPS) Divisor High Byte(to Base + 1) Low Byte(to Base) 50 2304 &H9 (2304) &H0 (0) 300 384 &H1 (256) &H80 (128) 600 192 &H0 &HC0 (192) 1200 96 &H0 &H60 (96) 2400 48 &H0 &H30 (48) 4800 24 &H0 &H18 (24) 9600 12 &H0 &HC (12) 19200 6 &H0 &H6 (6) 115200 1 &H0 &H1 (1)
The divisor is based on 115,200 as the maximum Baud Rate. Speeds above 300 just send 0 to the IER, but it must be sent too! Below is a routine to set up any COM port without an OPEN statement. Simply use OUT to set it up:
BaseAdd% = &H3F8 'use any valid COM base address on your PC Out32 BaseAdd% + 3, &H80 'set DLAB baud rate divisor bit 7 on with 128 (LCR) Out32 BaseAdd% + 1, &H0 'set DL High Byte on IE Register Out32 BaseAdd%, &H60 'set DL Low Byte to 1200 baud (96) on Base Register Out32 BaseAdd% + 3, &H3 'sets 8 bit Word, 1 Stop Bit, and No Parity (LCR) *
- Explanation: Setting the LCR register's value below 128 resets it from DLAB mode to normal Base Buffer and IER operations
Like the LCR Register, it is Read and Write! Bit 4 sets Loopback Mode that sends TD data back to RD buffer. Bits 1 and 0 force RTS and DTR signals. The Auxiliary Outputs are seldom used anymore! Just set those bits to 0.
Bit # Bit Value Description 4 16 LoopBack Mode (Test UART operation) 3 8 Aux Output 2(control by other circuitry) 2 4 Aux Output 1(MIDI, normally disconnected) 1 2 Force Request to Send (RTS) 0 1 Force Data Terminal Ready (DTR)
Read Only status register used to detect data buffer activity and errors. If ANY data error occurs, bit 7 will be turned on. Specific errors are also listed. Normally only Bit 0 is read, as these errors do not stop the port! Use this register to check for transfer errors.
Bit # INP Value Description 7 128 Error in Received FIFO 6 64 Empty Data Hold Registers(TD and RD Idle) 5 32 Empty Transmitter Hold Register(TD empty) 4 16 Break Interrupt (RD Word Time Out) 3 8 Framing Error (last bit not a Stop Bit) 2 4 Parity Error 1 2 Overrun Error (cannot read fast enough) 0 1 Data Ready (Receiver ready to be read)
This Read Only register monitors each bit each time read. Delta type bits denote any changes since the last time read. Bit 2 On indicates that there was a change from low to high on the RI line since the last read. Often used in modem data transfers!
Bit # INP Value Description 7 128 Carrier Detect (CD) 6 64 Ring Indicator (RI) 5 32 Data Set Ready (DSR) 4 16 Clear To Send (CTS) 3 8 Delta Data Carrier Detect 2 4 Trailing Edge Ring Indicator 1 2 Delta Data Set Ready 0 1 Delta Clear to Send
This unused register can be Read and Written to. You can use it to hold data for later use. It can also be used to test for a valid COM port with a device or loopback plug on it. To test a port, Read this register and try to change it. Then Read it again. If the second reading has changed, then the COM port is accessible and ready to use with a device. Normally a Scratch Register will always read 255 if there is no device connected! COM port registers will not work if the port is not connected to a serial device!