Article View: comp.sys.amiga.tech
Article #7187Re: Using the serial port h/w
From: John Schultz
Date: Tue, 04 Dec 1990 18:14
Date: Tue, 04 Dec 1990 18:14
736 lines
17643 bytes
17643 bytes
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
Technical Details
Message-ID:
<6075@crash.cts.com>Path:
arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!wuarchive!sdd.hp.com!ucsd!nosc!crash!jcsReferences:
<1953@m1.cs.man.ac.uk>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!wuarchive!sdd.hp.com!ucsd!nosc!crash!jcs From: jcs@crash.cts.com (John Schultz) Newsgroups: comp.sys.amiga.tech Subject: Re: Using the serial port h/w Message-ID: <6075@crash.cts.com> Date: 4 Dec 90 18:14:25 GMT References: <1953@m1.cs.man.ac.uk> Organization: Crash TimeSharing, El Cajon, CA Lines: 736