; CE-2800 StackAndSubroutines.asm ; Purpose: Demonstrate how to use the Stack for data storage and subroutine calls .NOLIST .INCLUDE "m32def.inc" ; contains .EQU's for DDRB, PORTB, and a lot more .LIST .EQU init = 0x2a ; define a symbol to represent address 0x2a in program memory .CSEG ; this means further directives are for the Code Segment .ORG 0x0 ; this directive is optional; the first .CSEG instruction defaults to being put at 0x0 rjmp init ; initialize Reset Vector at 0x0000 .DSEG ; switch subsequent directives to apply to the Data Segment .ORG SRAM_START ; SRAM_START from m32def.inc: the address (0x0060) where SRAM memory begins in the Data Segment .CSEG ; switch back to Code Segment .DEF temp=R30 ; temp: register for temporary data store .ORG init ; defines where these instructions start getting placed in Program Memory ; Initialize the Stack Pointer (SP) register for subsequent use ldi temp, LOW(RAMEND) ; get low byte of the address of the highest byte in Data Memory we can use out SPL, temp ; load it into the low byte of SP ldi temp, HIGH(RAMEND) ; high byte of address of highest Data Memory byte out SPH, temp ; load it into the high byte of SP ldi temp, 5 ; initialize temp to 5 rcall initPortB ; call the helper to init PortB for output rcall initPortD ; call the helper to init PortD for input loop: ; this label "loop" is automatically assigned the address of the following "inc" instruction ; inc temp ; increment temp ; push temp ; put incremented value of temp on the Stack ; inc temp ; increment temp ; push temp ; put incremented value of temp on the Stack ; inc temp ; incremeent temp ; push temp ; put incremented value of temp on the Stack ; pop R0 ; remove last-added value from Stack add put in R0 ; pop R0 ; remove next-to-last value and put in R0 (replacing the previous value in R0) ; pop R0 ; remove next-to-next-to-last value and put in R0 ; rcall sub1 ; call subroutine via address label "sub1"; ; this rcall automatically pushes the 2-byte address of the next instruction onto the stack rcall readInput ; read the inputs on PortD rjmp loop ; jump back to the top of the loop ; end of "main" routine; subroutines follow the main routine ; subroutine sub1 ; does nothing but return. ; Note: RCALL automatically PUSHes the address of the following instruction on the Stack, ; so that the subroutine knows where to return to after completing sub1: ; rcall sub1 ; if this line is uncommented, a recursive call will be made that never returns push temp ; push the current value of temp onto the stack, so we can restore it later on inc temp ; now we can modify temp all we want! pop temp ; pop the original value of temp off the stack, restoring temp to its initial value ; pop temp ; if this line is uncommented, we'll pop one of the 2 bytes of the return address off the stack, ; and the "ret" statement that follows won't work correctly! ret ; return to caller; automatically POPs the 2-byte return address of the "rjmp loop" instruction off the Stack ; end of subroutine sub1 ; subroutine addValues ; This subroutine adds two registers together and places the result in another register ; The expected "arguments" to the subroutine are registers R20 and R21, ; while the "return value" is placed into R0 addValues: push R20 ; first, save the current values of R20 and R21 push R21 add R20, R21 ; add, putting the result in R20 mov R0, R20 ; move the value of R20 into R0 pop R21 ; restore the original value of R20 (not really needed, since it wasn't modified) pop R20 ; restore the original value of R20 (which WAS modified) ret ; return to caller, who picks up the sum in R0 ; end of subroutine addValues ; subroutine initPortB ; initializes PortB for digital output initPortB: push R20 ; save current value ldi R20, 0xff ; load bit pattern that sets all pins as output out DDRB, R20 ; configure PORTB ldi R20, 0x0 out PORTB, R20 ; turn all output pins of PORTB off pop R20 ; restore R20 to its original value ret ; return to caller ; subroutine initPortD ; initializes PortD for digital input initPortD: push R20 ; save current value ldi R20, 0x00 ; load bit pattern that sets all pins as input out DDRD, R20 ; configure PORTD ; These next two lines activate some circuitry that "pulls up" the voltage on the input ; pins to 5v, thereby stabilizing the voltage in the absense of actual external voltage inputs. ; When these lines are commented out, that circuitry is not enabled, and the voltages on ; the pins "floats", and changes rather randomly. Put your finger on the exposed pins of ; PortD when the program is running, and watch the behavior of the LEDs - this is caused by ; the voltage fluctuations that occur from your touch. ; Next, uncomment the following lines and repeat the experiment. What happens when the pull-up circuitry ; is enabled? (No, you won't receive an electrical shock.) ; Once the program is running, press the pushbutton switches labeled SW1, SW2, INT0, and INT1 and ; observe what happens to the lights. These switches ground out the first 4 pins on PortD, dragging ; the voltage on those pins to 0v (logical 0). ; ldi R20, 0xff ; out PORTD, R20 ; enable the circuitry that "pulls up" the voltage on the input pins to +5v (logical 1's) pop R20 ; restore R20 to its original value ret ; return to caller ; subroutine readInput ; reads the value of the input pins on PortD, and outputs those values to PortB ; Thus, whatever pins on PortD are high (logical 1) will cause the corresponding pins on PortB to light up readInput: push R20 ; save in R20, PIND ; read from the PortD input register out PORTB, R20 ; echo the input values to PortB pop R20 ; restore ret ; return to caller