;*************************************************************************** ; ; 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 ZY 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 ; ;