Re: [buildcheapeeg] SOFTWARE: Input Stage -> Processing (data Stream)

From: Jim Meissner (jpmeissner_at_mindspring.com)
Date: 2002-01-24 20:52:29


Dear Chuck:

I am very happy that you are getting involved. I have seen you lurking andmaking occasional comments.

Your comment about the software development involving "man years" of "talented" people is right on.

It seems to me that no one else on this list realizes this. They all thinkit is "so" easy. I have yet to see "any" code.

( Except for Rob who wrote a complete working program which he calls a rough prototype.)

It took me over a year to write the code for my brain monitor and I had to pay four different consultants to help. You have to know that I am not a programmer, but the interesting thing is that any "one" of these professional programmers ( by themselves ) could not have written the complete code required. . For example a "real" programmer does not get his hands dirty working on micros like the HC11 chip! The UNIX guy that helped me with the FFT would not look at my PC. etc.

(BTW) We used an interrupt driven DOS assembly language routine similar to what you listed and called it from C.
snip

FILE *fp; /* stream for logfile */

UINT int_num[] = {0xc,0xb,0xc,0xb}; /* ser interrupt enable/disable bits */

UINT int_msk[] = {0xef,0xf7,0xef,0xf7};

extern void interrupt comIntSrv(void);

snip

Rob "sort of" asked what I was doing on this list since I have working hardware and software already. I guess the answer is that since I am now retired, I am willing to share and pass on what I have learned. I am attemptingto steer the project toward completion so that the 200 or so people waiting will have their own brain monitor soon. Maybe I'm playing the devil advocate a little also.

Juergen P. (Jim) Meissner
Check out my Website at www.MeissnerResearch.com
Read about the benefits of the Brain State Synchronizer sounds for improving your life and health.
----- Original Message -----
From: Chuck Davis
To: Rob Sacks
Sent: Thursday, January 24, 2002 4:51 AM
Subject: Re: [buildcheapeeg] SOFTWARE: Input Stage -> Processing (data Stream)

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... 

Yahoo! Groups Sponsor

------------------------------------------------------------------------

To unsubscribe from this group, send an email to: buildcheapeeg-unsubscribe_at_egroups.com

Your use of Yahoo! Groups is subject to the Yahoo! Terms of Service.

------------------------------------------------------------------------------

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