From: Chuck Davis (roshicorp_at_roshi.com)
Date: 2002-01-24 17:51:42
On 23-Jan-02, Rob Sacks, wrote:
[...]
>So the whole interface out of Stage 1 could be as simple
>as this (I'm forgetting the queues for a moment and thinking
>only of making this as easy as possible for novice programmers):
>get_channel_quantity ();
>get_low_end_of_range ( channelX );
>get_high_end_of_range ( channelX );
>flush (channelX );
>get_next_number (channelX);
>(If we try to do the nearly-real-time stuff that
>Chuck Davis was talking about yesterday, there
>will also have to be a Stage 1 service that the rest of
>the program uses to register a function of its
>own that Stage1 calls when data comes in, but
>that's for later.)
[...]
Rob, this is a note that I posted to Dr. Hudspeth. He wants
to embed a ROSHI type kernel into a 19 channel QEEG thingy.
This is a kewl testing routine, but can't be run thru windbloats :)
Much more efficient than polling, IMHO.
See, I do pass along a few "secrets" ;~)
I dislike watching the wheel being reinvented.
If you've modeled your program to read the HAL4, then
using your proggie on the ROSHI(AVS) EEG frontend should
be a snap. I set the thing up, for this to happen. Kind
of an open platform, as it were, for intrepid EEG programmers.
--------------------------------------------------------------
From: Chuck Davis <roshicorp_at_roshi.com>
To: "William J. Hudspeth, Ph.D." <wjhud_at_finestplanet.com>
Date: Sat, 19 Jan 2002 08:23:20 PST
Subject: x86 interrupts
Hi Bill,
These are the two routines that I used, to create the ROSHI
interrupts.
The attached file, x86 COMMO.asm, is from the HAL4 that I modeled
into 68K code, on the Amiga, circa 1988, the year that I started
coding for the ROSHI.
---------------------------------------------------------------------
Interrupts
Assembly
Language
© Copyright Brian Brown, 1988-2001. All rights reserved.
| Notes | Home
Page |
AN INTRODUCTION TO INTERRUPTS
Interrupt Driven RS232 on IBM-PC
This technique involves using the I/O device to tell the CPU when
it is ready to present data. This frees the CPU for other tasks
during this time, and also guarantees adequate response times. In
our case, it involves enabling interrupt driven operation for the
receive section of the serial port.
When a character arrives, the serial port will issue an
interrupt request to the computers Priority Interrupt Controller
chip (PIC). The PIC will interrupt the CPU and place the vector
of the receive routine onto the data bus. The CPU uses this
vector as an entry into a lookup table which then generates the
address of the routine servicing that interrupt.
The receive interrupt routine reads the character stored in
the RBR and deposits it into a circular queue buffer. Once the
interrupt is serviced, the PIC must be reset to enable further
interrupts to take place.
The 8259 Priority Interrupt Controller
This chip is reponsible for prioritizing interrupts from various
I/O devices and passing them onto the CPU for servicing. When a
device requests service, it asserts its associated interrupt line
(IRQ0 to IRQ7), signalling the PIC. The PIC asserts IRQ to the
processor which responds with INTA (Interrupt Acknowledge). Upon
receiving this, the PIC presents a vector byte on the data bus
which the CPU uses to determine the address of the interrupt
service routine.
In the IBM-PC, the association of IRQ's to I/O devices is,
Interrupt DeviceVector byte
NMI Parity and 8087 device
IRQ0 Timer/Clock output 08
IRQ1 Keyboard 09
IRQ2 EGA/PC network 0A
IRQ3 COM2 0B
IRQ4 COM1 0C
IRQ5 Fixed disk controller XT0D
IRQ6 Floppy disk controller0E
IRQ7 Lpt1 0F
PIC port addresses are 0x20 and 0x21
At port 0x21 is the PIC MASK REGISTER. Each bit in the mask
register corresponds to an IRQ line. IRQ0 is associated with bit
0 and so on. Writing a ZERO to a selected bit enables the
interrupt line for that particular bit.
When the PC is reset or powered on, all interrupt devices are
disabled by writing 0x0f to the PIC mask register. The ROM
BIOS/DOS routines enable IRQ0, IRQ1, IRQ6 and IRQ7 as part of the
system initialisation.
The serial card on the PC uses IRQ4. Enabling bit 4 of the PIC
mask register will thus also enable the reception of interrupts
from the serial card.
To prevent destroying the current state of the mask register,
it is first read then bit 4 is enabled. The new mask setting is
then written back to the mask register.
Enabling of interrupt operation on the serial card
The Interrupt Enable register (IER) must be programmed for
receive interrupt generation. This is achieved by the statement
outportb( IER, 1 );
The PC serial card also has a little bit which needs to be set
in order to allow interrupts to be passed onto the PIC. This bit
must be set, and is done by the statement
outportb( MCR, 0x0b );
/* bit 0 = DTR, bit 1 = RTS, bit 3 = out2 */
Setting the interrupt vector to the receive interrupt
routine
This must be done before enabling the PIC controller, otherwise
it might cause the system to accept interrupts and branch to a
non-existent service routine.
The code which installs the interrupt service routine is,
setvect( 0x0c, recieve );
This places the segment:offset address of the service routine
into the interrupt vector for interrupt 0x0c. This is the vector
which the PIC chip presents when a device asserts IRQ4.
Enabling of the PIC to recognize the serial card
The PIC controller can provide masking of the various IRQ lines
coming from I/O cards. The PC serial card normally uses IRQ4 for
COM1. This interrupt must be programmed for acceptance by the
PIC.
The statements that achieve this are,
mask = inportb( PIC_MASK );
mask = mask & 0xef; /* enable IRQ4 */
outportb( PIC_MASK, mask );
Also, after servicing the interrupt, the PIC must be reset to
allow further interrupts, by the command,
outportb( PIC, 0x20 );
The following program shows a C program which performs
interrupt driven operation of the serial port to receive
characters from a TRANSMITTER station.
/* ser2.c, Recieve interrupt driven serial port */
#define TRUE 1
#define FALSE 0
#define RBR 0x3f8
#define THR 0x3f8
#define LSR 0x3fd
#define IER 0x3f9
#define MCR 0x3fc
#define BYTE unsigned char
#define PIC 0x20
#define PIC_MSK 0x21
#define maxsize 4096
#include <conio.h>
#include <stdio.h>
#include <dos.h>
static int EXIT = FALSE;
union REGS rgs;
static BYTE attribute = 0x07;
static BYTE mask;
static void interrupt (*oldint)();
static BYTE buffer[maxsize];
static BYTE *inptr, *outptr;
static int char_ready = FALSE;
static int overrun = FALSE;
void interrupt recieve( void );
void rs232_install(void) {
outportb( 0x3fb,0x80); /* access baud rate generator */
outportb( 0x3f8,0x0c); /* 9600 baud */
outportb( 0x3f9,0x00);
outportb( 0x3fb,0x03); /* 8 bits, no parity, one stop*/
oldint = getvect( 0x0c );
setvect( 0x0c, recieve ); /* install com1 routine */
outportb( MCR, 0x0b ); /* enable DTR and OUT2 */
outportb( IER, 1 );
mask = inportb( PIC_MSK ); /* enable com1 on pic */
mask = mask & 0xef;
outportb( PIC_MSK, mask );
}
void rs232_deinstall( void ) {
mask = inportb( PIC_MSK );
mask = mask | 0x10;
outportb( PIC_MSK, mask );
outportb( IER, 0 );
outportb( MCR, 0 );
setvect( 0x0c, oldint );
}
void send( BYTE ch) {
while( (inportb(LSR) & 0x20) != 0x20) ;
outportb( THR, ch );
}
void interrupt recieve( void ) {
disable();
*inptr = inportb( RBR );
++inptr;
if( inptr > buffer + maxsize ) inptr = buffer;
char_ready = TRUE;
if( (inportb( LSR ) & 0x02) == 0x02) overrun = TRUE;
else overrun = FALSE;
enable();
outportb( PIC, 0x20); /* signal EOI to 8239 PIC chip */
/* there is an IRET here */
}
void scrollup( tlc, tlr, brc, brr )
unsigned int tlc, tlr, brc, brr;
{
rgs.h.al = 1; rgs.h.ah = 6;
rgs.h.ch = tlr; rgs.h.cl = tlc;
rgs.h.dh = brr; rgs.h.dl = brc;
rgs.h.bh = 7; int86( 0x10, &rgs, &rgs );
}
void writechar( BYTE ch, BYTE attribute ) {
rgs.h.ah = 9; rgs.h.bh = 0;
rgs.x.cx = 1; rgs.h.al = ch;
rgs.h.bl = attribute; int86( 0x10, &rgs, &rgs );
}
main() {
BYTE recch;
unsigned int key;
static int recx = 35, recy = 3, transx = 2, transy = 3;
inptr = buffer;
outptr = buffer;
rs232_install();
recch = inportb( RBR ); /* dummy read of RBR to clear */
clrscr();
printf("+---------------------+ +---------------------+\n");
printf("|Transmit Window: | |Recieve Window: |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("+---------------------+ +---------------------+\n");
printf("\n Type keys to transmit to other PC.\n");
printf(" Incoming data displayed in recieve window.\n");
printf(" Press F10 to STOP.\n");
while( EXIT == FALSE ) {
if( kbhit() != 0) {
gotoxy( transx, transy );
key = getch();
if( key == 0x00 ) EXIT = TRUE;
else {
send( (BYTE) (key & 0xff) );
putchar( key & 0xff );
transx = transx + 1;
if( transx > 29 ) {
transx = 2;
transy++;
if( transy > 16 ) {
scrollup(1,2,28,15);
transy = 16;
}
}
}
}
else {
while( char_ready == TRUE ) {
recch = *outptr;
gotoxy( recx, recy );
if( overrun ) {
attribute |= 0x80;
putchar( 0x07 );
}
else attribute = 0x07;
writechar( recch, attribute );
++outptr;
if( outptr == inptr )
char_ready = FALSE;
if( outptr > buffer + maxsize )
outptr = buffer;
recx = recx + 1;
if( recx > 71 ) {
recx = 35;
recy++;
if( recy > 16 ) {
scrollup(34,2,71,15);
recy = 16;
}
}
}
}
}
rs232_deinstall();
}
© Copyright B Brown 1987-1997. All rights reserved.
-- .-. .-. / \ .-. .-. / \ / \ / \ .-. _ .-. / \ / \ -/--Chuck Davis -/-----\-----/---\---/-\---/---\-----/-----\-------/-------\ RoshiCorp_at_ROSHI.com \ / \_/ `-' \ / \ / \ / `-' 511-9-012 `-' \ / `-' `-' On a Path to Peace of Mind...
COMMENT @
========================================================================
Module: COMMO.ASM
***********************************
* - COMMO.ASM - *
***********************************
D. Schulze 4-6-87
Initials: Date: Comments:
DPS 3-29-86
Description:
This file contains the modules to initialize, restore and
use the communications interrupt.
The transmission protocol from the Brain Wave Monitor is
as follows:
.----------- Sample from the left hemisphere
Switch positions -----. | .-------------- Sample from right hemisphere
v v v
ss ll rr
The switch bits 00000011 represent the left and right switch
closures on the BWM processor pak. The max value expected from
the switch positions byte is 3.
========================================================================
@
; --------------------------------------------
DATA_ EQU 0 ;Offset to data port ... (CONTROL Bit 7 set to 0)
BAUD_L EQU 0 ;Baud rate low byte ........ (CONTROL.7 = 1)
BAUD_H EQU 1 ;Baud rate high byte ....... (CONTROL.7 = 1)
INT_NAB EQU 1 ;Interrupt enable register . (CONTROL.7 = 0)
INT_ID EQU 2 ;Interrupt identifiction register (read)
CONTROL EQU 3 ;Offset to line control register (write)
MODEM EQU 4 ;Offset to modem control register (write)
STATUS EQU 5 ;Offset to status register (read)
; ----------------------------
I_MASK EQU 21H ;Interrupt controller mask register
; -------------------------------------------------------------------
; -------------------------------------------------------------------
DGROUP GROUP DATA
DATA SEGMENT BYTE PUBLIC 'DATA'
ASSUME CS:CODE,DS:DGROUP
PUBLIC LEFT,RIGHT,THEPTR
VECTOR DW 0000,0000 ;Old interrupt service vector
LEFT DB 1024 DUP(0)
RIGHT DB 1024 DUP(0)
SWITCH DW 0000 ;Switch closure
THEPTR DW 0000
OLDPTR DW 0000
STATE DW 0000 ;Current state of input
BASE DW 0000 ;Base address of com port
INT_PTR DB 00 ;COM Interrupt vector number
; ----------------------------
PROCESS DW P0,P1,P2 ;Process vectors for interrupt service
DATA ENDS
; -------------------------------------------------------------------
; -------------------------------------------------------------------
CODE SEGMENT BYTE PUBLIC 'CODE'
; This is the communications interrupt service routine
PUBLIC COMMO
COMMO PROC FAR
ASSUME CS:CODE,DS:DGROUP
STI ;Re-enable the interrupts
PUSH AX ;Preserve the registers destroyed
PUSH BX
PUSH DX
PUSH DS
MOV AX,DATA
MOV DS,AX
MOV DX,BASE
IN AL,DX ;Read and clear the input buffer
MOV BX,STATE
JMP PROCESS[BX] ;Jump to state processing
; ---------------------------------
P0: CMP AL,4 ;Test for illegal switch transmission
JC P4 ;Sync up on switch transmission
JMP EXIT
; ---------------------------------
P1: CMP AL,4 ;Test for illegal switch transmission
JC P4 ;Sync up on switch transmission
SUB AX,7FH ;Minus the DC component
MOV BX,THEPTR
MOV LEFT[BX],AL
MOV STATE,0004 ;Expect the right hemisphere next
JMP EXIT
; ---------------------------------
P2: CMP AL,4 ;Test for illegal switch transmission
JC P4 ;Sync up on switch transmission
MOV BX,THEPTR
SUB AX,7FH ;Minus the DC component
MOV RIGHT[BX],AL
INC THEPTR
AND THEPTR,3FFH ;Mask off the queue size
MOV STATE,0000 ;Expect the switch byte next
JMP EXIT
; ---------------------------------
; Sync up on the switch closure transmission
P4: CBW ;Convert byte transmission to word size
MOV SWITCH,AX
MOV STATE,0002 ;Advance to expect the left hemisphere next
; ---------------------------------
EXIT: MOV AL,20H ;Reset the hardware for the next interrupt
OUT 20H,AL
POP DS
POP DX ;Restore the registers
POP BX
POP AX
IRET
COMMO ENDP
; -------------------------------------------------------------------
; -------------------------------------------------------------------
PUBLIC CLEARCOM
CLEARCOM PROC FAR
MOV DX,BASE ;Interrupt enable register
IN AL,DX ;Read and clear the input buffer
ADD DX,STATUS
IN AL,DX ;Clear the status port
RET
CLEARCOM ENDP
; -------------------------------------------------------------------
; -------------------------------------------------------------------
PUBLIC INITCOM,BAUD
INITCOM PROC FAR
ASSUME CS:CODE,DS:DGROUP
CLI ;Disable any interrupts temporarily
PUSH BP ;Preserve BP for Quick Basic
MOV BP,SP
MOV DI,WORD PTR[BP+6] ;Get address of the port number
MOV AX,WORD PTR[DI] ;Read the com port number
MOV BASE,3F8H ;Base port address for com 1
MOV INT_PTR,0CH ;COM1 Interrupt vector number
CMP AX,1 ;COM1?
JE IC1
MOV BASE,2F8H ;Base port address for com 2
MOV INT_PTR,0BH ;COM2 Interrupt vector number
IC1: MOV AL,10000000B ;Enable access to baud rate counter
MOV DX,BASE
ADD DX,CONTROL ;Point to the line control register
OUT DX,AL
BAUD: MOV AL,18H ;0FH = 7812 Baud 18H = 4800 Baud
MOV DX,BASE
ADD DX,BAUD_L
OUT DX,AL
XOR AL,AL
INC DX ;Point to baud rate high byte
OUT DX,AL ;Clear baud rate high byte
; .---------------- Parity (0 = odd parity)
; |.------------------ Parity (0 = disabled)
; ||.--------------------- Stop bits (0 = 1 stop bit)
; |||..---------------------- Word length (11 = 8 bits)
; vvvvv
MOV AL,00000011B ;Clear bit 7 and initialize parity,
MOV DX,BASE ;stop bits and word length.
ADD DX,CONTROL
OUT DX,AL
; --------------------------
; .------------- RTS (Pin 4 on DB25 connector)
;Enables interrupts ---. |.---------------- DTR (Pin 20 on connector)
; v vv
MOV AL,00001010B ;Turn DTR off and RTS on
MOV DX,BASE ;Point to the modem control register
ADD DX,MODEM
OUT DX,AL ;Configure RTS and DTR
; --------------------------
MOV AL,INT_PTR ;COM Interrupt vector number
MOV AH,35H ;Get the current interrupt vector
INT 21H
; Returns existing COM1 vector in ES:BX
MOV AX,ES
MOV VECTOR,AX ;Preserve the origional segment
MOV VECTOR+2,BX ;Preserve the origional address
; --------------------------
PUSH DS ;Preserve the current segment
MOV AL,INT_PTR ;COM Interrupt vector number
MOV DX,OFFSET COMMO ;Point to the code offset
MOV BX,CODE ;The segment pointer
MOV DS,BX
MOV AH,25H ;Set the interrupt vector
; DS:DX is the new communications interrupt service vector
INT 21H ;Load the interrupt vector
POP DS ;Restore current data segment
; --------------------------
; The 8259 Interrupt priority controller (0=Enabled 1=Disabled)
;COM2 ----------------..-------- COM1
;Fixed disk ---------.||.----------- I/O channel
;Floppy controller--.||||.---------------- Keyboard
;LPT1 -------------.||||||.--------------------- Time of day
; vvvvvvvv
MOV AL,10000000B ;Enable I/O communications interrupts
MOV DX,I_MASK ;8259 Interrupt mask register
OUT DX,AL
; --------------------------
; .----------- Transmission error
; |.---------------- Character transmitted
; ||.----------------------- Character recieved
; vvv
MOV AL,00000101B ;Enable interrupt on character recieved
MOV DX,BASE ;Interrupt enable register
ADD DX,INT_NAB
OUT DX,AL
STI ;Re-enable the interrupts
MOV DX,BASE ;Point to com status port
ADD DX,STATUS
IN AL,DX ;Clear the status port
POP BP
RET 2
INITCOM ENDP
; -------------------------------------------------------------------
; -------------------------------------------------------------------
PUBLIC RESCOM
; Restore origional interrupt vector configuration
RESCOM PROC FAR
ASSUME CS:CODE,DS:DGROUP
XOR AL,AL
MOV DX,BASE ;Interrupt enable register
ADD DX,INT_NAB
OUT DX,AL ;Disable the communications interrupt
PUSH DS ;Preserve the current segment
MOV AX,VECTOR ;The segment pointer
MOV DS,AX
MOV AL,INT_PTR ;COM Interrupt vector number
MOV AH,25H ;Set the interrupt vector
CLI ;Disable any interrupts temporarily
; DS:DX is the new communications interrupt service vector
INT 21H ;Load the interrupt vector
STI ;Re-enable the interrupts
POP DS ;Restore the current data segment
RET
RESCOM ENDP
; -------------------------------------------------------------------
; -------------------------------------------------------------------
PUBLIC SWITCHES
SWITCHES PROC FAR
ASSUME CS:CODE,DS:DGROUP
PUSH BP ;Preserve BP for Quick Basic
MOV BP,SP
MOV DI,WORD PTR[BP+6] ;Get the address of INDEX
MOV AX,SWITCH ;Read the switch configuration
MOV WORD PTR[DI],AX ;and load it
POP BP ;Restore BP for Quick Basic
RET 2
SWITCHES ENDP
; -------------------------------------------------------------------
; -------------------------------------------------------------------
PUBLIC XMIT
; This routine is used for testing only
XMIT PROC FAR
ASSUME CS:CODE,DS:DGROUP
PUSH BP ;Preserve BP for Quick Basic
MOV BP,SP
MOV DI,WORD PTR[BP+6]
MOV AX,SWITCH
MOV WORD PTR[DI],AX
MOV DI,WORD PTR[BP+8]
MOV WORD PTR[DI],0000
MOV SI,WORD PTR[BP+10]
MOV WORD PTR[SI],0000
MOV AX,OLDPTR
CMP AX,THEPTR
JE X1
MOV BX,OLDPTR
INC OLDPTR
AND OLDPTR,3FFH ;Mask off the queue size
MOV AL,LEFT[BX]
CBW
MOV WORD PTR[SI],AX
MOV AL,RIGHT[BX]
CBW
MOV WORD PTR[DI],AX
X1: POP BP ;Restore BP for Quick Basic
RET 6
XMIT ENDP
; -------------------------------------------------------------------
; -------------------------------------------------------------------
PUBLIC REMIT
; This routine is used for testing only
REMIT PROC FAR
ASSUME CS:CODE,DS:DGROUP
MOV OLDPTR,0000
MOV THEPTR,0000
RET
REMIT ENDP
; -------------------------------------------------------------------
; -------------------------------------------------------------------
PUBLIC GETINDX
GETINDX PROC FAR
ASSUME CS:CODE,DS:DGROUP
PUSH BP ;Preserve BP for Quick Basic
MOV BP,SP
MOV DI,WORD PTR[BP+6] ;Get the address of INDEX
MOV AX,THEPTR ;Read the offset into the recieved data
MOV WORD PTR[DI],AX ;Load the index value
POP BP ;Restore BP for Quick Basic
RET 2
GETINDX ENDP
; -------------------------------------------------------------------
CODE ENDS
END
This archive was generated by hypermail 2.1.4 : 2002-07-27 12:28:37 BST