snowdond@r2.cs.man.ac.uk (D.N.Snowdon (MSc PJ)) (12/03/90)
Has anyone out there got any code (C or asm) which reads & writes to the serial port by acessing the hardware (via misc.resource) and using the serial interupts? If you have (and you wouldn't mind sharing it) could you please mail me a copy - I've read the hardware manual but am not entirely clear on how to go about things. thanks snowdond@uk.ac.man.cs.r2
jcs@crash.cts.com (John Schultz) (12/05/90)
In <1953@m1.cs.man.ac.uk> snowdond@r2.cs.man.ac.uk (D.N.Snowdon (MSc PJ)) writes: > Has anyone out there got any code (C or asm) which reads & writes >to the serial port by acessing the hardware (via misc.resource) and >using the serial interupts? > If you have (and you wouldn't mind sharing it) could you please >mail me a copy - I've read the hardware manual but am not entirely >clear on how to go about things. > thanks >snowdond@uk.ac.man.cs.r2 Here you go: Interrupt-driven Serial I/O Testbed by John Schultz 4/16/90 This code directly accesses the hardware and achieves very high baud rates without errors. This code should be legal if the serial device is first opened and exclusive access is granted. Speeds of over 130k baud between a stock, nofastmem 500 and a 25mhz GVP 68030 have been achieved. The '030 Amiga can go much faster; it can read at least 223,720 baud. The 500 poops out at 132,575 baud. Writing fast is no problem, reading eats up some bandwidth, and is thus the bottleneck. Polled I/O (yuk) can yield faster rates. These baud rates were accomplished with a direct 2,3,7 wire, 50 foot shielded cable. Max baud rates may vary depending on cable and hardware configurations. The current test code, ST (Serial Test), writes characters typed on the keyboard 10 times. Errors at high speed show up when less than 10 characters are received or funny characters are inserted. I can run DNet and leave it running while I run this code! DNet must be idling, and the baud rate must be the same as DNet's (usually 19200) the last time you run ST. This is very useful as quiting and restarting DNet is not necessary. Part of the original assembly code came from the Amiga Transactor, September 1989: Volume 2, Issue 6,pp 42-44, by Mike Schwartz. The example changed the AutoVectors directly, and had a few typos. I use SetIntVector(), and do things a little differently with the rbf and tbe code. This was pretty much the first assembly language program I wrote, so experts feel free to point out improvements. I wrote the code using Lattice 5.04. I currently use the Cape or Devpac assemblers for speed, but for this example used Lattice's asm (everyone with Lattice should have it). As always, please point out any optimizations, errors, or illegal acts committed in this code. If you enhance the code, please email me a copy. Use this code as you wish; no strings, ropes, cables, or bungee cords attached. John #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # ser/input.c # ser/input.h # ser/inputhandler.a # ser/makefile # ser/readme # ser/serial.a # ser/st.c # This archive created: Tue Dec 4 10:10:20 1990 export PATH; PATH=/bin:$PATH if test -f 'ser/input.c' then echo shar: will not over-write existing file "'ser/input.c'" else cat << \SHAR_EOF > 'ser/input.c' /* input.c created by John Schultz, 10-Sep-89 */ #include <exec/types.h> #include <exec/io.h> #include <exec/ports.h> #include <exec/interrupts.h> #include <proto/exec.h> #include <proto/graphics.h> #include <devices/input.h> #include <devices/inputevent.h> #include "input.h" unsigned short volatile raw; /* raw key value */ struct MsgPort indevport; struct MsgPort * indevportptr; struct IOStdReq * inreqblockptr; struct Interrupt inhandler; struct InputEvent * eventp; extern void totalinput(); int createti(void) { indevportptr = CreatePort("indevport",0); if (!indevportptr) { return 0; } inreqblockptr = CreateStdIO((struct MsgPort *)indevportptr); if (!inreqblockptr) { return 0; } inhandler.is_Data = (APTR)&raw; inhandler.is_Code = totalinput; inhandler.is_Node.ln_Pri = 51; /* One ahead of Intuition */ if (OpenDevice("input.device",0L, (struct IORequest *)inreqblockptr,0L)) { return 0; } inreqblockptr->io_Command = IND_ADDHANDLER; inreqblockptr->io_Data = (APTR)&inhandler; if (DoIO((struct IORequest *)inreqblockptr)) { return 0; } GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0); if(!GfxBase) return 0; FreeSprite(0); CloseLibrary((struct Library *)GfxBase); return 1; } /* end createti */ void deleteti(void) { if ((indevportptr) && (inreqblockptr)) { inreqblockptr->io_Command = IND_REMHANDLER; inreqblockptr->io_Data = (APTR)&inhandler; DoIO((struct IORequest *)inreqblockptr); CloseDevice((struct IORequest *)inreqblockptr); DeleteStdIO(inreqblockptr); DeletePort(indevportptr); } /* end if */ } /* END deleteti */ /* END input.c */ SHAR_EOF fi # end of overwriting check if test -f 'ser/input.h' then echo shar: will not over-write existing file "'ser/input.h'" else cat << \SHAR_EOF > 'ser/input.h' /* input.h - total input created by John Schultz, 10-Sep-89 */ #ifndef INPUTTI #define INPUTTI extern unsigned short volatile raw; /* raw key input */ extern int createti(void); extern void deleteti(void); #endif /* end input.h */ SHAR_EOF fi # end of overwriting check if test -f 'ser/inputhandler.a' then echo shar: will not over-write existing file "'ser/inputhandler.a'" else cat << \SHAR_EOF > 'ser/inputhandler.a' ; Input Handler ; created by John Schultz, 17-Sep-89 section "TotalInput",code xdef _totalinput ; Input event structure offsets IECLASS equ 4 IECODE equ 6 ; A0 = inputevent pointer ; A1 = data pointer _totalinput: cmp.b #1,IECLASS(A0) ; event->ie_Class == IECLASS_RAWKEY ? bne.b notrawkey move.w IECODE(A0),(A1) ; raw = event->ie_Code notrawkey: moveq.l #0,D0 ; don't pass events to intuition rts END SHAR_EOF fi # end of overwriting check if test -f 'ser/makefile' then echo shar: will not over-write existing file "'ser/makefile'" else cat << \SHAR_EOF > 'ser/makefile' # serial makefile, created by John Schultz, 17-Sep-89 st: st.o serial.o input.o inputhandler.o blink from lib:c.o+st.o+serial.o+input.o+inputhandler.o to st \ lib lib:lc.lib sc sd nd st.o: st.c lc -O st.c serial.o: serial.a asm serial.a input.o: input.h input.c lc input.c inputhandler.o: inputhandler.a asm inputhandler.a SHAR_EOF fi # end of overwriting check if test -f 'ser/readme' then echo shar: will not over-write existing file "'ser/readme'" else cat << \SHAR_EOF > 'ser/readme' Interrupt-driven Serial I/O Testbed by John Schultz 4/16/90 This code directly accesses the hardware and achieves very high baud rates without errors. This code should be legal if the serial device is first opened and exclusive access is granted. Speeds of over 130k baud between a stock, nofastmem 500 and a 25mhz GVP 68030 have been achieved. The '030 Amiga can go much faster; it can read at least 223,720 baud. The 500 poops out at 132,575 baud. Writing fast is no problem, reading eats up some bandwidth, and is thus the bottleneck. Polled I/O (yuk) can yield faster rates. These baud rates were accomplished with a direct 2,3,7 wire, 50 foot shielded cable. Max baud rates may vary depending on cable and hardware configurations. The current test code, ST (Serial Test), writes characters typed on the keyboard 10 times. Errors at high speed show up when less than 10 characters are received or funny characters are inserted. I can run DNet and leave it running while I run this code! DNet must be idling, and the baud rate must be the same as DNet's (usually 19200) the last time you run ST. This is very useful as quiting and restarting DNet is not necessary. Part of the original assembly code came from the Amiga Transactor, September 1989: Volume 2, Issue 6,pp 42-44, by Mike Schwartz. The example changed the AutoVectors directly, and had a few typos. I use SetIntVector(), and do things a little differently with the rbf and tbe code. This was pretty much the first assembly language program I wrote, so experts feel free to point out improvements. I wrote the code using Lattice 5.04. I currently use the Cape or Devpac assemblers for speed, but for this example used Lattice's asm (everyone with Lattice should have it). As always, please point out any optimizations, errors, or illegal acts committed in this code. If you enhance the code, please email me a copy. Use this code as you wish; no strings, ropes, cables, or bungee cords attached. John SHAR_EOF fi # end of overwriting check if test -f 'ser/serial.a' then echo shar: will not over-write existing file "'ser/serial.a'" else cat << \SHAR_EOF > 'ser/serial.a' ; Serial.a ; created by John Schultz, 17-Sep-89 ; last modified 17-April-90 section Serial,CODE xref _raw xdef _baudper xdef _openserial xdef _closeserial xdef _sendser xdef _readser xdef _checkser xdef _rawtoascii xdef _sendsernobuff _LVOSetIntVector equ $FFFFFF5E ESCAPE equ 69 ; input-handler rawkey code CLOCK equ 3579545 _baudper dc.w (CLOCK/19200)-1 ; defaualt CUSTOM equ $dff000 SERDATR equ $000018 SERDAT equ $000030 SERPER equ $000032 INTENA equ $00009a INTENAR equ $00001c INTREQ equ $00009c TBEQSIZE equ 128 ; buffer size in bytes, RBFQSIZE equ 128 ; increase or decrease as needed LED equ $bfe001 ; toggle power led, audio filter ledroll dc.b %00010001 ; flash every 4 bytes ledpad dc.b 0 oldintena dc.w 0 tbequeue ds.b TBEQSIZE ; circular queue tbeqend tbehead dc.l tbequeue tbetail dc.l tbequeue oldtbeinterrupt dc.l 0 oldrbfinterrupt dc.l 0 tbeinterrupt dc.l 0 ; ln_succ dc.l 0 ; ln_prec dc.b 2 ; NT_INTERRUPT dc.b 127 ; ln_pri dc.l 0 ; ln_name dc.l tbehead ; is_data dc.l tbehandler ; is_code rbfinterrupt dc.l 0 ; ln_succ dc.l 0 ; ln_prec dc.b 2 ; NT_INTERRUPT dc.b 127 ; ln_pri dc.l 0 ; ln_name dc.l rbftail ; is_data dc.l rbfhandler ; is_code tbehandler move.w #1,INTREQ(a0) ; clear TBE interrupt move.l (a1),a1 ; a1 = tbehead, (a1) = pointer cmp.l tbetail,a1 ; now a1 = pointer beq.b 99$ move.w SERDATR(a0),d0 ; check to make sure emtpy btst #13,d0 beq.b 99$ move.w #$100,d0 move.b (a1)+,d0 ; get byte from queue move.w d0,SERDAT(a0) ; send byte cmpa.l #tbeqend,a1 bne.b 10$ lea tbequeue(pc),a1 10$ move.l a1,tbehead 99$ rts rbfqueue ds.b RBFQSIZE rbfqend rbfhead dc.l rbfqueue rbftail dc.l rbfqueue rbfhandler move.w SERDATR(a0),d0 ; a0 = custom move.w #$800,INTREQ(a0) ; clear RBF interrupt move.l (a1),a0 ; a1 = rbftail, get pointer move.b d0,(a0)+ ; write byte to buffer cmpa.l #rbfqend,a0 bne.b 10$ lea rbfqueue(pc),a0 10$ move.l a0,(a1) ; a1 = rbftail ;99$ ; move.b ledroll(pc),d0 ; remove from 99$-100$ for more ; rol.b #1,d0 ; speed ; move.b d0,ledroll ; btst #1,d0 ; beq.b 100$ ; bchg.b #1,LED ; toggle power led / audio filter ;100$ rts _openserial move.l a6,-(sp) lea CUSTOM,a0 move.w #$4000,INTENA(a0) ; disable move.l $4,a6 ; execbase lea rbfinterrupt(pc),a1 moveq.l #11,d0 jsr _LVOSetIntVector(a6) move.l d0,oldrbfinterrupt lea tbeinterrupt(pc),a1 moveq.l #0,d0 jsr _LVOSetIntVector(a6) move.l d0,oldtbeinterrupt lea CUSTOM,a0 move.w _baudper,SERPER(a0) ; set period move.w #$3fff,$bfd0fe ; init handshake lines move.w INTENAR(a0),oldintena move.w #$8801,INTENA(a0) ; turn on RBF and TBE INTENA move.w #$0801,INTREQ(a0) ; clear RBF and TBE INTREQ move.w #$c000,INTENA(a0) ; enable move.l (sp)+,a6 rts _closeserial move.l a6,-(sp) lea CUSTOM,a0 move.w #$4000,INTENA(a0) ; disable move.l $4,a6 ; execbase move.l oldrbfinterrupt,a1 moveq.l #11,d0 jsr _LVOSetIntVector(a6) move.l oldtbeinterrupt,a1 moveq.l #0,d0 jsr _LVOSetIntVector(a6) lea CUSTOM,a0 move.w #$0801,INTENA(a0) ; turn off RBF and TBE INTENA move.w oldintena,d0 and.w #$0801,d0 ; mask for bit 11 and bit 1. bset #15,d0 ; set/clr = 1 (set) move.w d0,INTENA(a0) ; reset old RBF and TBE INTENA bclr.b #1,LED move.w #$0801,INTREQ(a0) ; clear RBF and TBE INTREQ move.w #$c000,INTENA(a0) ; enable move.l (sp)+,a6 rts _sendser lea CUSTOM,a0 move.w #$4000,INTENA(a0) move.l tbetail,a1 cmpa.l tbehead,a1 beq.b 5$ move.w #$8001,INTREQ(a0) ; generate a tbe interrupt 5$ move.b d0,(a1)+ ; queue up byte cmpa.l #tbeqend,a1 bne.b 10$ lea tbequeue(pc),a1 10$ move.l a1,tbetail move.w #$c000,INTENA(a0) rts ; send byte in d0 to serial port, non-buffered. _sendsernobuff lea CUSTOM,a0 move.w #$4000,INTENA(a0) 1$ move.w SERDATR,d1 btst #13,d1 ; check TBE beq.b 1$ ; buffer busy move.w #$100,d1 move.b d0,d1 move.w d1,SERDAT(a0) 2$ move.w SERDATR(a0),d1 btst #13,d1 ; check TBE beq.b 2$ ; buffer busy move.w #$001,INTREQ(a0) ; clear TBE interrupt move.w #$c000,INTENA(a0) rts ; uses a0 _readser cmp.w #ESCAPE,_raw ; quits on ESC from input handler beq.b 20$ move.l rbfhead,a0 cmpa.l rbftail,a0 beq.b _readser move.b (a0)+,d0 cmpa.l #rbfqend,a0 bne.b 10$ lea rbfqueue(pc),a0 10$ move.l a0,rbfhead 20$ rts _checkser move.l rbfhead,a0 cmpa.l rbftail,a0 bne.b dataisin moveq.l #0,d0 rts dataisin moveq.l #1,d0 rts ; rawkey in d0 ; ascii returned in d0 _rawtoascii cmp.b #79,d0 bls.b getascii moveq.l #0,d0 rts getascii lea asciitable,a0 move.b 0(a0,d0),d0 rts section asciitable,data ; This is a quick trick, don't do this for anything other than ; home use, boys and girls. Use rawkeyconvert, etc. asciitable dc.b '`' ; backquote ` dc.b '1' dc.b '2' dc.b '3' dc.b '4' dc.b '5' dc.b '6' dc.b '7' dc.b '8' dc.b '9' dc.b '0' dc.b '-' dc.b '=' dc.b $0D ; \ dc.b 0 dc.b '0' ; keypad 0 dc.b 'q' dc.b 'w' dc.b 'e' dc.b 'r' dc.b 't' dc.b 'y' dc.b 'u' dc.b 'i' dc.b 'o' dc.b 'p' dc.b '[' dc.b ']' dc.b 0 dc.b '1' dc.b '2' dc.b '3' dc.b 'a' dc.b 's' dc.b 'd' dc.b 'f' dc.b 'g' dc.b 'h' dc.b 'j' dc.b 'k' dc.b 'l' dc.b ';' dc.b $27 ; ' single quote dc.b 0 dc.b 0 dc.b '4' dc.b '5' dc.b '6' dc.b 0 dc.b 'z' dc.b 'x' dc.b 'c' dc.b 'v' dc.b 'b' dc.b 'n' dc.b 'm' dc.b ',' dc.b '.' dc.b '/' dc.b 0 dc.b '.' ; keypad dc.b '7' dc.b '8' dc.b '9' dc.b ' ' dc.b $8 ; backspace dc.b $9 ; tab dc.b $D ; enter cr dc.b $D ; return cr dc.b $1B ; ESC dc.b $7f ; delete dc.b '' dc.b 0 dc.b 0 dc.b '-' ; keypad dc.b 0 dc.b 0 dc.b $b ; up arrow dc.b $a ; down arrow dc.b $20 ; rt arrow dc.b $8 ; left arrow END SHAR_EOF fi # end of overwriting check if test -f 'ser/st.c' then echo shar: will not over-write existing file "'ser/st.c'" else cat << \SHAR_EOF > 'ser/st.c' /* ST.c : Serial Test */ /* Created by John Schultz, 4/16/90 */ /* Interrupt driven serial I/O testbed */ #include <stdio.h> #include <dos.h> #include "input.h" #define CLOCK 3579545 #define MINBAUD 300 /* Change these at will */ #define MAXBAUD 230000 #define BUFFSIZE 80 #define ESCAPE 69 extern void openserial(void); extern void __asm sendser(register __d0 char c); extern void __asm sendsernobuff(register __d0 char c); extern char __asm readser(void); extern short __asm checkser(void); extern char __asm rawtoascii(register __d0 char c); extern short far baudper; void main(int argc,char ** argv) { short i = 0; long console; long baud; char traw; short notquit = 1; char buffer[BUFFSIZE]; if (argc != 2) { printf("USAGE: st <baud>\n"); exit(0); } baud = atoi(argv[1]); if (baud < MINBAUD) baud = MINBAUD; else if (baud > MAXBAUD) baud = MAXBAUD; printf("Setting baud to %d. Press ESC to quit.\n",baud); baudper = (CLOCK / baud)-1; console = _dopen("*",MODE_OLDFILE); if (console == -1) { printf("Couldn't open console.\n"); exit(0); } if (createti()) { openserial(); traw = 0; raw = 0; while (notquit) { if (raw) { traw = raw; raw = 0; if (traw == ESCAPE) notquit = 0; /* ESC */ traw = rawtoascii(traw); sendser(traw); sendser(traw); sendser(traw); sendser(traw); sendser(traw); sendser(traw); sendser(traw); sendser(traw); sendser(traw); sendser(traw); } if (checkser()) for (i = 0; (i < BUFFSIZE) && (checkser()); buffer[i++] = readser()); if (i > 0) _dwrite(console,buffer,i); i = 0; } /* while notquit */ closeserial(); deleteti(); } /* if createti */ printf("\n"); } /* END ST.c */ SHAR_EOF fi # end of overwriting check # End of shell archive exit 0