Full Version : Vanhorn's Memory Buffer Managment (ASM)
avr >>ASSMBLER ROUTINES >>Vanhorn's Memory Buffer Managment (ASM)


Admin5- 04-19-2006
David Vanhorn's Memory.asm

Buffer management Routine


CODE

;***************************************************************************
;
; File Name     :'MEMORY.asm"
; Title         :
; Date          :
; Version       :
; Support telephone :765 287 1987  David B. VanHorn
; Support fax       :765 287 1989
; Support Email     :dvanhorn@cedar.net
; Target MCU        :AT90S8515
;
; DESCRIPTION
;
; All routines that put data in or out of memory
;
;***************************************************************************;
;   M O D I F I C A T I O N   H I S T O R Y
;
;       rev.      date    who   why
;   ----    --------  ---   ------------------------------------------
;   0.01    98.07.29  dvh   Creation
;   0.02    98.08.28  dvh   Regularized to XL,XH notation instead of R26.R27 etc
;   0.03    98.09.01  dvh   STRING_SEROUT now enables UDRIE, so we start transmitting.
;               The UDRIE ISR will turn it off when we're done talking.
;   0.04    09.10.19  dvh   Fixed a stack bug that would crash you if the serout buffer became
;               full.
;
;***************************************************************************
;
;Tail pointing at head means empty
;Head will contain a byte always, which is junk, or previous data.
;Adding data means inserting the data at tail, then incrementing tail.
;
;   HEAD    Head+1  Head+2  Tail
;   junk    AA  55  A5
;
;To use a byte from head, you take data from HEAD+1
;then call Kill_HEAD, which moves the data as follows:
;
;   HEAD    Head+1  Head+2  Tail
;   junk    AA  55  A5
;   AA<-----/       |
;               55<-----/
;                       A5
;
;Tail becomes Tail-1, AA was the last entity used, and 55 will be the next
;
;   HEAD    Head+1  Tail
;   AA  55  A5

INIT_Buffers:
   ldi ZL,low(SERIAL_IN_BUF)  ;The low byte of the address of the buffer
   ldi ZH,high(SERIAL_IN_BUF) ;high byte
   sts SERIAL_IN_TAIL,ZL  ;Store the address of the head, in the tail
   sts SERIAL_IN_TAIL+1,ZH;

   ldi ZL,low(SERIAL_OUT_BUF) ;
   ldi ZH,high(SERIAL_OUT_BUF);
   sts SERIAL_OUT_TAIL,ZL ;
   sts SERIAL_OUT_TAIL+1,ZH   ;
   ret
;
;************************************************************
;
;This routine takes a string, pointed to by Z, and places it in the serial tx buf.
;It will return when the string has been placed, which may be a while if there
;is a backup and the buffer is full.
;The calling routine points Z at the string
;
String_Serout:
   
   lpm        ;Get the byte into R0 and incr Z
   and R0,R0      ;Is it null
   breq    STR_Serout_Exit;Yes, all done
   mov TEMP,R0    ;
   rcall   Str_Ser_Loop   ;This will wait forever, until the buffer can take
              ;more data, so no worries about over-writing it.
   ld  TEMP,Z+    ;Inc Z, toss the data.
   in  TEMP,UCR   ;
   ori TEMP,$20   ;Enable UDRIE
   out UCR,TEMP   ;
   rjmp    String_Serout  ;

STR_Serout_Exit:
  ;rcall  WT4Clear   ;Forced delay, till serial empty
   ret
;
;
;Hands a single byte off to the storage routine, and keeps trying till it's taken
;
STR_SER_LOOP:
   push    TEMP       ;
   rcall   Timed_Smack;Since we could be looping here for a while
   pop TEMP       ;

   rcall   Store_Serout   ;Take this byte and shove it
   and TEMP2,TEMP2;Did it go?
   brne    STR_SER_LOOP   ;Nope, try again!
   ret


;Buffer store code. Checks buffer for space available, dosen't
;store if no space available. Calling routine places data in
;TEMP, and gets a flag back in TEMP, FF if no store, 00 if
;stored. It's up to the calling routine to decide what to do
;about it when there's no more room at the inn.
;

;Input is in TEMP
;No output
;

Store_Serout:
   push    R28
   push    R29
   push    R30
   push    R31
   push    TEMP

   rcall   Serout_Check       ;
   cpi TEMP,SEROUT_SIZE   ;Is it full?
   breq    Store_Ser_out_Bad  ;Yes, then just exit, can't take more
   
Store_Ser_out_B:
   lds R28,(SERIAL_OUT_TAIL)  ;Stored pointer to tail
   lds R29,(SERIAL_OUT_TAIL+1);
   ld  TEMP,Y+        ;  
   pop TEMP           ;
   st  Y,TEMP         ;Store the byte
   sts (SERIAL_OUT_TAIL),R28  ;
   sts (SERIAL_OUT_TAIL+1),R29;
   ldi TEMP2,$00      ;Flag success
   rjmp    Store_Ser_out_Done ;

Store_Ser_out_Bad:
   ldi TEMP2,$FF      ;Flag for no more incoming data?
   pop TEMP           ;Balance stack

Store_Ser_out_Done:

   pop R31
   pop R30
   pop R29
   pop R28
   
   ret

;
;************************************************************
;
;This routine is called by the serial input ISR
;
Store_Serin:
   push    YL
   push    YH
   push    ZL
   push    ZH
   push    TEMP

   rcall   SERIN_Check    ;See what's stored
   cpi TEMP,(SERIN_SIZE-3);Is it almost full?
   breq    Store_Ser_in_Full  ;Store, but set handshake off
   cpi TEMP,(SERIN_SIZE-2);Is it full?
   breq    Store_Ser_in_bad   ;Yes, then just exit, can't take more
   
Store_Ser_in_B:
   lds YL,(SERIAL_IN_TAIL);Stored pointer to tail
   lds YH,(SERIAL_IN_TAIL+1)  ;
   ld  TEMP,Y+        ;
   pop TEMP           ;  
   st  Y,TEMP         ;Store the byte
   sts (SERIAL_IN_TAIL),YL;
   sts (SERIAL_IN_TAIL+1),YH  ;
   rcall   HS_XON         ;Sets Xon if not already
   ldi TEMP,$00       ;Flag success
   rjmp    Store_Ser_in_Done  ;

Store_Ser_in_Full:
   lds YL,(SERIAL_IN_TAIL);Stored pointer to tail
   lds YH,(SERIAL_IN_TAIL+1)  ;
   ld  TEMP,Y+        ;
   pop TEMP           ;
   st  Y+,TEMP        ;Store the byte
   sts (SERIAL_IN_TAIL),YL;
   sts (SERIAL_IN_TAIL+1),YH  ;
   rcall   HS_XOFF    ;I'm full
   ldi TEMP,$00       ;
   rjmp    Store_Ser_in_done  ;

Store_Ser_in_Bad:
   pop TEMP           ;Balance stack
   ldi TEMP,$FF       ;Bad return flag    
  ;NOTE: There is currently no check for this flag, but it's there.

Store_Ser_in_Done:
  ;THERE IS NO POP TEMP HERE! THAT IS CORRECT
   pop ZH
   pop ZL
   pop YH
   pop YL
   ret
;
;*************************************************************
;
;Used to delete a single char off the head of a buffer.
;Point R30,31 to beginning of buffer Head
;Point R28,29 to current end (Next place a byte could go)
;R27,26 will point to the destination during shuffle-down
;Returns junk in temp
;and decremented tail pointer in R30.31
;
;
;Move head+1 to head
;inc head
;Head=tail? done, else loop

;if Z<y then we have nothing in the buffer, just exit
;If Z=Y then we have 1 char in the buffer, kill it, dec Z
;If Z>Y then we have lots, shuffle down, dec Z
;

Kill_Head:
   push    XL
   push    XH
   push    TEMP

   cp  ZH,YH      ;If these are <> no sense checking the rest
   brne    Kill_Head_A;If <> then go shuffle
   cp  ZL,YL      ;If these are = too, then the buffer's empty
   breq    Kill_Head_Out  ;

  ;Check if the buffer is zero len.
  ;could use space check, but I'd have to reload after

Kill_Head_A:


   mov XL,ZL      ;Point 26,27 at head
   mov XH,ZH      ;

  ;R26,27 (X) working pair pointing at head
  ;R28,29 (Y) pointing at tail
  ;R30,31 (Z) Pointing at head

   ld  temp,Z+    ;X now dest, Y src

  ;R26,27 (X) working pair pointing at head
  ;R28,29 (Y) pointing at tail
  ;R30,31 (Z) currently pointing at head+1

Kill_Head_B:
   ld  TEMP,Z+    ;Head+1 > TEMP, Now pointing at head +2
   st  X+,TEMP    ;TEMP > HEAD, now pointing at head+1
   cp  XL,YL      ;
   brne    Kill_Head_B;If so, we're done
   cp  XH,YH      ;
   brne    Kill_Head_B;

Kill_Head_Done:        ;Data is tossed
   ldi TEMP,$00   ;
   st  Y,TEMP     ;Store a zero in the old tail location
   ld  TEMP,-Y    ;Decrement Z, pointing to old-tail-1, which may be head.

Kill_Head_Out:

   pop TEMP       ;
   pop XH     ;
   pop XL     ;
   ret        ;
;
;*************************************************************
;
;Used to delete a single char off the tail of a buffer
;Point R30,31 to beginning of buffer
;Point R28,29 to current end
;Returns junk in temp
;and decremented pointer in R30.31
;
Kill_Tail:
  ;Make sure we aren't backing up past the beginning!
   cp  ZL,YL
   brne    Kill_Tail_B
   cp  ZH,YH
   brne    Kill_Tail_B
   rjmp    Kill_Tail_Done

Kill_Tail_B:
   ld  TEMP,-Y    ;Decrement Y

Kill_Tail_Done:        ;Data is tossed
   ret        ;
;
;***************************************************************************
;
Ram_Init:  ;Load all variables with harmless or nonsense values
   rcall   Clean_Ram      ;Go nuke the ram space
   rcall   FLAG_RAM       ;
   ret

;**********************************************************************************
;Debug only, stores distinctive data into each buffer, making it easy to see them
;in the simulator.
;
Flag_Ram:
;  

F_1:
   ldi ZH,high(SERIAL_IN_BUF) ;
   ldi ZL,low(SERIAL_IN_BUF)  ;
   ldi TEMP,$22       ;
   ldi TEMP2,SER_SIZE     ;
   rcall   Fill_Buf       ;

F_2:    
   ldi ZH,high(Serial_OUT_BUF);
   ldi ZL,low(Serial_OUT_BUF) ;
   ldi TEMP,$33       ;
   ldi TEMP2,SER_SIZE     ;
   rcall   Fill_Buf       ;

F_Fin:  
   ret            ;Done.

;
;Fill a given buffer to a given size with
;given data.
;
Fill_Buf:
   st  Z+,TEMP        ;Fill a byte
   dec TEMP2          ;One less to fill
   brne    Fill_Buf       ;Are we done?
   ret            ;yes, exit


;***************************************************************************
;
;This routine flushes any previous junk out of our workspace.
;Avoids any pattern-sensitivity or programmer headspace errors with
;uninitialized variables.
;
Clean_Ram:
   ldi ZH,$00         ;Point @ $0060
   ldi ZL,$60         ;
   clr TEMP           ;

CleanLoop:
   st  Z+,TEMP        ;Store $00 at each location
   cpi ZH,$02         ;If not at least here, then go do it again
   brne    CleanLoop      ;
   cpi ZL,$5C         ;DONT EAT THE STACK!
   brne    CleanLoop      ;Getting close!
   ret            ;Done.

;
;*************************************************************
;
;These buffer size routines all return # of bytes stored in TEMP.
;No special reason to export them except to make the routines easier to
;read.
;
SERIN_Check:
   ldi R30,low(SERIAL_IN_BUF) ;
   ldi R31,high(SERIAL_IN_BUF);
   lds R28,(SERIAL_IN_TAIL)   ;Stored pointer to tail
   lds R29,(SERIAL_IN_TAIL+1) ;
   rjmp    Check_It       ;
;
;
;
SEROUT_Check:
   ldi R30,low(SERIAL_OUT_BUF);
   ldi R31,high(SERIAL_OUT_BUF);
   lds R28,(SERIAL_OUT_TAIL)  ;Stored pointer to tail
   lds R29,(SERIAL_OUT_TAIL+1);  
   rjmp    Check_It       ;
;
;*************************************************************
;
;R28,29 are buffer tail pointer R30,31 are buffer head
;Return number of chars stored between R28,30
;Carry flag on exit says no more
;
Check_It:
   sub R29,R31
   sbc R28,R30
   mov TEMP,R28;pointing at head means empty
   ret
;
;



Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.