!
Example.s -- Serial I/O Interface Routines
!
! Harry
Porter - 08/01/01
!
! This
program serves as an example of BLITZ assembly code and
! of the
recommended style for indenting and commenting assembly code.
!
! This
program provides a "main" function which reads input characters
! from the
terminal and echoes them back. It
can be used to explore
! the
differences between "raw" and "cooked" serial input modes.
!
! In
addition to the "main" function, this program also provides the
!
following interface for the serial I/O device; these routines might
! provide
the starting point for some other program.
!
.export getChar
.export putChar
.export putString
.export flush
.export initSerial
.export checkSerialDevice
!
! Program
entry point
!
.text
_entry:
!
! Here is
the interrupt vector, which will be loaded at address 0x00000000.
! Each
entry is 4 bytes. They are located
at fixed, pre-defined addresses.
! This program
will only handle SERIAL_INTERRUPTS.
The asynchronous,
! hardware
interrupts (i.e., TIMER and DISK) will be ignored by returning
!
immediately. None of the other
interrupts should occur; if they do, this
! program
will get stuck in an infinite loop.
!
PowerOnReset:
jmp main
TimerInterrupt:
reti
DiskInterrupt:
reti
SerialInterrupt:
jmp
SerialInterruptHandler
HardwareFault:
jmp
HardwareFault
IllegalInstruction:
jmp
IllegalInstruction
ArithmeticException:
jmp
ArithmeticException
AddressException:
jmp
AddressException
PageInvalidException:
jmp
PageInvalidException
PageReadonlyException:
jmp
PageReadonlyException
PrivilegedInstruction:
jmp PrivilegedInstruction
AlignmentException:
jmp
AlignmentException
ExceptionDuringInterrupt:
jmp
ExceptionDuringInterrupt
SyscallTrap:
jmp
SyscallTrap
!
!
Interrupt Service routines
!
SerialInterruptHandler:
call checkSerialDevice
reti
!
! main
!
! The main
program repeatedly prints a prompt, then gets and echoes characters
! until a
NEWLINE is entered. It then
repeats the prompt.
!
main:
set
STACK_START,r15 ! initialize the
stack pointer
call
initSerial
! initialize the serial I/O device
seti
! enable interrupts
loop1:
! loop
set
prompt,r1
! putString ("Enter
something: ")
call
putString
! .
loop2:
! loop
call
getChar
! r1 :=
getChar
cmp
r1,'\n'
! if (r1
== '\n' or '\r') then
be then
! .
cmp
r1,'\r'
! .
bne
else
! .
then:
! .
mov
'\r',r1
!
putChar ('\r')
call putChar
!
.
mov
'\n',r1
!
putChar ('\n')
call
putChar
!
.
jmp
exit
!
break
else:
! else
cmp
r1,'q'
!
if (r1 == 'q') then
bne
else2
!
.
set
bye,r1
! print "Good
bye"
call
putString
! .
call flush
!
wait until I/O completes
debug
! Invoke DEBUG
instruction
jmp
cont
!
else
else2:
!
.
call putChar
! putChar (r1)
cont:
!
end
jmp
loop2
! end
exit:
! .
jmp
loop1
! end
prompt:
.ascii "Enter something (or
'q' to terminate): \n\r\0"
bye: .ascii "\n\rAbout to execute DEBUG
instruction (type 'go' to resume)...
\n\r\0"
.align
!
! getChar
!
! This
routine reads one character from the terminal and returns it in r1.
! It does
not echo the character or process special characters in any way.
! It
checks the input buffer and gets a character from there if one is
!
available. Otherwise, it waits for
a key to be typed.
!
! r1 = the
character
! r2 =
addr of inBufferCount
! r3 =
inBufferCount
! r4 =
addr of inBufferOut
! r5 =
inBufferOut
!
!
Registers modified: r1
!
getChar:
push r2
! save registers
push r3
! .
push r4
! .
push r5 !
.
set
inBufferCount,r2 ! initialize address
registers
set
inBufferOut,r4 ! .
getChLoop:
! loop
! loop
cleari ! disable interrupts
load
[r2],r3
! if
(inBufferCount != 0)
cmp
r3,0
! .
bne
getChExit
!
then break
seti
! enable interrupts
jmp
getChLoop
! end
getChExit:
! .
sub
r3,1,r3
! inBufferCount --
store r3,[r2]
! .
load
[r4],r5 ! r1 := *inBufferOut
loadb [r5],r1
! .
add
r5,1,r5
! inBufferOut ++
cmp
r5,inBufferEnd ! if (inBufferOut == inBufferEnd)
bne
getChElse
! .
set
inBuffer,r5
!
inBufferOut := &inBuffer
getChElse:
! end
store r5,[r4]
! save inBufferOut
seti
! enable interrupts
cmp r1,'\0'
! until (r1 != '\0')
be
getChLoop
! .
pop
r5
! restore regs
pop
r4
! .
pop
r3
! .
pop r2
! .
ret
! return
!
! putChar
!
! This
routine is passed a character in r1.
It writes it to the terminal
! exactly
as it is. Normally, the output
character is added to a buffer
! and will
be written as soon as the device is ready. If the buffer is
! full,
this routine will busy-wait for the buffer to become not-full.
!
! r1 = the
character
! r2 =
addr of outBufferCount
! r3 =
outBufferCount
! r4 =
addr of outBufferIn
! r5 =
outBufferIn
!
! Registers
modified: none
!
putChar:
push r2
! save registers
push r3
! .
push r4
! .
push r5
! .
set
outBufferCount,r2 ! initialize address registers
set
outBufferIn,r4 ! .
putChLoop:
! loop
cleari
! disable interrupts
load
[r2],r3
! if (outBufferCount
< BUFFER_SIZE)
cmp
r3,BUFFER_SIZE ! .
bl
putChExit
! then
break
seti
! enable interrupts
jmp
putChLoop
! end
putChExit:
! .
add
r3,1,r3
! outBufferCount ++
store r3,[r2]
! .
load
[r4],r5
! *outBufferIn := r1
storeb r1,[r5]
! .
add
r5,1,r5 ! outBufferIn ++
cmp
r5,outBufferEnd ! if (outBufferIn
== outBufferEnd) then
bne
putChElse
! .
set
outBuffer,r5
! outBufferIn :=
&outBuffer
putChElse:
! end
store r5,[r4]
! save outBufferIn
call
checkSerialDevice ! start output if necessary
seti
! enable interrupts
pop
r5
! restore regs
pop r4
! .
pop
r3
! .
pop
r2
! .
ret
! return
!
!
putString
!
! This
routine is passed a pointer to a string of characters, terminated
! by
'\0'. It sends all of them except
the final '\0' to the terminal by
! calling
'putChar' repeatedly.
!
!
Registers modified: none
!
putString:
push r1
! save registers
push r2
! .
mov
r1,r2
! r2 := ptr into string
putStLoop:
! loop
loadb [r2],r1
! r1 := next char
add
r2,1,r2
! incr ptr
cmp
r1,0 ! if (r1 == '\0')
be
putStExit
! then
break
call
putChar
! putChar (r1)
jmp
putStLoop
! end
putStExit:
! .
pop
r2 !
restore regs
pop
r1
! .
ret
! return
!
! flush
!
! This
routine waits until the output buffer has been emptied, then returns.
! It
busy-waits until the buffer has been emptied.
!
!
Registers modified: none
!
flush:
push r1
! save registers
push r2
! .
flushLoop:
! loop
cleari
! disable interrupts
set
outBufferCount,r1 ! r2 = outBufferCount
load
[r1],r2
! .
cmp
r2,0
! if (r2 == 0)
be
flushLoopEx
! break
seti
! re-enable interrupts
jmp
flushLoop
! end
flushLoopEx:
! .
seti
! re-enable interrupts
pop
r2
! restore regs
pop r1
! .
ret
! return
!
!
initSerial
!
! This
routine initializes the serial input and output buffers.
!
!
Registers modified: r1, r2
!
initSerial: set inBuffer,r1
! inBuferIn = &inBuffer
set
inBufferIn,r2
! .
store r1,[r2]
! .
set
inBufferOut,r2
! inBufferOut = &inBuffer
store r1,[r2]
! .
set outBuffer,r1
! outBufferIn = &outBuffer
set
outBufferIn,r2
! .
store r1,[r2]
! .
set
outBufferOut,r2
! outBufferOut = &outBuffer
store r1,[r2]
! .
clr
r1
! inBufferCount = 0
set
inBufferCount,r2
! .
store r1,[r2]
! .
set
outBufferCount,r2
! outBufferCount = 0
store r1,[r2]
! .
ret
! return
!
!
checkSerialDevice
!
! This
routine is called whenever there is a SerialInterrupt. If a character
! is ready
on the input, it is moved into the inBuffer. If there is no
! more
room in the buffer, the character is simply dropped, with no
! error
indication. If the output device
is ready to for another character
! and
there are any characters in the outBuffer, then the next character is
!
transmitted to the output device.
!
! No
arguments, no result.
! This
routine must be called with interrupts disabled!
!
Registers modified: none
!
! r8 =
addr of SERIAL_STATUS_WORD
! r1 =
SERIAL_STATUS_WORD
! r11 =
addr of SERIAL_DATA_WORD
! r2 = the
character
! r9 =
addr of inBufferCount
! r5 =
inBufferCount
! r10 =
addr of inBufferIn
! r3 =
inBufferIn
! r7 =
addr of outBufferOut
! r4 =
outBufferOut
! r6 =
addr of outBufferCount
! r5 =
outBufferCount
!
checkSerialDevice:
push r1
! save all registers we use
push r2
! .
push r3
! .
push r4
! .
push r5
! .
push r6 !
.
push r7
! .
push r8
! .
push r9
! .
push r10
! .
push r11
! .
set SERIAL_DATA,r11
! r11 = addr of SERIAL_DATA_WORD
set
SERIAL_STAT,r8 ! r1 :=
serial status word
load
[r8],r1
! .
btst
0x00000001,r1 ! if
status[charAvail] == 1 then
be
end1
! .
load
[r11],r2
! r2 := input char
set
inBufferCount,r9 ! if inBufferCount <
bufSize then
load
[r9],r5
! .
cmp
r5,BUFFER_SIZE ! .
bge end1
! .
set
inBufferIn,r10 ! *inBufferIn := char
load
[r10],r3
! .
storeb r2,[r3]
! .
add
r5,1,r5
! inBufferCount++
store r5,[r9]
! .
add
r3,1,r3
!
inBufferIn++
cmp
r3,inBufferEnd ! if inBufferIn ==
inBufferEnd then
bne
end2
! .
set inBuffer,r3
!
inBufferIn = &inBuffer
end2:
! end
store r3,[r10]
! store
inBufferIn
end1:
! end
!
end
btst
0x00000002,r1 ! if
status[outputReady] == 1 then
be
end4
! .
set
outBufferCount,r6 ! if outBufferCount>0 then
load
[r6],r5
! .
cmp r5,0
! .
ble
end4
! .
set
outBufferOut,r7 ! r2 := *outBufferOut
load
[r7],r4
! .
loadb [r4],r2
! .
store r2,[r11]
! send
char in r2 to serial output
sub
r5,1,r5
!
outBufferCount--
store r5,[r6]
! .
add
r4,1,r4
!
outBufferOut++
cmp r4,outBufferEnd
! if
outBufferOut==outBufferEnd then
bne
end3
! .
set
outBuffer,r4
!
outBufferOut = &outBuffer
end3:
! end
store r4,[r7]
! store
outBufferOut
end4:
! end
! end
pop
r11
! restore all registers
pop
r10
! .
pop r9
! .
pop
r8
! .
pop
r7
! .
pop
r6
! .
pop
r5
! .
pop
r4
! .
pop r3
! .
pop
r2
! .
pop
r1
! .
ret
! return
.data
BUFFER_SIZE = 128
inBuffer:
.skip BUFFER_SIZE ! Serial Input buffer
area
inBufferEnd:
inBufferIn: .word 0
! Addr of next place to add to
inBufferOut: .word 0
! Addr of next place to remove from
inBufferCount: .word 0
! Number of characters in inBuffer
outBuffer: .skip BUFFER_SIZE ! Serial Output
buffer area
outBufferEnd:
outBufferIn: .word 0
! Addr of next place to add to
outBufferOut: .word 0
! Addr of next place to remove from
outBufferCount:
.word 0
! Number of characters in outBuffer
STACK_START =
0x00ffff00
SERIAL_STAT =
0x00ffff00
! Addr of SERIAL_STATUS_WORD
SERIAL_DATA =
0x00ffff04
! Addr of SERIAL_DATA_WORD