Example BLITZ Assembly Language Program

! 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