Example BLITZ Assembly 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