* =============================================================== *
*   Thomas C. Penick   ID# 452 80 6040              Program   8   *
*   EE319K Microprocessor Programming               due 4/24/98   *
*   Instructor: Laurence                            Section   B   *
*   MC6811 Program Source Code  PA8.asm             Roster#  32   *
*   Linked List Command Interpreter and a FIFO Queue              *
* =============================================================== *

; In this program, the user is prompted to enter a command.
; A subroutine is called to compare the command entered with those
; in the linked list.  If if match is found, the appropriate 
; subroutine is then called, otherwise an error message is
; displayed.  The commands are:
;   i    - initialize; clear the queue
;   pb   - push a byte-size character onto the queue
;   gt   - get a single character from the queue
;   s    - size; return the number of items on the queue
;   ?    - help with the commands
;   stop - terminate the program
; If the user wishes to add a byte to the queue, he will be promted 
; to input a character.  If the queue is full, an error message is
; displayed.  Similarly, the user may retrieve a byte from the queue.
; If the queue is empty, an error message is displayed.  If the user
; issues a command that is not on the list, an error message is
; displayed.

qsize   equ 3       ; Q capacity in bytes
bufsize equ 21       ; size of the input command character buffer
        org  0       ; start of RAM

*****  Globals  ***************************************************
cmdptr rmb  2       ; points to the current command (or on stack)
topptr rmb  2       ; points to next location to receive an item (top)
botptr rmb  2       ; next available item, one below bottom of Q
count  rmb  1       ; number of items currently in the Q
queue  rmb  qsize   ; the size of the queue (16 bytes) and pointer (queue)
endq   rmb  1       ; points just past the end of the queue
combuf rmb  bufsize ; Input command character buffer

*****  Program  ***************************************************
       org  $E000     ; start of ROM
Main   lds  #$00FF    ; initialize the stack pointer
       jsr  InitSCI   ; initialize serial port
       jsr  doInit    ; initialize the Q
Begin1 ldx  #BeginMsg
       jsr  OutString
Begin2 ldx  #combuf   ; load address of buffer for InString
       ldab #bufsize  ; load buffer size for InString
       jsr  InString
       jsr  ReadCmd   ; read the command that the user entered
       bra  Begin1
pStop  stop           ; end the program
       bra  Begin1    ; restart the program if desired

*****  Linked List  ***********************************************
Next   equ  2      ; index for next command entry
Cmd    equ  4      ; index for command letter(s)
Cmdi   fdb  doInit ; pointer to subroutine, reset the Q
       fdb  Cmdpd  ; pointer to next command entry
       fcc  "i"    ; command letter
       fcb  EOT    ; end of command letter(s)
Cmdpd  fdb  doPut  ; pointer to subroutine, push a byte onto Q
       fdb  Cmdgt  ; pointer to next command entry
       fcc  "pb"   ; command letters
       fcb  EOT    ; end of command letter(s)
Cmdgt  fdb  doGet  ; pointer to subroutine, get a byte from the Q
       fdb  Cmds   ; pointer to next command entry
       fcc  "gt"   ; command letters
       fcb  EOT    ; end of command letter(s)
Cmds   fdb  doSize ; pointer to subroutine, output # of items on Q
       fdb  Cmd?   ; pointer to next command entry
       fcc  "s"    ; command letter
       fcb  EOT    ; end of command letter(s)
Cmd?   fdb  doHelp ; pointer to subroutine, output description of commands
       fdb  Cmdst  ; pointer to next command entry
       fcc  "?"    ; command letter
       fcb  EOT    ; end of command letter(s)
Cmdst  fdb  doStop ; pointer to subroutine, quit the program
       fdb  0      ; indicates no more entries
       fcc  "stop" ; command letters
       fcb  EOT    ; end of command letter(s)
*****  end Linked List  *******************************************

*****  Subroutine ReadCmd  ****************************************
*                                                                 *
*  Compares user-entered characters to a linked list of           *
*    commands and calls the proper subroutine or displays         *
*    an error.                                                    *
*  INPUTS:  combuf (command buffer) in global                     *
*  OUTPUTS: none                                                  *
*                                                                 *
*******************************************************************
;  Rloop1 - checks each character of a user-entered command against
;           each character in an item on the linked list
;  Rloop2 - checks each item on the linked list against the
;           user-entered command
ReadCmd pshx         \
        pshy          > save registers
        pshb         /
        ldx  #Cmdi   ; starting address of linked list
        stx  cmdptr  ; save the address of the command pointer
Rloop2  ldy  #combuf ; Y = address of command string (user-entered)
        ldab #Cmd    \ X = address of command string on linked list
        abx          /
Rloop1  ldab 0,x     ; load character from command in linked list
        cmpb 0,y     ; compare to character in command string
        bne  Rnot    ; no match
        cmpb #EOT    \ a match has been found because we made it
        beq  Rdone   /   to the end of the string
        inx          \
        iny           > check the next character
        bra  Rloop1  /
Rnot    ldx  cmdptr  ; X points to the beginning of the linked list element
        ldx  Next,x  ; X points to next element of the linked list
        stx  cmdptr  ; save the address of the command pointer
        cpx  #0      ; at end of list?
        bne  Rloop2  ; no, so check this list element
        ldx  #CmdErrMsg \ yes, so it's not a valid command
        jsr  OutString  /
        bra  Rend    ; leave this subroutine
Rdone   ldy  cmdptr  \ 
        ldx  0,y      > execute the subroutine that the user has
        jsr  0,x     /    selected
Rend    pulb         \
        puly          > restore registers
        pulx         /
        rts
*****  End ReadCmd  ***********************************************

*****  Subroutine doInit  *****************************************
*                                                                 *
*  Initializes the Q at queue                                     *
*  INPUTS:  queue in global                                       *
*  OUTPUTS: botptr in global                                      *
*           topptr in global                                      *
*                                                                 *
*******************************************************************
doInit pshx           \ save registers
       psha           /
       ldx  #queue    ; get the pointer to the Q
       stx  botptr    ; initialize get pointer
       stx  topptr    ; initialize put pointer
       clra           \ zero the queue counter
       staa count     /
       ldx  #InitMsg  \ display message
       jsr  OutString /
       pula           \ restore registers
       pulx           /
       rts
*****  end doInit  ************************************************

*****  Subroutine doPut   *****************************************
*                                                                 *
*  Prompts the user for a byte of information to be placed on     *
*    the queue and calls subroutine Put to do so.                 *
*  INPUTS:  none                                                  *
*  OUTPUTS: none                                                  *
*                                                                 *
*******************************************************************
doPut  pshx           \ save registers
       psha           /
       ldx  #PutMsg   \ 
       jsr  OutString  \ prompt user for input
       ldx  #Prompt    /
       jsr  OutString /
       jsr  InCh      \ 
       jsr  OutCh      > accept input, display on screen and put on queue
       jsr  Put       /
       bvc  doDone
       ldx  #PutErr   \ queue full error message
       jsr  OutString /
doDone pula           \ restore registers
       pulx           /
       rts
*****  end doPut  *************************************************

*****  Subroutine doGet   *****************************************
*                                                                 *
*  Gets a byte of information from the queue and displays it on   *
*    screen unless the queue is empty in which case an error      *
*    message is displayed.                                        *
*  INPUTS:  none                                                  *
*  OUTPUTS: none                                                  *
*                                                                 *
*******************************************************************
doGet  pshx           \ save registers
       psha           /
       jsr  Get       ; get a byte from the queue
       bvc  doTell    ; got a byte from the queue
       ldx  #GetErr   \
       jsr  OutString  > Queue empty error message
       jmp  doDid     /
doTell ldx  #GetMsg   \ 
       jsr  OutString  > Here's your byte message
       jsr  OutCh     /
doDid  pula           \ restore registers
       pulx           /
       rts
*****  end doGet  *************************************************

*****  Subroutine doSize  *****************************************
*                                                                 *
*  Displays a message giving the size of the queue in bytes.      *
*  INPUTS:	count in global                                       *
*  OUTPUTS: message to screen                                     *
*                                                                 *
*******************************************************************
doSize  pshx           \
        pshb            > save registers
        psha           /
        clra           \ get the Q size
        ldab count     /
        ldx  #SizeMsg1 \ display the first part of the message
        jsr  OutString /
        jsr  OutDec    ; display the size value
        cmpb #1
        bne  plural
        ldx  #SizeMsg2 \ display the second part of the message
        jsr  OutString /   (for one byte)
        bra  doThru
plural  ldx  #SizeMsg3 \ display the second part of the message
        jsr  OutString /   (for 0 or multiple bytes)
doThru  pula           \
        pulb            > restore registers
        pulx           /
        rts
*****  end doSize  ************************************************

*****  Subroutine doHelp  *****************************************
*                                                                 *
*  Displays a message listing the commands and their meanings.    *
*  INPUTS:  queue in global                                       *
*  OUTPUTS: message to screen                                     *
*                                                                 *
*******************************************************************
doHelp pshx           ; save X
       ldx  #HelpMsg  \ display the help message
       jsr  OutString /
       pulx           ; restore X
       rts
*****  end doHelp  ************************************************

*****  Subroutine doStop  *****************************************
*                                                                 *
*  Terminates the program.                                        *
*  INPUTS:  none                                                  *
*  OUTPUTS: message to screen                                     *
*  Destroyed:  everything                                         *
*                                                                 *
*******************************************************************
doStop pshx
       ldx  #StopMsg  \ display the program termination message
       jsr  OutString /
       pulx
       pulb           \
       puly            > cleanup for subroutine ReadCmd
       pulx           /
       ins            \ remove return address
       ins            /
       jmp  pStop     ; terminate the program with an empty stack
*****  end doStop  ************************************************

*****  Subroutine Get  ********************************************
*                                                                 *
*  This subroutine gets an item from the Q                        *
*  INPUTS:  botptr in global                                      *
*           topptr in global                                      *
*  OUTPUTS: A = item retreived from the Q                         *
*           Updates get pointer                                   *
*           V = 0: indicates A is valid,                          *
*           V = 1: indicates Queue is empty                       *
*  LIMITATIONS: none                                              *
*  Destroyed:   A, CCR                                            *
*                                                                 *
*******************************************************************
Get     pshx         ; save X
        ldx  botptr  ; X = get pointer
        cpx  topptr  ; are they equal? (get-put)
        beq  Gempty  ; if so, Q is empty
        ldaa 0,X     ; get the item off the Q
        dec  count   ; decrement the Q element counter
        inx          ; X=X+1, increment the pointer
        cpx  #endq-1 ; WRAP? Will X be past the end?
        bls  Gnowrap ; if less wrap not necessary
        ldx  #queue  ; reset X to start of Q
Gnowrap stx  botptr  ; update get pointer
        clv          ; V=0
        bra  Gdone
Gempty  sev          ; V=1, indicate empty
Gdone   pulx         ; restore X
        rts          ; return
*****  end Get  ***************************************************

*****  Subroutine Put  ********************************************
*                                                                 *
*  This subroutine puts an item on the Q                          *
*  INPUTS:   A = item to be put on the Q                          *
*            botptr in global                                     *
*            topptr in global                                     *
*  OUTPUTS:  Updates topptr                                       *
*            Item placed on the Q                                 *
*            V = 0: indicates item was put on Queue,              *
*            V = 1: indicates Queue is full                       *
*  LIMITATIONS:  none                                             *
*  Destroyed:    A, CCR                                           *
*                                                                 *
*******************************************************************
Put     pshx         ; save X
        ldx  topptr  ; X = put pointer
        staa 0,X     ; place the item on the Q
* this is OK even if the Q is full since there is an extra space
        inc  count   ; increment the Q element counter
        inx          ; X=X+1
        cpx  #endq-1 ; WRAP? Will X be past the end?
        bls  Pnowrap ; if less wrap is not necessary
        ldx  #queue  ; reset X to start of Q
Pnowrap cpx  botptr  ; FULL? After wrap put cannot equal get
        beq  Pfull   ; if so indicate full
        stx  topptr  ; update put pointer
        clv          ; V=0
        bra  Putdone
Pfull   dec  count   ; nothing was "put" after all
        sev          ; V=1, indicate full
Putdone pulx         ; restore X
        rts          ; return
*****  end Put  ***************************************************

*****  Messages  **************************************************
BeginMsg:
       fcb  CR
       fcc  "Enter a command:  (i pb gt s ? stop)"
Prompt fcb  CR
       fcc  ">"
       fcb  EOT
InitMsg:
       fcb  CR
       fcc  "Initialized."
       fcb  EOT
PutMsg:
       fcb  CR
       fcc  "Type a character for the queue."
       fcb  EOT
PutErr:
       fcb  CR
       fcc  "Oh no, the queue is full.  What are we gonna do?"
       fcb  EOT
GetMsg:
       fcb  CR
       fcc  "Here's your byte from the queue: "
       fcb  EOT
GetErr:
       fcb  CR
       fcc  "Oh no, the queue is empty.  What now?"
       fcb  EOT
SizeMsg1:
       fcb  CR
       fcc  "The queue contains "
       fcb  EOT
SizeMsg2:
       fcc  " byte."
       fcb  EOT
SizeMsg3:
       fcc  " bytes."
       fcb  EOT
HelpMsg:
       fcb  CR
       fcc  "The available commands are:"
       fcb  CR
       fcc  "  i    - reset the queue"
       fcb  CR
       fcc  "  pb   - push a byte onto the queue"
       fcb  CR
       fcc  "  gt   - get a byte from the queue"
       fcb  CR
       fcc  "  s    - give the size of the queue"
       fcb  CR
       fcc  "  ?    - help with commands"
       fcb  CR
       fcc  "  stop - quit the program"
       fcb  EOT
StopMsg:
       fcb  CR,CR,CR
       fcc  "Program terminated."
       fcb  EOT
fulmsg:
       fcb  CR
       fcc  "The Q is full"
       fcb  CR
       fcb  EOT
empmsg:
       fcb  CR
       fcc  "The Q is empty"
       fcb  CR
       fcb  EOT
CmdErrMsg:
       fcb  CR
       fcc  "Invalid Command.  Enter ? for Help."
       fcb  CR
       fcb  EOT
       org  $FFFE
       fdb  Main
       end


