Search code examples
assemblymicrocontrollerled

Counter from 00 to 99 in assembly language


Basically my task is to make the counter on a micro controller board count from 00-99 continuously using assembly language.

because it is not possible to show two 7-Seg to display at the same time, my solution is to display the tens(0), display ones(0), display tens(0), display one(1), display tens(0), display one(2), display tens(0), display one(3), etc. my approach to doing this is to have two loops (one for the tens digit, one for the ones digit) that goes through an array. once the ones digit loop have gone through the entire array, the loop breaks and goes back to the tens digit loop, move the tens digit to the next element, then back to the ones digit loop

    MSB_Display     equ     $0B ; display on 'tens' digit/second most right of 7-Seg
    LSB_Display     equ     $07 ; display on 'ones' digit/most right of 7-Seg


    D_1MS           equ     24000 / 6

    DelayVal        equ     35      ; 35 ms delay simulates both Hex Displays on at once

                    org     $1000
    ;                             Lookup table for LED segments
    array           db      $3F,$06,$5B,$4F,$66,$6D,$7C,$07,$7F,$6F
    ;                        0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9

    ; Memory Reserved for Delay Routine
    DCount          ds      1
    Counter         ds      1
    countones       db      0
    counttens       db      0

            org     $2000           ; Program starts here
            lds     #$2000          ; Initalize the stack

    ; Configure Hardware
            ldaa    #$FF
            staa    DDRB            ; Make PORTB output
            staa    DDRP            ; PTP as Output

    start
            clr     countones       ; clear count back to 0
            clr     counttens
        ldx     #array


    MSB
            ldaa    1,x+
            staa    PORTB
            ldaa    #MSB_Display
            staa    PTP             ; turn off 7-segment display
            bsr     Delay_ms
            inc     counttens
            ldaa    counttens
            cmpa    #10
            bne     LSB


    LSB
            ldy     #array
        ldab    1,y+
            stab    PORTB
            ldab    #LSB_Display
            stab    PTP
            bsr     Delay_ms
            inc     countones
            ldaa    countones
            cmpa    #10
            bne     LSB


            bra     MSB


            Delay_ms
                    psha              
                    pshy
                    ldaa   #DelayVal        ; Number of msec to delay
                    staa    DCount          ; store delay counter
                    ldaa    DCount          ; delay Dcount ms
                    staa    Counter
            Delay1  ldy     #D_1MS          ; 6000 x 4 = 24,000 cycles = 1ms
            Delay2  dey                     ; this instruction takes 1 cycle
                    bne     Delay2          ; this instruction takes 3 cycles
                    dec     Counter
                    bne     Delay1          ; not Dcount ms yet, delay again
                    pula                    ; Restore contents of ACC A before returning
                    puly
                    rts
                    end

right now it seems like the program enters the ones digit loop (LSB) and sits there, it does not exit that loop nor does it reloop itself. I can't seem to find whats wrong in the logic of my program


Solution

  • Pulling from stack should be done in reverse order from putting on stack. As Ira pointed out you got your counters mixed up...

    You should think of 'displaying two digit number' as a separate thing from increasing some counter in a loop. Displaying is simple switching 'active' output with a time delay, and setting correct digit value for each display cycle.

             'tens' = 0
    loop10   'ones' = 0
    loop1    display 'tens'
             delay
             display 'ones'
             delay
             inc 'ones'
             goto loop1 if 'ones' less than 10
             inc 'tens'
             goto loop10 if 'tens' less than 10
    

    Here is one way of doing this (adjust delay value - 35ms looks to fast...):

    MSB_Display     equ     $0B ; display on 'tens' digit/second most right of 7-Seg
    LSB_Display     equ     $07 ; display on 'ones' digit/most right of 7-Seg
    
    
    D_1MS           equ     24000 / 6
    
    DelayVal        equ     35      ; 35 ms delay simulates both Hex Displays on at once
    
                    org     $1000
    ;                             Lookup table for LED segments
    array           db      $3F,$06,$5B,$4F,$66,$6D,$7C,$07,$7F,$6F
    ;                        0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9
    
    ; Memory Reserved for Delay Routine
    DCount          ds      1
    Counter         ds      1
    countones       db      0
    counttens       db      0
    
            org     $2000           ; Program starts here
            lds     #$2000          ; Initalize the stack
    
    ; Configure Hardware
            ldaa    #$FF
            staa    DDRB            ; Make PORTB output
            staa    DDRP            ; PTP as Output
    
    start
            clr     counttens       ; 'tens' = 0
            ldx     #array          ; x will point to 'tens'
    
    MSB     clr     countones       ; 'ones' = 0
            ldy     #array          ; y will point to 'ones'
                                    ; at every start of 'tens' cycle
    LSB
            ldaa    x               ; Set value of 'tens' display
                                    ; Do not use 1,x+ here
                                    ; You don't want to increment 'tens'
                                    ; every time you display 'ones'
            staa    PORTB
            ldaa    #MSB_Display
            staa    PTP             ; turn on 'tens' 7-segment display
            bsr     Delay_ms        ; let it be lit for a while
    
            ldab    1,y+            ; set value of 'ones' display (and increment it)
            stab    PORTB
            ldab    #LSB_Display
            stab    PTP             ; turn on 'ones' 7-segment display
            bsr     Delay_ms        ; let it be lit for a while
    
            inc     countones
            ldaa    countones
            cmpa    #10
            bne     LSB
    
            inx                     ; now increment 'tens'
            inc     counttens
            ldaa    counttens
            cmpa    #10
            bne     MSB
    
            bra     start           ; start from '00'
    
    Delay_ms
            psha              
            pshy
            ldaa   #DelayVal        ; Number of msec to delay
            staa    Counter
    Delay1  ldy     #D_1MS          ; 6000 x 4 = 24,000 cycles = 1ms
    Delay2  dey                     ; this instruction takes 1 cycle
            bne     Delay2          ; this instruction takes 3 cycles
            dec     Counter
            bne     Delay1          ; not Dcount ms yet, delay again
            puly                    ; Restore contents of ACC A before returning
            pula
            rts
            end
    

    It would be even better to have display code in a separate routine, and to switch active display more than once per counter increase. That could be a nice exercise for you :)