(SOLVED) Problem Writing to Stack (TOSL)

Thread Starter

jpanhalt

Joined Jan 18, 2008
10,210
MCU = 16F1829 (enhanced mid-range, stack is accessible)
Language: MPASM
IDE: MPLAB 8.92

This code works and sets TOSL to the correct return address in FSR1L:
Code:
PutExit  
     banksel   TOSL           ;bank31      
     movf      FSR1L,w
     movwf     TOSL           ;return to end of table
     movf      FSR1H,w
     movwf     TOSH
     clrf      FSR1H
     movlb     0
     return
This code does not work. WREG = 0x23. Value entered to TOSLis the current position. Hence, on return, it stalls and resets. The other manipulations of EEADRH/L and EEDATH/L all work as expected.
Code:
PutExit
     movf      EEADRL,w       ;                                       |B3
     movlb     31             ;                                       |B31
     movwf     TOSL           ;                                       |B31
     movlb     3
     movf      EEADRH,w
     movlb     31
     movwf     TOSH
     movlb     0              ;                                       |B0
     return
I have tried strange things, like a movlw to WREG doesn't write to TOSH/L either.

In contrast, at the beginning of the routine, this works just fine to preserve the beginning of the return address:
Code:
PutText
     movlb     31             ;                                       |B31                                      
     movf      TOSH,w         ;                                       |B31
     movwf     temp           ;                                       |B?
     movf      TOSL,w         ;                                       |B31
     movlb     3              ;                                       |B3
     movwf     EEADRL         ;                                       |B3
     movf      temp,w         ;                                       |B?
     movwf     EEADRH         ;                                       |B3
Any insights as to why the exit is not working as "expected?"
John
 

JohnInTX

Joined Jun 26, 2012
4,280
@jpanhalt
If I understand correctly, the 2ed code isn't working.
You haven't selected the bank for the initial EEADRL at least in the code shown. That could be problematic. With that fixed, assuming you CALL PutExit to push the stack, you will return in bank 0 to whatever address is represented by the value in EEADRH/L. Not the data in the EEPROM but it's address..
Also make sure your default radix is 'dec' not hex. Better yet, specify the banks using banksel i.e. banksel TOSH.

This works in the sim:
Code:
#include "p16f1829.inc"
  radix  dec   ; if you specify banks in decimal!

  ORG   0
  nop
  nop

  banksel EEADRL
  movlw  low there
  movwf  EEADRL
  movlw  high there
  movwf  EEADRH
  call  PutExit ; returns to 'there'

  nop
  nop
  nop

there:
  banksel EEADRL
  clrf  EEADRL  ; set up a return to 0
  clrf  EEADRH
  nop
  nop
  nop
  call  PutExit ;returns to 0000h, or whatever the reset value of EEADDRH/L

  nop
  nop
  nop
  nop

  goto  0

PutExit:
  movlb  3  ; select bank for EEADRL - banksel would be better
  movf  EEADRL,w  ;  B3
  movlb  31  ;  |B31
  movwf  TOSL  ;  |B31
  movlb  3
  movf  EEADRH,w
  movlb  31
  movwf  TOSH
  movlb  0  ;  |B0
  return      ;
  end
 
Last edited:

Thread Starter

jpanhalt

Joined Jan 18, 2008
10,210
Hi John,

The code I posted also worked in simulation, but not when transplanted to the larger project. I believe the banks are properly selected. I keep track with the code on the far right. All of the EEPROM registers for that chip are in Bank 3. I am not using EEPROM per se, but rather its ability to access 14-bit program memory, which then allows me to pack two ASCII characters into a single line (i.e., using the da or dw directives). The program is a bit more then 1200 lines and with that packing, I save at least 100 lines.

Here's the section that leads to the branch to PutExit. It unpacks the lower byte and increments EEADRL for the next ASCII characters. Adjusting EEADRH is probably not necessary, but I included it at this point.
Code:
     movlb     3
     bcf       EEDATL,7
     movf      EEDATL,w       ;                                       |B3 
     btfsc     STATUS,2
     bra       PutExit   
     call      PUT232         ;accepts any bank, returns in |B0     
     movlb     3              ;                                       |B3
     incf      EEADRL,f       ;                                       |B3
     btfsc     STATUS,0
     incf      EEADRH,f
     bra       _GetChar
I will try using a literal again. When I tried using 'movlw' + 'movwf TOSL' as just a test, I got the same failed result to modify. Probably time to sleep on it.

BTW, the rest of the code works great for accessing and manipulating control registers of the AS3935 Lightning Detector. It uses a pedestrian dt structure for ascii strings. There is still plenty of room in the program memory that I won't likely need. I just want to polish it a little.
 

JohnInTX

Joined Jun 26, 2012
4,280
OK. If I understand you, you are packing two 7bit ASCII characters into the one 14bit program memory word using something like dw which just enters up to 14 bit data without the RETLW or MOVLW stuff. That way you double your storage efficiency - good way to do it.
I can see the basics of you reading the packed data, I think. Presumably when you get the high byte you get its LSB from the MSbit of the low byte and shift left?
I will try using a literal again. When I tried using 'movlw' + 'movwf TOSL' as just a test, I got the same failed result to modify. Probably time to sleep on it.
Me too. I assume that by 'modify' you think it's not changing TOSL and hence not correctly building the N-way return address and not trying to modify program memory itself because that takes something different.
Sorry for being obtuse but why N-way return in PutExit? I use the technique for lots of table/lookup/multi-task stuff so I understand how it works (or should work) just curious what it's doing here.
'nite

FWIW, nothing in the errata about this.
 
MCU = 16F1829 (enhanced mid-range, stack is accessible)
Language: MPASM
IDE: MPLAB 8.92

This code works and sets TOSL to the correct return address in FSR1L:
Code:
PutExit
     banksel   TOSL           ;bank31    
     movf      FSR1L,w
     movwf     TOSL           ;return to end of table
     movf      FSR1H,w
     movwf     TOSH
     clrf      FSR1H
     movlb     0
     return
This code does not work. WREG = 0x23. Value entered to TOSLis the current position. Hence, on return, it stalls and resets. The other manipulations of EEADRH/L and EEDATH/L all work as expected.
Code:
PutExit
     movf      EEADRL,w       ;                                       |B3
     movlb     31             ;                                       |B31
     movwf     TOSL           ;                                       |B31
     movlb     3
     movf      EEADRH,w
     movlb     31
     movwf     TOSH
     movlb     0              ;                                       |B0
     return
Should'nt you inc STKPTR before stuffing TOSL and TOSH? Then, when you execute the return, PC is loaded as you continue execution with the STKPTR and TOSL and H as you found it. Not sure why it would work in the first place and not in the second - unless you are not using the stack anywhere else (and no ints).
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
10,210
I can see the basics of you reading the packed data, I think. Presumably when you get the high byte you get its LSB from the MSbit of the low byte and shift left?
That is exactly the process, except there is a small wrinkle. You can't shift EEDATH left, since it is only 6-bits wide. So, you have to move it to an 8-bit register (like WREG), shift, and fix bit<0> or simply rotate it in from STATUS,C. Also, with an odd number of characters, EEDATL is padded with b'x000|0000', so you need to deal with two zeros -- the one to end the string and EEDATL. That part of the code works fine but can probably be made more efficient.

I am using MPLAB ICD3 for hardware simulation debugging (MPLAB SIM for simulation), and my suspicion is that the problem may be related to how program memory is allocated. I will certainly report back if I can pin it down.

John
 
Last edited:

Thread Starter

jpanhalt

Joined Jan 18, 2008
10,210
Should'nt you inc STKPTR before stuffing TOSL and TOSH? Then, when you execute the return, PC is loaded as you continue execution with the STKPTR and TOSL and H as you found it. Not sure why it would work in the first place and not in the second - unless you are not using the stack anywhere else (and no ints).
I will give that a play. Since it is a call and return, the call puts the beginning of the ASCII string at TOS. If I simply return, it will go back to the beginning. My approach was to simply modify the return address. Maybe I need to look at the pointer too.

This stack-based code for printing strings that was shared by Mike K8LH works perfectly when using an ordinary table built with the dt directive. I simply tried to build on that.
Code:
PutText
     banksel   TOSL           ;Bank31
     movf      TOSL,w      
     movwf     FSR1L
     movf      TOSH,w
     movwf     FSR1H
     bsf       FSR1H,7
     movlb     0
GetChar                          
     moviw     FSR1++              
     btfsc     STATUS,2       ;skpnz                      
     bra       PutExit
     call      Put232         ;Out_Str
     bra       GetChar    
PutExit    
     banksel   TOSL           ;bank31        
     movf      FSR1L,w
     movwf     TOSL           ;return to end of table
     movf      FSR1H,w
     movwf     TOSH
     clrf      FSR1H     
     movlb     0
     return
NB: The extra step to set FSR1H,7 is usually not necessary as the assembler does that automatically.
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
10,210
Sleep helps. As usual, the MCU was doing exactly as told to do. I was causing a stack underflow, reset and/or branch to oblivion.

Here is the working snippet (revised 02/27/18 @3:35 PM):
Code:
;        SCREEN PRINTING -- EEPROM READ OF PROGRAM MEMORY -- da Version
PutText
     movlb     31             ;                                       |B31
     movf      TOSL,w         ;                                       |B31
     movlb     3              ;                                       |B3
     movwf     EEADRL         ;                                       |B3
     movlb     31             ;                                       |B31
     movf      TOSH,w         ;                                       |B31
     movlb     3              ;                                       |B3
     movwf     EEADRH         ;                                       |B3
GetChar             
     bcf       EECON1,CFGS    ;not 'config' memory                    |B3
     bsf       EECON1,EEPGD   ;select 'program' memory                |B3
_GetChar
     bsf       EECON1,RD      ;initiate 'read'                        |B3
     nop                      ;required 'nop'                         |B3
     nop                      ;required 'nop'                         |B3
     incf      EEADRL,f       ;increment char address H/L             |B3
     btfsc     STATUS,2       ;                                       |B3
     incf      EEADRH,f       ;                                       |B3
     lslf      EEDATH,w       ;unpack high byte                       |B3 
     btfsc     STATUS,2       ;check for zero                         |B3
     bra       PutExit        ;                                       |B3
     btfsc     EEDATL,7       ;                                       |B3
     bsf       WREG,0         ;                                       |B3
     bcf       EEDATL,7       ;unpack low byte                        |B3
     call      Put232         ;print to LCD high byte                 |B0     
     movlb     3              ;                                       |B3
     movf      EEDATL,w       ;                                       |B3
     btfsc     STATUS,2       ;                                       |B3
     bra       PutExit        ;check for "padded 0" w/ odd # char     |B3
     call      Put232         ;print to LCD low byte                  |B0
     movlb     3              ;                                       |B3     
     bra       _GetChar       ;                                       |B3
     
PutExit
     movf      EEADRL,w       ;                                       |B3
     movlb     31             ;                                       |B31
     movwf     TOSL           ;                                       |B31
     movlb     3              ;                                       |B3     
     movf      EEADRH,w       ;                                       |B3
     movlb     31             ;                                       |B31
     movwf     TOSH           ;                                       |B31
     movlb     0              ;                                       |B0
     return 
;*******************************************************************************
"Put232" is just a hardware routine to put an ascii value to an LCD. The code seems awfully long for what it does, which is due to bank switching and lack of auto-increment for the address registers. I will look at it later to see if some of the steps can be reduced.

Regards, John

Edit: I forgot to show the macro.
Code:
StrPrint  macro     label  
          call      PutText                
          da        label,0        
          endm
EDIT#2: Cleaned up the code a little and was able to save a few steps. Seems to run fine. Newest version is above.
 
Last edited:

Thread Starter

jpanhalt

Joined Jan 18, 2008
10,210
Hi Mike,

Yes, that was an error in the original, which hopefully I would have caught and did correct the the newest code, but the error that caused spectacular failure was skipping to exit without incrementing. The most recent code avoids that by incrementing before the branches.

That is why sleep is such good medicine. It is easy to get too close to the instructions to see the code. By the way, thank you again for showing me how to use 14-bit eeprom reads as separate from using eeprom per se. That was a few years ago and has proved to be a useful tool.

Regards, John
 

MMcLaren

Joined Feb 14, 2010
845
That routine turned out very nice. Here's a slightly different 'untested' version (below).

Cheerful regards, Mike

Code:
PutStrx
        banksel TOSL            ; bank 31                        |31
        movf    TOSL,W          ; return address lo              |31
        banksel EEADRL          ; bank 3                         |03
        movwf   EEADRL          ; string address lo              |03
        banksel TOSH            ; bank 31                        |31
        movf    TOSH,W          ; return address hi              |31
        banksel EEADRH          ; bank 3                         |03
        movwf   EEADRH          ; string address hi              |03
;
;  read the 14-bit program memory 'word' from flash memory
;
        bcf     EECON1,CFGS     ; not 'config' memory            |03
        bsf     EECON1,EEPGD    ; select 'program' memory        |03
        bsf     EECON1,RD       ; initiate 'read'                |03
        nop                     ; required 'nop'                 |03
        nop                     ; required 'nop'                 |03
;
;  bump return address/string address value on the stack
;
        banksel TOSL            ; bank 31                        |31
        incf    TOSL,F          ; bump return address            |31
        skpnz                   ;  "                             |31
        incf    TOSH,F          ;  "                             |31
;
;  extract & print chars from the 14-bit program memory 'word'
;
        banksel EEDATL          ; bank 3                         |03
        rlf     EEDATL,W        ; put EEDATL.7 in Carry          |03
        rlf     EEDATH,W        ; get high byte character        |03
        skpnz                   ; zero? no, skip, else           |03
        bra     PutExit         ; exit (end-of-string)           |03
        call    Put232          ; send character to LCD          |00
        banksel EEDATL          ; bank 3                         |03
        movf    EEDATL,W        ; get low byte character         |03
        andlw   0x7F            ;  "                             |03
        skpnz                   ; zero? no, skip, else           |03
        bra     PutExit         ; exit (end-of-string)           |03
        call    Put232          ; send character to LCD          |00
        bra     PutStrx         ;                                |00
PutExit
        movlb   0               ; bank 0                         |00
        return                  ;
 
Last edited:

Thread Starter

jpanhalt

Joined Jan 18, 2008
10,210
Nice. I was thinking along the lines of changing the macro to include a count to be added. But, I didn't want to have to do that count each time for each string nor use another register.

Thanks. John
 

Thread Starter

jpanhalt

Joined Jan 18, 2008
10,210
While pasting the most recent code in post #12, I noticed steps 29-31. With that chip, 'rlf' and 'rrf' do not affect Z, but 'lsrf' and 'lslf' do. That is a small change, but requires testing EEDATH first for zero and printing it if not. Then test EEDATL and exit if zero. It would be nice if RLF and RRF affected Z instead of just C.
Here is the penultimate version (hopefully :)) with those changes:
Code:
;*******************************************************************************
;    SCREEN PRINTING -- EEPROM READ OF PROGRAM MEMORY -- da Version
;    Incorporates suggestions from Mike McLaren (K8LH)
;*******************************************************************************
PutText
     movlb     31             ;                                       |B31
     movf      TOSL,w         ;                                       |B31
     movlb     3              ;                                       |B3
     movwf     EEADRL         ;                                       |B3
     movlb     31             ;                                       |B31
     movf      TOSH,w         ;                                       |B31
     movlb     3              ;                                       |B3
     movwf     EEADRH         ;                                       |B3
GetChar           
     bcf       EECON1,CFGS    ;not 'config' memory                    |B3
     bsf       EECON1,EEPGD   ;select 'program' memory                |B3
     bsf       EECON1,RD      ;initiate 'read'                        |B3
     nop                      ;required 'nop'                         |B3
     nop                      ;required 'nop'                         |B3
     movlb     31             ;                                       |B31
     incf      TOSL,f         ;bump return address                    |B31
     btfsc     STATUS,2       ;bump return address                    |B31
     incf      TOSH,f         ;bump return address                    |B31
     movlb     3              ;                                       |B3
     lslf      EEDATH,w       ;check EEDATH = zero                    |B3
     btfsc     STATUS,2       ;                                       |B3
     bra       PutExit        ;                                       |B3
     btfsc     EEDATL,7       ;check whether EEDATH odd               |B3
     bsf       WREG,0         ;                                       |B3
     call      Put232         ;print high byte to LCD                 |B0
     movlb     3              ;                                       |B3
     movlw     0x7F           ;unpack EEDATL                          |B3
     andwf     EEDATL,w       ;unpack EEDATL                          |B3
     btfsc     STATUS,2       ;check EEDATL = 0                       |B3
     bra       PutExit        ;
     call      Put232         ;print low byte to LCD                  |B0
     bra       PutText        ;                                       |B0   
PutExit
     movlb     0              ;                                       |B0
     return                   ;                                       |B0
It has been tested with both odd and even numbers of characters and with odd and even valued ASCII characters.

Regards, John

EDIT: One can also just insert a 'movf WREG' between Mike's steps 30 and 31.
 
Last edited:

MMcLaren

Joined Feb 14, 2010
845
Oops! Thank you for pointing out a silly mistake...

Here's an attempt to pop the stack from within the 'put' sub-routine at end-of-string but I don't think there's any real advantage other than saving a couple memory words and cycles;

Code:
PutStrx
        banksel TOSL            ; bank 31                        |31
        movf    TOSL,W          ; return address lo              |31
        banksel EEADRL          ; bank 3                         |03
        movwf   EEADRL          ; string address lo              |03
        banksel TOSH            ; bank 31                        |31
        movf    TOSH,W          ; return address hi              |31
        incf    TOSL,F          ;                                |31
        skpnz                   ;                                |31
        incf    TOSH,F          ;                                |31
        banksel EEADRH          ; bank 3                         |03
        movwf   EEADRH          ; string address hi              |03
;
;  read the 14-bit program memory 'word' from flash memory
;
        bcf     EECON1,CFGS     ; not 'config' memory            |03
        bsf     EECON1,EEPGD    ; select 'program' memory        |03
        bsf     EECON1,RD       ; initiate 'read'                |03
        nop                     ; required 'nop'                 |03
        nop                     ; required 'nop'                 |03
;
;  extract & print chars from the 14-bit program memory 'word'
;
        rlf     EEDATL,W        ; put EEDATL.7 in Carry          |03
        rlf     EEDATH,W        ; get high byte character        |03
        call    PutLCD          ; send character to LCD          |00
        banksel EEDATL          ; bank 3                         |03
        movf    EEDATL,W        ; get low byte character         |03
        call    PutLCD          ; send character to LCD          |00
        bra     PutStrx         ;                                |00

PutLCD
        andlw   0x7F            ; end-of-string?                 |03
        btfss   STATUS,Z        ; yes, skip (exit), else         |03
        goto    Put232          ; send character to LCD          |00
        banksel STKPTR          ; bank 31                        |31
        decf    STKPTR,F        ; pop stack                      |31
        movlb   0               ; bank 0                         |00
        return                  ;
 

MMcLaren

Joined Feb 14, 2010
845
Gosh, it seems we can use some of these 'tricks' to reduce the size of that old 'DT' table version. In this case, however, it costs us some extra cycles processing time per character.

Cheerful regards, Mike

Code:
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  PutStrx 8-bit 'DT' table version              Mike McLaren     ~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PutStrx
        banksel TOSL            ; bank 31                         |31
        movf    TOSL,W          ; copy return address to FSR1     |31
        movwf   FSR1L           ;  "                              |31
        movf    TOSH,W          ;  "                              |31
        movwf   FSR1H           ;  "                              |31
        bsf     FSR1H,7         ; necessary?                      |31
        incf    TOSL,F          ; bump return/string address      |31
        skpnz                   ;  "                              |31
        incf    TOSH,F          ;  "                              |31
        movlb   0               ; bank 00                         |00
        movf    INDF1,W         ; end-of-string (0x00)?           |00
        skpnz                   ; no, skip (send char), else      |00
        return                  ; exit (end-of-string)            |00
        call    Put232          ; send char to LCD                |00
        bra     PutStrx         ; branch (unconditionally)        |00
 
Last edited:

Thread Starter

jpanhalt

Joined Jan 18, 2008
10,210
Just FYI for those who haven't followed this evolution. A few years ago, Mike McLaren posted a nice print routine for enhanced mid-range devices that used the stack. It may have been updated since, but this is the version as I have used it:
Code:
;*******************************************************************************
;                             SCREEN PRINTING
;*******************************************************************************
PutStr    macro     text         
          call      PutText               
          dt        text,0       
          endm
PutText
     banksel   TOSL           ;Bank31
     movf      TOSL,w     
     movwf     FSR1L
     movf      TOSH,w
     movwf     FSR1H
     bsf       FSR1H,7        ;necessary?
     movlb     0   
GetChar                         
     moviw     FSR1++           
     skpnz                    ;btfsc STATUS,2
     bra       PutExit
     call      OutStr     
     bra       GetChar   
PutExit   
     banksel   TOSL            ;bank31       
     movfw     FSR1L
     movwf     TOSL
     movfw     FSR1H
     movwf     TOSH
     movlb     0
     return
Excluding the macro, that is 19 instructions or 18, as setting bit<7> of FSRxH is usually done automatically by the Assembler. The improved code (post#19) is only 15 instructions.

Use of the da directive instead of dt for printing strings and a few other lesser improvements knocked 229 instructions (17%) from my bloated, fully working first version of code.

John
 
Top