Skip to content

Instantly share code, notes, and snippets.

@nlguillemot
Created June 20, 2012 06:01
Show Gist options
  • Save nlguillemot/2958369 to your computer and use it in GitHub Desktop.
Save nlguillemot/2958369 to your computer and use it in GitHub Desktop.
CSC 230 Assignment 3 - Treadmill
@ CSC230 Spring 2012 -- Treadmill program
@ Author: Nicolas Guillemot
@ Student ID number: V00695164
@ Global constants for physics
@ default value for weight
.equ DFT_WEIGHT, 100 @ lbs
@ minimum value for weight (see WeightMax for maximum)
.equ WEIGHT_MIN, 50 @ lbs
@ default value for target speed
.equ DFT_TARGET_SPEED, 20 @ .1 decimal fixed point. mph
@ minimum value for target speed
.equ TARGET_SPEED_MIN, 0 @ .1 decimal fixed point. mph
@ maximum value for target speed
.equ TARGET_SPEED_MAX, 150 @ .1 decimal fixed point. mph
@ Global constants for graphics
@ time interval in milliseconds at which screen is redrawn
.equ REDRAW_TIME, 100 @ ms
@ time interval in milliseconds at which lyrics are scrolled by one character
.equ LYRICS_TIME, 200 @ ms
@ row where lyrics are displayed
.equ LYRICS_ROW, 1
@ Global constants for travel information
@ time interval in milliseconds at which speed is updated in running state
.equ SPEED_UPDATE_INTERVAL, 100 @ ms
@ time for delay between flicker of LEDs during running states
.equ LED_FLICKER_INTERVAL, 100 @ ms
@ .1f mphs increased per speed update interval in running state
.equ RUNNING_SPEED_INCR, 1 @ dmph
@ 1.f mphs increased per speed update interval in shutdown state
.equ SHUTDOWN_SPEED_INCR, 5 @ dmph
@ 1.f mphs increased per speed update interval in pausing state
.equ PAUSING_SPEED_INCR, 1 @ dmph
@ Global constants for button indices. Note indices, not bitmasks.
@ black buttons
.equ PAUSE_KEY, 0x1
.equ EMERGENCY_STOP_KEY, 0x0
@ blue buttons
.equ START_KEY, 0x0
.equ RESET_KEY, 0x1
.equ SPEED_DOWN_2_KEY, 0x4
.equ SPEED_DOWN_1_KEY, 0x5
.equ SPEED_UP_1_KEY, 0x6
.equ SPEED_UP_2_KEY, 0x7
.equ WEIGHT_DOWN_2_KEY, 0x8
.equ WEIGHT_DOWN_1_KEY, 0x9
.equ WEIGHT_UP_1_KEY, 0xA
.equ WEIGHT_UP_2_KEY, 0xB
.equ SWITCH_OFF_KEY, 0xF
@ Global constants for SSD states
.equ SSD_OFF, 0x00
.equ SSD_PAUSING, 0xC7
.equ SSD_PAUSED, 0xD7
.equ SSD_RUNNING, 0xC3
.equ SSD_STOPPED, 0x8A
.equ SSD_SHUTDOWN, 0xAB
@ Global constants for LED states bit patterns
.equ LED_NONE, 0x0
.equ LED_RIGHT, 0x1
.equ LED_LEFT, 0x2
.equ LED_BOTH, 0x3
@ Global constants for screen dimensions
.equ SCREEN_WIDTH, 40
.equ SCREEN_HEIGHT, 15
@ Global constants for HUD stuff positioning
@ row at which informations begins being displayed
.equ INFORMATION_BEGIN, 2
@ row at which button information begins being displayed
.equ BUTTON_INFO_BEGIN, 9
@ maximum width of an info string such as "Weight: 100"
.equ INFO_WIDTH, 22
@ size of the indentation after a number like weight and the units
.equ INFO_INDENT_WIDTH, 2
@ Software interrupts for embest board
@ ends the execution of the program
.equ SWI_Exit, 0x11
@ stores current ticks in r0 in milliseconds
.equ SWI_GetTicks, 0x6d
@ stores bitset of pressed blue buttons in r0
.equ SWI_CheckBlue, 0x203
@ stores the bitset of pressed black buttons in r0
.equ SWI_CheckBlack, 0x202
@ sets state of SSD to value in r0
.equ SWI_SetSEG8, 0x200
@ sets state of LEDs to value in r0
.equ SWI_SetLED, 0x201
@ clears the LCD screen
.equ SWI_BlankLCD, 0x206
@ prints string in r2 to coordinate (r0, r1)
.equ SWI_PrintString, 0x204
@ prints string in r1 to file handle in r0
.equ SWI_PutString, 0x69
@ prints character in r0 to stdout
.equ SWI_PutCharacter, 0x0
@ stdout
.equ Stdout, 0x1
.section .rodata
@ lookup table to convert integers offsets to digit characters
DigitToCharacterLookup:
.ascii "0123456789"
@ strings used to display the HUD
TreadmillHeaderString:
.asciz "Treadmill--Nicolas Guillemot,V00695164"
ButtonInfoDisplayString1:
.asciz "Buttons: Pause, STOP!"
ButtonInfoDisplayString2:
.asciz "Keys: Start, Reset, --, --"
ButtonInfoDisplayString3:
.asciz " Sp -2, Sp -1, Sp +1, Sp +2"
ButtonInfoDisplayString4:
.asciz " Wt -2, Wt -1, Wt +1, Wt +2"
ButtonInfoDisplayString5:
.asciz " --, --, --, Off"
.align
ButtonInfoDisplayStrings:
.word ButtonInfoDisplayString1, ButtonInfoDisplayString2
.word ButtonInfoDisplayString3, ButtonInfoDisplayString4
.word ButtonInfoDisplayString5, 0
WeightString:
.asciz "Weight:"
LbsString:
.asciz "lbs"
TargetSpeedString:
.asciz "Target speed:"
ActualSpeedString:
.asciz "Actual speed:"
MphString:
.asciz "mph"
TimeString:
.asciz "Time:"
SecondsString:
.asciz "seconds"
DistanceString:
.asciz "Distance:"
MilesString:
.asciz "miles"
EnergyString:
.asciz "Energy:"
CaloriesString:
.asciz "Calories"
GoodbyeString:
.asciz "*** Treadmill program shutting down! ***"
ArrowString:
@ arrow pointing down used in some debug printing stuff
.asciz "|\nv\n"
.align
CurrentLyricsBufferLen:
.word SCREEN_WIDTH
eyeofthetiger0:
.asciz "Risin' up, back on the street"
eyeofthetiger1:
.asciz "Did my time, took my chances"
eyeofthetiger2:
.asciz "Went the distance"
eyeofthetiger3:
.asciz "Now I'm back on my feet"
eyeofthetiger4:
.asciz "Just a man and his will to survive"
eyeofthetiger5:
.asciz "So many times, it happens too fast"
eyeofthetiger6:
.asciz "You trade your passion for glory"
eyeofthetiger7:
.asciz "Don't lose your grip on the dreams of the past"
eyeofthetiger8:
.asciz "You must fight just to keep them alive"
eyeofthetiger9:
.asciz "It's the eye of the tiger"
eyeofthetiger10:
.asciz "It's the thrill of the fight"
eyeofthetiger11:
.asciz "Risin' up to the challenge"
eyeofthetiger12:
.asciz "Of our rival"
eyeofthetiger13:
.asciz "And the last known survivor"
eyeofthetiger14:
.asciz "Stalks his prey in the night"
eyeofthetiger15:
.asciz "And he's watching us all with the"
eyeofthetiger16:
.asciz "Eye of the tiger"
eyeofthetiger17:
.asciz "Face to face, out in the heat"
eyeofthetiger18:
.asciz "Hangin' tough, stayin' hungry"
eyeofthetiger19:
.asciz "They stack the odds"
eyeofthetiger20:
.asciz "Still we take to the street"
eyeofthetiger21:
.asciz "For the kill with the skill to survive"
eyeofthetiger22:
.asciz "It's the eye of the tiger"
eyeofthetiger23:
.asciz "It's the thrill of the fight"
eyeofthetiger24:
.asciz "Risin' up to the challenge"
eyeofthetiger25:
.asciz "Of our rival"
eyeofthetiger26:
.asciz "And the last known survivor"
eyeofthetiger27:
.asciz "Stalks his prey in the night"
eyeofthetiger28:
.asciz "And he's watching us all with the"
eyeofthetiger29:
.asciz "Eye of the tiger"
eyeofthetiger30:
.asciz "Risin' up straight to the top"
eyeofthetiger31:
.asciz "Had the guts, got the glory"
eyeofthetiger32:
.asciz "Went the distance"
eyeofthetiger33:
.asciz "Now I'm not gonna stop"
eyeofthetiger34:
.asciz "Just a man and his will to survive"
eyeofthetiger35:
.asciz "It's the eye of the tiger"
eyeofthetiger36:
.asciz "It's the thrill of the fight"
eyeofthetiger37:
.asciz "Risin' up to the challenge"
eyeofthetiger38:
.asciz "Of our rival"
eyeofthetiger39:
.asciz "And the last known survivor"
eyeofthetiger40:
.asciz "Stalks his prey in the night"
eyeofthetiger41:
.asciz "And he's watching us all with the"
eyeofthetiger42:
.asciz "Eye of the tiger"
eyeofthetiger43:
.asciz "The eye of the tiger"
eyeofthetiger44:
.asciz "The eye of the tiger"
eyeofthetiger45:
.asciz "The eye of the tiger"
eyeofthetiger46:
.asciz "The eye of the tiger"
.align
eyeofthetigerLyrics:
.word eyeofthetiger0
.word eyeofthetiger1
.word eyeofthetiger2
.word eyeofthetiger3
.word eyeofthetiger4
.word eyeofthetiger5
.word eyeofthetiger6
.word eyeofthetiger7
.word eyeofthetiger8
.word eyeofthetiger9
.word eyeofthetiger10
.word eyeofthetiger11
.word eyeofthetiger12
.word eyeofthetiger13
.word eyeofthetiger14
.word eyeofthetiger15
.word eyeofthetiger16
.word eyeofthetiger17
.word eyeofthetiger18
.word eyeofthetiger19
.word eyeofthetiger20
.word eyeofthetiger21
.word eyeofthetiger22
.word eyeofthetiger23
.word eyeofthetiger24
.word eyeofthetiger25
.word eyeofthetiger26
.word eyeofthetiger27
.word eyeofthetiger28
.word eyeofthetiger29
.word eyeofthetiger30
.word eyeofthetiger31
.word eyeofthetiger32
.word eyeofthetiger33
.word eyeofthetiger34
.word eyeofthetiger35
.word eyeofthetiger36
.word eyeofthetiger37
.word eyeofthetiger38
.word eyeofthetiger39
.word eyeofthetiger40
.word eyeofthetiger41
.word eyeofthetiger42
.word eyeofthetiger43
.word eyeofthetiger44
.word eyeofthetiger45
.word eyeofthetiger46
.word 0
@ maximum value in lbs for weight setting
WeightMax:
.word 350
@ maximum value of timer in milliseconds. Used to handle wraparound.
TimerMax:
.word 32767
@ A state is defined as a list of function pointers in the following order.
@ Note that unimplemented functions can be substituted with a no-op function.
@ ExampleState:
@ flow callbacks:
@ OnEnter, OnUpdate, OnRedraw, OnLeave
@ black button callbacks:
@ OnPause, OnEmergencyStop
@ blue button callbacks:
@ OnStart, OnReset
@ OnSpeedDown2, OnSpeedDown1, OnSpeedUp1, OnSpeedUp2
@ OnWeightDown2, OnWeightDown1, OnWeightUp1, OnWeightUp2
@ OnOff
StateRTTIOffset:
@ byte offset of RTTI string for state
.word 68
.align
@ Placeholder state
NoOpState:
@ flow callbacks:
.word no_op, no_op, no_op, no_op
@ black button callbacks:
.word no_op, no_op
@ blue button callbacks:
.word no_op, no_op
.word no_op, no_op, no_op, no_op
.word no_op, no_op, no_op, no_op
.word no_op
@ RTTI
.asciz "NoOpState"
.align
@ Final things which are done before the machine is turned off
ExitingState:
@ flow callbacks:
.word exiting_on_enter, exiting_on_update, draw_exiting_hud, no_op
@ black button callbacks:
.word no_op, no_op
@ blue button callbacks:
.word no_op, no_op
.word no_op, no_op, no_op, no_op
.word no_op, no_op, no_op, no_op
.word no_op
@ RTTI
.asciz "ExitingState"
.align
@ duration in milliseconds of exiting state
ExitingDuration:
.word 3000
.align
@ Initial state of the machine
StoppedState:
@ flow callbacks:
.word stopped_on_enter, no_op, draw_hud, no_op
@ black button callbacks:
.word no_op, no_op
@ blue button callbacks:
.word stopped_on_start, reset_stats
.word lower_speed_2, lower_speed_1, increase_speed_1, increase_speed_2
.word weight_down_2, weight_down_1, weight_up_1, weight_up_2
.word stopped_on_off
@ RTTI
.asciz "StoppedState"
.align
@ State when user is running on treadmill at normal operation
RunningState:
@ flow callbacks:
.word running_on_enter, running_on_update, draw_hud, no_op
@ black button callbacks:
.word running_on_pause, running_on_emergency_stop
@ blue button callbacks
.word no_op, no_op
.word lower_speed_2, lower_speed_1, increase_speed_1, increase_speed_2
.word no_op, no_op, no_op, no_op
.word no_op
@ RTTI
.asciz "RunningState"
.align
@ State when the treadmill is quickly stopping when responding to emergency stop
ShutdownState:
@ flow callbacks:
.word shutdown_on_enter, shutdown_on_update, draw_hud, no_op
@ black button callbacks:
.word no_op, no_op
@ blue button callbacks:
.word no_op, no_op
.word no_op, no_op, no_op, no_op
.word no_op, no_op, no_op, no_op
.word no_op
@ RTTI
.asciz "ShutdownState"
.align
@ Intermediary state during which the state is slowing down for a pause
PausingState:
@ flow callbacks:
.word pausing_on_enter, pausing_on_update, draw_hud, no_op
@ black button callbacks:
.word no_op, pausing_on_emergency_stop
@ blue button callbacks:
.word pausing_on_start, no_op
.word no_op, no_op, no_op, no_op
.word no_op, no_op, no_op, no_op
.word no_op
@ RTTI
.asciz "PausingState"
.align
@ State reached from the PausingState where the machine is temporarily not traveling
PausedState:
@ flow callbacks:
.word paused_on_enter, no_op, draw_hud, no_op
@ black button callbacks:
.word no_op, no_op
@ blue button callbacks:
.word paused_on_start, paused_on_reset
.word no_op, no_op, no_op, no_op
.word no_op, no_op, no_op, no_op
.word paused_on_off
@ RTTI
.asciz "PausedState"
.align
@ Order in which black buttons are polled ending with -1
BlackButtonUpdateList:
.word PAUSE_KEY, EMERGENCY_STOP_KEY, -1
@ Order in which blue buttons are polled ending with -1
BlueButtonUpdateList:
.word START_KEY, RESET_KEY, SPEED_DOWN_2_KEY
.word SPEED_DOWN_1_KEY, SPEED_UP_1_KEY, SPEED_UP_2_KEY
.word WEIGHT_DOWN_2_KEY, WEIGHT_DOWN_1_KEY, WEIGHT_UP_1_KEY
.word WEIGHT_UP_2_KEY, SWITCH_OFF_KEY, -1
.text
@ Purpose:
@ Asks hardware for the value of the current ticks.
@ Returns:
@ Current ticks in r0 masked to 15 bits.
@ Notes:
@ Ticks are counted in milliseconds.
@ Ticks are stored as a 15 bit value, so their maximum value is ~32 seconds.
@ The ticks are masked to 15 bits to ensure same behaviour on ARMSim#, which uses a 32bit timer.
@ Assumptions allowed:
@ The value of no other register than r0 and r3 are changed.
get_ticks:
@ function prologue
stmfd sp!, {lr}
swi SWI_GetTicks
mov r3, r0
ldr r0, =TimerMax
ldr r0, [r0]
and r0, r3, r0
@ functino epilogue
ldmfd sp!, {lr}
bx lr
@ Purpose:
@ Asks hardware for blue button bitset lookup table,
@ which is used to determine if buttons are pressed.
@ Returns:
@ Blue button bitset in r0.
@ Assumptions allowed:
@ The value of no other register than r0 is changed.
get_blue_buttons_state:
swi SWI_CheckBlue
bx lr
@ Purpose:
@ Asks hardware for black button bitset lookup table,
@ which is used to determine if buttons are pressed.
@ Returns:
@ Black button bitset in r0.
@ Assumptions allowed:
@ The value of no other register than r0 is changed.
get_black_buttons_state:
swi SWI_CheckBlack
bx lr
@ Purpose:
@ Check if a button is being pressed.
@ Parameters:
@ r0 : index of button to check.
@ r1 : contains bitset of button states.
@ Preconditions:
@ r0 is an integer in the range of valid buttons.
@ Returns:
@ Sets r0 to zero if not pressed, non-zero if pressed.
is_button_pressed:
mov r3, #1
and r0, r1, r3, LSL r0
bx lr
@ Purpose:
@ Changing the appearance of the SSD (Seven Segment Display)
@ Parameters:
@ r0 : Pattern to switch to
set_ssd_pattern:
swi SWI_SetSEG8
bx lr
@ Purpose:
@ Changing the state of the LEDs.
@ This also resets the timer for the flickering of LEDs during traveling states.
@ Parameters:
@ r0 : the bitset representing the new state
set_led_pattern:
ldr r3, =LEDState
str r0, [r3]
swi SWI_SetLED
@ reset flicker accumulator as described by specification
ldr r3, =LEDFlickerUpdateAccumulator
mov r0, #0
str r0, [r3]
bx lr
@ Purpose:
@ Calculating the length of a string
@ Parameters:
@ r0 : pointer to null terminated ascii string
@ Returns:
@ string length in r0 without counting trailing '\0'
string_length:
mov r3, #0
string_length_loop:
@ get current character
ldrb r2, [r0, r3]
@ if is null terminator, found end of string.
cmp r2, #0
beq string_length_end
add r3, r3, #1
bal string_length_loop
string_length_end:
mov r0, r3
bx lr
@ Purpose:
@ Copying one string null terminated string into another string which is not necessarily null terminated.
@ Parameters:
@ r0 : pointer to target string
@ r1 : pointer to source string
string_copy:
string_copy_loop:
ldrb r3, [r1], #1
strb r3, [r0], #1
@ stop reading at null terminator
cmp r3, #0
beq string_copy_end
bal string_copy_loop
string_copy_end:
bx lr
@ Purpose:
@ Copying one string null terminated string into another string which is not necessarily null terminated up to n characters
@ Parameters:
@ r0 : pointer to destination string
@ r1 : pointer to source string
@ r2 : number of characters to copy
string_n_copy:
string_n_copy_loop:
cmp r2, #0
ble string_n_copy_end
ldrb r3, [r1], #1
strb r3, [r0], #1
@ stop reading at null terminator or n reached
cmp r3, #0
beq string_n_copy_end
sub r2, r2, #1
bal string_n_copy_loop
string_n_copy_end:
bx lr
@ Purpose:
@ Copying an array of bytes to another.
@ Parameters:
@ r0 : pointer to destination buffer
@ r1 : pointer to source buffer
@ r2 : number of bytes to read from source
memory_copy:
sub r2, r2, #1
memory_copy_loop:
cmp r2, #0
blt memory_copy_end
ldrb r3, [r1, r2]
strb r3, [r0, r2]
sub r2, r2, #1
bal memory_copy_loop
memory_copy_end:
bx lr
@ Purpose:
@ Writing a byte consecutively in memory
@ Parameters:
@ r0 : pointer to start of memory
@ r1 : size of memory to write to in bytes
@ r2 : byte to write
memory_set:
sub r1, r1, #1
memory_set_loop:
cmp r1, #0
blt memory_set_end
strb r2, [r0, r1]
sub r1, r1, #1
bal memory_set_loop
memory_set_end:
bx lr
@ Code taken from http://www.virag.si/2010/02/simple-division-algorithm-for-arm-assembler/
@ Accessed March 12 2012
@ Authored by Jernej Virag, and slightly modified by me (Nicolas Guillemot).
@ Purpose:
@ Providing a way to divide integers due to lack of instruction for it in target ARM platform.
@ The implementation is naive. Not to be used for performance sensitive code.
@ Parameters:
@ r1 : divided
@ r2 : divisor
@ Returns:
@ If divisor is 0, return value is not valid.
@ Otherwise, r0 will contain the result of the division
divide_integer:
@ function prologue
stmfd sp!, {r4,r5,lr}
@ local variables:
@ r4: keep track of divided
@ r5: keep track of divisor
mov r4, r1
mov r5, r2
cmp r2, #0
beq divide_integer_end
@ check for divide by zero!
mov r0, #0 @ clear r0 to accumulate result
mov r3, #1 @ set bit 0 in r3, which will be
@ shifted left then right
divide_integer_start:
cmp r2, r1
movls r2, r2, LSL #1
movls r3, r3, LSL #1
bls divide_integer_start
@ shift r2 left until it is about to
@ be bigger than r1
@ shift r3 left in parallel in order
@ to flag how far we have to go
divide_integer_next:
cmp r1, r2 @ carry set if r1>r2 (don't ask why)
subcs r1, r1, r2 @ subtract r2 from r1 if this would
@ give a positive answer
addcs r0, r0, r3 @ and add the current bit in r3 to
@ the accumulating answer in r0
movs r3, r3, LSR #1 @ Shift r3 right into carry flag
movcc r2, r2, LSR #1 @ and if bit 0 of r3 was zero, also
@ shift r2 right
bcc divide_integer_next @ If carry not clear, r3 has shifted
@ back to where it started, and we
@ can end
divide_integer_end:
@ function epilogue
ldmfd sp!, {r4,r5,lr}
bx lr
@ Purpose:
@ Finding the result of an integer modulo operation.
@ Parameters:
@ r1 : divided
@ r2 : divisor
@ Returns:
@ result of divided % divisor in r0.
@ If divisor is 0, return value is not valid.
mod_integer:
@ function prologue
stmfd sp!, {r4,r5,lr}
@ local variables:
@ r4: divided passed in
@ r5: divisor passed in
mov r4, r1
mov r5, r2
@ divided % divisor = divided - (divided/divisor) * divisor
mov r1, r4
mov r2, r5
bl divide_integer
mov r3, r0
mul r0, r3, r5
sub r0, r4, r0
@ function epilogue
ldmfd sp!, {r4,r5,lr}
bx lr
@ Purpose:
@ Write an integer to a string with right alignment.
@ Parameters:
@ r0 : integer to convert
@ r1 : radix
@ r2 : string buffer to write to
@ r3 : size of buffer
@ top of stack: index of decimal point for fixed point OR -1 for no decimal point
@ Returns:
@ writes NON-NULL TERMINATED string to END OF buffer in r2.
@ note that if you pass in the full length of the actual buffer as r3, your string will not be null terminated!!!
@ Preconditions:
@ size of buffer passed in big enough to write the number in base radix in it.
int_to_string:
@ function prologue
stmfd sp!, {r4,r5,r6,fp,lr}
mov fp, sp
@ Allocate stack and store input arguments
sub sp, sp, #20
str r0, [fp, #-4] @ integer to convert
str r1, [fp, #-8] @ radix
str r2, [fp, #-12] @ buffer
str r3, [fp, #-16] @ size of buffer
ldr r3, [fp, #20] @ decimal position
str r3, [fp, #-20]
@ local variables
@ r4: int i;
@ r5: int m;
@ r6: int x;
@ int i = 0;
mov r4, #0
@ int x = integer_to_convert;
ldr r6, [fp, #-4]
@ do {
int_to_string_loop:
@ if (i == decimal_point_position)
ldr r3, [fp, #-20]
cmp r4, r3
bne int_to_string_handled_point
@ {
@ buf[buflen - i - 1] = '.';
ldr r3, [fp, #-16]
sub r3, r3, r4
sub r3, r3, #1
ldr r2, [fp, #-12]
mov r1, #46
strb r1, [r2, r3]
@ i++;
add r4, r4, #1
@ continue;
bal int_to_string_loop
@ }
int_to_string_handled_point:
@ m = x % r;
mov r1, r6
ldr r2, [fp, #-8] @ get radix
bl mod_integer
mov r5, r0 @ store back result
@ x = x / r;
mov r1, r6
ldr r2, [fp, #-8] @ get radix
bl divide_integer
mov r6, r0 @ store back result
@ buf[buflen - i - 1] = digits[m]
ldr r3, [fp, #-12] @ get buffer
ldr r2, [fp, #-16] @ get buflen
sub r2, r2, r4
sub r2, r2, #1
ldr r1, =DigitToCharacterLookup
ldrb r1, [r1, r5]
strb r1, [r3, r2]
add r4, r4, #1
@ } while (x != 0 || decimal_point_position >= i - 1);
cmp r6, #0
bne int_to_string_loop
ldr r3, [fp, #-20]
sub r2, r4, #1
cmp r3, r2
bge int_to_string_loop
@ function epilogue
mov sp, fp
ldmfd sp!, {r4,r5,r6,fp,lr}
bx lr
@ Purpose:
@ Reprints a string such as "Weight: 100 lbs"
@ Parameters:
@ r0 : Pointer to null terminated string for information name ("Weight:")
@ r1 : Number to print for the information (100)
@ r2 : Pointer to null terminated string for name of units
@ r3 : Position of decimal point or -1 if none
@ top of stack : Buffer where the string will be reprinted
@ Preconditions:
@ target buffer has size at least SCREEN_WIDTH
print_information_string:
@ function prologue
stmfd sp!, {r4-r8,fp,lr}
mov fp, sp
@ extra space on the stack is allocated for the passed in value to int_to_string
sub sp, sp, #4
@ local variables:
@ r4 : pointer to information name string
@ r5 : number to print
@ r6 : pointer to units string
@ r7 : position of decimal point
@ r8 : pointer to target buffer
mov r4, r0
mov r5, r1
mov r6, r2
mov r7, r3
ldr r8, [fp, #28]
@ clear target string with spacebar character
mov r0, r8
mov r1, #SCREEN_WIDTH
mov r2, #32
bl memory_set
@ write information name to target buffer
mov r0, r4
bl string_length
mov r2, r0
mov r0, r8
mov r1, r4
bl memory_copy
@ write number in number space
mov r0, r5
mov r1, #10
mov r2, r8
mov r3, #INFO_WIDTH
mov ip, r7
str ip, [fp, #-4]
bl int_to_string
@ write units after indent with null terminator
mov r3, #INFO_WIDTH
add r3, r3, #INFO_INDENT_WIDTH
add r0, r8, r3
mov r1, r6
bl string_copy
@ function epilogue
mov sp, fp
ldmfd sp!, {r4-r8,fp,lr}
bx lr
@ Purpose:
@ Initialize state machine with default state
initialize_state_machine:
@ function prologue
stmfd sp!, {lr}
@ Clear the buffer for the lyrics
ldr r0, =CurrentLyricsBuffer
ldr r1, =CurrentLyricsBufferLen
ldr r1, [r1]
mov r2, #0
bl memory_set
@ reset data and display info
bl reset_stats
@ set default state of state machine
ldr r3, =StoppedState
ldr r2, =CurrentState
str r3, [r2]
@ call OnEnter function of state
ldr r2, [r3]
mov lr, pc
bx r2
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Purpose:
@ Deinitialize the state machine to cleanly exit
deinitialize_state_machine:
@ function prologue
stmfd sp!, {lr}
@ call OnLeave function of state
ldr r3, =CurrentState
ldr r3, [r3]
ldr r3, [r3, #12]
mov lr, pc
bx r3
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Purpose:
@ Being able to know the name of a state at runtime using RTTI (RunTime Type Information)
@ Parameters:
@ r0 : pointer to state
print_state_typeinfo:
@ function prologue
stmfd sp!, {lr}
ldr r3, =StateRTTIOffset
ldr r3, [r3]
@ get string pointer
add r1, r0, r3
@ print to stdout
mov r0, #Stdout
swi SWI_PutString
mov r0, #10
swi SWI_PutCharacter
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Purpose:
@ Switch the state of the state machine
@ Parameters:
@ r0 : Pointer to the new state
@ Preconditions:
@ State machine is initialized
switch_state:
@ function prologue
stmfd sp!, {r4,lr}
@ local variables:
@ r4: pointer to new state
mov r4, r0
@ print old state
ldr r0, =CurrentState
ldr r0, [r0]
bl print_state_typeinfo
@ call OnLeave on old state
ldr r3, =CurrentState
ldr r3, [r3]
ldr r3, [r3, #12]
mov lr, pc
bx r3
@ change CurrentState to new state
ldr r3, =CurrentState
str r4, [r3]
@ print arrow
mov r0, #Stdout
ldr r1, =ArrowString
swi SWI_PutString
@ print new state
ldr r0, =CurrentState
ldr r0, [r0]
bl print_state_typeinfo
@ call OnEnter on new state
ldr r3, =CurrentState
ldr r3, [r3]
ldr r3, [r3]
mov lr, pc
bx r3
@ function epilogue
ldmfd sp!, {r4,lr}
bx lr
@ Purpose:
@ Changing the weight of the user while taking into account maximum and minimum weights.
@ Parameters:
@ r0 : the target weight
set_weight:
@ function prologue
stmfd sp!, {fp,lr}
mov fp, sp
sub sp, sp, #4
@ clamp value between WEIGHT_MIN and WeightMax
cmp r0, #WEIGHT_MIN
movlt r0, #WEIGHT_MIN
ldr r3, =WeightMax
ldr r3, [r3]
cmp r0, r3
movgt r0, r3
@ store in memory
ldr r3, =Weight
str r0, [r3]
@ reprint string
ldr r0, =WeightString
ldr r1, =Weight
ldr r1, [r1]
ldr r2, =LbsString
mov r3, #-1
ldr ip, =WeightLineBuffer
str ip, [fp, #-4]
bl print_information_string
@ function epilogue
mov sp, fp
ldmfd sp!, {fp,lr}
bx lr
@ Purpose:
@ Shortcut for incrementing or decrementing weight
@ Parameters:
@ r0 : difference in weight
incr_weight:
@ function prologue
stmfd sp!, {lr}
ldr r3, =Weight
ldr r2, [r3]
add r2, r2, r0
mov r0, r2
bl set_weight
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Purpose:
@ Changing the target speed while taking into account max/min speeds.
@ Parameters:
@ r0 : the target speed
set_target_speed:
@ function prologue
stmfd sp!, {fp,lr}
mov fp, sp
sub sp, sp, #4
@ clamp value between TARGET_SPEED_MIN and TARGET_SPEED_MAX
cmp r0, #TARGET_SPEED_MIN
movlt r0, #TARGET_SPEED_MIN
cmp r0, #TARGET_SPEED_MAX
movgt r0, #TARGET_SPEED_MAX
@ store in memory
ldr r3, =TargetSpeed
str r0, [r3]
@ reprint string
ldr r0, =TargetSpeedString
ldr r1, =TargetSpeed
ldr r1, [r1]
ldr r2, =MphString
mov r3, #1
ldr ip, =TargetSpeedLineBuffer
str ip, [fp, #-4]
bl print_information_string
@ function epilogue
mov sp, fp
ldmfd sp!, {fp,lr}
bx lr
@ Purpose:
@ Shortcut for incrementing or decrementing target speed
@ Parameters:
@ r0 : difference in target speed
incr_target_speed:
@ function prologue
stmfd sp!, {lr}
ldr r3, =TargetSpeed
ldr r2, [r3]
add r2, r2, r0
mov r0, r2
bl set_target_speed
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Purpose:
@ Changing the value of the actual speed
@ Parameters:
@ r0 : the new actual speed
set_actual_speed:
@ function prologue
stmfd sp!, {fp,lr}
mov fp, sp
sub sp, sp, #4
@ clamp value between TARGET_SPEED_MIN and TARGET_SPEED_MAX
cmp r0, #TARGET_SPEED_MIN
movlt r0, #TARGET_SPEED_MIN
cmp r0, #TARGET_SPEED_MAX
movgt r0, #TARGET_SPEED_MAX
@ store value
ldr r3, =ActualSpeed
str r0, [r3]
@ reprint string
ldr r0, =ActualSpeedString
ldr r1, =ActualSpeed
ldr r1, [r1]
ldr r2, =MphString
mov r3, #1
ldr ip, =ActualSpeedLineBuffer
str ip, [fp, #-4]
bl print_information_string
@ function epilogue
mov sp, fp
ldmfd sp!, {fp,lr}
bx lr
@ Purpose:
@ Changing the value of the time
@ Parameters:
@ r0 : the new time in .1f fixed point
set_time:
@ function prologue
stmfd sp!, {fp,lr}
mov fp, sp
sub sp, sp, #4
@ store value
ldr r3, =Time
str r0, [r3]
@ reprint string
ldr r0, =TimeString
ldr r1, =Time
ldr r1, [r1]
ldr r2, =SecondsString
mov r3, #1
ldr ip, =TimeLineBuffer
str ip, [fp, #-4]
bl print_information_string
@ function epilogue
mov sp, fp
ldmfd sp!, {fp,lr}
bx lr
@ Purpose:
@ Changing the value of the distance
@ Parameters:
@ r0 : the new distance
set_distance:
@ function prologue
stmfd sp!, {fp,lr}
mov fp, sp
sub sp, sp, #4
@ store value
ldr r3, =Distance
str r0, [r3]
@ reprint string
ldr r0, =DistanceString
ldr r1, =Distance
ldr r1, [r1]
ldr r2, =MilesString
mov r3, #6
ldr ip, =DistanceLineBuffer
str ip, [fp, #-4]
bl print_information_string
@ function epilogue
mov sp, fp
ldmfd sp!, {fp,lr}
bx lr
@ Purpose:
@ Changing the value of the energy
@ Parameters:
@ r0 : the new energy
set_energy:
@ function prologue
stmfd sp!, {fp,lr}
mov fp, sp
sub sp, sp, #4
@ store value
ldr r3, =Energy
str r0, [r3]
@ reprint string
ldr r0, =EnergyString
ldr r1, =Energy
ldr r1, [r1]
ldr r2, =CaloriesString
mov r3, #6
ldr ip, =EnergyLineBuffer
str ip, [fp, #-4]
bl print_information_string
@ function epilogue
mov sp, fp
ldmfd sp!, {fp,lr}
bx lr
@ Purpose:
@ to simplify explanation, this whole description assumes milliunits.
@ but if you pass in something else than 1000, you can get another conversion
@ such as 100 converts centiunits to units.
@ remember it's all relative...
@ Takes a value in memory where the units are milliunits
@ takes out as many full units as it can from them
@ and stores them in the other location in memory
@ where the units are just units.
@ This was made to simplify the update_travel function...
@ function is called to simplify set_time stuff
@ Parameters:
@ r0 : pointer to word measured in milliunits
@ r1 : pointer to word measured in units
@ r2 : pointer to function to set units
@ if r2 is 0, units will be set with a simple str
@ r3 : factor to multiply for unit conversion (eg 1000)
@ Returns:
@ r0 : 1 if units actually changed, 0 if not
convert_unit_base:
@ function prologue
stmfd sp!, {r4-r8,lr}
@ local variables:
@ r4: consistent pointer to source word
@ r5: consistent pointer to target word
@ r6: holds result of division
@ r7: consistent pointer to setting function
@ r8: consistent value of conversion
mov r4, r0
mov r5, r1
mov r7, r2
mov r8, r3
@ convert packs of 1000 milliunits in units
ldr r1, [r4]
mov r2, r8
bl divide_integer
@ if no packs can be taken out, just return
cmp r0, #0
beq convert_unit_base_end
mov r6, r0
@ add removed units to target units
ldr r2, [r5]
add r2, r2, r6
@ Either call function if defined or just simple store
cmp r7, #0
streq r2, [r5]
movne r0, r2
movne lr, pc
bxne r7
@ remove milliunits from source
ldr r1, [r4]
mov r2, r8
mul r0, r2, r6
sub r1, r1, r0
str r1, [r4]
@ prepare return value
mov r0, #1
convert_unit_base_end:
@ function epilogue
ldmfd sp!, {r4-r8,lr}
bx lr
@ Purpose:
@ Encapsulating the common behaviour of travelling on the treadmill.
@ This will update the data for the time, distance, and energy.
@ It is meant to be called in the OnUpdate of states where the treadmill is rolling.
@ Note that this accumulates the data in units more accurate than what is actually displayed, to work with few milliseconds of delta time.
@ Parameters:
@ r0: the delta time
@ Development notes: Solutions to problems for too small units
@ Delta distance for tiny delta time too small to be felt
@ Solution: Accumulate delta distance in nano miles
@ Then store back when representable in micro miles
@ Time on HUD displayed in seconds, not milliseconds
@ Solution: Accumulate in milliseconds
@ Then store back in seconds
@ Energy also increments too small in one dt
@ Solution: Accumulate energy in nano calories
@ Then store back when representable in micro calories
@ note: 0.1 mph = 28 nanomiles / millisecond
@ note: R = 80 * W * V
@ where R is Calories/millisecond
@ W is lbs
@ V is .1 fixed point mph
update_travel:
@ function prologue
stmfd sp!, {r4,lr}
@ local variables:
@ r4: stored passed in delta time
mov r4, r0
@ increment accumulator for time and store back
ldr r3, =TravelTime
ldr r2, [r3]
add r2, r2, r4
str r2, [r3]
@ increment accumulator for distance
@ calculate dp (nanomiles) = dt * V * 28
@ where V is in .1 fixed point mph
mov r3, #28
ldr r2, =ActualSpeed
ldr r2, [r2]
mul r1, r2, r3 @ r1 = V * 28
mul r1, r4, r1 @ r1 = dt * (V * 28)
@ accumulate and store back in TravelDistance
ldr r3, =TravelDistance
ldr r2, [r3]
add r2, r2, r1
str r2, [r3]
@ increment accumulator for energy
@ calculate current rate
ldr r3, =Weight
ldr r3, [r3]
ldr r2, =ActualSpeed
ldr r2, [r2]
mov r1, #80
mul r1, r2, r1 @ r1 = V * 80
mul r1, r3, r1 @ r1 = W * (V*80)
@ r1 now contains rate. now calculate and store back
mul r1, r4, r1 @ calculate delta energy
ldr r3, =TravelEnergy
ldr r2, [r3]
add r2, r2, r1 @ accumulate delta energy
str r2, [r3]
@ now need to drop the accumulated values in the actual memory
@ convert packs of 1000ms in TravelTime to ds in Time
ldr r0, =TravelTime
ldr r1, =Time
ldr r2, =set_time
mov r3, #100
bl convert_unit_base
@ convert packs of 1000 nanomiles to micromiles
ldr r0, =TravelDistance
ldr r1, =Distance
ldr r2, =set_distance
mov r3, #1000
bl convert_unit_base
@ convert packs of 1000 nanocalories to microcalories
ldr r0, =TravelEnergy
ldr r1, =Energy
ldr r2, =set_energy
mov r3, #1000
bl convert_unit_base
@ function epilogue
ldmfd sp!, {r4,lr}
bx lr
@ Purpose:
@ update actual speed to tend toward a certain target speed
@ Parameters:
@ r0 : delta time
@ r1 : value to tend to
@ r2 : increment of time required for an update
@ r3 : increment of speed per update
update_actual_speed:
@ function prologue
stmfd sp!, {r4-r8,lr}
@ local variables:
@ r4: delta time
@ r5: value to tend to
@ r6: increment of time required for an update
@ r7: pointer to accumulator
@ r8: increment of speed per update
mov r4, r0
mov r5, r1
mov r6, r2
ldr r7, =SpeedUpdateAccumulator
mov r8, r3
@ accumulate time and store back
ldr r2, [r7]
add r2, r2, r4
str r2, [r7]
@ check if enough time accumulated
ldr r3, [r7]
cmp r3, r6
blt update_actual_speed_end
@ if so, update speed to get close to actual speed and reset timer
@ reset accumulator
mov r2, #0
str r2, [r7]
@ get target/actual speeds and compare
mov r3, r5
ldr r2, =ActualSpeed
ldr r2, [r2]
cmp r3, r2
@ handle different cases
@ if TargetSpeed == ActualSpeed, don't do anything
beq update_actual_speed_end
blt update_actual_speed_decr
bgt update_actual_speed_incr
@ if TargetSpeed > ActualSpeed, increment ActualSpeed
update_actual_speed_incr:
ldr r3, =ActualSpeed
ldr r2, [r3]
add r2, r2, r8
mov r0, r2
bl set_actual_speed
bal update_actual_speed_end
@ if TargetSpeed < ActualSpeed, decrement ActualSpeed
update_actual_speed_decr:
ldr r3, =ActualSpeed
ldr r2, [r3]
sub r2, r2, r8
mov r0, r2
bl set_actual_speed
update_actual_speed_end:
@ function epilogue
ldmfd sp!, {r4-r8,lr}
bx lr
@ Purpose:
@ Updating the state of LEDs while they are asked to flicker by various states.
@ Parameters:
@ r0 : delta time from update
@ r1 : time accumulated necessary for flicker
@ r2 : flicker mode:
@ mode 0: flicker left and right
@ mode 1: flicker both on both off
update_led_flicker:
@ function prologue
stmfd sp!, {r4-r6,lr}
@ local variables:
@ r4: delta time
@ r5: time accumulated necessary for flicker
@ r6: flicker mode
mov r4, r0
mov r5, r1
mov r6, r2
@ accumulate time and store back
ldr r3, =LEDFlickerUpdateAccumulator
ldr r2, [r3]
add r2, r2, r4
str r2, [r3]
@ If accumulated enough, flicker LED and reset accumulator
ldr r3, =LEDFlickerUpdateAccumulator
ldr r3, [r3]
cmp r3, r5
blt update_led_flicker_dont_flicker_LED
@ this code only read if it's time for the LED to flicker
ldr r3, =LEDFlickerUpdateAccumulator
mov r0, #0
str r0, [r3]
ldr r3, =LEDState
ldr r3, [r3]
@ pick new LED state and set it
cmp r6, #0
beq update_led_flicker_left_right
cmp r6, #1
beq update_led_flicker_both_none
update_led_flicker_left_right:
cmp r3, #LED_NONE
moveq r0, #LED_LEFT
beq update_led_flicker_selected_led_state
cmp r3, #LED_LEFT
moveq r0, #LED_RIGHT
beq update_led_flicker_selected_led_state
cmp r3, #LED_RIGHT
moveq r0, #LED_LEFT
beq update_led_flicker_selected_led_state
update_led_flicker_both_none:
cmp r3, #LED_NONE
moveq r0, #LED_BOTH
beq update_led_flicker_selected_led_state
cmp r3, #LED_LEFT
moveq r0, #LED_NONE
beq update_led_flicker_selected_led_state
cmp r3, #LED_RIGHT
moveq r0, #LED_NONE
beq update_led_flicker_selected_led_state
update_led_flicker_selected_led_state:
bl set_led_pattern
update_led_flicker_dont_flicker_LED:
@ function epilogue
ldmfd sp!, {r4-r6,lr}
bx lr
@ Purpose:
@ Updating the scrolling lyrics of eye of the tiger
@ Parameters:
@ r0 : delta time in ms
update_tiger:
@ function prologue
stmfd sp!, {r4,r5,lr}
@ local variables:
@ r4: temporary
@ r5: temporary
@ update accumulator and store back
ldr r3, =LyricsAccumulator
ldr r2, [r3]
add r2, r2, r0
str r2, [r3]
@ update offset/lyrics if necessary
ldr r3, =LyricsAccumulator
ldr r3, [r3]
cmp r3, #LYRICS_TIME
blt dont_update_tiger
@ reset timer
ldr r3, =LyricsAccumulator
mov r2, #0
str r2, [r3]
@ update offset and store back
ldr r3, =CurrentLyricsOffset
ldr r2, [r3]
add r2, r2, #1
str r2, [r3]
@ if offset out of bounds, increment current line
ldr r3, =CurrentLyrics
ldr r3, [r3]
ldr r3, [r3]
mov r0, r3
bl string_length
add r0, r0, #SCREEN_WIDTH
add r0, r0, #1
ldr r3, =CurrentLyricsOffset
ldr r3, [r3]
cmp r3, r0
blt dont_update_tiger_line
@ increment current line of lyrics and restart if necessary
ldr r3, =CurrentLyricsOffset
mov r2, #0
str r2, [r3]
ldr r3, =CurrentLyrics
ldr r2, [r3]
add r2, r2, #4
str r2, [r3]
ldr r3, =CurrentLyrics
ldr r3, [r3]
ldr r3, [r3]
cmp r3, #0
bne dont_restart_lyrics
ldr r3, =CurrentLyrics
ldr r2, =eyeofthetigerLyrics
str r2, [r3]
dont_restart_lyrics:
dont_update_tiger_line:
@ copy new state of line to buffer
@ clear buffer
ldr r0, =CurrentLyricsBuffer
ldr r1, =CurrentLyricsBufferLen
ldr r1, [r1]
mov r2, #32 @ space ascii
bl memory_set
@ copy new letters over
@ calculate offset in buffer properly
ldr r2, =CurrentLyricsBufferLen
ldr r2, [r2]
ldr r1, =CurrentLyricsOffset
ldr r1, [r1]
sub r2, r2, r1
cmp r2, #0
bge update_tiger_right
blt update_tiger_left
@ string is to the right of the start of the screen
update_tiger_right:
ldr r0, =CurrentLyricsBuffer
add r0, r0, r2
ldr r1, =CurrentLyrics
ldr r1, [r1]
ldr r1, [r1]
ldr r2, =CurrentLyricsOffset
ldr r2, [r2]
mov r5, r2
mov r4, r0
mov r0, r1
bl string_length
mov r2, r5
cmp r0, r2
movlt r2, r0
mov r0, r4
bl memory_copy
bal update_tiger_end_right_left
@ string is to the left of the start of the screen
update_tiger_left:
ldr r2, =CurrentLyricsOffset
ldr r2, [r2]
ldr r1, =CurrentLyrics
ldr r1, [r1]
ldr r1, [r1]
add r1, r1, r2
ldr r0, =CurrentLyricsBufferLen
ldr r0, [r0]
sub r1, r1, r0
mov r0, r1
bl string_length
mov r2, r0
ldr r0, =CurrentLyricsBuffer
bl memory_copy
update_tiger_end_right_left:
@ replace null terminator of copied string with empty space
@ null terminate
ldr r3, =CurrentLyricsBuffer
ldr r2, =CurrentLyricsBufferLen
sub r2, r2, #1
mov r1, #0
strb r1, [r3, r2]
dont_update_tiger:
@ function epilogue
ldmfd sp!, {r4,r5,lr}
bx lr
@ No-op used as a placeholder for unimplemented function pointers
no_op:
bx lr
@ Bring settings back to default settings
reset_stats:
@ function prologue
stmfd sp!, {lr}
@ reset accumulators for travel
mov r3, #0
ldr r2, =TravelDistance
str r3, [r2]
ldr r2, =TravelTime
str r3, [r2]
ldr r2, =TravelEnergy
str r3, [r2]
@ reset Weight
mov r0, #DFT_WEIGHT
bl set_weight
@ reset target speed
mov r0, #DFT_TARGET_SPEED
bl set_target_speed
@ reset actual speed
mov r0, #0
bl set_actual_speed
@ reset time
mov r0, #0
bl set_time
@ reset distance
mov r0, #0
bl set_distance
@ reset energy
mov r0, #0
bl set_energy
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Lower speed by 0.5 mph
lower_speed_2:
@ function prologue
stmfd sp!, {lr}
mov r0, #-5
bl incr_target_speed
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Lower speed by 0.1 mph
lower_speed_1:
@ function prologue
stmfd sp!, {lr}
mov r0, #-1
bl incr_target_speed
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Increase speed by 0.1 mph
increase_speed_1:
@ function prologue
stmfd sp!, {lr}
mov r0, #1
bl incr_target_speed
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Increase speed by 0.5 mph
increase_speed_2:
@ function prologue
stmfd sp!, {lr}
mov r0, #5
bl incr_target_speed
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Lower weight by 10
weight_down_2:
@ function prologue
stmfd sp!, {lr}
mov r0, #-10
bl incr_weight
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Lower weight by 5
weight_down_1:
@ function prologue
stmfd sp!, {lr}
mov r0, #-5
bl incr_weight
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Increase weight by 5
weight_up_1:
@ function prologue
stmfd sp!, {lr}
mov r0, #5
bl incr_weight
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Increase weight by 10
weight_up_2:
@ function prologue
stmfd sp!, {lr}
mov r0, #10
bl incr_weight
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Draw the HUD which displays session info
draw_hud:
@ function prologue
stmfd sp!, {r4-r6,lr}
@ local variables registers:
@ r4: pointer to current string
@ r5: current x
@ r6: current y
@ set header data
ldr r4, =TreadmillHeaderString
mov r5, #0
mov r6, #0
@ print header
mov r0, r5
mov r1, r6
mov r2, r4
swi SWI_PrintString
@ print lyrics
mov r0, #0
mov r1, #LYRICS_ROW
ldr r2, =CurrentLyricsBuffer
swi SWI_PrintString
@ print information strings
ldr r4, =InformationLineBuffers
@ current column to write to (always 0)
mov r0, #0
@ current row to write to
mov r1, #INFORMATION_BEGIN
@ current index of iteration
mov r3, #0
draw_hud_print_information_loop:
@ get current string
ldr r2, [r4, r3, LSL #2]
cmp r2, #0
beq draw_hud_print_information_end
@ print the current row
swi SWI_PrintString
@ increment current row and index
add r3, r3, #1
add r1, r1, #1
bal draw_hud_print_information_loop
draw_hud_print_information_end:
@ print footer
ldr r4, =ButtonInfoDisplayStrings
@ current column to write to (always 0)
mov r0, #0
@ current row to write to
mov r1, #BUTTON_INFO_BEGIN
@ current index of iteration
mov r3, #0
draw_hud_print_footer_loop:
@ get current string
ldr r2, [r4, r3, LSL #2]
cmp r2, #0
beq draw_hud_print_footer_end
@ print the current row
swi SWI_PrintString
@ increment current row and index
add r3, r3, #1
add r1, r1, #1
bal draw_hud_print_footer_loop
draw_hud_print_footer_end:
@ function epilogue
ldmfd sp!, {r4-r6,lr}
bx lr
@ Special case where the HUD draws the goodbye message
draw_exiting_hud:
mov r0, #0
mov r1, #7 @ middle of the screen
ldr r2, =GoodbyeString
swi SWI_PrintString
bx lr
@ Purpose:
@ sets the current state to passed in state if the actual speed is 0
@ may also depend on the target speed
@ Parameters:
@ r0 : 1 if target speed must also be zero to stop. anything else otherwise.
@ r1 : pointer to state to switch to if condition is true
switch_state_if_zero_speed:
@ function prologue
stmfd sp!, {r4,lr}
@ local variables:
@ r4: pointer to state to switch to if condition is true
mov r4, r1
@ if r0 is 1 then target speed must be 0 to stop
cmp r0, #1
bne stop_if_zero_speed_disregard_target
ldr r3, =TargetSpeed
ldr r3, [r3]
cmp r3, #0
bne stop_if_zero_speed_dont_stop
stop_if_zero_speed_disregard_target:
ldr r3, =ActualSpeed
ldr r3, [r3]
cmp r3, #0
bne stop_if_zero_speed_dont_stop
@ passed tests so switch state to selected state
mov r0, r4
bl switch_state
stop_if_zero_speed_dont_stop:
@ function epilogue
ldmfd sp!, {r4,lr}
bx lr
@ Called when the exiting state is entered
exiting_on_enter:
@ function prologue
stmfd sp!, {lr}
@ clear screen
swi SWI_BlankLCD
@ turn off LEDs
mov r0, #LED_NONE
bl set_led_pattern
@ turn off SSD
mov r0, #SSD_OFF
bl set_ssd_pattern
@ reset shutdown timer
ldr r0, =ExitingStateTimer
mov r3, #0
str r3, [r0]
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Called at each update of the exit state.
@ delta time in ms stored in r0
exiting_on_update:
@ accumulate delta time on timer
ldr r3, =ExitingStateTimer
ldr r2, [r3]
add r2, r2, r0
@ store accumulated time back in timer
str r2, [r3]
@ if has accumulated enough time, set program running flag to false.
ldr r1, =ExitingDuration
ldr r1, [r1]
cmp r2, r1
ldrge r1, =IsProgramRunning
movge r0, #0
strge r0, [r1]
bx lr
@ Called when the stopped state is entered
stopped_on_enter:
@ function prologue
stmfd sp!, {lr}
@ set SSD to proper pattern
mov r0, #SSD_STOPPED
bl set_ssd_pattern
@ set LED to proper pattern
mov r0, #LED_NONE
bl set_led_pattern
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Called when the start button is pressed in the stopped state
stopped_on_start:
@ function prologue
stmfd sp!, {lr}
@ See if target speed is non-zero
ldr r3, =TargetSpeed
ldr r3, [r3]
cmp r3, #0
@ target speed non-zero, switch to running state
ldrne r0, =RunningState
blne switch_state
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Called when the off button is pressed in the stopped state
stopped_on_off:
@ function prologue
stmfd sp!, {lr}
@ go to exiting state
ldr r0, =ExitingState
bl switch_state
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Called when the running state is entered
running_on_enter:
@ function prologue
stmfd sp!, {lr}
@ set SSD to proper pattern
mov r0, #SSD_RUNNING
bl set_ssd_pattern
@ set LED to proper pattern
mov r0, #LED_LEFT
bl set_led_pattern
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Called at each update of the running state.
@ delta time in ms stored in r0
running_on_update:
@ function prologue
stmfd sp!, {r4,lr}
@ local variables:
@ r4: passed in delta time
mov r4, r0
@ update travel info from delta time
mov r0, r4
bl update_travel
@ update lyrics info from delta time
mov r0, r4
bl update_tiger
@ update actual speed to match target speed
mov r0, r4
ldr r1, =TargetSpeed
ldr r1, [r1]
mov r2, #SPEED_UPDATE_INTERVAL
mov r3, #RUNNING_SPEED_INCR
bl update_actual_speed
@ update led flickering
mov r0, r4
mov r1, #LED_FLICKER_INTERVAL
mov r2, #0
bl update_led_flicker
@ stop if target speed = actual speed = 0
mov r0, #1
ldr r1, =StoppedState
bl switch_state_if_zero_speed
@ function epilogue
ldmfd sp!, {r4,lr}
bx lr
@ Called when the emergency stop button is pressed in running state
running_on_emergency_stop:
@ function prologue
stmfd sp!, {lr}
ldr r0, =ShutdownState
bl switch_state
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Called when the pause button is pressed in running state
running_on_pause:
@ function prologue
stmfd sp!, {lr}
ldr r0, =PausingState
bl switch_state
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Called when the shutdown state is entered
shutdown_on_enter:
@ function prologue
stmfd sp!, {lr}
@ set SSD to proper pattern
mov r0, #SSD_SHUTDOWN
bl set_ssd_pattern
@ set LED to proper pattern
mov r0, #LED_NONE
bl set_led_pattern
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Called when the shutdown state updates
@ delta time in ms stored in r0
shutdown_on_update:
@ function prologue
stmfd sp!, {r4,lr}
@ local variables:
@ r4: passed in delta time
mov r4, r0
@ update travel info from delta time
mov r0, r4
bl update_travel
@ update lyrics info from delta time
mov r0, r4
bl update_tiger
@ update actual speed to tend toward 0
mov r0, r4
mov r1, #0
mov r2, #SPEED_UPDATE_INTERVAL
mov r3, #SHUTDOWN_SPEED_INCR
bl update_actual_speed
@ update led flickering
mov r0, r4
mov r1, #LED_FLICKER_INTERVAL
mov r2, #1
bl update_led_flicker
@ stop when actual speed reaches 0 regardless of target
mov r0, #0
ldr r1, =StoppedState
mov r2, #1
bl switch_state_if_zero_speed
@ function epilogue
ldmfd sp!, {r4,lr}
bx lr
@ Called when the pausing state is entered
pausing_on_enter:
@ function prologue
stmfd sp!, {lr}
@ set SSD to proper pattern
mov r0, #SSD_PAUSING
bl set_ssd_pattern
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Called when the pausing state updates
@ delta time is passed in to r0
pausing_on_update:
@ function prologue
stmfd sp!, {r4,lr}
@ local variables:
@ r4: delta time
mov r4, r0
@ update travel info from delta time
mov r0, r4
bl update_travel
@ update lyrics info from delta time
mov r0, r4
bl update_tiger
@ slow down actual speed by 0.1mph per 0.1 seconds
mov r0, r4
mov r1, #0
mov r2, #SPEED_UPDATE_INTERVAL
mov r3, #PAUSING_SPEED_INCR
bl update_actual_speed
@ update led flickering
mov r0, r4
mov r1, #LED_FLICKER_INTERVAL
mov r2, #0
bl update_led_flicker
@ go to Paused state if actual speed is 0
mov r0, #0
ldr r1, =PausedState
bl switch_state_if_zero_speed
@ function epilogue
ldmfd sp!, {r4,lr}
bx lr
@ Called when the emergency stop button is pressed while pausing
pausing_on_emergency_stop:
@ function prologue
stmfd sp!, {lr}
ldr r0, =ShutdownState
bl switch_state
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Called when the start button is pressed while pausing
pausing_on_start:
@ function prologue
stmfd sp!, {lr}
ldr r0, =RunningState
bl switch_state
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Called when the paused state is entered
paused_on_enter:
@ function prologue
stmfd sp!, {lr}
@ turn on LEDs
mov r0, #LED_BOTH
bl set_led_pattern
@ set SSD to proper pattern
mov r0, #SSD_PAUSED
bl set_ssd_pattern
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Called when the start button is pressed during the paused state
paused_on_start:
@ function prologue
stmfd sp!, {lr}
ldr r0, =RunningState
bl switch_state
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Called when the reset button is pressed during the paused state
paused_on_reset:
@ function prologue
stmfd sp!, {lr}
@ reset statistics
bl reset_stats
@ switch state
ldr r0, =StoppedState
bl switch_state
@ function epilogue
ldmfd sp!, {lr}
bx lr
@ Called when the off button is pressed during the paused state
paused_on_off:
@ function prologue
stmfd sp!, {lr}
@ switch state to exiting
ldr r0, =ExitingState
bl switch_state
@ function epilogue
ldmfd sp!, {lr}
bx lr
.global _start
_start:
@ function prologue
stmfd sp!, {lr}
@ local variable registers:
@ r4: pointer to pointer to current state
@ r5: previous time
@ r6: delta time
@ r7: temporary for button bitset
@ r8: temporary for button iterator
@ r9: accumulator for redraw time
@ initialize local variables in registers
@ pointer to current state
ldr r4, =CurrentState
@ previous time initialized to time at start of program
bl get_ticks
mov r5, r0
@ delta time initialized to 0
mov r6, #0
@ redraw accumulator initialized to 0
mov r9, #0
@ self explanatory
bl initialize_state_machine
main_loop_start:
@ check if program end was requested
ldr r3, =IsProgramRunning
ldr r3, [r3]
cmp r3, #0
beq main_loop_end
@ poll black buttons and handle events
bl get_black_buttons_state
mov r7, r0
@ start current index
mov r8, #0
black_button_poll_start:
@ check for end of list
ldr r3, =BlackButtonUpdateList
ldr r3, [r3, r8, LSL #2]
cmp r3, #-1
beq black_button_poll_end
@ check if button pressed
mov r0, r3
mov r1, r7
bl is_button_pressed
@ if pressed, load corresponding function pointer and call the function
cmp r0, #0
beq black_button_poll_incr
ldr r3, [r4]
add r3, r3, #16
ldr r3, [r3, r8, LSL #2]
mov lr, pc
bx r3
black_button_poll_incr:
@ increment current index and loop
add r8, r8, #1
bal black_button_poll_start
black_button_poll_end:
@ poll blue buttons and handle events
bl get_blue_buttons_state
mov r7, r0
@ start current index
mov r8, #0
blue_button_poll_start:
@ check for end of list
ldr r3, =BlueButtonUpdateList
ldr r3, [r3, r8, LSL #2]
cmp r3, #-1
beq blue_button_poll_end
@ check if button pressed
mov r0, r3
mov r1, r7
bl is_button_pressed
@ if pressed, load corresponding function pointer and call the function
cmp r0, #0
beq blue_button_poll_incr
ldr r3, [r4]
add r3, r3, #24
ldr r3, [r3, r8, LSL #2]
mov lr, pc
bx r3
blue_button_poll_incr:
@ increment current index and loop
add r8, r8, #1
bal blue_button_poll_start
blue_button_poll_end:
@ update delta time
@ get current time and store in r3
bl get_ticks
mov r3, r0
@ check if need to modify formula to handle timer wraparound
cmp r3, r5
subge r6, r3, r5
ldr r0, =TimerMax
ldr r0, [r0]
sublt r6, r0, r5
addlt r6, r6, r3
@ store current time in previous time
mov r5, r3
@ call OnUpdate on current state
ldr r3, [r4]
ldr r3, [r3, #4]
@ store delta time in r0
mov r0, r6
mov lr, pc
bx r3
@ update redraw accumulator
add r9, r9, r6
@ handle redraw event if accumulated enough
cmp r9, #REDRAW_TIME
blt main_loop_dont_redraw
@ reset accumulator
mov r9, #0
@ call OnRedraw on current state
ldr r3, [r4]
ldr r3, [r3, #8]
mov lr, pc
bx r3
main_loop_dont_redraw:
@ restart main loop
bal main_loop_start
main_loop_end:
@ deinit state machine
bl deinitialize_state_machine
@ function epilogue
ldmfd sp!, {lr}
@ absolute end of program
swi SWI_Exit
bx lr
.data
.align
@ Flag used to denote a program end request
IsProgramRunning:
.word 1
SpeedUpdateAccumulator:
@ Used to accumulate time to handle increase in speed.
.word 0
LEDFlickerUpdateAccumulator:
@ Used to accumulate time to flicker LEDs
.word 0
@ Variables of the state machine:
CurrentState:
@ Pointer to current state initialized to NULL
.word 0
LEDState:
@ Remembers the state of the LED
.word LED_NONE
TravelTime:
@ To be used by update_travel
@ Time accumulated while traveling in milliseconds
.word 0
TravelDistance:
@ To be used by update_travel
@ Distance accumulated while traveling in nanomiles
.word 0
TravelEnergy:
@ To be used by update_travel
@ Energy accumulated while traveling in nanocalories
Weight:
@ lbs
.word DFT_WEIGHT
WeightLineBuffer:
@ Buffer for line to print on screen for weight
.skip SCREEN_WIDTH
.align
TargetSpeed:
@ .1 decimal fixed point Mph.
.word DFT_TARGET_SPEED
TargetSpeedLineBuffer:
@ Buffer for line to print on screen for target speed
.skip SCREEN_WIDTH
.align
ActualSpeed:
@ .1 decimal fixed point. Mph.
.word 0
ActualSpeedLineBuffer:
@ Buffer for line to print on screen for actual speed
.skip SCREEN_WIDTH
.align
Time:
@ .1 decimal fixed point. Seconds.
.word 0
TimeLineBuffer:
@ Buffer for line to print on screen for time
.skip SCREEN_WIDTH
.align
Distance:
@ .6 decimal fixed point. Miles.
.word 0
DistanceLineBuffer:
@ Buffer for line to print on screen for distance
.skip SCREEN_WIDTH
.align
Energy:
@ .6 decimal fixed point. Calories.
.word 0
EnergyLineBuffer:
@ Buffer for line to print on screen for energy
.skip SCREEN_WIDTH
.align
InformationLineBuffers:
@ List of strings to display stats with
.word WeightLineBuffer
.word TargetSpeedLineBuffer
.word ActualSpeedLineBuffer
.word TimeLineBuffer
.word DistanceLineBuffer
.word EnergyLineBuffer
.word 0
@ Lyrics of the eye of the tiger to boil the blood of the runner
LyricsAccumulator:
@ accumulates time to offset lyrics each x ms
.word 0
CurrentLyricsOffset:
@ offset of start of letters from the right of the screen as they scroll
.word 0
CurrentLyrics:
@ pointer to current line of lyrics
.word eyeofthetigerLyrics
CurrentLyricsBuffer:
@ buffer to store current state of lyrics plus null terminator
.skip SCREEN_WIDTH
.align
@ Data for specific states
ExitingStateTimer:
@ Used to accumulate time and shut off the machine after displaying the message
.word 0
.end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment