// Convention:
// R0 / R1 is method input. Should not be clobbered unless specified otherwise
// R2 - R5 + R8 - R10 is general purpose.
// R6 is the result

// Please note that this will clobber the R6 register.
// To make cpulator ignore this, turn off "Function clobbered caller-saved register" in settings.

.global _start

.section .data // RW Memory
  // This is the input you are supposed to check for a palindrome
  // You can modify the string during development, however you
  // are not allowed to change the label 'input'!
  input: .asciz "level"
  // input: .asciz "8448"
  // input: .asciz "KayAk"
  // input: .asciz "step on no pets"
  // input: .asciz "Never odd or even"
.align

.text // RO Memory
  notAPalindrome:  .asciz "Not a palindrome"
  palindromeDetected:  .asciz "Palindrome detected"
.align

// ------

LED_BASE = 0xff200000
UART_BASE = 0xff201000

// ------

// Returns a pointer to the end of the string
findStringEnd:
  MOV R6, R0
  MOV R0, R0

  fse_loop:
  LDRB R5, [R6]
  CMP R5, #0x0
  BXEQ LR
  ADD R6, R6, #1
  B   fse_loop

// Checks whether the character is valid. Returns 0 or 1
isValidCharacter:
	CMP R0, #'A'
	BLT ivc_subrange2
	CMP R0, #'Z'
	BLE ivc_validChar

	ivc_subrange2:
	CMP R0, #'a'
	BLT ivc_subrange3
	CMP R0, #'z'
	BLE ivc_validChar

	ivc_subrange3:
	CMP R0, #'0'
	BLT ivc_invalidChar
	CMP R0, #'9'
	BLE ivc_validChar

	ivc_invalidChar:
	MOV R6, #0
	BX LR

	ivc_validChar:
  MOV R6, #1
	BX LR

// Makes the letter lowercase if possible
lowercaseCharacter:
  MOV R6, R0
	CMP R6, #'A'
  BXLT LR
	CMP R6, #'Z'
  BXGT LR

  ADD R6, #0x20
	BX LR

isPalindrome:
  // R0 is string start
  // R1 is string length
  PUSH {R0, R1}

  // R2 is string pointer moving from start
  // R3 is string pointer moving from end
  MOV R2, R0
  MOV R3, R1

  // R8 stores valid lowercased start character for later comparison
  // R9 stores valid lowercased end character for later comparison
  ip_loop:
	CMP R2, R3
	BGT ip_returnTrue

  // Loop until start char is valid,
  // lowercase the letter, and save result in R8
  LDRB R0, [R2]
	BL  isValidCharacter
  CMP R6, #1
  ADDNE R2, R2, #1
  BNE ip_loop
  BL  lowercaseCharacter
  MOV R8, R6

  // Same for end char, save in R9
  LDRB R0, [R3]
	BL  isValidCharacter
  CMP R6, #1
  SUBNE R3, R3, #1
  BNE ip_loop
  BL  lowercaseCharacter
  MOV R9, R6

  // Compare, potentially return false
  CMP R8, R9
	BNE ip_returnFalse

  // Move both pointers and loop
  ADD R2, R2, #1
  SUB R3, R3, #1
	B   ip_loop

  ip_returnFalse:
  POP {R0, R1}
  MOV R6, #0
  B   evaluatePalindromeResult

  ip_returnTrue:
  POP {R0, R1}
  MOV R6, #1
  B   evaluatePalindromeResult

_start:
  BL  resetLEDs
  LDR R0, =input
  BL  findStringEnd
  MOV R1, R6

  // Palindromes can not be less that two chars
  SUB R2, R1, R0
  CMP R2, #2
  BLT onNotPalindrome

  B   isPalindrome

evaluatePalindromeResult:
  CMP R6, #1
  BLEQ onPalindrome
  BL   onNotPalindrome

resetLEDs:
  LDR R8, =LED_BASE
  MOV R5, #0b0000000000
  STR R5, [R8]
  BX  LR

onPalindrome:
  // Light up LEDS
  LDR R8, =LED_BASE
  MOV R5, #0b0000011111
  STR R5, [R8]

  // Write to UART
  LDR R8, =UART_BASE
  LDR R9, =palindromeDetected
  op_write_uart_loop:
  LDRB R0, [R9]
  CMP R0, #0x0
  BEQ _end
  STR R0, [R8]
  ADD R9, R9, #1
  B   op_write_uart_loop

onNotPalindrome:
  // Light up LEDS
  LDR R8, =LED_BASE
  MOV R5, #0b1111100000
  STR R5, [R8]

  // Write to UART
  LDR R8, =UART_BASE
  LDR R9, =notAPalindrome
  onp_write_uart_loop:
  LDRB R0, [R9]
  CMP R0, #0x0
  BEQ _end
  STR R0, [R8]
  ADD R9, R9, #1
  B   onp_write_uart_loop

_end:
  B   _end

.end

