/*
 * assign21.S
 *
 *  Updated on: Sep 16, 2012
 *      Author: patb
 *
 *  Emulate a vending machine
 *
 */
	.syntax unified
#include "stm32f10x_asm.h"
#define PBIN GPIOB_BASE+IDR
#define PAIN GPIOA_BASE+IDR
#define PAOUT GPIOA_BASE+ODR
#define PBOUT GPIOB_BASE+ODR
#define PCOUT GPIOC_BASE+ODR
	.global _start, state, selection, payment_count, money, total_money, vends
    .type   _start, %function

	.text

	.word 0x20002000       // ideally, these should be in the vector table
	.word _start

_start:
main:
	bl	initHardware
	bl  initVariables
main_loop:
	ldr r4,=state
	mov r5,#0
	strb r5,[r4]		// state = 0
	bl idle				// idle()

	mov r5,#1
	strb r5,[r4]		// state = 1
	bl select			// select()

	mov r5,#2
	strb r5,[r4]		// state = 2
	bl purchase			// purchase()

	mov r5,#3
	strb r5,[r4]		//state = 3
	bl vend				// vend()

	ldr r3,=selection
	mov r0,#0
	strb r0,[r3]       // after each vend, reset the selection and payment and money
	ldr r3,=payment_count
	strb r0,[r3]
	ldr r3,=money
	str r0,[r3]

	b main_loop			// and repeat


/* void initHardware(void) */
initHardware:
	// power up the zones of the chip
	ldr r0, =0x40021018			//RCC
	ldr	r1, =0x00044a3d
	str r1,[r0]

	// turn on the normally not-connected pins
	ldr r0,=0x02008000
	ldr r1,=0x40010004
	str r0,[r1]

	// set the data direction registers
	ldr r1,=0x40010800		// DDRA_LO
	// nothing required
	ldr r1,=0x40010804		// DDRA_HI
	ldr r0,=0x44441444
	str r0,[r1]				// turn beeper pin into an output

	ldr r1,=0x40010C00		// DDRB_HI
	// nothring required
	ldr r1,=0x40010C04		// DDRB_HI
	ldr r0,=0x44444411
	str r0,[r1]            // enable the motor

	ldr r1,=0x40011000		// DDRC_HI
	mov r0,#0x11111111
	str r0,[r1]
	ldr r1,=0x40011004		// DDRC_HI
	str r0,[r1]
	bx lr


/* void initVariables(void) */
initVariables:
	// set up the state variables, all to zero
	mov r0,#0
	ldr r1,=state
	strb r0,[r1]
	ldr r1,=selection
	strb r0, [r1]
	ldr r1,=payment_count
	str r0,[r1]
	ldr r1,=money
	str r0,[r1]
	ldr r1,=total_money
	str r0,[r1]
	ldr r1,=vends
	str r0,[r1]
	bx lr

/* void idle(void) */
idle:
	push {r4,lr}		// we'll use r4 to keep track of time
main_idle_loop:
	mov r4, #0			// keep track of flashing lights
						// r4=0...99, lights on
						// r4=100...199 lights off
	ldr r1,=money
	str r4,[r1]			// money = 0

inner_idle_loop:

	// watch the keyboard, and flash the lights
	ldr r1, =PBIN
	ldr r0, [r1]
	tst r0, #4			// check PBIN-2, ButtonA
	bne si_done
	ldr r1, =PAIN
	ldr r0, [r1]
	tst r0, #2			// check PAIN-1, main keybd
	bne si_done

state_next:
	mov r0, #10			// delayMsec(10)
	bl	delayMsec

	add r4, r4, #1		// increment the light counter
	cmp r4, #99
	blo si_on
	cmp r4, #199
	bhi main_idle_loop

	// if we get here, the lights should go off
	mov r0, #0
	ldr r1,=PCOUT
	str r0, [r1]
	b inner_idle_loop
si_on:
	movw r0,#0xFF00		// all the high bits of PortC on
	ldr r1,=PCOUT
	str r0,[r1]
	b inner_idle_loop
si_done:
	pop {r4, pc}

/* void select(void) */
select:
	// first we wait for the button to be released
	ldr r1, =PBIN
	ldr r0,[r1]
	tst r0,#4
	bne select

	// check the other key as well
	ldr r1, =PAIN
	ldr r0,[r1]
	tst r0,#2
	bne select

	// if we get here, the main keyboard is pressed and released
	ldr r1,=selection
	ldrb r0,[r1]			// get the last selection
	lsl r0, r0, #1
	ands r0, r0, #0x1F		// only keep the bottom 5 bits
	bne ss_selection_ok		// check to see if we've got 0 left
	mov r0,#1				// if so, start at bottom again
ss_selection_ok:
	strb r0,[r1]			// selection = (selection << 1) & 0xFF
	// put the selection on the LEDs
	ldr r2,=0x4001100C		// PCOUT
	lsl r3, r0, #8
	strh r3, [r2]			// light up the LED's

ss_again:
	// waiting for the  keyboard to be pressed
	ldr r1, =0x40010808		// PAIN
	ldr r0,[r1]
	tst r0,#2				// PAIN-1 main keyboard
	bne select

	// also check for ButtonA press
	ldr r1, =0x40010C08		// PBIN
	ldr r0,[r1]
	tst r0,#4				// ButtonA
	beq ss_again

	// Button A is pressed, time to go back and change states
	bx lr					// and return


/* void purchase(void) */
purchase:
	push {r4,r5,r6,r7,lr}
sp_main_loop:
	// put the money number on the 7 segment display
	ldr r2,=payment_count
	ldrb r6,[r2]				// r3=payment_count
	ldr r4,=sevenSegLookup
	ldrb r5,[r4,r6]			// look up the pattern for the 7 segment display
	ldr r7,=0x4001100C		// PCOUT
	strh r5,[r7]			// and light up the lights

	// now we count the wheel rotations
sp_down:
	ldr r1,=PAIN
	ldr r0,[r1]
	tst r0,#1				// bottom bit is the rotary sensor
	beq sp_down

	// make a click sound and spin the motor
	ldr r2,=PAOUT
	ldrh r3,[r2]
	eor r3,r3,#0x0800		// change bit 11
	strh r3,[r2]

	ldr r2,=PBOUT
	ldrh r3,[r2]
	orr r3,r3,#0x0100       // spin the motor
	strh r3,[r2]

	mov r0,#40
	bl delayMsec

    ldr r2,=PBOUT
    ldrh r3,[r2]
    and r3,r3,#0xffffFCFF        // stop spinning the motor
    strh r3,[r2]

    ldr r2,=payment_count
    ldrb r3,[r2]
    add r3,#1       // payment_count = payment_count + 1
    strb r3,[r2]

	ldr r2,=money
	ldr r3,[r2]
	add r3, r3, #10
	str r3,[r2]				// money = money + 10

	// if we've reached 90, then change to next state
	cmp r3,#90
	beq sp_done

	// and wait for the wheel signal to go low again
sp_up:
    ldr r1,=PAIN
	ldr r0,[r1]
	tst r0,#1				// bottom bit is the rotary sensor
	bne sp_up
	b sp_main_loop
sp_done:
	pop {r4,r5,r6,r7,pc}	// and return from subroutine

seven_segment_data:
    .byte   0x3f,0x06,0x5B, 0x4F, 0x66, 0x6d, 0x7d, 0x07, 0x7F, 0x6F

/* void vend(void) */
vend:
	push {r4,lr}		// r4 will count up to 3 for the 3 flashes
	mov r0,#3
	ldr r1,=state
	strb r0,[r1]			// state = 3

	ldr r1,=vends
	ldr r0,[r1]
	add r0,r0,#1
	str r0,[r1]			// vends = vends + 1

	ldr r1,=total_money
	ldr r0,[r1]
	ldr r2,=money
	ldr r3,[r2]
	add r0,r0,r3
	str r0,[r1]			// total_money = total_money + money

	// now we just need to flash the lights
	// for (i=0; i<3; i++)
	mov r4,#3
sv_again:
	ldr r2,=selection
	ldrb r0,[r2]
	lsl r0, r0, #8
	ldr r1,=0x4001100C		// PCOUT
	strh r0,[r1]

	movw r0,#1000
	bl delayMsec

	ldr r1,=0x4001100C
	mov r0,#0
	strb r0,[r1]			// lights off

	movw r0, #1000
	bl delayMsec

	subs r4, r4, #1
	bne sv_again

	pop {r4,pc}		// return from subroutine

/* void delayMsec(int n)
 * delays for n msec
 * n is passed in r0
 * returns void
 * destroys r0, r1
 */
delayMsec:
	ldr r1,=2667	// do this loop enough times to make a msec
s1:	subs r1, r1, #1
	bne s1			// this loop is 3 cycles = .375usec
	subs r0, r0, #1	// are we done?
	bne delayMsec
	bx	lr

// this is the table that changes a small number into the pattern for the 7 segment display
sevenSegLookup:
	.byte		0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77


	.data
state: 			.byte 0
selection: 	.byte 0
payment_count: .byte 0
				.byte 0
money:			.word 0
total_money:	.word 0
vends:			.word 0

