# Code Examples from Class

• Factorial
```; Factorial Calculation
; The answer is in R10

; Rob Frohne 2013

GLOBAL Reset_Handler
ENTRY

Reset_Handler
movs r8, #0 ; Take the factorial of this number, n.
mov r10, #1	 ; 0! and 1! are 1
beq stop   ; If r8 is zero we are done
cmp r8, #1	 ; If r8 is 1,
beq stop	  ; we are done
subs r9, r8, #1	; n-1
loop	mulne r10, r9, r8 ; n(n-1)
mov r8, r10
subs r9, r9, #1
bne loop
stop 	b stop
END

```

• Factorial 2014
```_	AREA Add, CODE
ENTRY
; This program takes the factorial of n (in r2)
n EQU 0

ldr r2,=n ; Load r2 with the number to take the factorial of.
cmp r2,#0 ; Is n=0
bne check_for_1
mov r4,#1
b stop
check_for_1
cmp r2,#1
bne initialize_r4
mov r4,#1
b stop
initialize_r4
mov r4,r2
sub r3,r4,#1
loop
mul r5,r4,r3
mov r4,r5
subs r3,r3,#1
bne loop
stop b stop
END

```

```VAL1A EQU 0xffffffff
VAL1B EQU 0x0000000f
VAL2A EQU 0x00000001
VAL2B EQU 0x00000001
ENTRY
ldr R0, = VAL1A
ldr R1, = VAL1B
ldr R2, = VAL2A
ldr R3, = VAL2B
stop 	b stop
END
```

• Copy to RAM
```   AREA Exaddress, CODE, READONLY
EXPORT Reset_Handler
Reset_Handler
LDR R9, =list
mov R7, #4 ; number in list
ldr r6, =datastart
loop
ldr r8,[r9],#4
str r8,[r6],#4
subs r7,r7,#1
bne loop

stop b stop
ALIGN
list DCW 0x1111, 0x2222, 0x3333, 0x4444, 0x5555
ALIGN
string DCB "This is a test.",0 ; This string is not used in the copy to ram program
string2 DCB 'T','h','i' ; This string2 is not used in the copy to ram program
ALIGN
datastart SPACE 20

END
```

• Subroutine Example1
```; First Subroutine Example
; This program demonstrates using a subroutine,
; saving registers on the stack, etc.

; Rob Frohne, 11/3/2013

stack_start EQU 0x40001000
AREA Subroutine_Example, CODE

ENTRY

Start
ldr sp, =stack_start ; Tell where we will place the stack.
; (It goes down (lower addresses from here.)
mov r1, #1           ; Store some numebers in some registers
mov r2, #2
mov r3, #3
bl subroutine
stop b stop

; This subroutine saves the registers,
; messes up the registers locally,
; then restores the registers and returns.
subroutine
stmfd sp!, {r1-r2,lr} ; save used registers and the link register (r14)
mov r1,#0xffffffff    ; mess up the registers
mov r2, r1
ldmfd sp!, {r1,r2,pc} ; pop the stack and return
END
```

• Minimum of a Signed Number
```; This program finds the minimum of a list of constant words in signed format
; The result is in r3 at the end of the routine.
; Rob Frohne 10/30/2013

ENTRY

ldr r0,=end	; load the end address of the code & initialize it as the pointer.
mov r3,#0x7fffffff ; Initialize r3 as the most positive number.

loop
ldr r6,[r2],#4 ; load the next data into r6 and post increment r2 for the next data.
cmp r6,r3      ; Find out if r6 > r3. result from r6-r3  like subs r7,r6,r3 without r6 necessary.
bgt no_update  ; If it is no update.
mov r3,r6      ; update if r6 is lower than r3.
no_update
cmp r0,r2      ;  are we at the end yet
bne loop       ; if r7 != 0 then keep looping

stop b stop
ALIGN
begin
DCD 0x8fffffff, 0x55555555, 0x44444444, 0x77777777, 0xffffffff
end
END

```

• Compare Two Null Terminated Strings
```; Subroutine to compare two null terminated ASCII strings.
; The address where the two strings start are in r0 and r1
; The return is in r0, 1 for match and 0 for don't match.
; If they don't match, r1 gives the number of the first
; character that didn't match, starting with 0.
; r2 and r3 are not protected.
; Rob Frohne 11/6/2013
stack_start EQU 0x40001000
AREA String_Compare, CODE
ENTRY
Start
ldr sp, =stack_start ; Tell where we will place the stack.
; (It goes down (lower addresses from here.)
ldr r0, =string1
ldr r1, =string2
bl compare_strings
stop b stop

compare_strings
stmfd sp!, {r2,r4,r5,lr} ; save used registers and the link register (r14)
mov r2, #0 ; Initialize the counter for the first position.
loop
ldrb r4,[r0],#1
ldrb r5,[r1],#1
cmp r5,r4
bne do_not_match
cmp r4, #0 ; check for end of string.
beq match
b loop
do_not_match
mov r0, #0 ; don't match
b finish
match
mov r0, #1
finish
mov r1,r2 ; Move the count into the result register.
ldmfd sp!, {r2,r4,r5,pc} ; pop the stack and return
string1
DCB "This is the first string.",00	; For testing purposes.
ALIGN
string2
DCB "This is the first string.",00
ALIGN
END

```

```; Subroutine to add two BCD numbers in 32 bit form with each nibble
; representing a digit.
; working BCD_carry, r7
; working result in r4
; the summed digits go in r3
; inputs: r0+r1
; outputs: r0 result and r1 carry
; only r4-r8 are saved.
; Rob Frohne 11/12/13
stack_start EQU 0x40001000
ENTRY
Start
ldr sp, =stack_start ; Tell where we will place the stack.
; (It goes down (lower addresses from here.)
ldr r0, =0x12345678	  ; The two numbers to add.
ldr r1, =0x87654321
stop b stop

stmfd sp!, {r4-r8,lr}
mov r8, #8 ; Set the counter to move through the eight digits
mov r7, #0 ; Set the carry to zero to start with
mov r3, #0	; This is where the resulting digits of the sum are stored.
loop
and r5, r0, #0xf  ; mask for the rightmost digit.
and r6, r1, #0xf
add r4, r4, r6 ; r4=r5+r6
subs r4, r4, #10 ; Subtract 10 (base 10) to see if there is a carry.
bpl carry
mov r7, #0 ; set carry to zero
add r4, r4, #10 ; Add the 10 back in as there was no carry needed.
b roll_to_next_digit
carry
mov r7, #1 ; set the carry
roll_to_next_digit
orr r3, r3, r4 ; Add these digits into the sum.
ror r0, #4
ror r1, #4
ror r3, #4
subs r8, r8, #1	 ; Decrement the digit counter
bne loop	  ; If it isn't zero do the next digit.
mov r0, r3 ; Set the output registers. r0 is result. r1 is carry.
mov r1, r7
ldmfd sp!, {r4-r8,pc}
END

```

• Bubble Sort
```; This is a program to bubble sort a list.
; Rob Frohne and the CPTR 215 class

stack_start EQU 0x40001000
length EQU (end_of_list - begin)
end_ram EQU (datastart + length)
; EQU statements must be at the beginning of the program.
AREA Subroutine_Example, CODE

ENTRY
; Copy list to RAM
ldr sp, =stack_start
LDR R9, =begin
ldr R7, =end_of_list
ldr r6, =datastart
loop
ldr r8,[r9],#4
str r8,[r6],#4
cmp r9,r7
bne loop
; End copy to RAM

start_sort
ldr r5,=(end_ram - 4)
loop_sort_outer ; r2 is our counter, it goes from 1 to item_count
mov r1, #1 ; has_changed = false (0 is true)
ldr r2,=datastart ; start of data in ram goes into r2
loop_sort_inner ;
ldr r3,[r2],#4
ldr r4,[r2],#0
cmp r3,r4
blhi subroutine_swap ; swaps the last two and sets has_changed
cmp r2,r5
bne loop_sort_inner
sub r5,r5,#4 ; the last item is in order, so we don't need to check it again.
cmp r1,#1
bne loop_sort_outer
stop 	b stop

subroutine_swap ; swaps the contents of the addresses held in
; r2 and r2 -4 (the previous address)
; has_changed is r1 and it sets it to 0 (true)
stmfd sp!, {r0,r2-r4,lr}
mov r1,#0 ; setting the has_changed to true.
ldr r0,[r2],#-4  ; swapping data
ldr r3,[r2]
str r0,[r2],#4
str r3,[r2]
ldmfd sp!, {r0,r2-r4,pc}
ALIGN
begin
DCD 0x8fffffff, 0x55555555, 0x44444444, 0x77777777, 0xffffffff
end_of_list
datastart SPACE 20
END

```

• GPIO Example
```;---------------------------------------------------------------------------
;
;   Programmer  : Larry Aamodt
;
;   File name  	: shell_2148.s
;   Class       : CPTR-215
;   Language    : ARM assembly
;   Assembler   : Keil
;   Target MCU	: NXP LPC-2148 on Embedded Artists board
;   Date Written: 11/30/09
;    change history:  11/30/09  LDA  Updated with hardware start-up
;    		      12/03/09  LDA  PWM register definitions added
;   Description	:
;
;   Inputs      :
;
;   Outputs     :
;
;   Special     :
;  requirements
;
;
; 	NOTES:
;
;
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:

; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR        EQU     0x10
Mode_FIQ        EQU     0x11
Mode_IRQ        EQU     0x12
Mode_SVC        EQU     0x13
Mode_ABT        EQU     0x17
Mode_UND        EQU     0x1B
Mode_SYS        EQU     0x1F

I_Bit           EQU     0x80            ; when I bit is set, IRQ is disabled
F_Bit           EQU     0x40            ; when F bit is set, FIQ is disabled

;  Memory addresses for standard GPIO definitions
IO0PIN		    EQU     0xE0028000
IO0SET		    EQU     0xE0028004
IO0DIR		    EQU     0xE0028008
IO0CLR		    EQU     0xE002800C
;  Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR         EQU     0xE0014004      ; PWM_TCR Timer Control Register
PWM_PR          EQU     0xE001400C      ; PWM_PR Prescaler Register
PWM_MCR         EQU     0xE0014014      ; PWM_MCR Match Control Register
PWM_MR0         EQU     0xE0014018      ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4         EQU     0xE0014040      ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6         EQU     0xE0014048      ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR         EQU     0xE001404C      ; PWM_CR Control Register
PWM_LER         EQU     0xE0014050      ; PWM_LER Latch Enable Register

PINSEL0         EQU     0xE002C000      ; Pin connect block - port 0
PINSEL1         EQU     0xE002C004      ; Pin connect block - port 1

;  Stack size definitions
UND_Stack_Size  EQU     0x00000000
SVC_Stack_Size  EQU     0x00000008
ABT_Stack_Size  EQU     0x00000000
FIQ_Stack_Size  EQU     0x00000000
IRQ_Stack_Size  EQU     0x00000080
USR_Stack_Size  EQU     0x00000400

ISR_Stack_Size  EQU     (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size)

Stack_Mem       SPACE   USR_Stack_Size
__initial_sp    SPACE   ISR_Stack_Size
Stack_Top

; Area Definition and Entry Point
;  Startup Code must be linked first at Address at which it expects to run.

ARM

; Exception Vectors
;  Absolute addressing mode must be used.
;  Dummy Handlers are implemented as infinite loops which can be modified.

NOP                            ; Reserved Vector
LDR     PC, [PC, #-0x0FF0]     ; Vector from VicVectAddr

Undef_Handler   B       Undef_Handler
SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler
IRQ_Handler     B       IRQ_Handler
FIQ_Handler     B       FIQ_Handler

; Reset Handler

EXPORT  Reset_Handler
Reset_Handler

; Setup Stack for each mode

LDR     R0, =Stack_Top

;  Enter Undefined Instruction Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #UND_Stack_Size

;  Enter Abort Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #ABT_Stack_Size

;  Enter FIQ Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #FIQ_Stack_Size

;  Enter IRQ Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #IRQ_Stack_Size

;  Enter Supervisor Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #SVC_Stack_Size

;  Enter User Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_USR

MOV     SP, R0
SUB     SL, SP, #USR_Stack_Size

; User program code goes here

main
ldr r1, =PINSEL0
mov r0, #0		  ; set P0.0 to P0.15 as GPIO
str r0,[r1]
ldr r1, =IO0DIR		; Get ready to set direction of data
ldr r0, =0xffff		; output
str r0, [r1] 	;	set the direction
mov r0, #0
next
str r0, [r1]
ldr  r2, =0x200
delay
subs r2,r2,#1
bne delay
b next

ALIGN
datastart EQU 0x40000004
SPACE 20
;FILL 20, 0xff		; Why is this stored at 0x50 instead of 0x58?
; Or maybe a better question:  How do I set data at
; a specific memory location?

END

```

• LCD Example
```;---------------------------------------------------------------------------
;
;   Programmer  : Larry Aamodt
;
;   File name  	: shell_2148.s
;   Class       : CPTR-215
;   Language    : ARM assembly
;   Assembler   : Keil
;   Target MCU	: NXP LPC-2148 on Embedded Artists board
;   Date Written: 11/30/09
;    change history:  11/30/09  LDA  Updated with hardware start-up
;    		      12/03/09  LDA  PWM register definitions added
;   Description	:
;
;   Inputs      :
;
;   Outputs     :
;
;   Special     :
;  requirements
;
;
; 	NOTES:
;
;
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:

; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR        EQU     0x10
Mode_FIQ        EQU     0x11
Mode_IRQ        EQU     0x12
Mode_SVC        EQU     0x13
Mode_ABT        EQU     0x17
Mode_UND        EQU     0x1B
Mode_SYS        EQU     0x1F

I_Bit           EQU     0x80            ; when I bit is set, IRQ is disabled
F_Bit           EQU     0x40            ; when F bit is set, FIQ is disabled

;  Memory addresses for standard GPIO definitions
IO0PIN		    EQU     0xE0028000
IO0SET		    EQU     0xE0028004
IO0DIR		    EQU     0xE0028008
IO0CLR		    EQU     0xE002800C
;  Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR         EQU     0xE0014004      ; PWM_TCR Timer Control Register
PWM_PR          EQU     0xE001400C      ; PWM_PR Prescaler Register
PWM_MCR         EQU     0xE0014014      ; PWM_MCR Match Control Register
PWM_MR0         EQU     0xE0014018      ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4         EQU     0xE0014040      ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6         EQU     0xE0014048      ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR         EQU     0xE001404C      ; PWM_CR Control Register
PWM_LER         EQU     0xE0014050      ; PWM_LER Latch Enable Register

PINSEL0         EQU     0xE002C000      ; Pin connect block - port 0
PINSEL1         EQU     0xE002C004      ; Pin connect block - port 1

;  Stack size definitions
UND_Stack_Size  EQU     0x00000000
SVC_Stack_Size  EQU     0x00000008
ABT_Stack_Size  EQU     0x00000000
FIQ_Stack_Size  EQU     0x00000000
IRQ_Stack_Size  EQU     0x00000080
USR_Stack_Size  EQU     0x00000400

ISR_Stack_Size  EQU     (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size)

Stack_Mem       SPACE   USR_Stack_Size
__initial_sp    SPACE   ISR_Stack_Size
Stack_Top

; Area Definition and Entry Point
;  Startup Code must be linked first at Address at which it expects to run.

ARM

; Exception Vectors
;  Absolute addressing mode must be used.
;  Dummy Handlers are implemented as infinite loops which can be modified.

NOP                            ; Reserved Vector
LDR     PC, [PC, #-0x0FF0]     ; Vector from VicVectAddr

Undef_Handler   B       Undef_Handler
SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler
IRQ_Handler     B       IRQ_Handler
FIQ_Handler     B       FIQ_Handler

; Reset Handler

EXPORT  Reset_Handler
Reset_Handler

; Setup Stack for each mode

LDR     R0, =Stack_Top

;  Enter Undefined Instruction Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #UND_Stack_Size

;  Enter Abort Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #ABT_Stack_Size

;  Enter FIQ Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #FIQ_Stack_Size

;  Enter IRQ Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #IRQ_Stack_Size

;  Enter Supervisor Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #SVC_Stack_Size

;  Enter User Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_USR

MOV     SP, R0
SUB     SL, SP, #USR_Stack_Size

; User program code goes here

main
bl lcdReset ; set up the lcd
bl lcdClear
mov r0, #32
loop
bl lcdWRdata ; write to the LCD
add r0, r0, #1 ; increment the data
mov r9, r0
ldr r0, =1000000
bl delay
mov r0, r9
b loop

; User data area definition follows

;-----------------------------------------------------------------------------
;
;   Programmer  : Larry Aamodt
;
; 	File name  	: lcd_routines.asm
;   Language    : ARM assembly
;   Assembler   ; Keil
; 	Target MCU	: NXP LPC2148 on Embedded Artists education board
;   Date        : 12/05/12   LDA   lcd_routines.asm.  debugged

;
; 	Description	:	LCD display routines
;					Copy this code to your own program
;
;                  Procedures you can call are:
;                    lcdReset       must be called first.  no parameters.
;                    lcdClear       use to clear the display & home the cursor
;                                     no parameters are required.
;                    lcdWRdata      use to write one character on the display
;                                     place an ASCII char in r0 before calling.
;                    lcdCursorAt    use to move the cursor.  Two parameters
;                                     r0 = row (1 or 2)
;                                     r1 = column to move cursor to (0-15)
;
;                  Example use of the LCD routines:
;                    bl    lcdReset   ; do this once at the top of your program
;
;                    mov   r0,#31h    ; place the ascii code for number one in r0
;					 bl    lcdWRdata  ; display the character '1' on LCD display
;
;					 bl    lcdClear   ; clear the display
;
;                    mov   r0,#1      ; select LCD row 1
;                    mov   r1,#4      ; select LCD column 4
;                    bl    lcdCursorAt ;  move the cursor
;
;
;          NOTE:   All registers are saved by these routines, i.e. upon return
;                  from the subroutine all registers, except flags, have the
;                  same value in them as before the subroutine was called (this
;                  breaks with the ARM convention for subroutines but is done
;                  to make it easier for you).
;
;-----------------------------------------------------------------------------

EXPORT	lcdReset
EXPORT	lcdClear
EXPORT	lcdWRdata
EXPORT	lcdCursorAt
EXPORT	delay

; -----------------------------------------------------------------------------
;  LCD Procedures follow.
;------------------------------------------------------------------------------
; ***** LCD equates	******
;IO0PIN          EQU     0xE0028000
;IO0SET          EQU     0xE0028004
;IO0DIR          EQU     0xE0028008
;IO0CLR          EQU     0xE002800C

IO1PIN          EQU     0xE0028010
IO1SET          EQU     0xE0028014
IO1DIR          EQU     0xE0028018
IO1CLR          EQU     0xE002801C

; -----------------------------------------------------------------------------
;   Procedure lcdReset        Must be called before writing to the LCD
;                             No parameters required
; -----------------------------------------------------------------------------
lcdReset			STMFD	SP!,{LR,r0,r1,r2,r3,r4,r5,r6,r7,r8,r9}

MOV		r2,#2 					; prescale count value
STR		r2,[r1]					; Prescaler will divide by 3

LDR		r1,=T0_MCR_ADDR			; Match control register
MOV		r2,#0x6					; bit 2 = one
STR		r2,[r1]					; stop counter when match reached

LDR		r5,=IO0CLR
LDR		r6,=IO0SET
LDR		r7,=IO1CLR
LDR		r8,=IO1SET

LDR		r0,=35000
BL		delay			; delay 35ms

LDR		r0,=IO0DIR
LDR		r4,[r0]			; read current Port 0 direction bits
LDR		r3,=0x40408000  ; one's for pins to be set for output
ORR		r4,r4,r3		;    bits 22 & 30 to be outputs
STR		r4,[r0]			; set lcd port bits to output
LDR		r3,=0x00400000	;
STR		r3,[r5]			; write 0 to LCD R/W signal
LDR		r3,=0x40008000
STR		r3,[r6]			; turn on backlight - write 1 to P0.30

LDR		r0,=IO1DIR
LDR		r4,[r0]			; read current Port 1 direction bits
LDR		r3,=0x03FF0000  ; one's for lcd pins - set for output
ORR		r4,r4,r3		;    bits 16 to 25 to be outputs
STR		r4,[r0]			; set lcd port direction bits to output
STR		r3,[r7]			; clear the LCD data & control bits

LDR		r9,=0x02000000	; bit pattern to turn on/off E

LDR		r2,=0x00380000	; function: 2 lines, 5x7 dots
STR		r2,[r8]			; set lcd function bits
BL		pulse_e
STR		r3,[r7]			; clear lcd bits

MOV		r0,#40
BL		delay			;  wait 40 microseconds

LDR		r2,=0x000E0000	; function:  display on, cursor on
STR		r2,[r8]			; set lcd function bits
BL		pulse_e
STR		r3,[r7]			; clear lcd bits

MOV		r0,#40
BL		delay			;  wait 40 microseconds

LDR		r2,=0x00010000	; lcd function:  clear display
STR		r2,[r8]			; set lcd function bits
BL		pulse_e
STR		r3,[r7]			; clear lcd bits

MOV		r0,#1600
BL		delay			; delay for 1600 microseconds

LDR		r2,=0x00030000	; lcd function:  set entry mode
STR		r2,[r8]			; set lcd function bits
BL		pulse_e
STR		r3,[r7]			; clear lcd bits

end_lcdReset		LDMFD	SP!,{PC,r0,r1,r2,r3,r4,r5,r6,r7,r8,r9}

;------------------------------------------------------------------------------
;   Procedure pulse_e		uses r0. Not interuptable (r14 not saved)
;							assumes r9 has 0x02000000 in it
;                           assumes r7 has IO1CLR address in it
;							assumes r8 has IO1SET address in it
;------------------------------------------------------------------------------
pulse_e 			MOV		r0,#4
STR		r9,[r8]			; assert E
ploop1				SUBS	r0,r0,#1		; delay	600ns or so
BNE		ploop1
STR		r9,[r7]			; de-assert E
MOV		r0,#8
ploop2				SUBS	r0,r0,#1		; delay 1100ns or so
BNE		ploop2
end_pulse_e			BX		LR

; -----------------------------------------------------------------------------
;   Procedure lcdClear    Clears the LCD and positions cursor to line1, left col
;               					no parameters required
; -----------------------------------------------------------------------------
lcdClear			STMFD	SP!,{LR,r0,r1,r2,r3,r7,r8,r9}

LDR		r9,=0x02000000	; bit pattern to turn on/off E
LDR		r3,=0x03FF0000  ; one's for pins used for lcd
LDR		r7,=IO1CLR
LDR		r8,=IO1SET

LDR		r2,=0x00010000	; lcd function:  clear display
STR		r2,[r8]			; set lcd function bits
BL		pulse_e
STR		r3,[r7]			; clear lcd bits

MOV		r0,#1600
BL		delay

end_lcdClear		LDMFD	SP!,{PC,r0,r1,r2,r3,r7,r8,r9}
; -----------------------------------------------------------------------------
;   Procedure lcdCursorAt
;                          r0 assumed to contain the row# (1 or 2)
;                          r1 assumed to contain col#(0-15)
;                          No cursor movement if invalid row or col # is found
; -----------------------------------------------------------------------------

lcdCursorAt			STMFD	SP!,{LR,r0,r1,r2,r3,r7,r8,r9}

CMP		r1,#15			; check column & row range
BHI		end_lcdCursorAt
CMP		r0,#0
BEQ		end_lcdCursorAt
CMP		r0,#2
BHI		end_lcdCursorAt
LDR		r0,=0x00800000
ORR		r2,r0,r1,LSL #16 ; r2 now has the address

LDR		r3,=0x03FF0000  ; one's for pins used for lcd
LDR		r7,=IO1CLR
LDR		r8,=IO1SET
LDR		r9,=0x02000000	; bit pattern to turn on/off E

STR		r2,[r8]			; set lcd function bits
BL		pulse_e
STR		r3,[r7]			; clear lcd bits

MOV		r0,#40
BL		delay

end_lcdCursorAt		LDMFD	SP!,{PC,r0,r1,r2,r3,r7,r8,r9}

; -----------------------------------------------------------------------------
;   Procedure lcdWRdata         Writes data (an ASCII character) to the
;                               current cursor position on the LCD
;                               r0 is assumed to contain the character to send
; -----------------------------------------------------------------------------
lcdWRdata	   		STMFD	SP!,{LR,r0,r1,r2,r3,r7,r8,r9}

LDR		r9,=0x02000000	; bit pattern to turn on/off E
LDR		r3,=0x03FF0000  ; one's for pins used for lcd
LDR		r7,=IO1CLR
LDR		r8,=IO1SET

AND		r0,r0,#0xFF		; make sure only 8 bits are non-zero
ORR		r0,r0,#0x100	; set bit that indicates writing
LSL		r0,r0,#16
STR		r0,[r8]			; write data
BL		pulse_e
STR		r3,[r7]			; clear lcd bits

end_lcdWRData		LDMFD	SP!,{PC,r0,r1,r2,r3,r7,r8,r9}

; -----------------------------------------------------------------------------
;   Procedure  delay          microsecond delay using timer 0
;                               (12Mhz clock & VPB divide by 4 assumed)
;                             reg R0 must contain a delay value in uS
;                             Not interruptable (r14 not saved)
;							  assumes lcdReset has been called prior to delay
;                             r0, r1 not preserved
; -----------------------------------------------------------------------------

delay				LDR		r1,=T0_MR0_ADDR			; Match register zero
STR		r0,[r1]					; load match count per R0

;					ldr		r0,=0x8000
;					str		r0,[r5]					; turn on p0.15 led

LDR		r1,=T0_TCR_ADDR			; Timer 0 Control Register
MOV		r0,#2					; bit 1 = one
STR		r0,[r1]					; Clear counter & prescaler
MOV		r0,#0
STR		r0,[r1]

MOV		r0,#1					; bit 0 = one
STR		r0,[r1]					; Turn on counter

dloop				LDR     r0,[r1]					; Read TCR register
ANDS	r0,r0,#1
BNE		dloop

;					ldr		r0,=0x8000
;					str		r0,[r6]					; turn off p0.15 led

end_delay			BX		LR						; Return

END

```
• PWM Example for running the servos on the robots
```;---------------------------------------------------------------------------
;
;   Programmer  : Larry Aamodt
;
;   File name  	: shell_2148.s
;   Class       : CPTR-215
;   Language    : ARM assembly
;   Assembler   : Keil
;   Target MCU	: NXP LPC-2148 on Embedded Artists board
;   Date Written: 11/30/09
;    change history:  11/30/09  LDA  Updated with hardware start-up
;    		      12/03/09  LDA  PWM register definitions added
;   Description	:
;
;   Inputs      :
;
;   Outputs     :
;
;   Special     :
;  requirements
;
;
; 	NOTES:
;
;
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:

; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR        EQU     0x10
Mode_FIQ        EQU     0x11
Mode_IRQ        EQU     0x12
Mode_SVC        EQU     0x13
Mode_ABT        EQU     0x17
Mode_UND        EQU     0x1B
Mode_SYS        EQU     0x1F

I_Bit           EQU     0x80            ; when I bit is set, IRQ is disabled
F_Bit           EQU     0x40            ; when F bit is set, FIQ is disabled

;  Memory addresses for standard GPIO definitions
IO0PIN		    EQU     0xE0028000
IO0SET		    EQU     0xE0028004
IO0DIR		    EQU     0xE0028008
IO0CLR		    EQU     0xE002800C
T1TCR			EQU	 	0xE0008004
T1CTCR			EQU	  	0xE0008070
T1PR			EQU	 	0xE000800C

T1MCR			EQU	   	0xE0008014
T1EMR 			EQU	  	0xE000803C

;  Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR         EQU     0xE0014004      ; PWM_TCR Timer Control Register
PWM_PR          EQU     0xE001400C      ; PWM_PR Prescaler Register
PWM_MCR         EQU     0xE0014014      ; PWM_MCR Match Control Register
PWM_MR0         EQU     0xE0014018      ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4         EQU     0xE0014040      ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6         EQU     0xE0014048      ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR         EQU     0xE001404C      ; PWM_CR Control Register
PWM_LER         EQU     0xE0014050      ; PWM_LER Latch Enable Register

PINSEL0         EQU     0xE002C000      ; Pin connect block - port 0
PINSEL1         EQU     0xE002C004      ; Pin connect block - port 1

;  Stack size definitions
UND_Stack_Size  EQU     0x00000000
SVC_Stack_Size  EQU     0x00000008
ABT_Stack_Size  EQU     0x00000000
FIQ_Stack_Size  EQU     0x00000000
IRQ_Stack_Size  EQU     0x00000080
USR_Stack_Size  EQU     0x00000400

ISR_Stack_Size  EQU     (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size)

Stack_Mem       SPACE   USR_Stack_Size
__initial_sp    SPACE   ISR_Stack_Size
Stack_Top

; Area Definition and Entry Point
;  Startup Code must be linked first at Address at which it expects to run.

ARM

; Exception Vectors
;  Absolute addressing mode must be used.
;  Dummy Handlers are implemented as infinite loops which can be modified.

NOP                            ; Reserved Vector
LDR     PC, [PC, #-0x0FF0]     ; Vector from VicVectAddr

Undef_Handler   B       Undef_Handler
SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler
IRQ_Handler     B       IRQ_Handler
FIQ_Handler     B       FIQ_Handler

; Reset Handler

EXPORT  Reset_Handler
Reset_Handler

; Setup Stack for each mode

LDR     R0, =Stack_Top

;  Enter Undefined Instruction Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #UND_Stack_Size

;  Enter Abort Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #ABT_Stack_Size

;  Enter FIQ Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #FIQ_Stack_Size

;  Enter IRQ Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #IRQ_Stack_Size

;  Enter Supervisor Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #SVC_Stack_Size

;  Enter User Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_USR

MOV     SP, R0
SUB     SL, SP, #USR_Stack_Size

; User program code goes here

main
BL   pwm_init
mov r0, #3
ldr r1, =1600
mov r2, r1
bl pwm_update
stop			b stop

;*****************************************************************************
;    Pulse width modulator initialization routine.
;          pulse period = 20ms;  initial pulse length = 1.5ms
;          (values shown are for controlling model plane servos)
;    Inputs: none
;    Returned values: none
;    Uses registers r0, r1
;
;    Calling method:   BL   pwm_init
;                                                   LDA   rev 1.0  12/01/10
;													Rob Frohne rev 1.1 12/3/13
;*****************************************************************************
; Stepper motor connections on the Board of Education are from page 24 of the board
; Users Guide.  A stepper motor operation is explained at:
; http://www.haydonkerk.com/Resources/StepperMotorTheory/tabid/192/Default.aspx
;
; PWM is explained in Chapter 16 (page 253 and following) of the reference manual.
; Note the registers in the reference manual are labeled without the _, i.e.

pwm_init		STMFD	sp!,{r14}		; save link register on the stack

LDR     r0,=PWM_PR
LDR     r1,=2			; prescaler = 2 divides PCLK by 3
; PCLK rate is 1 MHZ (1 uSEC period) after division.
LDR     r0,=PWM_MCR		; PWMMCR PWM Match Control Register.
; The PWMMCR is used to control if an

; interrupt is generated and if the PWMTC
; is reset when a Match occurs.  See page 261 of the manual.
LDR     r1,=0x02        ; bit 1 is set, others are zero
STR     r1,[r0]         ; load PWM_MCR: PWM_TC resets when PWM_TC=PWM_MR0

LDR     r0,=PWM_MR0
LDR     r1,=20000       ; count 20,000 1uSEC intervals = 20 milli secs
STR     r1,[r0]         ; load match register 0. sets pulse period.  Page 261.

LDR     r0,=PWM_MR4
LDR     r1,=1500		; initial pulse width = 1.5 milli secs
STR     r1,[r0]         ; load match register 4.  Page 261
LDR     r0,=PWM_MR6
LDR     r1,=1500		; initial pulse width
STR     r1,[r0]			; load match register 6

LDR     r0,=PWM_LER
LDR     r1,=0x51
STR     r1,[r0]         ; Enable latch registers 0, 4, & 6

LDR     r0,=PWM_PCR
LDR     r1,=0x5000      ; bits 12 & 14 set
STR     r1,[r0]         ; load PWM_CR: enables PWM4 and PWM6 outputs

LDR     r0,=PWM_TCR
LDR     r1,=0x09        ; bits 3,1,0 set
STR     r1,[r0]         ; load PWM_TCR enable PWM counter & prescaler

LDR     r0,=PINSEL0
LDR     r1,=0xA0000     ; bits 19 & 17 set.  selects output PWM 6 & 4
STR     r1,[r0]		    ; Enable PWM6 & PWM4 outputs

end_pwm_init	LDMFD	sp!,{pc}		; Return from subroutine  pwm_init

;*****************************************************************************
;   Pulse width modulator update routine
;	Inputs:  r0 = command
;                   1 = update PWM channel 4
;                   2 = update PWM channel 6
;                   3 = update both channel 4 and 6
;            r1 = new pulse width (# of microseconds) for PWM channel 4
;            r2 = new pulse width (# of microseconds) for PWM channel 6
;   Returned values: none
;   Uses registers r0, r1, r2, r3
;
;   Calling method:  first put values in r0, r1, r2 as appropriate
;                    BL pwm_update
;                                                   LDA    rev 2.0  12/01/11
;*****************************************************************************

pwm_update		STMFD	sp!,{r14}		; r0 specifies channels to update

LDR     r3,=PWM_MR4
STR     r1,[r3]         ; load match register 4

LDR     r3,=PWM_MR6
STR     r2,[r3]			; load match register 6

BNE		pwm_reg_update
LDR		r3,=PWM_MR4
STR		r1,[r3]
LDR		r3,=PWM_MR6
STR		r2,[r3]

pwm_reg_update	CMP		r0,#1			; check for just pwm 4 update
MOVEQ	r1,#0x10
CMP		r0,#2			; check for just pwm 6 update
MOVEQ	r1,#0x40
CMP		r0,#3			; check for pwm 4 & 6 update
MOVEQ	r1,#0x50

LDR		r3,=PWM_LER
STR		r1,[r3]			; enable latch registers

end_pwm_update	LDMFD	sp!,{pc}		; Return from subroutine  pwm_update

; User data area definition follows

END

```
• Stepper Motor on the Embedded Artists Board of Education
```
;---------------------------------------------------------------------------
;
;   Programmer  : Larry Aamodt
;				: Modifications for propeller drive: Rob Frohne
;
;   File name  	: shell_2148.s
;   Class       : CPTR-215
;   Language    : ARM assembly
;   Assembler   : Keil
;   Target MCU	: NXP LPC-2148 on Embedded Artists board
;   Date Written: 11/30/09
;    change history:  11/30/09  LDA  Updated with hardware start-up
;    		      12/03/09  LDA  PWM register definitions added
;   Description	:
;
;   Inputs      :
;
;   Outputs     :
;
;   Special     :
;  requirements
;
;
; 	NOTES:
;
;
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:

; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR        EQU     0x10
Mode_FIQ        EQU     0x11
Mode_IRQ        EQU     0x12
Mode_SVC        EQU     0x13
Mode_ABT        EQU     0x17
Mode_UND        EQU     0x1B
Mode_SYS        EQU     0x1F

I_Bit           EQU     0x80            ; when I bit is set, IRQ is disabled
F_Bit           EQU     0x40            ; when F bit is set, FIQ is disabled

;  Memory addresses for standard GPIO definitions
IO0PIN		    EQU     0xE0028000
IO0SET		    EQU     0xE0028004
IO0DIR		    EQU     0xE0028008
IO0CLR		    EQU     0xE002800C

;  Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR         EQU     0xE0014004      ; PWM_TCR Timer Control Register
PWM_PR          EQU     0xE001400C      ; PWM_PR Prescaler Register
PWM_MCR         EQU     0xE0014014      ; PWM_MCR Match Control Register
PWM_MR0         EQU     0xE0014018      ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4         EQU     0xE0014040      ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6         EQU     0xE0014048      ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR         EQU     0xE001404C      ; PWM_CR Control Register
PWM_LER         EQU     0xE0014050      ; PWM_LER Latch Enable Register

PINSEL0         EQU     0xE002C000      ; Pin connect block - port 0
PINSEL1         EQU     0xE002C004      ; Pin connect block - port 1

;  Stack size definitions
UND_Stack_Size  EQU     0x00000000
SVC_Stack_Size  EQU     0x00000008
ABT_Stack_Size  EQU     0x00000000
FIQ_Stack_Size  EQU     0x00000000
IRQ_Stack_Size  EQU     0x00000080
USR_Stack_Size  EQU     0x00000400

ISR_Stack_Size  EQU     (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size)

Stack_Mem       SPACE   USR_Stack_Size
__initial_sp    SPACE   ISR_Stack_Size
Stack_Top

; Area Definition and Entry Point
;  Startup Code must be linked first at Address at which it expects to run.

ARM

; Exception Vectors
;  Absolute addressing mode must be used.
;  Dummy Handlers are implemented as infinite loops which can be modified.

NOP                            ; Reserved Vector
LDR     PC, [PC, #-0x0FF0]     ; Vector from VicVectAddr

Undef_Handler   B       Undef_Handler
SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler
IRQ_Handler     B       IRQ_Handler
FIQ_Handler     B       FIQ_Handler

; Reset Handler

EXPORT  Reset_Handler
Reset_Handler

; Setup Stack for each mode

LDR     R0, =Stack_Top

;  Enter Undefined Instruction Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #UND_Stack_Size

;  Enter Abort Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #ABT_Stack_Size

;  Enter FIQ Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #FIQ_Stack_Size

;  Enter IRQ Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #IRQ_Stack_Size

;  Enter Supervisor Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #SVC_Stack_Size

;  Enter User Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_USR

MOV     SP, R0
SUB     SL, SP, #USR_Stack_Size

; User program code goes here

quarter_period EQU	250000 ; uS
main
BL init_propeller
mov r1, #1	 ; for handy access
mov r2, #0	  ; for handy access

mov r0, r1, LSL #12		; turn one on
mov r4, r2, LSL #21		; two still off
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
ldr r0,=quarter_period
bl delay

mov r0, r1, LSL #12
mov r4, r1, LSL #21
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
ldr r0,=quarter_period
bl delay

mov r0, r2, LSL #12		; turn one off
mov r4, r1, LSL #21		; two still on
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
ldr r0,=quarter_period
bl delay

mov r0, r2, LSL #12		; turn one off
mov r4, r2, LSL #21		; turn two off
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
ldr r0,=quarter_period
bl delay

b main

; -----------------------------------------------------------------------------
;   Procedure init_propeller  Must be called before to initialize the propeller.
;							  Set up the counter T0 and GPIO P0.12 and P0.21 for output.
;                             No parameters required
; -----------------------------------------------------------------------------
init_propeller		STMFD	SP!,{LR,r0,r1,r2,r3,r4}

MOV		r2,#2 					; prescale count value
STR		r2,[r1]					; Prescaler will divide by 3  (1 uS period)

LDR		r1,=T0_MCR_ADDR			; Match control register
MOV		r2,#0x4					; bit 2 = one: Stop on MR0: the TC and PC
; will be stopped and TCR[0] will be set to 0 if
; MR0 matches the TC
STR		r2,[r1]					; stop counter when match reached

LDR		r0,=IO0DIR
LDR		r4,[r0]			; read current Port 0 direction bits
LDR		r3,=2101248  	; one's for pins to be set for output (2^12+2^21 = 2101248)
; 2134016 = 2^12+2^21+2^15 (for debugging LED on P0.15
ORR		r4,r4,r3
STR		r4,[r0]			; set propeller port bits to output without changing others.

LDR		r0,=PINSEL0
LDR		r4,[r0]			; read current Port 0 direction bits
LDR		r3,=50331648  	; zero's for pins to be set for output (2^24+2^25 = 50331648)
BIC		r4,r4,r3		;
STR		r4,[r0]			; set propeller port P0.12 to GPIO without changing others.

LDR		r0,=PINSEL1
LDR		r4,[r0]			; read current Port 0 direction bits
LDR		r3,=3072  		; zero's for pins to be set for output (2^10+2^11 = 3072)
BIC		r4,r4,r3		;
STR		r4,[r0]			; set propeller port P0.21 to GPIO without changing others.

end_init_propeller	LDMFD	SP!,{PC,r0,r1,r2,r3,r4}

; -----------------------------------------------------------------------------
;   Procedure  delay          microsecond delay using timer 0
;                               (12Mhz clock & VPB divide by 4 assumed)
;                             reg R0 must contain a delay value in uS
;                             Not interruptable (r14 not saved)
;							  assumes lcdReset has been called prior to delay
;                             r0, r1 not preserved
; -----------------------------------------------------------------------------

delay				STMFD	SP!,{LR,r0,r1}
LDR		r1,=T0_MR0_ADDR			; Match register zero
STR		r0,[r1]					; load match count per R0

;					ldr		r0,=0x8000
;					str		r0,[r5]					; turn on p0.15 led

LDR		r1,=T0_TCR_ADDR			; Timer 0 Control Register
MOV		r0,#2					; bit 1 = one
STR		r0,[r1]					; Clear counter & prescaler
MOV		r0,#0
STR		r0,[r1]

MOV		r0,#1					; bit 0 = one
STR		r0,[r1]					; Turn on counter

dloop				LDR     r0,[r1]					; Read TCR register
ANDS	r0,r0,#1
BNE		dloop

;					ldr		r0,=0x8000
;					str		r0,[r6]					; turn off p0.15 led

end_delay			LDMFD	SP!,{PC,r0,r1}			; Return

END

```
• Propeller Drive Using Interrupts (not polled)
```
;---------------------------------------------------------------------------
;
;   Shell Programmer  : Larry Aamodt, main: Rob Frohne
;
;
;   File name  	: shell_2148.s
;   Class       : CPTR-215
;   Language    : ARM assembly
;   Assembler   : Keil
;   Target MCU	: NXP LPC-2148 on Embedded Artists board
;   Date Written: 11/30/09
;    change history:  11/30/09  LDA  Updated with hardware start-up
;    		      12/03/09  LDA  PWM register definitions added
;   Description	:
;
;   Inputs      :
;
;   Outputs     :
;
;   Special     :
;  requirements
;
;
; 	NOTES:
;
;
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:

; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR        EQU     0x10
Mode_FIQ        EQU     0x11
Mode_IRQ        EQU     0x12
Mode_SVC        EQU     0x13
Mode_ABT        EQU     0x17
Mode_UND        EQU     0x1B
Mode_SYS        EQU     0x1F

I_Bit           EQU     0x80            ; when I bit is set, IRQ is disabled
F_Bit           EQU     0x40            ; when F bit is set, FIQ is disabled

;  Memory addresses for standard GPIO definitions
IO0PIN		    EQU     0xE0028000
IO0SET		    EQU     0xE0028004
IO0DIR		    EQU     0xE0028008
IO0CLR		    EQU     0xE002800C

VICIntSelect		EQU 0xFFFFF00C
VICVectCntl4		EQU	0xFFFFF210
VICIntEnable		EQU	0xFFFFF010

T0PC				EQU	0xE0004010  ; Timer 0 Prescale counter
T0IR				EQU	0xE0004000	; Timer 0 Interrupt Register

;  Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR         EQU     0xE0014004      ; PWM_TCR Timer Control Register
PWM_PR          EQU     0xE001400C      ; PWM_PR Prescaler Register
PWM_MCR         EQU     0xE0014014      ; PWM_MCR Match Control Register
PWM_MR0         EQU     0xE0014018      ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4         EQU     0xE0014040      ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6         EQU     0xE0014048      ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR         EQU     0xE001404C      ; PWM_CR Control Register
PWM_LER         EQU     0xE0014050      ; PWM_LER Latch Enable Register

PINSEL0         EQU     0xE002C000      ; Pin connect block - port 0
PINSEL1         EQU     0xE002C004      ; Pin connect block - port 1

;  Stack size definitions
UND_Stack_Size  EQU     0x00000000
SVC_Stack_Size  EQU     0x00000008
ABT_Stack_Size  EQU     0x00000000
FIQ_Stack_Size  EQU     0x00000000
IRQ_Stack_Size  EQU     0x00000080
USR_Stack_Size  EQU     0x00000400

ISR_Stack_Size  EQU     (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size)

Stack_Mem       SPACE   USR_Stack_Size
__initial_sp    SPACE   ISR_Stack_Size
Stack_Top

; Area Definition and Entry Point
;  Startup Code must be linked first at Address at which it expects to run.

ARM

; Exception Vectors
;  Absolute addressing mode must be used.
;  Dummy Handlers are implemented as infinite loops which can be modified.

NOP                            ; Reserved Vector
LDR     PC, [PC, #-0x0FF0]     ; Vector from VicVectAddr

; Exception Handlers
Undef_Handler   B       Undef_Handler
SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler
IRQ_Handler     B       IRQ_Handler
FIQ_Handler     B       FIQ_Handler

; Reset Handler

EXPORT  Reset_Handler
Reset_Handler

; Setup Stack for each mode

LDR     R0, =Stack_Top

;  Enter Undefined Instruction Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #UND_Stack_Size

;  Enter Abort Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #ABT_Stack_Size

;  Enter FIQ Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #FIQ_Stack_Size

;  Enter IRQ Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #IRQ_Stack_Size

;  Enter Supervisor Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOV     SP, R0
SUB     R0, R0, #SVC_Stack_Size

;  Enter User Mode and set its Stack Pointer
MSR     CPSR_c, #Mode_USR
MOV     SP, R0
SUB     SL, SP, #USR_Stack_Size

step_time_uS EQU	250000 ; uS between steps of the propeller motor.

main 				BL init_propeller	 ; Initialize GPIO and IRQ, and Timer 0.

LDR r0,=step1
STR r0,[r1]
bl delay ; start the whole automated thing

do_nothing			b do_nothing	; because the ISR will do it all!

; -----------------------------------------------------------------------------
;   Step the motor routines.  These are called by the Timer0 ISR (timer0ISR)
; -----------------------------------------------------------------------------

step1				STMFD	SP!,{LR,r0-r12}
mov r1, #1	 ; for handy access
mov r2, #0	  ; for handy access
mov r0, r1, LSL #12		; turn one on
mov r4, r2, LSL #21		; two still off
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
LDR r0,=step2
STR r0,[r1]
bl delay
LDMFD	SP!,{PC,r0-r12}

step2				STMFD	SP!,{LR,r0-r12}
mov r1, #1	 ; for handy access
mov r2, #0	  ; for handy access
mov r0, r1, LSL #12
mov r4, r1, LSL #21
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
LDR r0,=step3
STR r0,[r1]
bl delay
LDMFD	SP!,{PC,r0-r12}

step3				STMFD	SP!,{LR,r0-r12}
mov r1, #1	 ; for handy access
mov r2, #0	  ; for handy access
mov r0, r2, LSL #12		; turn one off
mov r4, r1, LSL #21		; two still on
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
LDR r0,=step4
STR r0,[r1]
bl delay
LDMFD	SP!,{PC,r0-r12}

step4				STMFD	SP!,{LR,r0-r12}
mov r1, #1	 ; for handy access
mov r2, #0	  ; for handy access
mov r0, r2, LSL #12		; turn one off
mov r4, r2, LSL #21		; turn two off
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
LDR r0,=step1
STR r0,[r1]
bl delay
LDMFD	SP!,{PC,r0-r12}

; -----------------------------------------------------------------------------
;   Procedure init_propeller  Must be called before to initialize the propeller.
;							  Set up GPIO P0.12 and P0.21 for output.
;							  Set up timer 0 and interrupts for IRQ (low priority)
;                             No parameters required
; -----------------------------------------------------------------------------

init_propeller		STMFD	SP!,{LR,r0,r1,r2,r3,r4}
MOV		r2,#2 					; prescale count value
STR		r2,[r1]					; Prescaler will divide by 3  (1 uS period

;  T0MCR = 0x00000003;                           	//reset counter and generate IRQ on MR0 match

LDR		r1,=T0_MCR_ADDR			; Match control register
MOV		r2,#0x3					; bit 2 = one: Stop on MR0: the TC and PC : bit 1 = 1 gives interrupt
; will be stopped and TCR[0] will be set to 0 if
; MR0 matches the TC
STR		r2,[r1]					; stop counter when match reached

;	VICIntSelect &= ~0x10;           //Timer0 interrupt is assigned to IRQ (not FIQ)
LDR		r1,=VICIntSelect
MOV 	r0,#~0x10
STR		r0,[r1]

LDR 	r0,=timer0ISR
STR		r0,[r1]

;	VICVectCntl4  = 0x24;            //enable vector interrupt for timer0
LDR		r1,=VICVectCntl4
MOV 	r0,#0x24
STR		r0,[r1]

;	VICIntEnable  = 0x10;            //enable timer0 interrupt
LDR		r1,=VICIntEnable
MOV 	r0,#0x10
STR		r0,[r1]

LDR		r0,=IO0DIR
LDR		r4,[r0]			; read current Port 0 direction bits
LDR		r3,=2101248  	; one's for pins to be set for output (2^12+2^21 = 2101248)
; 2134016 = 2^12+2^21+2^15 (for debugging LED on P0.15
ORR		r4,r4,r3
STR		r4,[r0]			; set propeller port bits to output without changing others

LDR		r0,=PINSEL0
LDR		r4,[r0]			; read current Port 0 direction bits
LDR		r3,=50331648  	; zero's for pins to be set for output (2^24+2^25 = 50331648)
BIC		r4,r4,r3		;
STR		r4,[r0]			; set propeller port P0.12 to GPIO without changing others.

LDR		r0,=PINSEL1
LDR		r4,[r0]			; read current Port 0 direction bits
LDR		r3,=3072  		; zero's for pins to be set for output (2^10+2^11 = 3072)
BIC		r4,r4,r3		;
STR		r4,[r0]			; set propeller port P0.21 to GPIO without changing others.

end_init_propeller	LDMFD	SP!,{PC,r0,r1,r2,r3,r4}

; -----------------------------------------------------------------------------
;  			Timer 0 Interrupt Service Routine  timer0ISR
; 			Some comments are C equivalents.
; -----------------------------------------------------------------------------

timer0ISR		 	SUB LR,LR,#4	 ; Update the LR
STMFD SP!, {r0-r12,LR}	  ; Store registers (including LR)

;  T0IR        = 0xff;        //reset all IRQ flags
LDR		r1,=T0IR
MOV 	r0,#~0xff
STR		r0,[r1]

;  VICVectAddr = 0x00;        //dummy write to VIC to signal end of interrupt
MOV 	r0,#~0x00
STR		r0,[r1]

; Here we will branch to the appropriate next step routine.
push {r14}  ; save LR for this return.
ldr r14,=end_timer0ISR  ; load the LR because we are using an LDR PC below.
ldr PC,[r0] ; branch to the subroutine pointed to by NextRoutine_Address
end_timer0ISR		pop {r14}	; restore the LR for this return.
LDMFD SP!, {r0-r12,PC}^  			; Return from ISR

; -----------------------------------------------------------------------------
;   Procedure  delay          microsecond delay using timer 0
;                               (12Mhz clock & VPB divide by 4 assumed)
;                  				Delay is set by step_time_uS
; -----------------------------------------------------------------------------

delay				STMFD	SP!,{LR,r0,r1}

;  T0PC  = 0x00000000;    Didn't do this yet.  Unnecessary?                       //no prescale of clock

;	LDR		r1,=T0PC
;	MOV 	r0,#0x00000000
;	STR		r0,[r1]

;  T0IR  = 0x000000ff;                           //reset all flags before enable IRQs

LDR		r1,=T0IR
MOV 	r0,#0x000000ff
STR		r0,[r1]

;  T0MR0 = delayInMs *                           //calculate no of timer ticks
;         ((CRYSTAL_FREQUENCY * PLL_FACTOR) / (1000 * VPBDIV_FACTOR));
LDR		r1,=T0_MR0_ADDR			; Match register zero
LDR		r0,=step_time_uS
STR		r0,[r1]					; load match count from step_time_uS

;  T0TCR = 0x00000002;                           //disable and reset Timer0
LDR		r1,=T0_TCR_ADDR			; Timer 0 Control Register
MOV		r0,#2					; bit 1 = one
;  T0TCR = 0x00000001;                           //start Timer0
STR		r0,[r1]					; Clear counter & prescaler
MOV		r0,#0
STR		r0,[r1]

MOV		r0,#1					; bit 0 = one
STR		r0,[r1]					; Turn on counter

end_delay			LDMFD	SP!,{PC,r0,r1}			; Return