/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * Copyright (C) 2012 by Amaury Pouly
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "config.h"

.syntax unified

.data
was_aborted:
    .word   0

.section    .text.safe_read8
.type       safe_read8, %function
.global     safe_read8
@ bool safe_read8(uint8_t *addr, uint8_t *value)
safe_read8:
    @ was_aborted = 0
    ldr     r2, =was_aborted
    mov     r3, #0
    str     r3, [r2]
    @ r0=*addr
safe_read8_faulty_addr:
    ldrb    r0, [r0]
    @ if(was_aborted)
    ldr     r2, [r2]
    cmp     r2, #1
    @   return false;
    moveq   r0, #0
    bxeq    lr
    @ if(value != NULL)
    cmp     r1, #0
    @   *value = r0
    strbne  r0, [r1]
    @ return true;
    mov     r0, #1
    bx      lr
.size       safe_read8, . - safe_read8

.section    .text.safe_read16
.type       safe_read16, %function
.global     safe_read16
@ bool safe_read16(uint16_t *addr, uint16_t *value)
safe_read16:
    @ was_aborted = 0
    ldr     r2, =was_aborted
    mov     r3, #0
    str     r3, [r2]
    @ r0=*addr
safe_read16_faulty_addr:
    ldrh    r0, [r0]
    @ if(was_aborted)
    ldr     r2, [r2]
    cmp     r2, #1
    @   return false;
    moveq   r0, #0
    bxeq    lr
    @ if(value != NULL)
    cmp     r1, #0
    @   *value = r0
    strhne  r0, [r1]
    @ return true;
    mov     r0, #1
    bx      lr
.size       safe_read16, . - safe_read16

.section    .text.safe_read32
.type       safe_read32, %function
.global     safe_read32
@ bool safe_read32(uint32_t *addr, uint32_t *value)
safe_read32:
    @ was_aborted = 0
    ldr     r2, =was_aborted
    mov     r3, #0
    str     r3, [r2]
    @ r0=*addr
safe_read32_faulty_addr:
    ldr     r0, [r0]
    @ if(was_aborted)
    ldr     r2, [r2]
    cmp     r2, #1
    @   return false;
    moveq   r0, #0
    bxeq    lr
    @ if(value != NULL)
    cmp     r1, #0
    @   *value = r0
    strne   r0, [r1]
    @ return true;
    mov     r0, #1
    bx      lr
.size       safe_read32, . - safe_read32

#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(CPU_ARM_CLASSIC)
.section    .text.data_abort_handler
.type       data_abort_handler, %function
.global     data_abort_handler
data_abort_handler:
    @ store minimal amount of registers
    stmfd   sp!, {r0-r1}
    @ compute faulty address
    sub     r0, lr, #8
    @ compare to safe_read8
    ldr     r1, =safe_read8_faulty_addr
    cmp     r0, r1
    beq     1f
    @ compare to safe_read16
    ldr     r1, =safe_read16_faulty_addr
    cmp     r0, r1
    beq     1f
    @ compare to safe_read32
    ldr     r1, =safe_read32_faulty_addr
    cmp     r0, r1
    beq     1f
    @ otherwise just normally to UIE
    mov     r1, #2
    b       UIE
1:
    @ set was_aborted
    ldr     r1, =was_aborted
    mov     r0, #1
    str     r0, [r1]
    @ restore registers
    ldmfd   sp!, {r0-r1}
    @ restore mode and jump back to the *next* instruction
    subs    pc, lr, #4
.size       data_abort_handler, . - data_abort_handler
#endif
