Full Code of ICTeam28/PiFox for AI

master 4ae6f73c4913 cached
28 files
174.3 KB
66.8k tokens
1 symbols
1 requests
Download .txt
Repository: ICTeam28/PiFox
Branch: master
Commit: 4ae6f73c4913
Files: 28
Total size: 174.3 KB

Directory structure:
gitextract_441jipqv/

├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── assets/
│   ├── enemy.s
│   ├── pillar.s
│   ├── rock.s
│   ├── rocket.s
│   └── ship.s
├── bullets.s
├── enemies.s
├── game.s
├── gfx.s
├── imager.py
├── input.s
├── kernel.ld
├── kernel.s
├── math.s
├── mbox.s
├── objects.s
├── pillars.s
├── player.s
├── ports.s
├── printf.s
├── rockets.s
├── sound.s
├── test.s
└── toolchains/
    └── arm-none-eabi.cmake

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# CMake
build/

# Object files
*.o
*.ko
*.obj
*.elf

# Libraries
*.lib
*.a

# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib

# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex


================================================
FILE: CMakeLists.txt
================================================
# This file is part of the Team 28 Project
# Licensing information can be found in the LICENSE file
# (C) 2014 The Team 28 Authors. All rights reserved.
cmake_minimum_required(VERSION 2.8)
project(PiFox ASM)

# Find the python interpreter
set( PythonInterp_FIND_VERSION 2 )
find_package( PythonInterp REQUIRED )

set(SOURCES
  kernel.s
  math.s
  mbox.s
  gfx.s
  ports.s
  input.s
  printf.s
  game.s
  bullets.s
  objects.s
  pillars.s
  player.s
  sound.s
  rockets.s
  enemies.s
  assets/rock.s
  assets/ship.s
  assets/enemy.s
  assets/rocket.s
)

SET(IMAGES
  bullet.png
  rocket.png
  wrench.png
  flare.png
  mountains.png
  tristan.png
  monkey.png
  pifox.png
)

set( CMAKE_EXECUTABLE_SUFFIX .elf )
set( CMAKE_ASM_FLAGS "-march=armv6zk -mfpu=vfp -g -Wa,--fatal-warnings -nostartfiles -nostdlib" )
set( CMAKE_EXE_LINKER_FLAGS "-Wl,-T,${PROJECT_SOURCE_DIR}/kernel.ld" )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )

# Compile all images
foreach(file ${IMAGES})
  get_filename_component(file_path ${file} PATH)
  get_filename_component(file_name ${file} NAME_WE)

  set(file "${file_name}")
  set(object "${CMAKE_BINARY_DIR}/assets${file_path}/${file_name}.bin")
  set(source "${CMAKE_SOURCE_DIR}/assets${file_path}/${file_name}.png")
  get_filename_component(dir ${object} PATH)

  file(MAKE_DIRECTORY ${dir})
  add_custom_command(
    OUTPUT ${object}
    DEPENDS ${source}
    COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/imager.py ${source} --out=${object}
  )

  list(APPEND IMAGES_BIN ${object})
endforeach(file)

# Build a binary file out of the elf
add_executable( kernel ${SOURCES} ${IMAGES_BIN} )

# Without an operating system, there is no executable loader, so we just need
# the raw machine code (or binary) from the ELF output of the toolchain.
# objcopy can do that for us.
add_custom_command(
    TARGET kernel POST_BUILD
    COMMAND ${CMAKE_OBJCOPY} ${CMAKE_BINARY_DIR}/kernel.elf -O binary ./kernel.img
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    COMMENT "Convert the ELF output file to a binary image" )


================================================
FILE: LICENSE
================================================
Copyright (c) 2014, Licker Nandor, Ilija Radosavovic, David Avedissian, Nic Prettejohn
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of the {organization} nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
PiFox
=====

Video of the game in action: https://www.youtube.com/watch?v=-5n9IxSQH1M

Developed as an extension to a first year group project at Imperial College London.

Bare Metal 3D rail shooter game

* 5800 lines of ARM assembly (ARMv6 + VFP1)
* Software rasterizer
* 3D objects
* 2D billboards
* Sound using DMA
* NES controller input
* Math & utility library
* GitHub: https://github.com/ICTeam28/PiFox
* Emulator: https://github.com/ICTeam28/PiEmu

Dependencies
-----

Using your favourite Linux package manager, install:

* arm-none-eabi-binutils
* cmake

Build
-----

The project uses CMake and requires an ARM assembler supporting
GNU as syntax. 

    mkdir build
    cd build
    cmake ..
    make
    
Emulation
-----

PiEmu (https://github.com/ICTeam28/PiEmu) can run the game without sound. Assuming PiFox and PiEmu have been cloned in the same directory and both are built, PiEmu must be invoked with the following command inside PiFox's build directory:

    ../../PiEmu/build/piemu --graphics --quiet --memory=256M --addr=65536 --nes kernel.img 

A qemu branch can be used to emulate the game at a higher framerate, but sound must be disabled. (https://github.com/Torlus/qemu/tree/rpi)

config.txt
----------

In order to be compatible with qemu, the kernel must be loaded at address 0x10000.

    disable_overscan=1
    disable_pvt=1
    force_turbo=1
    gpu_mem_256=160
    gpu_mem_512=316
    cma_lwm=16
    cma_hwm=32
    kernel_address=65536

Wiring the controller
---------------------

|    NES   |  Raspberry Pi  |
|:--------:|:--------------:|
| GND      | Ground         |
| VCC      | 3v3            |
| CUP      | GPIO 10        |
| OUT 0    | GPIO 11        |
| D1       | GPIO 4         |

![NES Pinout](https://raw.github.com/ICTeam28/PiFox/master/assets/nes-controller-pinout.png)

![Raspberry PI Pinout](https://raw.github.com/ICTeam28/PiFox/master/assets/raspbery-pi-pinout.png)

Authors
-------
Nandor Licker
- Email: nandor.licker13[at]imperial.ac.uk
- Github [@nandor](https://github.com/nandor)

Ilija Radosavovic
- Email: ilija.radosavovic13[at]imperial.ac.uk
- Github [@ir413](https://github.com/ir413)

David Avedissian
- Email: david.avedissian13[at]imperial.ac.uk
- Github [@davedissian](https://github.com/davedissian)
- Twitter [@davedissian](https://twitter.com/davedissian)

Nic Prettejohn
- Email: nicolas.prettejohn13[at]imperial.ac.uk
- Github [@nkp](https://github.com/nkp)
- Twitter [@thisisnkp](https://twitter.com/thisisnkp)

Special thanks
--------------

* https://github.com/dwelch67/raspberrypi
* https://github.com/PeterLemon/RaspberryPi


Special thanks to chpatrick (Patrick Chilton) for his advice.


================================================
FILE: assets/enemy.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global enemy_vtx
.global enemy_idx

@ ------------------------------------------------------------------------------
@ Enemy model
@ X Y Z 1.0
@ ------------------------------------------------------------------------------
.align 2
enemy_vtx:
  .float  0.00,    0.75,    0.0,  1.0 @ 0
  .float -0.75,    0.00,    0.0,  1.0 @ 1
  .float  0.00,   -0.75,    0.0,  1.0 @ 2
  .float  0.75,    0.00,    0.0,  1.0 @ 3

  .float -0.90,    0.90,    0.0,  1.0 @ 4
  .float -0.90,   -0.90,    0.0,  1.0 @ 5
  .float  0.90,   -0.90,    0.0,  1.0 @ 6
  .float  0.90,    0.90,    0.0,  1.0 @ 7

  .float  0.00,    0.00,    2.0,  1.0 @ 8

  .float -0.60,    0.60,    1.0,  1.0 @ 9
  .float -0.60,   -0.60,    1.0,  1.0 @ 10
  .float  0.60,   -0.60,    1.0,  1.0 @ 11
  .float  0.60,    0.60,    1.0,  1.0 @ 12

enemy_idx:
  .long 0, 1, 8
  .float 1.0, 0.2, 0.2
  .long 1, 2, 8
  .float 1.0, 0.2, 0.2
  .long 2, 3, 8
  .float 1.0, 0.2, 0.2
  .long 3, 0, 8
  .float 1.0, 0.2, 0.2

  .long 1, 0, 9
  .float 1.0, 0.7, 0.2
  .long 0, 4, 9
  .float 1.0, 0.7, 0.2
  .long 4, 1, 9
  .float 1.0, 0.7, 0.2

  .long 2, 1, 10
  .float 1.0, 0.7, 0.2
  .long 1, 5, 10
  .float 1.0, 0.7, 0.2
  .long 5, 2, 10
  .float 1.0, 0.7, 0.2

  .long 3, 2, 11
  .float 1.0, 0.7, 0.2
  .long 2, 6, 11
  .float 1.0, 0.7, 0.2
  .long 6, 3, 11
  .float 1.0, 0.7, 0.2

  .long 0, 3, 12
  .float 1.0, 0.7, 0.2
  .long 7, 0, 12
  .float 1.0, 0.7, 0.2
  .long 3, 7, 12
  .float 1.0, 0.7, 0.2



================================================
FILE: assets/pillar.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global pillar_vtx
.global pillar_idx

@ ------------------------------------------------------------------------------
@ Cube model
@ X Y Z 1.0
@ ------------------------------------------------------------------------------
.align 2
pillar_vtx:
  @ Front
  .float -0.5, -0.5,  0.5,  1.0
  .float  0.5, -0.5,  0.5,  1.0
  .float  0.5,  0.5,  0.5,  1.0
  .float -0.5,  0.5,  0.5,  1.0
  @ Back
  .float -0.5, -0.5, -0.5,  1.0
  .float  0.5, -0.5, -0.5,  1.0
  .float  0.5,  0.5, -0.5,  1.0
  .float -0.5,  0.5, -0.5,  1.0
  @ Top
  .float  0.5,  0.5, -0.5,  1.0
  .float -0.5,  0.5, -0.5,  1.0
  .float -0.5,  0.5,  0.5,  1.0
  .float  0.5,  0.5,  0.5,  1.0
  @ Left
  .float  0.5, -0.5, -0.5,  1.0
  .float  0.5,  0.5, -0.5,  1.0
  .float  0.5,  0.5,  0.5,  1.0
  .float  0.5, -0.5,  0.5,  1.0
  @ Right
  .float -0.5,  0.5, -0.5,  1.0
  .float -0.5, -0.5, -0.5,  1.0
  .float -0.5, -0.5,  0.5,  1.0
  .float -0.5,  0.5,  0.5,  1.0

pillar_idx:
  .long 0, 1, 3
  .float 0.5, 0.5, 0.5
  .long 3, 1, 2
  .float 0.5, 0.5, 0.5

  .long 4, 7, 5
  .float 0.5, 0.5, 0.5
  .long 7, 6, 5
  .float 0.5, 0.5, 0.5

  .long 8, 9, 11
  .float 0.5, 0.5, 0.5
  .long 11, 9, 10
  .float 0.5, 0.5, 0.5

  .long 12, 13, 15
  .float 0.5, 0.5, 0.5
  .long 15, 13, 14
  .float 0.5, 0.5, 0.5

  .long 16, 17, 19
  .float 0.5, 0.5, 0.5
  .long 19, 17, 18
  .float 0.5, 0.5, 0.5



================================================
FILE: assets/rock.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global rock_vtx
.global rock_idx

@ ------------------------------------------------------------------------------
@ Rock model
@ X Y Z 1.0
@ ------------------------------------------------------------------------------
.align 2
rock_vtx:
  .float  0.0,    0.75,    1.2135,  1.0
  .float  0.0,    0.75,   -1.2135,  1.0
  .float  0.0,   -0.75,    1.2135,  1.0
  .float  0.0,   -0.75,   -1.2135,  1.0
  .float  0.75,    1.2135,  0.0,    1.0
  .float  0.75,   -1.2135,  0.0,    1.0
  .float -0.75,    1.2135,  0.0,    1.0
  .float -0.75,   -1.2135,  0.0,    1.0
  .float  1.2135,  0.0,    0.75,    1.0
  .float  1.2135,  0.0,   -0.75,    1.0
  .float -1.2135,  0.0,    0.75,    1.0
  .float -1.2135,  0.0,   -0.75,    1.0

rock_idx:
  .long 0, 2, 8
  .float 0.9, 0.2, 0.2
  .long 10, 2, 0
  .float 0.9, 0.2, 0.2
  .long 0, 4, 6
  .float 0.9, 0.2, 0.2
  .long 8, 4, 0
  .float 0.9, 0.2, 0.2
  .long 0, 6, 10
  .float 0.9, 0.2, 0.2
  .long 1, 9, 3
  .float 0.9, 0.2, 0.2
  .long 11, 1, 3
  .float 0.9, 0.2, 0.2
  .long 4, 1, 6
  .float 0.9, 0.2, 0.2
  .long 9, 1, 4
  .float 0.9, 0.2, 0.2
  .long 6, 1, 11
  .float 0.9, 0.2, 0.2
  .long 2, 7, 5
  .float 0.9, 0.2, 0.2
  .long 5, 8, 2
  .float 0.9, 0.2, 0.2
  .long 10, 7, 2
  .float 0.9, 0.2, 0.2
  .long 7, 3, 5
  .float 0.9, 0.2, 0.2
  .long 3, 9, 5
  .float 0.9, 0.2, 0.2
  .long 11, 3, 7
  .float 0.9, 0.2, 0.2
  .long 4, 8, 9
  .float 0.9, 0.2, 0.2
  .long 5, 9, 8
  .float 0.9, 0.2, 0.2
  .long 11, 10, 6
  .float 0.9, 0.2, 0.2
  .long 10, 11, 7
  .float 0.9, 0.2, 0.2

  .long 0, 2, 8
  .float 0.7, 0.5, 0.5
  .long 10, 2, 0
  .float 0.7, 0.5, 0.5
  .long 0, 4, 6
  .float 0.7, 0.5, 0.5
  .long 8, 4, 0
  .float 0.7, 0.5, 0.5
  .long 0, 6, 10
  .float 0.7, 0.5, 0.5
  .long 1, 9, 3
  .float 0.7, 0.5, 0.5
  .long 11, 1, 3
  .float 0.7, 0.5, 0.5
  .long 4, 1, 6
  .float 0.7, 0.5, 0.5
  .long 9, 1, 4
  .float 0.7, 0.5, 0.5
  .long 6, 1, 11
  .float 0.7, 0.5, 0.5
  .long 2, 7, 5
  .float 0.7, 0.5, 0.5
  .long 5, 8, 2
  .float 0.7, 0.5, 0.5
  .long 10, 7, 2
  .float 0.7, 0.5, 0.5
  .long 7, 3, 5
  .float 0.7, 0.5, 0.5
  .long 3, 9, 5
  .float 0.7, 0.5, 0.5
  .long 11, 3, 7
  .float 0.7, 0.5, 0.5
  .long 4, 8, 9
  .float 0.7, 0.5, 0.5
  .long 5, 9, 8
  .float 0.7, 0.5, 0.5
  .long 11, 10, 6
  .float 0.7, 0.5, 0.5
  .long 10, 11, 7
  .float 0.7, 0.5, 0.5

  .long 0, 2, 8
  .float 0.5, 0.5, 0.5
  .long 10, 2, 0
  .float 0.5, 0.5, 0.5
  .long 0, 4, 6
  .float 0.5, 0.5, 0.5
  .long 8, 4, 0
  .float 0.5, 0.5, 0.5
  .long 0, 6, 10
  .float 0.5, 0.5, 0.5
  .long 1, 9, 3
  .float 0.5, 0.5, 0.5
  .long 11, 1, 3
  .float 0.5, 0.5, 0.5
  .long 4, 1, 6
  .float 0.5, 0.5, 0.5
  .long 9, 1, 4
  .float 0.5, 0.5, 0.5
  .long 6, 1, 11
  .float 0.5, 0.5, 0.5
  .long 2, 7, 5
  .float 0.5, 0.5, 0.5
  .long 5, 8, 2
  .float 0.5, 0.5, 0.5
  .long 10, 7, 2
  .float 0.5, 0.5, 0.5
  .long 7, 3, 5
  .float 0.5, 0.5, 0.5
  .long 3, 9, 5
  .float 0.5, 0.5, 0.5
  .long 11, 3, 7
  .float 0.5, 0.5, 0.5
  .long 4, 8, 9
  .float 0.5, 0.5, 0.5
  .long 5, 9, 8
  .float 0.5, 0.5, 0.5
  .long 11, 10, 6
  .float 0.5, 0.5, 0.5
  .long 10, 11, 7
  .float 0.5, 0.5, 0.5


================================================
FILE: assets/rocket.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global rocket_vtx
.global rocket_idx

@-------------------------------------------------------------------------------
@ Rocket model
@ X Y Z 1.0
@-------------------------------------------------------------------------------
.align 2
rocket_vtx:
  @ base
  .float  0.2,   0.0,     0.5,  1.0  @b0
  .float  0.06,  0.19,    0.5,  1.0  @b1
  .float -0.16,  0.1174,  0.5,  1.0  @b2
  .float -0.16, -0.1174,  0.5,  1.0  @b3
  .float  0.06, -0.19,    0.5,  1.0  @b4
  .float  0.0,   0.0,     0.5,  1.0  @b5

  @ top
  .float  0.2,   0.0,    -0.5,  1.0  @t0
  .float  0.06,  0.19,   -0.5,  1.0  @t1
  .float -0.16,  0.1174, -0.5,  1.0  @t2
  .float -0.16, -0.1174, -0.5,  1.0  @t3
  .float  0.06, -0.19,   -0.5,  1.0  @t4
  .float  0.0,   0.0,    -1.5,  1.0  @t5

  @ mid
  .float  0.2,   0.0,     0.0,  1.0  @m0
  .float  0.06,  0.19,    0.0,  1.0  @m1
  .float -0.16,  0.1174,  0.0,  1.0  @m2
  .float -0.16, -0.1174,  0.0,  1.0  @m3
  .float  0.06, -0.19,    0.0,  1.0  @m4

  @ wings
  .float  0.4,   0.0,     0.5,  1.0  @w0
  .float  0.12,  0.38,    0.5,  1.0  @w1
  .float -0.32,  0.2348,  0.5,  1.0  @w2
  .float -0.32, -0.2348,  0.5,  1.0  @w3
  .float  0.12, -0.38,    0.5,  1.0  @w4

rocket_idx:
  @ base
  .long 0, 5, 4
  .float 1.0, 0.4, 0.0
  .long 3, 4, 5
  .float 1.0, 0.4, 0.0
  .long 2, 3, 5
  .float 1.0, 0.4, 0.0
  .long 1, 2, 5
  .float 1.0, 0.4, 0.0
  .long 0, 1, 5
  .float 1.0, 0.4, 0.0

  @ sides
  .long 0, 4, 10
  .float 1.0, 0.0, 0.0
  .long 0, 10, 6
  .float 1.0, 0.0, 0.0

  .long 4, 3, 9  
  .float 1.0, 0.0, 0.0
  .long 4, 9, 10  
  .float 1.0, 0.0, 0.0

  .long 3, 2, 8  
  .float 1.0, 0.0, 0.0
  .long 3, 8, 9  
  .float 1.0, 0.0, 0.0

  .long 2, 1, 7  
  .float 1.0, 0.0, 0.0
  .long 2, 7, 8  
  .float 1.0, 0.0, 0.0

  .long 1, 0, 6  
  .float 1.0, 0.0, 0.0
  .long 1, 6, 7  
  .float 1.0, 0.0, 0.0

  @ top
  .long 6, 10, 11
  .float 1.0, 1.0, 1.0
  .long 10, 9, 11
  .float 1.0, 1.0, 1.0  
  .long 9, 8, 11
  .float 1.0, 1.0, 1.0  
  .long 8, 7, 11
  .float 1.0, 1.0, 1.0  
  .long 7, 6, 11
  .float 1.0, 1.0, 1.0

  @ wings
  .long 0, 17, 12
  .float 1.0, 1.0, 1.0
  .long 1, 18, 13
  .float 1.0, 1.0, 1.0
  .long 2, 19, 14
  .float 1.0, 1.0, 1.0
  .long 3, 20, 15
  .float 1.0, 1.0, 1.0
  .long 4, 21, 16
  .float 1.0, 1.0, 1.0

  .long 17, 0, 12
  .float 1.0, 1.0, 1.0
  .long 18, 1, 13
  .float 1.0, 1.0, 1.0
  .long 19, 2, 14
  .float 1.0, 1.0, 1.0
  .long 20, 3, 15
  .float 1.0, 1.0, 1.0
  .long 21, 4, 16
  .float 1.0, 1.0, 1.0


================================================
FILE: assets/ship.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global ship_vtx
.global ship_idx

@ ------------------------------------------------------------------------------
@ Cube model
@ X Y Z 1.0
@ ------------------------------------------------------------------------------
.align 2
ship_vtx:
  @ Center - backside
  .float -0.4,  0.0,  0.0,  1.0
  .float  0.0,  0.2,  0.0,  1.0
  .float  0.4,  0.0,  0.0,  1.0
  .float -0.0, -0.1,  0.0,  1.0
  @ Left - backside
  .float -0.8,  0.0,  0.0,  1.0
  .float -0.6,  0.1,  0.0,  1.0
  .float -0.6, -0.2,  0.0,  1.0
  @ Right - backside
  .float  0.6,  0.1,  0.0,  1.0
  .float  0.8,  0.0,  0.0,  1.0
  .float  0.6, -0.2,  0.0,  1.0
  @ Left tip
  .float -0.6,  0.0, -2.0, 1.0
  @ Center tip
  .float  0.0,  0.0, -3.0, 1.0
  @ Right tip
  .float  0.6,  0.0, -2.0, 1.0
  @ Engine
  .float  0.3,  0.0, -1.0,  1.0
  .float  0.7,  0.5,  0.0,  1.0
  .float -0.3,  0.0, -1.0,  1.0
  .float -0.7,  0.5,  0.0,  1.0

ship_idx:
  .long 1, 0, 2
  .float 0.0, 0.0, 1.0
  .long 0, 3, 2
  .float 0.0, 0.0, 1.0

  .long 5, 4, 0
  .float 1.0, 1.0, 1.0
  .long 0, 4, 6
  .float 1.0, 1.0, 1.0

  .long 7, 2, 8
  .float 1.0, 1.0, 1.0
  .long 8, 2, 9
  .float 1.0, 1.0, 1.0

  .long 10, 4, 5
  .float 1.0, 0.0, 0.0
  .long 10, 6, 4
  .float 1.0, 0.0, 0.0
  .long 10, 5, 0
  .float 1.0, 0.0, 0.0
  .long 10, 0, 6
  .float 1.0, 0.0, 0.0

  .long 11, 0, 1
  .float 1.0, 1.0, 1.0
  .long 11, 3, 0
  .float 1.0, 1.0, 1.0
  .long 11, 1, 2
  .float 1.0, 1.0, 1.0
  .long 11, 2, 3
  .float 1.0, 1.0, 1.0

  .long 12, 2, 7
  .float 1.0, 0.0, 0.0
  .long 12, 9, 2
  .float 1.0, 0.0, 0.0
  .long 12, 7, 8
  .float 1.0, 0.0, 0.0
  .long 12, 8, 9
  .float 1.0, 0.0, 0.0

  .long  14, 13, 2
  .float 0.0, 0.0, 20.0
  .long  14, 2, 13
  .float 0.0, 0.0, 20.0
  .long  16, 0, 15
  .float 0.0, 0.0, 20.0
  .long  16, 15, 0
  .float 0.0, 0.0, 20.0


================================================
FILE: bullets.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global draw_bullets
.global collide_bullets
.global reset_bullets

.include "ports.s"

.section .data
@ ------------------------------------------------------------------------------
@ List of bullets
@ ------------------------------------------------------------------------------
.equ          BULLET_COUNT, 20
bullet_last:  .long 0
bullet_list:
  .rept BULLET_COUNT
    .float 0.0, 0.0, 0.0, 1.0
    .long  0
  .endr

.section .text
@ ------------------------------------------------------------------------------
@ Renders all bullets
@ ------------------------------------------------------------------------------
draw_bullets:
  stmfd       sp!, {lr}

  @ Reset model matrix
  ldr         r0, =mtx_id
  vldm.f32    r0, {s0 - s15}
  ldr         r0, =mtx_model
  vstm.f32    r0, {s0 - s15}

  @ Read system timer
  ldr         r7, =STIMER_CLO
  ldr         r7, [r7]

  @ Loop through all bullets
  ldr         r12, =BULLET_COUNT
  ldr         r11, =bullet_list
  ldr         r10, =bullet_last
  ldr         r6, [r10]
1:
  vldmia.f32  r11!, {s3 - s6}
  ldmia       r11!, {r9}

  ldr         r0, =0x40400000
  vmov.f32    s2, r0
  vsub.f32    s5, s5, s2

  tst         r9, r9
  bne         2f

  @ Check if sprite can be spawned
  sub         r8, r7, r6
  cmp         r8, #0x50000
  blt         2f

  ldr         r5, =button_pressed
  ldr         r5, [r5]
  tst         r5, #0x01
  beq         2f

  @ Play shot sound
  bl          snd_play_bullet

  @ Spawn a new sprite
  add         r9, r7, #0x100000
  mov         r6, r7
  str         r7, [r10]

  ldr         r0, =player_pos
  vldm.f32    r0, {s3 - s6}
  ldr         r1, =0xc0000000
  vmov.f32    s7, r1
  vsub.f32    s4, s4, s7
  ldr         r1, =0xc1200000
  vmov.f32    s5, r1
  b           3f
2:
  cmp         r9, r7
  movlt       r9, #0
3:
  stmdb       r11!, {r9}
  vstmdb      r11!, {s3 - s6}

  @ Check if bullet is active
  tst         r9, r9
  blne        draw_bullet

2:
  add         r11, r11, #20
  subs        r12, r12, #1
  bne         1b

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Renders a single bullet
@ ------------------------------------------------------------------------------
draw_bullet:
  stmfd       sp!, {lr}

  ldr         r0, =mtx_model
  vmov.f32    s0, s3
  vneg.f32    s1, s4
  vmov.f32    s2, s5
  bl          mat4_translate

  ldr         r0, =mtx_vp
  ldr         r1, =mtx_model
  ldr         r2, =mtx_mvp
  bl          mat4_mul_mat4

  @ Draw a sprite
  ldr         r0, =bullet
  ldr         r1, =mtx_mvp
  ldr         r2, =mtx_view
  ldr         r3, =0x3e800000
  vmov.f32    s0, r3
  vmov.f32    s1, r3
  bl          gfx_draw_sprite

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Checks whether the bullet collided with something.
@ In case of a collision, the bullet is destroyed
@ Arguments:
@   s0 - x
@   s1 - y
@   s2 - z
@   s31 - radius
@ Returns:
@   r0 - 0 if collision happened
@ Clobbers:
@   none
@ ------------------------------------------------------------------------------
collide_bullets:
  stmfd       sp!, {r1 - r12, lr}
  mov         r0, #1

  ldr         r12, =BULLET_COUNT
  ldr         r11, =bullet_list
1:
  vldmia.f32  r11!, {s27 - s30}
  ldmia       r11!, {r9}

  tst         r9, r9
  beq         2f

  vsub.f32    s26, s0, s27
  vabs.f32    s26, s26
  vcmp.f32    s26, s31
  fmstat
  bgt         2f            @ x

  vsub.f32    s26, s1, s28
  vabs.f32    s26, s26
  vcmp.f32    s26, s31
  fmstat
  bgt         2f            @ y

  vsub.f32    s26, s2, s29
  vabs.f32    s26, s26
  vcmp.f32    s26, s31
  fmstat
  bgt         2f            @ z

  mov         r0, #0
  str         r0, [r11, #-4]
  b           3f
2:
  subs        r12, r12, #1
  bne         1b
3:

  ldmfd       sp!, {r1 - r12, pc}

@-------------------------------------------------------------------------------
@ Resets bullets
@-------------------------------------------------------------------------------
reset_bullets:
  stmfd       sp!, {r0 - r3, lr}
  vstmdb.f32  sp!, {s0 - s4}

  ldr         r3, =bullet_last
  mov         r0, #0
  str         r0, [r3]

  ldr         r3, =BULLET_COUNT
  ldr         r2, =bullet_list
  ldr         r1, =0x3F800000      @ 1.0

1:
  vldm.f32    r2, {s0 -s4}

  vmov.f32    s0, r0
  vmov.f32    s1, r0
  vmov.f32    s2, r0
  vmov.f32    s3, r1
  vmov.f32    s4, r0

  vstm.f32    r2!, {s0 - s4}

  subs        r3, #1
  bne         1b

  vldm.f32    sp!, {s0 - s4}
  ldmfd       sp!, {r0 - r3, pc}


================================================
FILE: enemies.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global setup_enemies
.global draw_enemies
.global reset_enemies
.global reset_flares
.global enemy_count
.global enemies
.global ENEMY_COUNT

.section .data
@ ------------------------------------------------------------------------------
@ List of enemies
@ ------------------------------------------------------------------------------
.equ ENEMY_COUNT,    5
.equ FLARE_COUNT,    30

enemy_count:    .long 0
enemy_counter:  .long 0
enemy_timer:    .long 0
enemies:
  .rept ENEMY_COUNT
    .float 0.0, 0.0, 0.0      @ x, y, z
    .float 0.0                @ s3 - target z
    .float 0.0                @ s4 - rot
    .float 0.0                @ s5 - move dir
    .long  0                  @ s6 - lives
    .long  0                  @ s7 - bullet timer
  .endr

flares:
  .rept FLARE_COUNT
    .float 0.0, 0.0, -20.0    @ x, y, z
    .float 0.0, 0.0,   0.0    @ dx, dy, dz
    .long 0                   @ s6 - alive
  .endr

.section .text
@ ------------------------------------------------------------------------------
@ Resets counters
@ ------------------------------------------------------------------------------
setup_enemies:
  ldr         r0, =enemy_count
  mov         r1, #0
  str         r1, [r0]

  ldr         r0, =enemy_counter
  mov         r1, #0
  str         r1, [r0]

  ldr         r0, =enemy_timer
  mov         r1, #1000
  str         r1, [r0]

  mov         pc, lr

@ ------------------------------------------------------------------------------
@ Renders all enemies
@ ------------------------------------------------------------------------------
draw_enemies:
  stmfd       sp!, {lr}

  ldr         r0, =enemy_count
  ldr         r0, [r0]
  tst         r0, r0
  bne         2f

  @ Test if enemies can be spawned
  ldr         r0, =enemy_timer
  ldr         r1, [r0]
  subs        r1, r1, #1
  moveq       r1, #1000
  str         r1, [r0]
  ldmnefd     sp!, {pc}

  @ Pop-up the monkey
  ldr         r0, =monkey_timer
  mov         r1, #120
  str         r1, [r0]

  @ Play cant let you do that
  bl          snd_play_cantlet

  @ Increment number of enemies, clamp to 5
  ldr         r0, =enemy_counter
  ldr         r11, [r0]
  add         r11, r11, #1
  cmp         r11, #5
  movgt       r11, #5
  str         r11, [r0]
  ldr         r0, =enemy_count
  str         r11, [r0]

  @ Initialise them
  ldr         r12, =enemies
  mov         r10, #0
1:
  bl          spawn_enemy
  vstm.f32    r12!, {s0 - s7}
  subs        r11, r11, #1
  bne         1b

2:
  ldr         r12, =enemies
  ldr         r11, =enemy_count
  ldr         r11, [r11]
  mov         r10, #0
3:
  @ Update & draw enemies
  vldm.f32    r12, {s0 - s7}
  vmov.f32    r0, s6
  tst         r0, r0
  addeq       r12, r12, #32
  beq         4f
  bl          update_enemy
  vstm.f32    r12!, {s0 - s7}
  vmov.f32    r0, s6
  tst         r0, r0
  blne        draw_enemy
4:
  subs        r11, r11, #1
  bne         3b

  tst         r10, r10
  ldr         r11, =enemy_count
  streq       r10, [r11]

  @ Reset model matrix
  ldr         r0, =mtx_id
  vldm.f32    r0, {s0 - s15}
  ldr         r0, =mtx_model
  vstm.f32    r0, {s0 - s15}

  @ Draw flares shot by enemies
  ldr         r11, =FLARE_COUNT
  ldr         r12, =flares
1:
  vldm.f32    r12, {s0 - s6}
  vmov.f32    r0, s6
  tst         r0, r0
  addeq       r12, r12, #28
  beq         2f
  bl          update_flare
  vstm.f32    r12!, {s0 - s6}
  bl          draw_flare
2:
  subs        r11, r11, #1
  bne         1b

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Updates an enemy
@ Arguments:
@
@ Returns:
@
@ Clobbers:
@
@ ------------------------------------------------------------------------------
update_enemy:
  stmfd       sp!, {lr}

  @ Move down z axis up to target z
  ldr         r0, =player_speed
  vldr.f32    s8, [r0]
  vadd.f32    s2, s2, s8
  ldr         r0, =0x3f800000
  vmov.f32    s9, r0
  vadd.f32    s2, s2, s9
  vcmp.f32    s2, s3
  fmstat
  vmovgt.f32  s2, s3

  @ Lateral movement
  vadd.f32    s0, s0, s5
  ldr         r0, =0xc1080000
  vmov.f32    s8, r0
  vcmp.f32    s0, s8
  fmstat
  vmovlt.f32  s0, s8
  vneglt.f32  s5, s5
  vneg.f32    s8, s8
  vcmp.f32    s0, s8
  fmstat
  vmovgt.f32  s0, s8
  vneggt.f32  s5, s5

  @ Rotation
  ldr         r0, =0x3c23d70a
  vmov.f32    s8, r0
  vadd.f32    s4, s4, s8

  @ Decrement bullet timeout
  vmov.f32    r0, s7
  subs        r0, r0, #1
  moveq       r0, #40
  vmov.f32    s7, r0
  bne         2f

  @ Fire a flare
  vcmp.f32    s2, s3
  fmstat
  blge        fire_flare

2:
  @ Test for collision with bullets
  ldr         r0, =0x40000000
  vmov.f32    s31, r0
  bl          collide_bullets
  tst         r0, r0
  vmoveq.f32  r0, s6
  subeq       r0, r0, #1
  vmoveq.f32  s6, r0

  @ Increment score
  ldreq       r0, =player_score     @ Add to score
  ldreq       r1, [r0]
  addeq       r1, r1, #50
  streq       r1, [r0]

  @ Increment live enemy count
  add         r10, r10, #1

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Renders an enemy
@ Arguments:
@   none
@ Returns:
@   none
@ Clobbers:
@   s0 - s31
@ ------------------------------------------------------------------------------
draw_enemy:
  stmfd       sp!, {lr}

  ldr         r0, =mtx_id
  vldm.f32    r0, {s16 - s31}
  ldr         r0, =mtx_model
  vstm.f32    r0, {s16 - s31}
  ldr         r0, =mtx_temp
  vstm.f32    r0, {s16 - s31}

  @ Damage
  vmov.f32    r5, s6
  subs        r5, r5, #1
  mov         r6, #4
  mov         r7, #3
  mla         r5, r7, r5, r6

  @ Translate & rotate
  vneg.f32    s1, s1
  ldr         r0, =mtx_model
  bl          mat4_translate
  vmov.f32    s0, s4
  ldr         r0, =mtx_temp
  bl          mat4_rot_z
  ldr         r0, =mtx_model
  ldr         r1, =mtx_temp
  ldr         r2, =mtx_model
  bl          mat4_mul_mat4

  @ Compute MVP matrix
  ldr         r0, =mtx_vp
  ldr         r1, =mtx_model
  ldr         r2, =mtx_mvp
  bl          mat4_mul_mat4

  @ Draw the enemy
  ldr         r0, =enemy_vtx
  ldr         r1, =enemy_idx
  mov         r2, r5
  ldr         r3, =mtx_mvp
  ldr         r4, =light_dir
  bl          gfx_draw_trgs

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Creates a randomized enemy
@ Arguments:
@   r10 - index of enemy
@ Returns:
@   s0 - s5: enemy data
@ Clobbers:
@   Increments r10
@ ------------------------------------------------------------------------------
spawn_enemy:
  stmfd       sp!, {lr}

  @ Randomize x in range [-8.5, 8.5]
  bl          random
  and         r1, r0, #0xFF
  sub         r1, r1, #0x7F
  vmov.f32    s8, r1
  fsitos      s8, s8
  ldr         r1, =0x41400000
  vmov.f32    s9, r1
  vdiv.f32    s0, s8, s9

  @ Randomize y in range [-2.8, 5.2]
  bl          random
  and         r1, r0, #0xFF
  sub         r1, r1, #0x7F
  vmov.f32    s8, r1
  fsitos      s8, s8
  ldr         r1, =0x42700000
  vmov.f32    s9, r1
  vdiv.f32    s1, s8, s9

  @ Set z to -300.0
  ldr         r0, =0xc3960000
  vmov.f32    s2, r0

  vmov.f32    s8, r10
  fsitos      s8, s8
  ldr         r0, =0x40a00000
  vmov.f32    s9, r0
  vmul.f32    s8, s8, s9
  ldr         r0, =0xc2480000
  vmov.f32    s9, r0
  vsub.f32    s3, s9, s8

  @ Rotation
  ldr         r0, =0
  vmov.f32    s4, r0

  @ Random lateral movement
  ldr         r0, =0x3e4ccccd
  vmov.f32    s5, r0
  bl          random
  tst         r0, #1
  vnegne.f32  s5, s5
  and         r0, #1
  add         r0, #1
  vmov.f32    s8, r0
  fsitos      s8, s8
  vmul.f32    s5, s5, s8

  @ Lives
  mov         r0, #5
  vmov.f32    s6, r0

  @ Bullet timer
  mov         r1, #15
  add         r10, r10, #1
  mul         r0, r1, r10
  vmov.f32    s7, r0

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Updates a flare
@ Arguments:
@   none
@ Returns:
@   none
@ Clobers:
@   none
@ ------------------------------------------------------------------------------
update_flare:
  stmfd       sp!, {lr}

  vadd.f32    s0, s0, s3
  vadd.f32    s1, s1, s4
  vadd.f32    s2, s2, s5

  @ Check whether bullet is still active
  ldr         r0, =0xc1200000
  vmov.f32    s31, r0
  vcmp.f32    s2, s31
  fmstat
  ldmltfd     sp!, {pc}

  @ Despawn bullet and damage player
  mov         r0, #0
  vmov.f32    s6, r0

  ldr         r0, =player_pos
  vldm.f32    r0, {s30 - s31}
  vsub.f32    s7, s0, s30
  vabs.f32    s7, s7
  vsub.f32    s8, s1, s31
  vabs.f32    s8, s8

  ldr         r0, =0x3fe00000
  vmov.f32    s9, r0

  vcmp.f32    s7, s9
  fmstat
  ldmgtfd     sp!, {pc}
  vcmp.f32    s8, s9
  fmstat
  ldmgtfd     sp!, {pc}

  ldr         r0, =player_rolling
  ldr         r0, [r0]
  tst         r0, r0
  ldmnefd     sp!, {pc}

  mov         r0, #20
  bl          player_damage
1:
  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Draws a flare
@ Arguments:
@   s0 - s2: position
@ Returns:
@   none
@ Clobers:
@   none
@ ------------------------------------------------------------------------------
draw_flare:
  stmfd       sp!, {lr}

  ldr         r0, =mtx_model
  vneg.f32    s1, s1
  bl          mat4_translate

  ldr         r0, =mtx_vp
  ldr         r1, =mtx_model
  ldr         r2, =mtx_mvp
  bl          mat4_mul_mat4

  @ Draw a sprite
  ldr         r0, =flare
  ldr         r1, =mtx_mvp
  ldr         r2, =mtx_view
  ldr         r3, =0x3e800000
  vmov.f32    s0, r3
  vmov.f32    s1, r3
  bl          gfx_draw_sprite

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Fires a flare
@ Arguments:
@   none
@ Returns:
@   none
@ Clobers:
@   none
@ ------------------------------------------------------------------------------
fire_flare:
  stmfd       sp!, {r11 - r12, lr}

  @ Load player position
  ldr         r11, =player_pos
  vldm.f32    r11, {s22 - s24}

  @ Draw flares shot by enemies
  ldr         r11, =FLARE_COUNT
  ldr         r12, =flares
1:
  vldm.f32    r12, {s25 - s31}
  vmov.f32    r0, s31
  tst         r0, r0
  addne       r12, r12, #28
  beq         2f
  subs        r11, r11, #1
  bne         1b

2:
  @ Position
  vmov.f32    s25, s0
  vmov.f32    s26, s1
  vmov.f32    s27, s2
  vabs.f32    s21, s27

  @ Direction
  vsub.f32    s28, s22, s25
  vsub.f32    s29, s23, s26
  vsub.f32    s30, s24, s27

  vdiv.f32    s28, s28, s21
  vdiv.f32    s29, s29, s21
  vdiv.f32    s30, s30, s21

  @ Live flag
  mov         r0, #1
  vmov.f32    s31, r0

  vstm.f32    r12!, {s25 - s31}

  ldmfd       sp!, {r11 - r12, pc}

@-------------------------------------------------------------------------------
@ Resets enemies
@-------------------------------------------------------------------------------
reset_enemies:
  stmfd       sp!, {r0 - r2, lr}
  vstmdb.f32  sp!, {s0 - s7}

  ldr         r2, =ENEMY_COUNT
  ldr         r1, =enemies
  mov         r0, #0

1:
  vldm.f32    r1, {s0 - s7}

  vmov.f32    s0, r0
  vmov.f32    s1, r0
  vmov.f32    s2, r0
  vmov.f32    s3, r0
  vmov.f32    s4, r0
  vmov.f32    s5, r0
  vmov.f32    s6, r0
  vmov.f32    s7, r0

  vstm.f32    r1!, {s0 - s7}

  subs        r2, #1
  bne         1b

  vldmia.f32  sp!, {s0 - s7}
  ldmfd       sp!, {r0 - r2, pc}

@-------------------------------------------------------------------------------
@ Resets flares
@-------------------------------------------------------------------------------
reset_flares:
  stmfd       sp!, {r0 - r3, lr}
  vstmdb.f32  sp!, {s0 - s6}

  ldr         r3, =FLARE_COUNT
  ldr         r2, =flares
  ldr         r1, =0xC1A00000      @ r1 = -20
  mov         r0, #0

1:
  vldm.f32    r2, {s0 - s6}

  vmov.f32    s0, r0
  vmov.f32    s1, r0
  vmov.f32    s2, r1
  vmov.f32    s3, r0
  vmov.f32    s4, r0
  vmov.f32    s5, r0
  vmov.f32    s6, r0

  vstm.f32    r2!, {s0 - s6}

  subs        r3, #1
  bne         1b

  vldmia.f32  sp!, {s0 - s6}
  ldmfd       sp!, {r0 - r3, pc}


================================================
FILE: game.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global setup_game
.global mtx_proj
.global mtx_model
.global mtx_view
.global mtx_temp
.global mtx_vp
.global mtx_mvp
.global mtx_mp
.global mtx_id
.global player_pos
.global light_dir
.global wrench
.global rocket
.global bullet
.global flare
.global score
.global building_small
.global building_medium
.global building_large
.global mountains
.global tristan
.global monkey

.include "ports.s"

.section .data
@ ------------------------------------------------------------------------------
@ Common assets
@ ------------------------------------------------------------------------------
bullet:          .incbin  "assets/bullet.bin"
rocket:          .incbin  "assets/rocket.bin"
wrench:          .incbin  "assets/wrench.bin"
flare:           .incbin  "assets/flare.bin"
mountains:       .incbin  "assets/mountains.bin"
tristan:         .incbin  "assets/tristan.bin"
monkey:          .incbin  "assets/monkey.bin"
pixfox:          .incbin  "assets/pifox.bin"

@ ------------------------------------------------------------------------------
@ Pad ascii to a fixed width
@ ------------------------------------------------------------------------------
.macro death_msg x, string
8:
  .long \x
  .ascii "\string"
9:
  .iflt 64 - (9b - 8b)
    .error "String too long"
  .endif
  .ifgt 64 - (9b - 8b)
    .zero 64 - (9b - 8b)
  .endif
.endm

@ ------------------------------------------------------------------------------
@ Transformation matrices
@ ------------------------------------------------------------------------------
mtx_proj:        .float 1.810660, 0.0,        0.0,        0.0
                 .float 0.0,      2.4142136,  0.0,        0.0
                 .float 0.0,      0.0,       -1.0040080, -1.0
                 .float 0.0,      0.0,       -2.0040080,  0.0
mtx_model:       .float 1.0,      0.0,        0.0,        0.0
                 .float 0.0,      1.0,        0.0,        0.0
                 .float 0.0,      0.0,        1.0,        0.0
                 .float 0.0,      0.0,        0.0,        1.0
mtx_view:        .float 1.0,      0.0,        0.0,        0.0
                 .float 0.0,      1.0,        0.0,        0.0
                 .float 0.0,      0.0,        1.0,        0.0
                 .float 0.0,      0.0,        0.0,        1.0
mtx_temp:        .space 64, 0
mtx_vp:          .space 64, 0
mtx_mvp:         .space 64, 0
mtx_mp:          .space 64, 0
mtx_id:          .float 1.0, 0.0, 0.0, 0.0
                 .float 0.0, 1.0, 0.0, 0.0
                 .float 0.0, 0.0, 1.0, 0.0
                 .float 0.0, 0.0, 0.0, 1.0

@ ------------------------------------------------------------------------------
@ Light direction
@ ------------------------------------------------------------------------------
light_dir:       .float 0.3, -0.57, -0.57, 0.0

.section .text
@ ------------------------------------------------------------------------------
@ Enables interrupts & starts the game
@ ------------------------------------------------------------------------------
setup_game:
  mov         r0, #0xDF
  msr         cpsr, r0
  b           start_loop

@ ------------------------------------------------------------------------------
@ Start screen displayed on boot
@ ------------------------------------------------------------------------------
start_loop:
  ldr         r0, =0xFF441111
  bl          gfx_clear

  @ Draw pifox title card
  ldr         r0, =pixfox
  mov         r1, #203
  mov         r2, #30
  bl          gfx_draw_image

  bl          draw_sprites
  bl          draw_start
  bl          gfx_swap

  bl          update_sound
  bl          update_input

  @ Loop until start is pressed
  ldr         r0, =button_pressed
  ldr         r0, [r0]
  tst         r0, #0x08
  beq         start_loop
  b           game_loop


@ ------------------------------------------------------------------------------
@ Death screen
@ ------------------------------------------------------------------------------
death_loop:

  @ Clear sounds 
  bl          snd_stop_roll
  bl          snd_stop_bullet
  bl          snd_stop_rock
  bl          snd_stop_crash
  bl          snd_stop_rocket
  bl          snd_stop_cantlet
  bl          snd_stop_pickup

  @ Play fail sound
  bl          snd_play_fail

  @ Record high score
  ldr         r1, =player_score
  ldr         r1, [r1]
  ldr         r0, =player_high_score
  ldr         r0, [r0]
  cmp         r1, r0
  ldrgt       r0, =player_high_score
  strgt       r1, [r0]

  @ Generate random death message index
  bl          random
  and         r1, r0, #0x3
1:
  ldr         r0, =0xFF0044ff
  bl          gfx_clear
  bl          draw_death
  bl          draw_sprites
  bl          draw_start
  bl          gfx_swap

  bl          update_sound
  bl          update_input

  @ Loop until start is pressed
  ldr         r0, =button_pressed
  ldr         r0, [r0]
  tst         r0, #0x08
  beq         1b

  bl          snd_stop_fail
  bl          reset_enemies
  bl          reset_flares
  bl          reset_rockets 
  bl          reset_objects
  bl          reset_bullets
  bl          reset_player_mov

  b           game_loop

@-------------------------------------------------------------------------------
@ Pause loop
@-------------------------------------------------------------------------------
pause_loop:
  stmfd       sp!, {lr}

1:
  bl          update_input  
  bl          update_sound

  @ Loop until select is pressed
  ldr         r0, =button_clicked
  ldr         r0, [r0]
  tst         r0, #0x04
  beq         1b

  ldmfd       sp!, {pc}  

@ ------------------------------------------------------------------------------
@ Main game loop
@ ------------------------------------------------------------------------------
game_loop:
  bl          setup_player
  bl          setup_enemies
  bl          snd_play_rock

1:
  bl          gfx_swap
  bl          update_input
  
  ldr         r0, =button_clicked
  ldr         r0, [r0]
  tst         r0, #0x04
  blne        pause_loop

  bl          update_player
  bl          update_sound
  bl          draw_pillars
  bl          draw_enemies
  bl          draw_rocks
  bl          draw_bullets
  bl          draw_rockets
  bl          draw_player
  bl          draw_fps
  bl          draw_high_score

  ldr         r0, =player_health
  ldr         r0, [r0]
  cmp         r0, #0
  bgt         1b

  b           death_loop

.section .data
@ ------------------------------------------------------------------------------
@ FPS counter
@ ------------------------------------------------------------------------------
frame_counter:
  .long 0
last_frame:
  .long 0
fps:
  .long 0

.section .text
@ ------------------------------------------------------------------------------
@ Renders the FPS counter on screen
@ ------------------------------------------------------------------------------
draw_fps:
  stmfd       sp!, {lr}

  @ Updates the FPS counter
  ldr         r0, =last_frame
  ldr         r1, [r0]
  ldr         r2, =1000000
  add         r1, r1, r2            @ Update fps once every second

  ldr         r2, =STIMER_CLO       @ Read system timer
  ldr         r2, [r2]

  ldr         r3, =frame_counter    @ Increment frame count
  ldr         r4, [r3]
  add         r4, r4, #1
  str         r4, [r3]

  cmp         r1, r2
  bgt         1f                    @ If 1s passed, update fps
  ldr         r1, =fps
  str         r4, [r1]
  str         r2, [r0]
  mov         r4, #0
1:
  str         r4, [r3]

  @ Renders the FPS on screen
  ldr         r0, =1f
  mov         r1, #15
  mov         r2, #10
  ldr         r3, =0xFFFFFFFF
  ldr         r4, =fps
  ldr         r4, [r4]
  push        {r4}
  ldr         r4, =player_score
  ldr         r4, [r4]
  push        {r4}
  bl          printf
  add         sp, sp, #8

  ldmfd       sp!, {pc}
1:
  .ascii "Score: %5d\nFPS: %2d"
  .byte  0x0
  .align 2

@ ------------------------------------------------------------------------------
@ Renders the high score for this session
@ ------------------------------------------------------------------------------
draw_high_score:
  stmfd       sp!, {lr}
  
  ldr         r0, =1f
  mov         r1, #512
  sub         r1, r1, #23
  mov         r2, #10
  ldr         r3, =0xFFFFFFFF
  ldr         r4, =player_high_score
  ldr         r4, [r4]
  push        {r4}
  bl          printf
  add         sp, sp, #4
  
  ldmfd       sp!, {pc}
1:
  .ascii "High Score: %5d\n"
  .byte 0x0
  .align 2


@ ------------------------------------------------------------------------------
@ Renders the welcome message
@ ------------------------------------------------------------------------------
draw_start:
  stmfd       sp!, {r0 - r3, lr}

  ldr         r0, =1f
  mov         r1, #240
  mov         r2, #400
  ldr         r3, =0xFFFFFFFF
  bl          printf

  ldmfd       sp!, {r0 - r3, pc}
1:
  .ascii "Press START to begin"
  .byte  0x0
  .align 2

@ ------------------------------------------------------------------------------
@ Prints the death message
@ Arguments:
@   r0 - Random number between 0 and number of strings
@ Returns:
@   Nothing
@ ------------------------------------------------------------------------------
draw_death:
  stmfd       sp!, {r0 - r4, lr}

  @ Draw death message text
  ldr         r0, =2f
  add         r0, r0, r1, lsl #6
  ldr         r1, [r0], #4
  mov         r2, #170
  ldr         r3, =0xFFFFFFFF
  bl          printf

  @ Draw score
  ldr         r0, =1f
  mov         r1, #272
  mov         r2, #300
  ldr         r3, =0xFFFFFFFF
  ldr         r4, =player_score
  ldr         r4, [r4]
  push        {r4}
  bl          printf

  add         sp, sp, #4

  ldmfd       sp!, {r0 - r4, pc}
1:
  .ascii "Score: %5d\0"
  .align 2
2:
  death_msg 200, "You have died, what a tragedy!\0"
  death_msg 172, "You have died, better luck next time!\0"
  death_msg 124, "You have died, maybe you should go back to Mario?\0"
  death_msg 184, "You have died, give it another go?\0"

.section .text
@ ------------------------------------------------------------------------------
@ Prints rotating sprites
@ Arguments:
@   none
@ Returns:
@   none
@ Clobbers:
@   s0 - s31
@ ------------------------------------------------------------------------------
draw_sprites:
  stmfd       sp!, {r0 - r4, lr}

  @ Clear model matrix
  ldr         r0, =mtx_id
  vldm.f32    r0, {s0 - s15}
  ldr         r0, =mtx_model
  vstm.f32    r0, {s0 - s15}
  ldr         r0, =mtx_temp
  vstm.f32    r0, {s0 - s15}

  @ Compute the proj - view matrix
  ldr         r0, =0
  vmov.f32    s0, r0
  ldr         r0, =0
  vmov.f32    s1, r0
  ldr         r0, =0xc0e00000
  vmov.f32    s2, r0
  ldr         r0, =mtx_view
  bl          mat4_translate

  ldr         r0, =mtx_proj
  ldr         r1, =mtx_view
  ldr         r2, =mtx_vp
  bl          mat4_mul_mat4

  @ Prepare the model matrix
  ldr         r0, =0x3f800000     @ 1.0
  vmov.f32    s0, r0
  ldr         r0, =0
  vmov.f32    s1, r0
  ldr         r0, =0
  vmov.f32    s2, r0
  ldr         r0, =mtx_model
  bl          mat4_translate

  @ Update rotation around y
  ldr         r0, =1f
  vldr.f32    s0, [r0]
  ldr         r1, =0x3d000000     @ 0.03125
  vmov.f32    s1, r1
  vadd.f32    s0, s1
  vstr.f32    s0, [r0]
  ldr         r0, =mtx_temp
  bl          mat4_rot_y
  ldr         r0, =mtx_temp
  ldr         r1, =mtx_model
  ldr         r2, =mtx_model
  bl          mat4_mul_mat4

  @ Rotate around y by 120 degrees
  ldr         r0, =0x40060a92     @ 2 * PI / 3
  vmov.f32    s0, r0
  ldr         r0, =mtx_temp
  bl          mat4_rot_y

  @ Draw a bullet
  ldr         r0, =mtx_vp
  ldr         r1, =mtx_model
  ldr         r2, =mtx_mvp
  bl          mat4_mul_mat4
  ldr         r0, =bullet
  ldr         r1, =mtx_mvp
  ldr         r2, =mtx_model      @ don't ask me why
  ldr         r3, =0x3e800000
  vmov.f32    s0, r3
  vmov.f32    s1, r3
  bl          gfx_draw_sprite

  @ Draw a rocket
  ldr         r0, =mtx_temp
  ldr         r1, =mtx_model
  ldr         r2, =mtx_model
  bl          mat4_mul_mat4
  ldr         r0, =mtx_vp
  ldr         r1, =mtx_model
  ldr         r2, =mtx_mvp
  bl          mat4_mul_mat4
  ldr         r0, =rocket
  ldr         r1, =mtx_mvp
  ldr         r2, =mtx_model
  ldr         r3, =0x3e800000
  vmov.f32    s0, r3
  vmov.f32    s1, r3
  bl          gfx_draw_sprite

  @ Draw a wrench
  ldr         r0, =mtx_temp
  ldr         r1, =mtx_model
  ldr         r2, =mtx_model
  bl          mat4_mul_mat4
  ldr         r0, =mtx_vp
  ldr         r1, =mtx_model
  ldr         r2, =mtx_mvp
  bl          mat4_mul_mat4
  ldr         r0, =wrench
  ldr         r1, =mtx_mvp
  ldr         r2, =mtx_model
  ldr         r3, =0x3e800000
  vmov.f32    s0, r3
  vmov.f32    s1, r3
  bl          gfx_draw_sprite

  ldmfd       sp!, {r0 - r4, pc}
1:
  .float      0.0


================================================
FILE: gfx.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global setup_gfx
.global gfx_fb
.global gfx_swap
.global gfx_clear
.global gfx_draw_trgs
.global gfx_draw_char
.global gfx_draw_text
.global gfx_draw_sprite
.global gfx_draw_image
.global gfx_draw_rect
.global gfx_draw_frame

.include "ports.s"

@ ------------------------------------------------------------------------------
@ Useful macros
@ ------------------------------------------------------------------------------
.macro vswap a, b, c
  vmov.f32    \c, \a
  vmov.f32    \a, \b
  vmov.f32    \b, \c
.endm

.macro swap a, b, c
  mov         \c, \a
  mov         \a, \b
  mov         \b, \c
.endm

.section .data
@ ------------------------------------------------------------------------------
@ Framebuffer structure
@ ------------------------------------------------------------------------------
.align 4
gfx_fb:
  .int 640    @ +0x00: Physical width
  .int 480    @ +0x04: Physical height
  .int 640    @ +0x08: Virtual width
  .int 480    @ +0x0C: Virtual height
  .int 0      @ +0x10: Pitch
  .int 32     @ +0x14: Bit depth
  .int 0      @ +0x18: X
  .int 0      @ +0x1C: Y
  .int 0      @ +0x20: Address
  .int 0      @ +0x24: Size
.align 2

@-------------------------------------------------------------------------------
@ Font bitmap
@ Uses 8x16 bits per character, stored left to right top to bottom
@ 1 - white pixel
@ 0 - black pixel
@-------------------------------------------------------------------------------
.align 4
font:
  .incbin "assets/font.bin"
.align 2

.section .text
@ ------------------------------------------------------------------------------
@ Initialises the framebuffer, retrieving its address
@ ------------------------------------------------------------------------------
setup_gfx:
  stmfd     sp!, {lr}

  @ Request a framebuffer config
  ldr       r0, =0x1
  ldr       r1, =gfx_fb
  orr       r1, #0x40000000
  bl        mbox_write
  bl        mbox_read

  ldmfd     sp!, {pc}

@ ------------------------------------------------------------------------------
@ Clears the screen and depth buffer, setting depth to -1.0f and filling the
@ colour buffer to a given value
@
@ Arguments:
@   r0 - colour
@ Returns:
@   none
@ Clobbers:
@   s0 - s16
@ ------------------------------------------------------------------------------
gfx_clear:
  stmfd     sp!, {r0 - r3}
  ldr       r3, =0x3f800000

  @ Clear registers
  vmov.f32  s0,  r0
  vmov.f32  s1,  r0
  vmov.f32  s2,  r0
  vmov.f32  s3,  r0
  vmov.f32  s4,  r0
  vmov.f32  s5,  r0
  vmov.f32  s6,  r0
  vmov.f32  s7,  r0
  vmov.f32  s8,  r0
  vmov.f32  s9,  r0
  vmov.f32  s10, r0
  vmov.f32  s11, r0
  vmov.f32  s12, r0
  vmov.f32  s13, r0
  vmov.f32  s14, r0
  vmov.f32  s15, r0

  @ Clear 8 pixels at once
  ldr       r0, =gfx_buffer
  ldr       r2, =640 * 480
1:
  vstm.f32  r0!, {s0 - s15}
  subs      r2, r2, #16
  bne       1b

  ldmfd     sp!, {r0 - r3}
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Copies color data from back buffer to framebuffer (color data is interleaved
@ with depth data, so it must be extracted). Sets back buffer to the default
@ colour and depth. Every iteration of the loop processes 8 pixels. pld is used
@ to hint the CPU to prefetch data
@ Arguments:
@   none
@ Returns:
@   none
@ Clobbers:
@   s0 - s31
@ ------------------------------------------------------------------------------
gfx_swap:
  stmfd     sp!, {r0 - r12, lr}

  @ Compute addreses
  ldr       r0, =gfx_fb
  ldr       r0, [r0, #0x20]
  ldr       r1, =gfx_buffer

  @ Top half of the screen - blue gradient
  ldr       r12, =0xffffee00
  ldr       r11, =240
1:
  ldr       r2, =640 * 4
  ldr       r4, =0x00020200
  sub       r12, r12, r4
  vmov.f32  s0, r12
  vmov.f32  s1, r12
  vmov.f32  s2, r12
  vmov.f32  s3, r12
  vmov.f32  s4, r12
  vmov.f32  s5, r12
  vmov.f32  s6, r12
  vmov.f32  s7, r12
  vmov.f32  s8, r12
  vmov.f32  s9, r12
  vmov.f32  s10, r12
  vmov.f32  s11, r12
  vmov.f32  s12, r12
  vmov.f32  s13, r12
  vmov.f32  s14, r12
  vmov.f32  s15, r12

  @ Fill 4 lines with one colour
2:
  pld       [r1, #0x100]

  vldm.f32  r1, {s16 - s31}
  vstm.f32  r0!, {s16 - s31}
  vstm.f32  r1!, {s0 - s15}

  subs      r2, r2, #16
  bne       2b

  subs      r11, r11, #4
  bne       1b

  @ Bottom half of the screen - green gradient
  ldr       r12, =0xFF8C9C63
  ldr       r11, =240

3:
  ldr       r2, =640 * 4
  vmov.f32  s0, r12
  vmov.f32  s1, r12
  vmov.f32  s2, r12
  vmov.f32  s3, r12
  vmov.f32  s4, r12
  vmov.f32  s5, r12
  vmov.f32  s6, r12
  vmov.f32  s7, r12
  vmov.f32  s8, r12
  vmov.f32  s9, r12
  vmov.f32  s10, r12
  vmov.f32  s11, r12
  vmov.f32  s12, r12
  vmov.f32  s13, r12
  vmov.f32  s14, r12
  vmov.f32  s15, r12

  ldr       r4, =0x00010101
  sub       r12, r12, r4

  @ Fill 4 lines
4:
  pld       [r1, #0x100]

  vldm.f32  r1, {s16 - s31}
  vstm.f32  r0!, {s16 - s31}
  vstm.f32  r1!, {s0 - s15}

  subs      r2, r2, #16
  bne       4b

  subs      r11, r11, #4
  bne       3b

  @ Draw mountains
  ldr          r0, =mountains
  mov          r1, #0
  mov          r2, #110
  bl           gfx_draw_image

  ldmfd     sp!, {r0 - r12, pc}

@-------------------------------------------------------------------------------
@ Renders a character to the screen
@ Arguments:
@   r0 - Character
@   r1 - x coordinate
@   r2 - y coordinate
@   r3 - RGBA
@ Returns:
@   none
@ Clobbers:
@   r0 - r12
@-------------------------------------------------------------------------------
gfx_draw_char:
  cmp       r0, #127              @ guard against invalid character inputs
  movgt     pc, lr
  stmfd     sp!, {r0 - r10}

  @ calculate start address of the character
  ldr       r9, =font
  add       r9, r9, r0, lsl #4    @ r3 = font + ASCII value * 16

  @ Store address of the buffer & size of the pitch
  ldr       r6, =gfx_buffer
  ldr       r7, =gfx_fb
  ldr       r7, [r7, #0x10]       @ r7 = pitch

  @ loop through the rows
1:
  ldrb      r4, [r9]              @ load byte
  @ loop through the columns
  mov       r5, #0
2:
  tst       r4, #1
  beq       3f

  @ draw pixel at (x + r5, y)
  add       r10, r1, r5
  mla       r8, r7, r2, r6        @ r8 = gfx_buffer + y * pitch
  add       r8, r8, r10, lsl #2   @ r8 = gfx_buffer + y * pitch + (x + r5) * 8

  str       r3, [r8]
3:
  add       r5, #1
  lsr       r4, #1
  cmp       r5, #8
  blt       2b

  add       r2, #1                @ y = y + 1
  add       r9, #1                @ process next byte
  tst       r9, #15               @ check if address of the next char reached
  bne       1b

  ldmfd     sp!, {r0 - r10}
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Renders text on screen
@ Arguments:
@   r0 - Address of null terminated string
@   r1 - X position
@   r2 - Y position
@   r3 - RGBA
@ Returns:
@   none
@ Clobbers:
@   none
@ ------------------------------------------------------------------------------
gfx_draw_text:
  stmfd     sp!, {r0 - r5, lr}
  mov       r4, r0
  mov       r5, r1                @ save intial x coordinate

1:
  ldrb      r0, [r4]              @ load next character
  add       r4, #1
  cmp       r0, #0                @ check if '\0' reached
  beq       2f

  cmp       r0, #9                @ check if '\t'
  addeq     r1, #64               @ move 8 characters horizontally
  beq       1b

  cmp       r0, #10               @ check if '\n'
  moveq     r1, r5
  addeq     r2, #16               @ go to the new line
  beq       1b

  bl        gfx_draw_char
  add       r1, #8
  b         1b
2:
  ldmfd     sp!, {r0 - r5, pc}
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Renders a textured sprite on screen
@ Arguments:
@   r0 - address of the texture
@   r1 - Projection * view matrix
@   r2 - View matrix
@   s0 - width of the quad
@   s1 - height of the quad
@ Returns:
@   none
@ Clobbers:
@   s0 - s31
@ ------------------------------------------------------------------------------
gfx_draw_sprite:
  stmfd       sp!, {r0 - r12, lr}

  @ Get right vector
  vldr.f32    s25, [r2]
  vldr.f32    s26, [r2, #0x10]
  vldr.f32    s27, [r2, #0x20]
  vmul.f32    s25, s25, s0
  vmul.f32    s26, s26, s0
  vmul.f32    s27, s27, s0

  @ Get up vector
  vldr.f32    s28, [r2, #0x04]
  vldr.f32    s29, [r2, #0x14]
  vldr.f32    s30, [r2, #0x24]
  vmla.f32    s25, s28, s1
  vmla.f32    s26, s29, s1
  vmla.f32    s27, s30, s1

  vldm.f32    r1, {s0 - s16}

  @ Top left
  ldr         r3, =0x3f800000
  vmov.f32    s31, r3
  vmov.f32    s16, s25
  vmov.f32    s17, s26
  vmov.f32    s18, s27
  vmov.f32    s19, s31
  bl          mat4_fmul_vec4
  bl          transform_vertex

  @ Bottom right
  vneg.f32    s16, s25
  vneg.f32    s17, s26
  vneg.f32    s18, s27
  vmov.f32    s19, s31
  vmov.f32    s28, s20
  vmov.f32    s26, s21
  vmov.f32    s30, s22
  bl          mat4_fmul_vec4
  bl          transform_vertex
  vmov.f32    s29, s20
  vmov.f32    s25, s21

  @ Check depth range
  ldr         r5, =0xbf800000
  vmov.f32    s21, r5
  vcmp.f32    s30, s21
  fmstat
  ldmltfd     sp!, {r0 -r12, pc}

  vneg.f32    s21, s21
  vcmp.f32    s30, s21
  fmstat
  ldmgtfd     sp!, {r0 -r12, pc}

  mov         r7, #0
  vmov.f32    s2, r7                  @ pixelY
  vmov.f32    s3, r7                  @ pixelX

  ldr         r5, =479
  ftosizs     s24, s26
  vmov.f32    r1, s24
  cmp         r1, #0
  movlt       r1, #0
  vneglt.f32  s2, s26
  cmp         r1, r5
  movgt       r1, r5                  @ y0

  ftosizs     s24, s25
  vmov.f32    r2, s24
  cmp         r2, #0
  movlt       r2, #0
  cmp         r2, r5
  movgt       r2, r5                  @ y1

  ldr         r5, =639
  ftosizs     s24, s29
  vmov.f32    r3, s24
  cmp         r3, #0
  movlt       r3, #0
  vneglt.f32  s3, s29
  cmp         r3, r5
  movgt       r3, r5                  @ x0

  ftosizs     s24, s28
  vmov.f32    r4, s24
  cmp         r4, #0
  movlt       r4, #0
  cmp         r4, r5
  movgt       r4, r5                  @ x1

  ldr         r5, =gfx_fb
  ldr         r5, [r5, #0x10]         @ r5 = pitch

  ldr         r6, =gfx_buffer
  mla         r6, r5, r1, r6
  add         r6, r6, r3, lsl #2

  @ Check bounds
  subs        r2, r2, r1
  ble         3f
  cmp         r4, r3
  ble         3f

  ldr         r8, [r0], #4
  vmov.f32    s0, r8
  fsitos      s0, s0
  vsub.f32    s6, s25, s26            @ height
  vdiv.f32    s0, s0, s6

  ldr         r9, [r0], #4
  vmov.f32    s1, r9
  fsitos      s1, s1
  vsub.f32    s6, s28, s29            @ width
  vdiv.f32    s1, s1, s6
  lsl         r9, r9, #2

  ldr         r7, =0x3f800000         @ 1.0f
  vmov.f32    s4, r7

  @ Loop over scanline
1:
  mov         r12, r6
  subs        r7, r4, r3

  vmul.f32    s6, s2, s0
  ftosizs     s6, s6
  vmov.f32    r1, s6
  vmov.f32    s7, s3
  mla         r8, r1, r9, r0
2:
  vmul.f32    s6, s7, s1
  ftosizs     s6, s6
  vmov.f32    r1, s6

  @ Texture lookup
  ldr         r11, [r8, r1, lsl #2]
  lsrs        r1, r11, #24
  strne       r11, [r6]
  add         r6, #4
  vadd.f32    s7, s7, s4
  subs        r7, r7, #1
  bgt         2b

  add         r6, r12, r5
  vadd.f32    s2, s2, s4
  subs        r2, r2, #1
  bgt         1b
3:
  ldmfd       sp!, {r0 -r12, pc}

@ ------------------------------------------------------------------------------
@ Draws an image on screen
@ Arguments:
@   r0 - image
@   r1 - x on screen
@   r2 - y on screen
@ ------------------------------------------------------------------------------
gfx_draw_image:
  cmp         r1, #640
  movge       pc, lr
  cmp         r2, #480
  movge       pc, lr

  stmfd       sp!, {r0 - r12, lr}

  ldr         r4, [r0], #4           @ Width
  ldr         r3, [r0], #4           @ Height

  cmn         r0, r3
  stmmifd     sp!, {r0 - r12, pc}
  cmn         r1, r4
  stmmifd     sp!, {r0 - r12, pc}

  ldr         r10, =gfx_fb
  ldr         r10, [r10, #0x10]       @ r10 = pitch
  lsl         r12, r3, #2             @ image pitch
  ldr         r11, =gfx_buffer        @ buffer

  tst         r1, r1
  addmi       r3, r3, r1
  submi       r0, r0, r1, lsl #2      @ Clamp left
  movmi       r1, #0

  tst         r2, r2
  addmi       r4, r4, r2
  negmi       r2, r2
  mlami       r0, r12, r2, r0
  movmi       r2, #0                  @ Clamp top

  add         r6, r1, r3
  cmp         r6, #640
  subge       r6, #640
  subge       r3, r3, r6

  add         r6, r2, r4
  cmp         r6, #480
  subge       r6, #480
  subge       r4, r4, r6

  mla         r11, r2, r10, r11
  add         r11, r11, r1, lsl #2

  tst         r1, r1
  addmi       r3, r3, r1
  negmi       r1, r1
1:
  mov         r7, r0
  mov         r8, r11
  mov         r6, r3
2:
  ldr         r9, [r0], #4
  tst         r9, #0xFF000000
  strne       r9, [r11], #4
  addeq       r11, r11, #4

  subs        r6, r6, #1
  bne         2b

  add         r0, r7, r12
  add         r11, r8, r10
  subs        r4, r4, #1
  bne         1b

  ldmfd       sp!, {r0 - r12, pc}

@ ------------------------------------------------------------------------------
@ Draws a line using Bresenham's algorithm
@   r0 - x0
@   r1 - y0
@   r2 - x1
@   r3 - y1
@   r4 - colour
@ Returns:
@   none
@ Clobbers:
@   none
@ ------------------------------------------------------------------------------
gfx_draw_line:
  stmfd       sp!, {r0 - r12, lr}

  subs        r5, r2, r0
  neglt       r5, r5                  @ r5 = dx = abs(x1 - x0)
  subs        r6, r3, r1
  neggt       r6, r6                  @ r6 = -dy = -abs(y1 - y0)

  cmp         r0, r2
  movle       r7, #1
  movgt       r7, #-1                 @ r7 = x0 < x1 ? 1 : -1
  cmp         r1, r3
  movle       r8, #1
  movgt       r8, #-1                 @ r8 = y0 < y1 ? 1 : -1

  adds        r9, r5, r6              @ err = dx - dy

  ldr         r10, =gfx_fb
  ldr         r10, [r10, #0x10]       @ r10 = pitch

  ldr         r11, =gfx_buffer
1:
  cmp         r0, #0
  blt         2f
  cmp         r1, #0
  blt         2f

  ldr         r12, =639
  cmp         r0, r12
  bge         2f
  ldr         r12, =479
  cmp         r1, r12
  bge         2f

  mla         r12, r10, r1, r11
  add         r12, r12, r0, lsl #3    @ emit (x0, y0)
  str         r4, [r12]
2:
  teq         r0, r2
  teqeq       r1, r3                  @ bail if x0 == x1 && y0 == y1
  beq         3f

  lsl         r12, r9, #1
  cmp         r12, r6
  addgt       r9, r9, r6
  addgt       r0, r0, r7

  cmp         r12, r5
  addlt       r9, r9, r5
  addlt       r1, r1, r8

  b           1b
3:
  ldmfd       sp!, {r0 - r12, pc}

@ ------------------------------------------------------------------------------
@ Renders a list of triangles
@ Arguments:
@   r0 - Vertex data
@   r1 - Index data
@   r2 - Number of triangles
@   r3 - MVP matrix
@   r4 - Light direction
@ Returns:
@   none
@ Clobbers:
@   s0 - s31
@ ------------------------------------------------------------------------------
gfx_draw_trgs:
  stmfd       sp!, {r0 - r12, lr}

1:
  subs        r2, r2, #1
  blt         3f

  ldm         r1!, {r10, r11, r12}  @ Indices
  vldm.f32    r3, {s0 - s15}        @ MVP matrix
  eor         r5, r5, r5            @ Number of visible vertices
  add         r1, #12

  add         r10, r0, r10, lsl #4
  vldm.f32    r10, {s16 - s19}      @ r10 = &v0.xyz
  bl          mat4_fmul_vec4
  bl          transform_vertex
  vmov.f32    s30, s22
  vmov.f32    s29, s21
  vmov.f32    s28, s20              @ v0

  add         r11, r0, r11, lsl #4
  vldm.f32    r11, {s16 - s19}      @ r11 = &v1.xyz
  bl          mat4_fmul_vec4
  bl          transform_vertex
  vmov.f32    s27, s22
  vmov.f32    s26, s21
  vmov.f32    s25, s20              @ v1

  add         r12, r0, r12, lsl #4
  vldm.f32    r12, {s16 - s19}      @ r12 = &v2.xyz
  bl          mat4_fmul_vec4
  bl          transform_vertex
  vmov.f32    s24, s22
  vmov.f32    s23, s21
  vmov.f32    s22, s20              @ v2

  @ If at least one vertex is not clipped, continue
  tst         r5, r5
  bne         2f

  @ Check whether triangle intersects the viewport
  vcmp.f32    s22, #0
  fmstat
  vcmplt.f32  s25, #0
  fmstat
  vcmplt.f32  s28, #0
  fmstat
  blt         1b

  vcmp.f32    s23, #0
  fmstat
  vcmplt.f32  s26, #0
  fmstat
  vcmplt.f32  s29, #0
  fmstat
  blt         1b

  ldr         r5, =0xbf800000
  vmov.f32    s21, r5
  vcmp.f32    s24, s21
  fmstat
  vcmplt.f32  s27, s21
  fmstat
  vcmplt.f32  s30, s21
  fmstat
  blt         1b

  vneg.f32    s21, s21
  vcmp.f32    s24, s21
  fmstat
  vcmpgt.f32  s27, s21
  fmstat
  vcmpgt.f32  s30, s21
  fmstat
  bgt         1b

  ldr         r5, =0x43ef8000
  vmov.f32    s21, r5
  vcmp.f32    s23, s21
  fmstat
  vcmpgt.f32  s26, s21
  fmstat
  vcmpgt.f32  s29, s21
  fmstat
  bgt         1b

  ldr         r5, =0x441fc000
  vmov.f32    s21, r5
  vcmp.f32    s22, s21
  fmstat
  vcmpgt.f32  s25, s21
  fmstat
  vcmpgt.f32  s28, s21
  fmstat
  bgt         1b

  @ Cull back faces
2:
  vmul.f32    s0, s28, s26          @ Check if vertices are in ccw order
  vmla.f32    s0, s22, s29          @ (note: y coordinates are flipped)
  vmla.f32    s0, s25, s23          @ | x0 y0 1 |
  vmls.f32    s0, s22, s26          @ | x1 y1 1 | < 0
  vmls.f32    s0, s28, s23          @ | x2 y2 1 |
  vmls.f32    s0, s25, s29
  vmov.f32    r5, s0
  tst         r5, r5

  beq         1b
  bpl         1b

  bl          shade_triangle
  bl          draw_triangle
  b           1b
3:

  ldmfd       sp!, {r0 - r12, pc}

@ ------------------------------------------------------------------------------
@ Computes window coordinates from normalised device coordinates by performing
@ perspective division. Also checks if the vertex is visble on the screen.
@ If the vertex is not clipped, r5 is incremented.
@ Arguments:
@   s16 - s19: Vector
@ Returns:
@   s20 - s23: (x, y, z, w)
@ Clobbers:
@   s20 - s24, r5, r6
@ ------------------------------------------------------------------------------
transform_vertex:
  vcmp.f32    s23, #0
  fmstat
  ble         1f                @ w <= 0.0f

  vcmp.f32    s20, s23
  fmstat
  vcmple.f32  s21, s23
  fmstat
  vcmple.f32  s22, s23
  fmstat
  vneg.f32    s23, s23
  bgt         1f                @ x > w || y > w || z > w

  vcmp.f32    s23, s20
  fmstat
  vcmple.f32  s23, s21
  fmstat
  vcmple.f32  s23, s22
  fmstat
  addle       r5, #1            @ -w <= x || -w <= y || -w <= z
1:
  ldr         r6, =0x3f800000
  vmov.f32    s31, r6           @ 1.0f

  vdiv.f32    s20, s20, s23     @ x = (1.0f - x) * 320
  vsub.f32    s20, s31, s20
  ldr         r6, =0x43a00000
  vmov.f32    s24, r6           @ s24 = 320.0f
  vmul.f32    s20, s20, s24

  vdiv.f32    s21, s21, s23     @ y = (1.0f + y) * 240
  vadd.f32    s21, s31, s21
  ldr         r6, =0x43700000
  vmov.f32    s24, r6           @ s24 = 320.0f
  vmul.f32    s21, s21, s24

  vdiv.f32    s22, s22, s23     @ w = -w
  vneg.f32    s22, s22

  mov         pc, lr

@ ------------------------------------------------------------------------------
@ Performs per-polygon shading, computing a colour value for the entire
@ triangle
@ Arguments:
@   r10, r11, r12 - triangle indices
@ Returns:
@   r12 - colour value
@ Clobbers:
@   s0 - s15
@ ------------------------------------------------------------------------------
shade_triangle:
  vldm.f32      r10, {s0 - s2}      @ r10 = &v0.rgb
  vldm.f32      r11, {s3 - s5}      @ r11 = &v1.rgb
  vldm.f32      r12, {s6 - s8}      @ r12 = &v2.rgb

  @ Compute the normal vector
  vsub.f32      s0, s0, s3
  vsub.f32      s1, s1, s4
  vsub.f32      s2, s2, s5
  vsub.f32      s6, s6, s3
  vsub.f32      s7, s7, s4
  vsub.f32      s8, s8, s5

  vmul.f32      s3, s1, s8
  vmls.f32      s3, s7, s2
  vmul.f32      s4, s6, s2
  vmls.f32      s4, s0, s8
  vmul.f32      s5, s0, s7
  vmls.f32      s5, s1, s6

  @ Normalize it
  vmul.f32      s0, s3, s3
  vmla.f32      s0, s4, s4
  vmla.f32      s0, s5, s5
  vsqrt.f32     s0, s0

  vdiv.f32      s3, s3, s0
  vdiv.f32      s4, s4, s0
  vdiv.f32      s5, s5, s0

  @ Compute the dot product between the normal
  @ and the light direction
  vldm.f32      r4, {s0 - s2}
  vmul.f32      s0, s0, s3
  vmla.f32      s0, s1, s4
  vmla.f32      s0, s2, s5

  @ Compute light intensity: ambient + diffuse
  ldr           r5, =0x3e4ccccd
  ldr           r6, =0x3f800000
  vmov.f32      s1, r5
  vmov.f32      s2, r6

  vcmp.f32      s0, #0
  fmstat
  vmovlt.f32    s0, s1
  vaddgt.f32    s0, s1

  ldr           r5, =0x437f0000
  vmov.f32      s1, r5
  vmul.f32      s0, s1

  sub           r1, #12
  vldm.f32      r1, {s3 - s5}
  add           r1, #12
  vmul.f32      s3, s3, s0
  vmul.f32      s4, s4, s0
  vmul.f32      s5, s5, s0

  ldr           r12, =0xff000000

  ftosizs       s3, s3
  vmov.f32      r11, s3
  cmp           r11, #0
  movlt         r11, #0
  andgt         r11, r11, #0xFF
  orr           r12, r12, r11, lsl #0

  ftosizs       s4, s4
  vmov.f32      r11, s4
  cmp           r11, #0
  movlt         r11, #0
  andgt         r11, r11, #0xFF
  orr           r12, r12, r11, lsl #8

  ftosizs       s5, s5
  vmov.f32      r11, s5
  cmp           r11, #0
  movlt         r11, #0
  andgt         r11, r11, #0xFF
  orr           r12, r12, r11, lsl #16

  mov           pc, lr

@ ------------------------------------------------------------------------------
@ Rasterises a triangle with colour interpolation
@ Arguments:
@   r10: v0 data
@   s28 - s30: v0(x, y, z)
@   r11: v1 data
@   s25 - s27: v1(x, y, z)
@   r12: v2 data
@   s22 - s24: v2(x, y, z)
@ Returns:
@   none
@ Clobers:
@   s0 - s31, r0 - r12
@ ------------------------------------------------------------------------------
draw_triangle:
  stmfd         sp!, {r0 - r4, lr}

  @ Sort the points: v0.y < v1.y < v2.y
  vcmp.f32      s29, s26
  fmstat
  ble           1f
  vswap         s28, s25, s16
  vswap         s29, s26, s16
  vswap         s30, s27, s16
1:
  vcmp.f32      s29, s23
  fmstat
  ble           1f
  vswap         s28, s22, s16
  vswap         s29, s23, s16
  vswap         s30, s24, s16
1:
  vcmp.f32      s26, s23
  fmstat
  ble           1f
  vswap         s25, s22, s16
  vswap         s26, s23, s16
  vswap         s27, s24, s16
1:

  @ Convert y coordinates to integers and clamp them to range [0, 480)
  ldr           r3, =479

  ftosizs       s0, s29
  vmov.s32      r0, s0
  cmp           r0, #0
  movlt         r0, #0
  cmp           r0, r3
  movgt         r0, r3              @ r0 = clamp(v0.y, 0, 479)
  ftosizs       s0, s26
  vmov.s32      r1, s0
  cmp           r1, #0
  movlt         r1, #0
  cmp           r1, r3
  movgt         r1, r3              @ r1 = clamp(v1.y, 0, 479)
  ftosizs       s0, s23
  vmov.s32      r2, s0
  cmp           r2, #0
  movlt         r2, #0
  cmp           r2, r3
  movgt         r2, r3              @ r2 = clamp(v2.y, 0, 479)

  @ Compute bounds on x axis
  vcmp.f32      s25, s22
  fmstat
  vmovgt.f32    s1, s25
  vmovgt.f32    s0, s22
  vmovle.f32    s1, s22
  vmovle.f32    s0, s25
  vcmp.f32      s28, s0
  fmstat
  vmovle.f32    s0, s28             @ s0 = min(v0.x, v1.x, v2.x)
  vcmp.f32      s28, s1
  fmstat
  vmovgt.f32    s1, s28             @ s1 = max(v0.x, v1.x, v2.x)

  ldr           r3, =0
  vmov.f32      s21, r3
  vcmp.f32      s0, s21
  fmstat
  vmovlt.f32    s0, s21
  vcmp.f32      s1, s21
  fmstat
  vmovlt.f32    s1, s21

  ldr           r3, =0x441fc000     @ r3 = 639.0f
  vmov.f32      s21, r3
  vcmp.f32      s0, s21
  fmstat
  vmovgt.f32    s0, s21             @ s0 = clamp(s0, 0, 639)
  vcmp.f32      s1, s21
  fmstat
  vmovgt.f32    s1, s21             @ s1 = clamp(s1, 0, 639)

  @ Compute scanline length
  ldr           r3, =gfx_fb
  ldr           r3, [r3, #0x10]     @ r3 = pitch

@ ------------------------------------------------------------------------------
@ Rasterizes the top triangle
@ ------------------------------------------------------------------------------
raster_top:
  stmfd         sp!, {r0, r1, r2}
  vstmdb.f32    sp!, {s22 - s30}

  @ Gradient for v2 - v0
  vsub.f32      s31, s23, s29
  vsub.f32      s2, s22, s28
  vdiv.f32      s2, s2, s31       @ gradLeftX

  @ Gradient for v1 - v0
  vsub.f32      s22, s26, s29
  vsub.f32      s7, s25, s28
  vdiv.f32      s7, s7, s22       @ gradRightX

  @ If gradient of v1 - v0 is larger than the gradient
  @ of v2 - v0, the gradients must be swapped
  vcmp.f32      s7, s2
  fmstat
  blt           1f
  vswap         s7, s2, s4
  vswap         s3, s8, s4
  vswap         s31, s22, s4
1:

  @ Loop through all scanlines and fill triangles line by line
  ldr           r5, =0x3f800000
  vmov.f32      s22, r5

  ftosis        s31, s29
  vmov.f32      r5, s31
  cmp           r5, #0
  neglt         r5, r5
  movgt         r5, #0

  subs          r1, r1, r0
  ble           1f

  ldr           r4, =gfx_buffer
  mla           r4, r0, r3, r4    @ r4 = first row
2:
  vmov.f32      s31, r5
  fsitos        s31, s31

  bl            draw_span

  add           r4, r3
  add           r5, #1
  subs          r1, #1
  bge           2b
1:
  vldmia.f32    sp!, {s22 - s30}
  ldmfd         sp!, {r0, r1, r2}

@ ------------------------------------------------------------------------------
@ Rasterizes the bottom triangle
@ ------------------------------------------------------------------------------
raster_bottom:
  @ Gradient for v0 - v2
  vsub.f32      s2, s28, s22
  vsub.f32      s31, s23, s29
  vsub.f32      s3, s30, s24
  vdiv.f32      s2, s2, s31       @ gradLeftX

  @ Gradient for v1 - v2
  vsub.f32      s7, s25, s22
  vsub.f32      s29, s23, s26
  vsub.f32      s8, s27, s24
  vdiv.f32      s7, s7, s29       @ gradRightX

  @ If gradient of v1 - v0 is larger than the gradient
  @ of v2 - v0, the gradients must be swapped
  vcmp.f32      s7, s2
  fmstat
  blt           1f
  vswap         s2, s7, s4
  vswap         s3, s8, s4
  vswap         s4, s9, s4
  vswap         s29, s31, s4
1:

  @ Setup origins
  vmov.f32      s28, s22
  vmov.f32      s30, s24

  @ Loop through all scanlines and fill triangles line by line
  ldr           r4, =gfx_buffer
  mla           r4, r2, r3, r4    @ r4 = first row

  subs          r2, r1
  ble           1f

  ftosis        s24, s23
  vmov.f32      r6, s24
  ldr           r7, =479
  cmp           r6, r7
  subge         r5, r6, r7
  movlt         r5, #0

2:
  vmov.f32      s31, r5
  fsitos        s31, s31

  bl            draw_span

  sub           r4, r3
  add           r5, #1
  subs          r2, #1
  bge           2b
1:
  ldmfd         sp!, {r0 - r4, pc}

@ ------------------------------------------------------------------------------
@ Draws a span between two points on a scanline, interpolating colour and
@ depth values and writing data into the back buffer. Note that instructions
@ are arranged in order to make full use of the CPUs ability to place up to
@ 8 instructions in the pipeline
@
@ Arguments:
@   r4 - address of scanline
@   s0 - min X
@   s1 - max X
@   s2 - s6   - right edge gradient (x, d, r, g, b)
@   s7 - s11  - left edge gradient (x, d, r, g, b)
@ Returns:
@   none
@ Clobbers:
@   r6 - r12, s12 - s27
@ ------------------------------------------------------------------------------
draw_span:
  mov           r8, #0
  vmov.f32      s12, s28
  vmla.f32      s12, s7, s31          @ x0
  ftosizs       s12, s12
  fsitos        s12, s12
  vmov.f32      s16, s28
  vmla.f32      s16, s2, s31          @ x1

  vsub.f32      s20, s16, s12         @ length of span

  vcmp.f32      s0, s12
  fmstat
  vsubgt.f32    s26, s0, s12
  ftosizsgt     s26, s26
  vmovgt.f32    r8, s26               @ x0'
  vmovgt.f32    s12, s0
  vcmp.f32      s1, s12
  fmstat
  vmovlt.f32    s12, s1               @ clamp(x0, s0, s1)
  vcmp.f32      s1, s16
  fmstat
  vmovlt.f32    s16, s1
  vcmp.f32      s0, s16
  fmstat
  vmovgt.f32    s16, s0               @ clamp(x1, s0, s1)

  ftosis        s12, s12
  vmov.f32      r6, s12               @ x0
  ftosis        s16, s16
  vmov.f32      r7, s16               @ x1
  subs          r7, r6
  movle         pc, lr                @ x0 < x1

  add           r6, r4, r6, lsl #2    @ r6 - first pixel in scanline
  ldr           r10, =0x437f0000
  vmov.f32      s22, r10              @ 255.0f
1:
  vmov.f32      s26, r8
  fsitos        s26, s26

  @ Interpolate colour
  str           r12, [r6], #4
  add           r8, #1
  subs          r7, #1
  bge           1b

  mov           pc, lr

@-------------------------------------------------------------------------------
@ Renders a rectangle of size r2xr3 and colour r4 starting at (r0, r1)
@ Arguments:
@   r0 - x coordinate
@   r1 - y coordinate
@   r2 - width
@   r3 - height
@   r4 - colour
@ Returns:
@   none
@ Clobbers:
@   none
@-------------------------------------------------------------------------------
gfx_draw_rect:
  cmp       r2, #0
  moveq     pc, lr

  stmfd     sp!, {r0 - r10}

  ldr       r6, =gfx_buffer
  ldr       r7, =gfx_fb
  ldr       r7, [r7, #0x10]       @ r7 = pitch

  add       r10, r1, r3           @ r10 = y + height
1:
  mov       r5, #0
2:
  add       r9, r0, r5            @ r9 = x + r5
  mla       r8, r7, r1, r6        @ r8 = gfx_buffer + y * pitch
  add       r8, r8, r9, lsl #2    @ r8 = gfx_buffer + y * pitch + (x + r5) * 4

  str       r4, [r8]

  add       r5, #1
  cmp       r5, r2
  ble       2b

  add       r1, #1
  cmp       r1, r10
  ble       1b
3:
  ldmfd     sp!, {r0 - r10}
  mov       pc, lr

@-------------------------------------------------------------------------------
@ Renders a 2px frame of size r2xr3 and colour r4 starting at (r0, r1)
@ Arguments:
@   r0 - x coordinate
@   r1 - y coordinate
@   r2 - width
@   r3 - height
@   r4 - colour
@ Returns:
@   none
@ Clobbers:
@   none
@-------------------------------------------------------------------------------
gfx_draw_frame:
  cmp       r2, #0
  moveq     pc, lr

  stmfd     sp!, {r0 - r12}

  ldr       r6, =gfx_buffer
  ldr       r7, =gfx_fb
  ldr       r7, [r7, #0x10]

  @ Draw horizontal lines
  add       r10, r1, #1
  add       r11, r1, r3
  sub       r12, r11, #1
  mov       r5, #0

1:
  add       r9, r0, r5

  mla       r8, r7, r1, r6
  add       r8, r8, r9, lsl #2
  str       r4, [r8]

  mla       r8, r7, r10, r6
  add       r8, r8, r9, lsl #2
  str       r4, [r8]

  mla       r8, r7, r11, r6
  add       r8, r8, r9, lsl #2
  str       r4, [r8]

  mla       r8, r7, r12, r6
  add       r8, r8, r9, lsl #2
  str       r4, [r8]

  add       r5, #1
  cmp       r5, r2
  ble       1b

  @ Draw vertical lines
  add       r10, r0, #1
  add       r11, r0, r2
  sub       r5, r11, #1
  add       r1, r1, #2

2:
  mla       r2, r7, r1, r6

  add       r8, r2, r0, lsl #2
  str       r4, [r8]      

  add       r8, r2, r10, lsl #2
  str       r4, [r8]

  add       r8, r2, r11, lsl #2
  str       r4, [r8]

  add       r8, r2, r5, lsl #2
  str       r4, [r8]

  add       r1, #1
  cmp       r1, r12
  blt       2b

  ldmfd     sp!, {r0 - r12}
  mov       pc, lr


================================================
FILE: imager.py
================================================
#!/usr/bin/python2

"""
This file is part of the Team 28 Project
Licensing information can be found in the LICENSE file
(C) 2014 The Team 28 Authors. All rights reserved.
"""

from PIL import Image
import struct
import sys
import os
import argparse


def main():
    # Parse arguments
    parser = argparse.ArgumentParser(description='Converts an image to binary')
    parser.add_argument('file', metavar='N', type=str, nargs=1,
                        help='Input file')
    parser.add_argument('-o', '--out', help='output .bin file')
    parser.add_argument('-d', '--add-depth', action='store_true')

    # Get file names
    args = parser.parse_args()
    in_path = args.file[0]
    root, ext = os.path.splitext(in_path)
    out_path = args.out if args.out is not None else root + ".bmap"

    # Read image
    image = Image.open(in_path)
    f = open(out_path, 'wb')

    # Write size
    width, height = image.size
    f.write(struct.pack('<II', height, width))

    # Write data
    for y in xrange(height):
        for x in xrange(width):
            r, g, b, a = image.getpixel((x, y))

            assert 0 <= r < 256
            assert 0 <= g < 256
            assert 0 <= b < 256
            assert 0 <= a < 256

            f.write(struct.pack('<BBBB', r, g, b, a))
            if args.add_depth:
                f.write(struct.pack('<f', 0x38f00000))

    f.flush()
    f.close()


if __name__ == '__main__':
    main()


================================================
FILE: input.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global setup_input
.global update_input
.global button_pressed
.global button_clicked
.global button_dclicked
.include "ports.s"

@ ------------------------------------------------------------------------------
@ Macros used to wait for a while
@ ------------------------------------------------------------------------------
.macro wait reg, x
  mov     \reg, \x
9:
  subs    \reg, \reg, #1
  bne     9b
.endm

@ ------------------------------------------------------------------------------
@ Global variable storing the state of buttons
@ ------------------------------------------------------------------------------
.section .data
button_pressed:  .long 0  @ button is held down
button_clicked:  .long 0  @ button pressed during frame
button_hclicked: .long 0  @ button pressed during last 30 frames
button_dclicked: .long 0  @ button pressed twice
button_lclicked: .long 0  @ Last time button was clicked

.section .text
@ ------------------------------------------------------------------------------
@ Initialises the input module
@ ------------------------------------------------------------------------------
setup_input:
  stmfd     sp!, {r0 - r2}

  @ Setup GPIO ports 10 and 11 as outputs
  ldr       r0, =GPIO_FSEL1
  ldr       r1, [r0]
  ldr       r2, =0xFFFFFFC0
  and       r1, r1, r2
  mov       r2, #0x00000009
  orr       r1, r1, r2
  str       r1, [r0]

  ldmfd     sp!, {r0 - r2}
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Updates the state machine on receipt of an interrupt
@   GPIO 4 - data in
@   GPIO 10 - pulse
@   GPIO 11 - latch
@ ------------------------------------------------------------------------------
update_input:
  stmfd     sp!, {r0 - r9, lr}

  ldr       r0, =GPIO_SET0
  ldr       r1, =GPIO_CLR0
  ldr       r2, =GPIO_LEV0

  ldr       r3, =button_pressed
  ldr       r3, [r3]
  ldr       r4, =button_hclicked
  ldr       r4, [r4]

  ldr       r5, =button_lclicked
  ldr       r6, [r5]
  tst       r4, r4
  moveq     r6, #0
  addne     r6, r6, #1
  cmp       r6, #15
  movge     r6, #0
  movge     r7, r4
  movlt     r7, #0
  movge     r4, #0
  str       r6, [r5]

  ldr       r5, =button_clicked
  str       r7, [r5]

  ldr       r5, =button_dclicked
  ldr       r5, [r5]
  mov       r5, #0

  @ Latch
  mov       r6, #0x800
  str       r6, [r0]
  wait      r12, #128
  str       r6, [r1]
  wait      r12, #128

  mov       r7, #0
  mov       r8, #8
  mov       r9, #1
1:
  @ Read button state
  ldr       r6, [r2]
  tst       r6, #0x10
  bne       3f
  orr       r7, r7, r9
  tst       r3, r9
  bne       3f
  tst       r4, r9
  orreq     r4, r4, r9        @ Click: up -> down
  biceq     r5, r5, r9
  bicne     r4, r4, r9
  orrne     r5, r5, r9        @ Double click: up -> down -> up -> down
3:

  @ Pulse
  mov       r6, #0x400
  str       r6, [r0]
  wait      r12, #128
  str       r6, [r1]
  wait      r12, #128

  lsl       r9, r9, #1
  subs      r8, r8, #1
  bgt       1b

  teq       r7, #0xFF
  moveq     r7, #0

  ldr       r0, =button_pressed
  str       r7, [r0]
  ldr       r0, =button_hclicked
  str       r4, [r0]
  ldr       r0, =button_dclicked
  str       r5, [r0]

  ldmfd     sp!, {r0 - r9, pc}


================================================
FILE: kernel.ld
================================================
SECTIONS
{
  /**
   * Start address of the kernel.
   *
   * In QEMU, this is hardcoded to 0x100000 (64Kb).
   * On the actual device, it can be modified, but it defaults to 0x8000 (32Kb).
   */
  . = 0x10000;

  /**
   * Executable code. 
   * 
   * All text segments are copied here. The first file in the object file
   * is kernel.s and the first function in kernel.s is the kernel function
   * which initializes everything, so it gets placed at the start address
   * by the bootstrap code. After the text segment, padding is added in
   * order to align the next segment to a page boundary.
   */
  .text :
  {
    *(.text)
  }
  . = ALIGN(0x1000);

  /**
   * Program data, all .data segments concatenated after each other. Padding
   * is added afterwards to align the next segment to page boundary.
   */
  .data :
  {
    *(.data)
  }
  . = ALIGN(0x1000);

  /**
   * Stack spaces. These come after the data from the binary file,
   * thus they are not actually present in it. They are not loaded,
   * but the addresses are set up for them after the file. If the
   * length of the file is x, then the stack addresses are located
   * at the page boundary after x + 64Kb.
   */
  . = . + 0x800;
  stack_svc = .;
  . = . + 0x800;
  stack_und = .;
  . = . + 0x800;
  stack_abt = .;
  . = . + 0x800;
  stack_irq = .;
  . = . + 0x800;
  stack_fiq = .;
  . = . + 0x400000;
  stack_sys = .;

  /**
   * Front buffer. Since this buffer is quite large, around 2Mb in size,
   * it should not appear in the binary file since it would increase the
   * time it takes to load the kernel. Thus, the address space it requires
   * is only defined in the linker script and placed at the end of the
   * kernel, after all data that is loaded from the binary file.
   */
  . = . + 640 * 480 * 8;
  gfx_buffer = .;
}


================================================
FILE: kernel.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global start

.include "ports.s"

.section .text
@ ------------------------------------------------------------------------------
@ Entry point of the application
@ ------------------------------------------------------------------------------
kernel:
  bl        setup_stack
  bl        setup_ivt
  bl        setup_vfp
  bl        setup_gfx
  bl        setup_cache
  bl        setup_input
  bl        setup_sound
  b         setup_game

@ ------------------------------------------------------------------------------
@ Sets up stacks for all operating modes
@ ------------------------------------------------------------------------------
setup_stack:
  mov       r0, #0xD1       @ FIQ
  msr       cpsr, r0
  ldr       sp, =stack_fiq
  mov       r0, #0xD2       @ IRQ
  msr       cpsr, r0
  ldr       sp, =stack_irq
  mov       r0, #0xD7       @ ABT
  msr       cpsr, r0
  ldr       sp, =stack_abt
  mov       r0, #0xDB       @ UND
  msr       cpsr, r0
  ldr       sp, =stack_und
  mov       r0, #0xDF       @ SYS
  msr       cpsr, r0
  ldr       sp, =stack_sys
  mov       r0, #0xD3       @ SVC
  msr       cpsr, r0
  ldr       sp, =stack_svc
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Relocates the interrupt vector table
@ ------------------------------------------------------------------------------
setup_ivt:
  ldr       r10, =ivt_start
  ldr       r11, =0x00000000
  ldm       r10!, {r0 - r7}
  stm       r11!, {r0 - r7}
  ldm       r10,  {r0 - r7}
  stm       r11,  {r0 - r7}
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Enables the L1 cache
@ ------------------------------------------------------------------------------
setup_cache:
  mov       r0, #0
  mcr       p15, 0, r0, c7, c7, 0     @ Invalidate caches
  mcr       p15, 0, r0, c8, c7, 0     @ Invalidate TLB
  mrc       p15, 0, r0, c1, c0, 0
  ldr       r1, =0x1004
  orr       r0, r0, r1                @ Set L1 enable bit
  mcr       p15, 0, r0, c1, c0, 0
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Enables the vectored floating point unit
@ ------------------------------------------------------------------------------
setup_vfp:
  mrc       p15, #0, r0, c1, c0, #2
  orr       r0, r0, #0xF00000         @ Single + double precision
  mcr       p15, #0, r0, c1, c0, #2
  mov       r0, #0x40000000           @ Set VFP enable bit
  fmxr      fpexc, r0
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Interrupt vector table
@
@ On startup, this table has to be relocated to the start of memory.
@ It contains jump to interrupt handlers.
@ ------------------------------------------------------------------------------
ivt_start:
.rept 8
  ldr pc, [pc, #0x18]
.endr
.word handler_hang
.word handler_undef
.word handler_hang
.word handler_hang
.word handler_hang
.word .
.word handler_hang
.word handler_hang

@ ------------------------------------------------------------------------------
@ Hang when something bad happens
@ ------------------------------------------------------------------------------
handler_hang:
  b         .

@ ------------------------------------------------------------------------------
@ Undefined instructions - clears FP exception bit
@ Like pro windows devs, we put a shitton of effort
@ into making an awesome, blue panic screeen
@ ------------------------------------------------------------------------------
handler_undef:
  @ Reset VFP
  mov         r0, #0x40000000
  fmxr        fpexc, r0

  @ Arguments for printf
  vstm.f32    sp!, {s0 - s31}
  stmfd       sp!, {r0 - r12}
  stmfd       sp!, {lr}

  @ Nice blue background
  ldr         r0, =0xFFFF0000
  bl          gfx_clear

  @ Print address
  ldr         r0, =1f
  mov         r1, #100
  mov         r2, #100
  ldr         r3, =0xFFFFFFFF
  bl          printf
  add         sp, sp, #4

  @ Present error message
  bl          gfx_swap

  @ Hang
  b           .

1:
  .ascii      "VFP crashed:\n"
  .ascii      " PC: %8x\n"
  .ascii      " r0: %8x   r1: %8x   r2: %8x   r3: %8x\n"
  .ascii      " r4: %8x   r5: %8x   r6: %8x   r7: %8x\n"
  .ascii      " r8: %8x   r9: %8x  r10: %8x  r11: %8x\n"
  .ascii      " s0: %8x   s1: %8x   s2: %8x   s3: %8x\n"
  .ascii      " s4: %8x   s5: %8x   s6: %8x   s7: %8x\n"
  .ascii      " s8: %8x   s9: %8x  s10: %8x  s11: %8x\n"
  .ascii      "s12: %8x  s13: %8x  s14: %8x  s15: %8x\n"
  .ascii      "s16: %8x  s17: %8x  s18: %8x  s19: %8x\n"
  .ascii      "s20: %8x  s21: %8x  s22: %8x  s23: %8x\n"
  .ascii      "s24: %8x  s25: %8x  s26: %8x  s27: %8x\n"
  .ascii      "s28: %8x  s29: %8x  s30: %8x  s31: %8x\n"
  .ascii      "\0"
  .align 2


================================================
FILE: math.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global sin
.global cos
.global extract_frustum
.global mat4_mul_mat4
.global mat4_mul_vec4
.global mat4_fmul_vec4
.global mat4_view
.global mat4_translate
.global mat4_scale
.global mat4_rot_x
.global mat4_rot_y
.global mat4_rot_z
.global vec4_add
.global vec4_sub
.global vec4_len
.global vec4_norm
.global vec4_dot
.global vec4_cross
.global random
.global fmod

.section .text
@ ------------------------------------------------------------------------------
@ s1 = sin(s0)
@ Approximation of sine
@ Arguments:
@   s0 - angle in radians
@ Returns:
@   s1 - sine of angle
@ Clobbers:
@   None
@ ------------------------------------------------------------------------------
sin:
  stmfd         sp!, {r0}
  vstmdb.f32    sp!, {s2 - s6}

  ldr           r0, =0x40490FDB
  vmov.f32      s4, r0            @ s4 = pi
  ldr           r0, =0x40C90FDB
  vadd.f32      s2, s0, s4        @ s2 = angle + pi = x
  vmov.f32      s3, r0            @ s3 = 2 * pi = y

  @ compute (angle + pi) % 2pi;
  vdiv.f32      s5, s2, s3        @ s5 = x / y
  vcvt.s32.f32  s5, s5
  vcvt.f32.s32  s5, s5            @ s5 = floor(x/y)
  vmls.f32      s2, s3, s5        @ s2 =  x - y * floor(x/y) = m
  mov           r0, #0

  vcmp.f32      s2, s3
  fmstat
  vmovge.f32    s2, r0
  bge           2f
1:@ m < y
  vcmp.f32      s2, #0
  fmstat
  bge           2f                @ s2 = m
  @ m < 0
  vadd.f32      s2, s2, s3        @ m = m + y
  vcmp.f32      s2, s3
  fmstat
  vmoveq.f32    s2, r0
2:
  vsub.f32      s6, s2, s4        @ s6 = ((angle + pi) % 2pi) - pi
  ldr           r0, =0x3fa2f983   @ 4 / pi
  vmov.f32      s1, r0
  vmul.f32      s1, s6            @ s1 = 4 / pi * x

  vcmp.f32      s6, #0            @ sin(0) = 0
  fmstat
  beq           3f

  ldr           r0, =0x3ecf817b   @ 4 / pi ^ 2
  vmov.f32      s2, r0
  vmul.f32      s2, s2, s6
  vmul.f32      s2, s2, s6
  vmul.f32      s2, s2, s6        @ s2 = (4 * x * x * x) / pi ^ 2
  vabs.f32      s3, s6
  vdiv.f32      s2, s2, s3
  vsub.f32      s1, s1, s2
3:
  vldmia.f32    sp!, {s2 - s6}
  ldmfd         sp!, {r0}
  mov           pc, lr

@ ------------------------------------------------------------------------------
@ s1 = cos(s0)
@ Approximation of cos using sin(x) = cos(pi/2 - x)
@ Arguments:
@   s0 - angle in radians
@ Returns:
@   s1 - sine of angle
@ Clobbers:
@   None
@ ------------------------------------------------------------------------------
cos:
  stmfd         sp!, {r0}
  vstmdb.f32    sp!, {s2 - s6}

  ldr           r0, =0x3FC90FDB
  vmov.f32      s4, r0            @ s4 = pi/2
  vsub.f32      s6, s4, s0        @ angle = pi/2 - angle

  ldr           r0, =0x40490FDB
  vmov.f32      s4, r0            @ s4 = pi
  ldr           r0, =0x40C90FDB
  vadd.f32      s2, s6, s4        @ s2 = angle + pi = x
  vmov.f32      s3, r0            @ s3 = 2 * pi = y

  @ compute (angle + pi) % 2pi;
  vdiv.f32      s5, s2, s3        @ s5 = x / y
  vcvt.s32.f32  s5, s5
  vcvt.f32.s32  s5, s5            @ s5 = floor(x/y)
  vmls.f32      s2, s3, s5        @ s2 =  x - y * floor(x/y) = m
  mov           r0, #0

  vcmp.f32      s2, s3
  fmstat
  blt           1f
  vmov.f32      s2, r0            @ s2 = 0
  b             2f
1: @ m < y
  vcmp.f32      s2, #0
  fmstat
  bge           2f                @ s2 = m
  @ m < 0
  vadd.f32      s2, s2, s3        @ m = m + y
  vcmp.f32      s2, s3
  fmstat
  vmoveq.f32    s2, r0
2:
  vsub.f32      s6, s2, s4        @ s6 = ((angle + pi) % 2pi) - pi
  ldr           r0, =0x3fa2f983   @ 4 / pi
  vmov.f32      s1, r0
  vmul.f32      s1, s6            @ s1 = 4 / pi * x

  vcmp.f32      s6, #0
  fmstat
  beq           3f

  ldr           r0, =0x3ecf817b   @ 4 / pi ^ 2
  vmov.f32      s2, r0
  vmul.f32      s2, s2, s6
  vmul.f32      s2, s2, s6
  vmul.f32      s2, s2, s6        @ s2 = (4 * x * x * x) / pi ^ 2
  vabs.f32      s3, s6
  vdiv.f32      s2, s2, s3
  vsub.f32      s1, s1, s2
3:
  vldmia.f32    sp!, {s2 - s6}
  ldmfd         sp!, {r0}
  mov           pc, lr

@ ------------------------------------------------------------------------------
@ Multiplies two matrices together
@ r2 = r1 * r0
@ Arguments:
@   r0 - location of first matrix
@   r1 - location of second matrix
@   r2 - output matrix address
@ Return values:
@   none
@ Clobbers:
@   s0 - s23
@ ------------------------------------------------------------------------------
mat4_mul_mat4:
  stmfd     sp!, {r3, r4}

  vldm.f32  r0, {s0 - s15}

  mov       r4, #4
1:
  vldm.f32  r1!, {s16 - s19}

  vmul.f32  s20,  s0, s16
  vmla.f32  s20,  s4, s17
  vmla.f32  s20,  s8, s18
  vmla.f32  s20, s12, s19

  vmul.f32  s21,  s1, s16
  vmla.f32  s21,  s5, s17
  vmla.f32  s21,  s9, s18
  vmla.f32  s21, s13, s19

  vmul.f32  s22,  s2, s16
  vmla.f32  s22,  s6, s17
  vmla.f32  s22, s10, s18
  vmla.f32  s22, s14, s19

  vmul.f32  s23,  s3, s16
  vmla.f32  s23,  s7, s17
  vmla.f32  s23, s11, s18
  vmla.f32  s23, s15, s19

  vstm.f32  r2!, {s20 - s23}

  subs      r4, #1
  bne       1b

  @ Restore pointers
  sub       r1, #64
  sub       r2, #64

  ldmfd     sp!, {r3, r4}
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Multiplies a matrix with a vector
@ Arguments:
@   r0 - location of the matrix
@   r1 - location of the vector
@   r2 - output vector address
@ Returns:
@   none
@ Clobbers:
@   s0 - s19
@ ------------------------------------------------------------------------------
mat4_mul_vec4:
  vldm.f32  r0, {s0 - s15}
  vldm.f32  r1, {s16 - s19}

  vmul.f32  s20,  s0, s16
  vmul.f32  s21,  s1, s16
  vmul.f32  s22,  s2, s16
  vmul.f32  s23,  s3, s16
  vmla.f32  s20,  s4, s17
  vmla.f32  s21,  s5, s17
  vmla.f32  s22,  s6, s17
  vmla.f32  s23,  s7, s17
  vmla.f32  s20,  s8, s18
  vmla.f32  s21,  s9, s18
  vmla.f32  s22, s10, s18
  vmla.f32  s23, s11, s18
  vmla.f32  s20, s12, s19
  vmla.f32  s21, s13, s19
  vmla.f32  s22, s14, s19
  vmla.f32  s23, s15, s19

  vstm.f32  r2, {s20 - s23}

  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Multiplies a matrix with a vector in registers
@ Arguments:
@    s0 - s15: matrix
@   s16 - s19: vector
@   s20 - s23: output
@ Returns:
@   none
@ Clobbers:
@   s0 - s19
@ ------------------------------------------------------------------------------
mat4_fmul_vec4:
  vmul.f32  s20,  s0, s16
  vmul.f32  s21,  s1, s16
  vmul.f32  s22,  s2, s16
  vmul.f32  s23,  s3, s16
  vmla.f32  s20,  s4, s17
  vmla.f32  s21,  s5, s17
  vmla.f32  s22,  s6, s17
  vmla.f32  s23,  s7, s17
  vmla.f32  s20,  s8, s18
  vmla.f32  s21,  s9, s18
  vmla.f32  s22, s10, s18
  vmla.f32  s23, s11, s18
  vmla.f32  s20, s12, s19
  vmla.f32  s21, s13, s19
  vmla.f32  s22, s14, s19
  vmla.f32  s23, s15, s19
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Computes a view matrix
@ Arguments:
@   r0 - eye
@   r1 - at
@   r2 - up
@   r3 - destination matrix
@ Returns:
@   none
@ Clobbers:
@   s0 - s15
@ ------------------------------------------------------------------------------
mat4_view:
  stmfd     sp!, {r4}

  vldm.f32  r0, {s0 - s3}   @ eye{s0, s1, s2}
  vldm.f32  r1, {s3 - s6}   @ at{s3, s4, s5}
  vldm.f32  r2, {s6 - s9}   @ up{s6, s7, s8}

  @ z{s9, s10, s11} = norm(eye - at)
  vsub.f32  s9, s0, s3
  vsub.f32  s10, s1, s4
  vsub.f32  s11, s2, s5
  vmul.f32  s12, s9, s9
  vmla.f32  s12, s10, s10
  vmla.f32  s12, s11, s11
  vsqrt.f32 s12, s12
  vdiv.f32  s9, s9, s12
  vdiv.f32  s10, s10, s12
  vdiv.f32  s11, s11, s12

  @ x{s3, s4, s5} = norm(cross(up, z))
  vmul.f32  s3, s7, s11
  vmls.f32  s3, s8, s10
  vmul.f32  s4, s8, s9
  vmls.f32  s4, s6, s11
  vmul.f32  s5, s6, s10
  vmls.f32  s5, s7, s9
  vmul.f32  s12, s3, s3
  vmla.f32  s12, s4, s4
  vmla.f32  s12, s5, s5
  vsqrt.f32 s12, s12
  vdiv.f32  s3, s3, s12
  vdiv.f32  s4, s4, s12
  vdiv.f32  s5, s5, s12

  @ y{s6, s7, s8} = cross(z, x)
  vmul.f32  s6, s10, s5
  vmls.f32  s6, s11, s4
  vmul.f32  s7, s11, s3
  vmls.f32  s7, s9, s5
  vmul.f32  s8, s9, s4
  vmls.f32  s8, s10, s3

  mov       r4, #0
  vmov.f32  s15, r4

  @ First column
  vmov.f32  s12, s3
  vmov.f32  s13, s6
  vmov.f32  s14, s9
  vstm.f32  r3!, {s12 - s15}

  @ Second column
  vmov.f32  s12, s4
  vmov.f32  s13, s7
  vmov.f32  s14, s10
  vstm.f32  r3!, {s12 - s15}

  @ Third column
  vmov.f32  s12, s5
  vmov.f32  s13, s8
  vmov.f32  s14, s11
  vstm.f32  r3!, {s12 - s15}

  @ Fourth column
  vmov.f32  s12, s15
  vmls.f32  s12, s0, s3
  vmls.f32  s12, s1, s4
  vmls.f32  s12, s2, s5
  vmov.f32  s13, s15
  vmls.f32  s13, s0, s6
  vmls.f32  s13, s1, s7
  vmls.f32  s13, s2, s8
  vmov.f32  s14, s15
  vmls.f32  s14, s0, s9
  vmls.f32  s14, s1, s10
  vmls.f32  s14, s2, s11
  mov       r4, #0x3f800000
  vmov.f32  s15, r4

  vstm.f32  r3, {s12 - s15}

  sub       r3, r3, #48
  ldmfd     sp!, {r4}
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Builds a translation matrix
@ Arguments:
@   r0 - matrix
@   s0 - s3: translation
@ Returns:
@   none
@ Clobbers:
@   none
@ ------------------------------------------------------------------------------
mat4_translate:
  vstr.f32    s0, [r0, #48]
  vstr.f32    s1, [r0, #52]
  vstr.f32    s2, [r0, #56]
  mov         pc, lr

@ ------------------------------------------------------------------------------
@ Builds a scaling matrix
@ Arguments:
@   r0 - matrix
@   s0 - s3: translation
@ Returns:
@   none
@ Clobbers:
@   none
@ ------------------------------------------------------------------------------
mat4_scale:
  vstr.f32    s0, [r0]
  vstr.f32    s1, [r0, #20]
  vstr.f32    s2, [r0, #40]
  mov         pc, lr


@ ------------------------------------------------------------------------------
@ Creates a rotation matrix around X
@ Arguments:
@   r0 - matrix
@   s0 - angle
@ Returns:
@   none
@ Clobbers:
@   none
@ ------------------------------------------------------------------------------
mat4_rot_x:
  stmfd       sp!, {lr}

  bl          sin
  vstr.f32    s1, [r0, #24]
  vneg.f32    s1, s1
  vstr.f32    s1, [r0, #36]
  bl          cos
  vstr.f32    s1, [r0, #20]
  vstr.f32    s1, [r0, #40]

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Creates a rotation matrix
@ Arguments:
@   r0 - matrix
@   s0 - angle
@ Returns:
@   none
@ Clobbers:
@   none
@ ------------------------------------------------------------------------------
mat4_rot_y:
  stmfd       sp!, {lr}

  bl          sin
  vstr.f32    s1, [r0, #32]
  vneg.f32    s1, s1
  vstr.f32    s1, [r0, #8]
  bl          cos
  vstr.f32    s1, [r0, #0]
  vstr.f32    s1, [r0, #40]

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Creates a rotation matrix around Z
@ Arguments:
@   r0 - matrix
@   s0 - angle
@ Returns:
@   none
@ Clobbers:
@   none
@ ------------------------------------------------------------------------------
mat4_rot_z:
  stmfd       sp!, {lr}

  bl          sin
  vstr.f32    s1, [r0, #4]
  vneg.f32    s1, s1
  vstr.f32    s1, [r0, #16]
  bl          cos
  vstr.f32    s1, [r0, #0]
  vstr.f32    s1, [r0, #20]

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Computes a 3x3 determinant
@ Arguments:
@   s23 - s31: coefficients
@ Returns:
@   s22 - determinant
@ Clobbers:
@   s21
@ ------------------------------------------------------------------------------
mat3_fdet:
  vmul.f32    s21, s27, s31
  vmls.f32    s21, s28, s30
  vmul.f32    s22, s23, s21

  vmul.f32    s21, s28, s29
  vmls.f32    s21, s26, s31
  vmla.f32    s22, s24, s21

  vmul.f32    s21, s26, s30
  vmls.f32    s21, s27, s29
  vmla.f32    s22, s25, s21

  mov         pc, lr

@ ------------------------------------------------------------------------------
@ Adds two 4D vectors
@ Arguments:
@   r0 - location of first vector
@   r1 - location of second vector
@   r2 - location of destination
@ Return values:
@   None
@ Clobbers:
@   s0 - s11
@ ------------------------------------------------------------------------------
vec4_add:
  vldm.f32  r0, {s0 - s3}
  vldm.f32  r1, {s4 - s7}

  vadd.f32  s8, s0, s4
  vadd.f32  s9, s1, s5
  vadd.f32  s10, s2, s6
  vadd.f32  s11, s3, s7

  vstm.f32  r2, {s8 - s11}
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Subtracts a vector from another
@ Arguments:
@   r0 - location of first vector
@   r1 - location of second vector
@   r2 - location of destination
@ Return values:
@   None
@ Clobbers
@   s0 - s11
@ ------------------------------------------------------------------------------
vec4_sub:
  vldm.f32  r0, {s0 - s3}
  vldm.f32  r1, {s4 - s7}

  vsub.f32  s8, s0, s4
  vsub.f32  s9, s1, s5
  vsub.f32  s10, s2, s6
  vsub.f32  s11, s3, s7

  vstm.f32  r2, {s8 - s11}
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Computes the length of a vector
@ Arguments:
@   r0 - location of first vector
@ Return values:
@   s0 - length of the vector
@ Clobers:
@   s1 - s3
@ ------------------------------------------------------------------------------
vec4_len:
  vldm.f32  r0, {s0 - s3}

  vmul.f32  s0, s0, s0
  vmla.f32  s0, s1, s1
  vmla.f32  s0, s2, s2
  vsqrt.f32 s0, s0

  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Computes the normalised vector(unit vector) of the given vector
@ Arguments:
@   r0 - location of the vector
@   r1 - location of the destination
@ Return values:
@   None
@ Clobbers
@   s0 - s4
@ ------------------------------------------------------------------------------
vec4_norm:
  vldm.f32  r0, {s0 - s3}

  vmul.f32  s4, s0, s0
  vmla.f32  s4, s1, s2
  vmla.f32  s4, s2, s2
  vsqrt.f32 s4, s4      @ s4 = |v|

  vdiv.f32  s0, s0, s4
  vdiv.f32  s1, s1, s4
  vdiv.f32  s2, s2, s4

  vstm.f32  r1, {s0 - s3}
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Computes the dot product of two vectors
@ Arguments:
@   r0 - location of the first vector
@   r1 - location of the second vector
@ Return values:
@   s0 - dot product
@ Clobbers
@   s0 - s6
@ ------------------------------------------------------------------------------
vec4_dot:
  vldm.f32  r0, {s1 - s3}
  vldm.f32  r1, {s4 - s6}

  vmul.f32  s0, s1, s4
  vmla.f32  s0, s2, s5
  vmla.f32  s0, s3, s6

  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Computes the cross product of two vectors
@ Arguments:
@   r0 - location of the first vector
@   r1 - location of the second vector
@   r2 - location of the destination
@ Return values:
@   None
@ Clobbers:
@   s0 - s10
@ ------------------------------------------------------------------------------
vec4_cross:
  stmfd     sp!, {r3}

  vldm.f32  r0, {s0 - s3}        @ s0 = x1; s1 = y1; s2 = z1
  vldm.f32  r1, {s3 - s6}        @ s3 = x2; s4 = y2; s5 = z2

  vmul.f32  s6, s1, s5           @ s6 = y1 * z2
  vmls.f32  s6, s4, s2           @ s6 = s6 - (y2 * z1)
  vmul.f32  s7, s3, s2           @ s7 = x2 * z1
  vmls.f32  s7, s0, s5           @ s7 = s7 - (x1 * z2)
  vmul.f32  s8, s0, s4           @ s8 = x1 * y2
  vmls.f32  s8, s1, s3           @ s8 = s8 - (x2 * y1)

  mov       r3, #0
  vmov.f32  s9, r3

  vstm.f32  r2, {s6 - s9}

  ldmfd     sp!, {r3}
  mov       pc, lr

@ ------------------------------------------------------------------------------
@ Floating point modulo
@ Arguments:
@   s0 - x
@   s1 - y
@ Returns:
@   s2 - x % y
@ Clobbers:
@   None
@ ------------------------------------------------------------------------------
fmod:
  stmfd         sp!, {r0}
  vstmdb.f32    sp!, {s3}

  vcmp.f32      s1, #0
  fmstat
  beq           4f

  mov           r0, #0
  vdiv.f32      s3, s0, s1        @ s3 = x / y
  vcvt.s32.f32  s3, s3
  vcvt.f32.s32  s3, s3            @ s3 = floor(x/y)
  vmls.f32      s0, s1, s3        @ s0 = x - y * floor(x/y) = m

  vcmp.f32      s1, #0
  fmstat
  ble           2f
  vcmp.f32      s0, s1
  fmstat
  blt           1f
  vmov.f32      s0, r0
  b             4f                @ s0 = 0
1:
  vcmp.f32      s0, #0
  fmstat
  bge           4f                @ s0 = m
  vadd.f32      s0, s0, s1        @ s0 = m + y
  vcmp.f32      s0, s1
  vmoveq.f32    s0, r0            @ s0 = 0
  b             4f
2:
  vcmp.f32      s0, s1
  fmstat
  bgt           3f
  vmov.f32      s0, r0
  b             4f                @ s0 = 0
3:
  vcmp.f32      s0, #0
  fmstat
  ble           4f                @ s0 = m
  vadd.f32      s0, s0, s1        @ s0 = m + y
  vcmp.f32      s0, s1
  fmstat
  vmoveq.f32    s0, r0            @ s0 = 0
4:
  vmov.f32      s2, s0            @ s2 = s0

  vldmia.f32    sp!, {s3}
  ldmfd         sp!, {r0}
  mov           pc, lr

@ ------------------------------------------------------------------------------
@ Computes the intersection point of three planes using Kramer's rule
@ Arguments:
@   s0 - s3: (a1, b1, c1, d1)
@   s4 - s7: (a1, b1, c1, d1)
@   s8 - s11: (a1, b1, c1, d1)
@ Returns:
@   s12 - s14: (x, y, z)
@ Clobbers:
@   s15, s22 - s31
@ ------------------------------------------------------------------------------
insersect_planes:
  mov       r11, lr

  vmov.f32  s23, s0
  vmov.f32  s24, s4
  vmov.f32  s25, s8
  vmov.f32  s26, s1
  vmov.f32  s27, s5
  vmov.f32  s28, s9
  vmov.f32  s29, s2
  vmov.f32  s30, s6
  vmov.f32  s31, s10
  bl        mat3_fdet
  vmov.f32  s15, s22          @ det

  vmov.f32  s23, s3
  vmov.f32  s24, s7
  vmov.f32  s25, s11
  bl        mat3_fdet
  vdiv.f32  s12, s22, s15     @ x
  vneg.f32  s12, s12

  vmov.f32  s23, s0
  vmov.f32  s24, s4
  vmov.f32  s25, s8
  vmov.f32  s26, s3
  vmov.f32  s27, s7
  vmov.f32  s28, s11
  bl        mat3_fdet
  vdiv.f32  s13, s22, s15     @ y
  vneg.f32  s13, s13

  vmov.f32  s26, s1
  vmov.f32  s27, s5
  vmov.f32  s28, s9
  vmov.f32  s29, s3
  vmov.f32  s30, s7
  vmov.f32  s31, s11
  bl        mat3_fdet
  vdiv.f32  s14, s22, s15    @ z
  vneg.f32  s14, s14

  mov       pc, r11

@ ------------------------------------------------------------------------------
@ Extracts clip planes from a view-projection matrix
@ Arguments:
@   r0 - matrix
@   r1 - frustum
@ Returns:
@   8 vertices on the stack
@ Clobbers:
@   s0 - s31
@ ------------------------------------------------------------------------------
extract_frustum:
  mov           r12, lr
  add           r2, r1, #96

  vldr.f32      s16, [r0, #12]
  vldr.f32      s17, [r0, #28]
  vldr.f32      s18, [r0, #44]
  vldr.f32      s19, [r0, #60]

  bl            load_near
  vstmia.f32    r2!, {s0 - s3}
  bl            load_left
  vstmia.f32    r2!, {s8 - s11}
  bl            load_bottom
  vstmia.f32    r2!, {s4 - s7}

  bl            insersect_planes
  vstmia.f32    r1!, {s12 - s14}    @ near-left-bottom
  bl            load_top
  bl            insersect_planes
  vstmia.f32    r1!, {s12 - s14}    @ near-left-top
  bl            load_right
  bl            insersect_planes
  vstmia.f32    r1!, {s12 - s14}    @ near-right-top
  bl            load_bottom
  bl            insersect_planes
  vstmia.f32    r1!, {s12 - s14}    @ near-right-bottom
  bl            load_far
  vstmia.f32    r2!, {s0 - s3}
  bl            insersect_planes
  vstmia.f32    r1!, {s12 - s14}    @ far-right-bottom
  bl            load_left
  bl            insersect_planes
  vstmia.f32    r1!, {s12 - s14}    @ far-left-bottom
  bl            load_top
  vstmia.f32    r2!, {s4 - s7}
  bl            insersect_planes
  vstmia.f32    r1!, {s12 - s14}    @ far-left-top
  bl            load_right
  vstmia.f32    r2!, {s8 - s11}
  bl            insersect_planes
  vstmia.f32    r1!, {s12 - s14}    @ far-right-top

  mov           pc, r12

@ ------------------------------------------------------------------------------
@ Loads near plane into registers
@ ------------------------------------------------------------------------------
load_near:
  vldr.f32      s12, [r0, #8]
  vldr.f32      s13, [r0, #24]
  vldr.f32      s14, [r0, #40]
  vldr.f32      s15, [r0, #56]
  vadd.f32      s0, s16, s12
  vadd.f32      s1, s17, s13
  vadd.f32      s2, s18, s14
  vadd.f32      s3, s19, s15
  mov           pc, lr

@ ------------------------------------------------------------------------------
@ Loads far plane into registers
@ ------------------------------------------------------------------------------
load_far:
  vldr.f32      s12, [r0, #8]
  vldr.f32      s13, [r0, #24]
  vldr.f32      s14, [r0, #40]
  vldr.f32      s15, [r0, #56]
  vsub.f32      s0, s16, s12
  vsub.f32      s1, s17, s13
  vsub.f32      s2, s18, s14
  vsub.f32      s3, s19, s15
  mov           pc, lr

@ ------------------------------------------------------------------------------
@ Loads bottom plane into registers
@ ------------------------------------------------------------------------------
load_bottom:
  vldr.f32      s12, [r0, #4]
  vldr.f32      s13, [r0, #20]
  vldr.f32      s14, [r0, #36]
  vldr.f32      s15, [r0, #52]
  vadd.f32      s4, s16, s12
  vadd.f32      s5, s17, s13
  vadd.f32      s6, s18, s14
  vadd.f32      s7, s19, s15
  mov           pc, lr

@ ------------------------------------------------------------------------------
@ Loads top plane into registers
@ ------------------------------------------------------------------------------
load_top:
  vldr.f32      s12, [r0, #4]
  vldr.f32      s13, [r0, #20]
  vldr.f32      s14, [r0, #36]
  vldr.f32      s15, [r0, #52]
  vsub.f32      s4, s16, s12
  vsub.f32      s5, s17, s13
  vsub.f32      s6, s18, s14
  vsub.f32      s7, s19, s15
  mov           pc, lr


@ ------------------------------------------------------------------------------
@ Loads top plane into registers
@ ------------------------------------------------------------------------------
load_left:
  vldr.f32      s12, [r0, #0]
  vldr.f32      s13, [r0, #16]
  vldr.f32      s14, [r0, #32]
  vldr.f32      s15, [r0, #48]
  vadd.f32      s8, s16, s12
  vadd.f32      s9, s17, s13
  vadd.f32      s10, s18, s14
  vadd.f32      s11, s19, s15
  mov           pc, lr

@ ------------------------------------------------------------------------------
@ Loads top plane into registers
@ ------------------------------------------------------------------------------
load_right:
  vldr.f32      s12, [r0, #0]
  vldr.f32      s13, [r0, #16]
  vldr.f32      s14, [r0, #32]
  vldr.f32      s15, [r0, #48]
  vsub.f32      s8, s16, s12
  vsub.f32      s9, s17, s13
  vsub.f32      s10, s18, s14
  vsub.f32      s11, s19, s15
  mov           pc, lr

@ ------------------------------------------------------------------------------
@ Generates a random number using a linear shift register
@ Arguments:
@   none
@ Returns:
@   r0 - random number
@ Clobbers:
@   none
@ ------------------------------------------------------------------------------
random:
  stmfd         sp!, {r1 - r2}

  ldr           r1, =1f
  ldr           r0, [r1]

  mov           r2, r0
  eor           r2, r2, r0, lsr #2
  eor           r2, r2, r0, lsr #3
  eor           r2, r2, r0, lsr #5
  and           r2, r2, #1
  lsr           r0, r0, #1
  orr           r0, r0, r2, lsl #15

  str           r0, [r1]

  ldmfd         sp!, {r1 - r2}
  mov           pc, lr
1:
  .long         0xACE1


================================================
FILE: mbox.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global mbox_read
.global mbox_write

.include "ports.s"

.section .text
@ ------------------------------------------------------------------------------
@ Reads a value from the mailbox
@
@ Arguments:
@   r0 - Channel
@ Returns:
@   none
@ Clobbers:
@   r1 - Return data
@ ------------------------------------------------------------------------------
mbox_read:
  stmfd     sp!, {r2 - r4}
  ldr       r2, =MBOX_BASE
  eor       r4, r4, r4

1:
  @ Timeout
  add       r4, #1
  tst       r4, #0x80000
  mvnne     r1, #1
  bne       2f

  @ Flush cache
  mcr       p15, #0, r1, c7, c14, #0

  @ Check for ready flag
  ldr       r3, [r2, #0x18]
  tst       r3, #0x40000000
  bne       1b

  @ Read in data (dmb first)
  mcr       p15, #0, r1, c7, c10, #5
  ldr       r3, [r2, #0x00]

  @ Check if the channel is right
  and       r1, r3, #0x0F
  teq       r0, r1
  bne       1b

  @ Extract data
  bic       r1, r3, #0xF
2:
  ldmfd     sp!, {r2 - r4}
  mov       pc, lr


@ ------------------------------------------------------------------------------
@ Writes a value to the mailbox
@
@ Arguments:
@   r0 - channel
@   r1 - data
@ Returns:
@   none
@ Clobbers:
@   none
@ ------------------------------------------------------------------------------
mbox_write:
  stmfd     sp!, {r1 - r4}
  ldr       r2, =MBOX_BASE
  eor       r4, r4

  @ Wait until mailbox is ready
1:
  add       r4, #1
  tst       r4, #0x80000
  bne       2f

  @ Flush cache
  mcr       p15, #0, r3, c7, c14, #0

  ldr       r3, [r2, #0x18]
  tst       r3, #0x80000000
  bne       1b

  @ Send message (dmb first)
  mcr       p15, #0, r3, c7, c10, #5
  orr       r1, r0, r1
  str       r1, [r2, #0x20]

2:
  ldmfd     sp!, {r1 - r4}
  mov       pc, lr


================================================
FILE: objects.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global draw_rocks
.global reset_objects
.global OBJECT_COUNT
.global object_list

.include "ports.s"

.section .data
@ ------------------------------------------------------------------------------
@ Obstacles
@ ------------------------------------------------------------------------------
.equ             OBJECT_COUNT, 35
.equ             ROCK_LIVES, 3
object_spawn:      .long 0xfffff  @ Frame number of the last spawned rock
object_list:
  .rept OBJECT_COUNT
    .float 0.0, 0.0, 0.0        @ Position (x, y, z)
    .float 1.5                  @ Movement speed
    .float 0.0                  @ Rotation
    .long  0                    @ Type
    .long  ROCK_LIVES           @ Lives
  .endr

.section .text
@ ------------------------------------------------------------------------------
@ Renders the rocks on the sides
@ ------------------------------------------------------------------------------
draw_rocks:
  stmfd       sp!, {lr}

  bl          update_rocks
  bl          sort_objects

  ldr         r12, =OBJECT_COUNT
  ldr         r11, =object_list
1:
  vldm.f32    r11!, {s0 - s6}
  ldr         r0, =0xc1200000     @ -7.0
  vmov.f32    s7, r0
  vcmp.f32    s2, s7
  fmstat
  bllt        draw_object
  subs        r12, r12, #1
  bne         1b

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Updates the position of the rocks
@ ------------------------------------------------------------------------------
update_rocks:
  stmfd       sp!, {lr}

  ldr         r12, =OBJECT_COUNT
  ldr         r11, =object_list

1:
  vldm.f32    r11, {s0 - s6}

  @ Check whether rock is 'live'
  ldr         r0, =0xc1200000
  vmov.f32    s7, r0
  vcmp.f32    s2, s7
  fmstat
  blt         4f

  @ Rock moving out of bound
  ldr         r2, [r11, #8]
  tst         r2, r2
  beq         5f

  @ Clear depth
  mov         r1, #0
  vmov.f32    s2, r1

  @ Test for collision with player
  ldr         r0, =player_pos
  vldm.f32    r0, {s30 - s31}
  vsub.f32    s7, s0, s30
  vabs.f32    s7, s7
  vsub.f32    s8, s1, s31
  ldr         r0, =0x40000000
  vmov.f32    s9, r0
  vsub.f32    s8, s8, s9
  vabs.f32    s8, s8
  ldr         r0, =0x3fe00000
  vmov.f32    s9, r0
  vcmp.f32    s7, s9
  fmstat
  bgt         3f
  vcmp.f32    s8, s9
  fmstat
  bgt         3f

  vmov.f32    r0, s6
  tst         r0, #0x1E
  beq         2f

  @ Decrease health
  ldr         r0, =player_rolling
  ldr         r0, [r0]
  tst         r0, r0
  bne         5f

  mov         r0, #20
  bl          player_damage
  b           5f
2:
  tst         r0, #1
  ldreq       r0, =player_wrenches
  ldrne       r0, =player_rockets
  ldr         r1, [r0]
  add         r1, r1, #1
  cmp         r1, #4
  movge       r1, #3
  str         r1, [r0]
  bllt        snd_play_pickup
  b           5f
3:
  ldr         r0, =player_score     @ Add to score
  ldr         r1, [r0]
  add         r1, r1, #5
  str         r1, [r0]
5:
  @ Spawn a new rock once every 30 frames
  ldr         r0, =object_spawn
  ldr         r1, [r0]
  add         r1, r1, #1
  cmp         r1, #0x20
  movge       r1, #0
  blge        spawn_object
  str         r1, [r0]

  ldr         r0, =enemy_count
  ldr         r0, [r0]
  tst         r0, r0
  movne       r0, #0
  vmovne.f32  s2, r0

  vstm.f32    r11!, {s0 - s6}
  b           5f
4:
  @ Check whether object collided with a bullet
  ldr         r0, =0x40000000
  vmov.f32    s31, r0
  bl          collide_bullets
  tst         r0, r0
  bne         6f

  vmov.f32    r0, s5
  subs        r0, r0, #1
  vmov.f32    s5, r0
  vmoveq      s2, r0
  beq         3b

6:
  @ Updates position
  ldr         r0, =player_speed
  vldr.f32    s9, [r0]
  ldr         r0, =0x3dcccccd
  vmov.f32    s8, r0
  vadd.f32    s2, s2, s3
  vadd.f32    s2, s2, s9
  vmov.f32    r0, s6
  tst         r0, #0x1E
  vaddne.f32  s4, s4, s8
  vstm.f32    r11!, {s0 - s6}
5:
  subs        r12, r12, #1
  bne         1b

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Sorts rocks by their z coordinate
@ ------------------------------------------------------------------------------
sort_objects:
  ldr         r12, =OBJECT_COUNT
  sub         r10, r12, #1
  ldr         r11, =object_list
  ldr         r4,  =28 @ size of rock in bytes

  ldr         r5, =0x0 @ i
1:
  @ outer loop start
  ldr         r6, =0x0 @ j
2:
  @ inner loop start
  @ Calculate address of j and j+1
  mla         r7, r6, r4, r11
  add         r8, r7, r4

  @ Compare Z coordinates
  ldr         r0, [r7, #8]
  vmov.f32    s0, r0
  ldr         r1, [r8, #8]
  vmov.f32    s1, r1
  vcmp.f32    s0, s1
  fmstat
  ble         3f

  @ Swap
  vldm.f32    r7, {s18 - s24}
  vldm.f32    r8, {s25 - s31}
  vstm.f32    r8, {s18 - s24}
  vstm.f32    r7, {s25 - s31}

3: @ inner loop end
  add         r6, r6, #1
  cmp         r6, r10
  blt         2b

4: @ outer loop end
  add         r5, r5, #1
  sub         r10, r10, #1
  cmp         r5, r12
  blt         1b

  mov         pc, lr

@ ------------------------------------------------------------------------------
@ Spawns a new rock
@ Arguments:
@   none
@ Returns:
@   s0 - s4: rock attributes
@ Clobbers:
@   none
@ ------------------------------------------------------------------------------
spawn_object:
  stmfd       sp!, {r0 - r1, lr}

  @ Randomize x in range [-8.5, 8.5]
  bl          random
  and         r1, r0, #0xFF
  sub         r1, r1, #0x7F
  vmov.f32    s8, r1
  fsitos      s8, s8
  ldr         r1, =0x41400000
  vmov.f32    s9, r1
  vdiv.f32    s0, s8, s9

  @ Randomize y in range [-2.8, 5.2]
  bl          random
  and         r1, r0, #0xFF
  sub         r1, r1, #0x7F
  vmov.f32    s8, r1
  fsitos      s8, s8
  ldr         r1, =0x41f00000
  vmov.f32    s9, r1
  vdiv.f32    s1, s8, s9

  @ Set z to -300.0
  ldr         r0, =0xc3960000
  vmov.f32    s2, r0

  @ Reset rotation
  ldr         r0, =0x00000000
  vmov.f32    s4, r0

  @ Randomize type
  bl          random
  and         r0, #0x1F
  vmov.f32    s6, r0

  @ Lives for rocks
  ldr         r0, =ROCK_LIVES
  vmov.f32    s5, r0

  ldmfd       sp!, {r0 - r1, pc}

@ ------------------------------------------------------------------------------
@ Draws an object
@
@ Arguments:
@   s0 - s4: Rock attributes
@ Returns:
@   none
@ Clobbers:
@  s0 - s31
@ Remarks:
@   tail call - called function pops return address from stack
@ ------------------------------------------------------------------------------
draw_object:
  stmfd       sp!, {lr}
  vstmdb.f32  sp!, {s5 - s6}

  @ Reset model matrix
  ldr         r0, =mtx_id
  vldm.f32    r0, {s16 - s31}
  ldr         r0, =mtx_model
  vstm.f32    r0, {s16 - s31}
  ldr         r0, =mtx_temp
  vstm.f32    r0, {s16 - s31}

  @ Translate & rotate
  vneg.f32    s1, s1
  ldr         r0, =mtx_model
  bl          mat4_translate
  vmov.f32    s0, s4
  ldr         r0, =mtx_temp
  bl          mat4_rot_y
  ldr         r0, =mtx_model
  ldr         r1, =mtx_temp
  ldr         r2, =mtx_model
  bl          mat4_mul_mat4

  @ Render the cube
  ldr         r0, =mtx_vp
  ldr         r1, =mtx_model
  ldr         r2, =mtx_mvp
  bl          mat4_mul_mat4

  vldmia.f32  sp!, {s5 - s6}
  vmov.f32    r0, s6
  teq         r0, #0
  beq         draw_wrench_sprite
  teq         r0, #1
  beq         draw_rocket_sprite
  b           draw_rock

@ ------------------------------------------------------------------------------
@ Draws a single rock
@ Arguments:
@   s0 - s4: Rock attributes
@ Returns:
@   none
@ Clobbers:
@  s0 - s31
@ ------------------------------------------------------------------------------
draw_rock:
  vmov.f32    r5, s5
  sub         r5, r5, #1
  ldr         r6, =480

  ldr         r0, =rock_vtx
  ldr         r1, =rock_idx
  mla         r1, r5, r6, r1
  ldr         r2, =20
  ldr         r3, =mtx_mvp
  ldr         r4, =light_dir
  bl          gfx_draw_trgs

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Draws a wrench sprite
@ Arguments:
@   s0 - s4: Rock attributes
@ Returns:
@   none
@ Clobbers:
@  s0 - s31
@ ------------------------------------------------------------------------------
draw_wrench_sprite:
  ldr         r0, =wrench
  ldr         r1, =mtx_mvp
  ldr         r2, =mtx_view
  ldr         r3, =0x3f800000
  vmov.f32    s0, r3
  vmov.f32    s1, r3
  bl          gfx_draw_sprite

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Draws a rocket sprite
@ Arguments:
@   s0 - s4: Rock attributes
@ Returns:
@   none
@ Clobbers:
@  s0 - s31
@ ------------------------------------------------------------------------------
draw_rocket_sprite:
  ldr         r0, =rocket
  ldr         r1, =mtx_mvp
  ldr         r2, =mtx_view
  ldr         r3, =0x3f800000
  vmov.f32    s0, r3
  vmov.f32    s1, r3
  bl          gfx_draw_sprite

  ldmfd       sp!, {pc}

@-------------------------------------------------------------------------------
@ Resets objects
@-------------------------------------------------------------------------------
reset_objects:
  stmfd       sp!, {r0 - r4, lr}
  vstmdb.f32  sp!, {s0 - s6}

  ldr         r4, =OBJECT_COUNT
  ldr         r3, =object_list
  ldr         r2, =ROCK_LIVES
  ldr         r1, =0x3FC00000    @ 1.5
  mov         r0, #0

1:
  vldm.f32    r3, {s0 - s6}

  vmov.f32    s0, r0
  vmov.f32    s1, r0
  vmov.f32    s2, r0
  vmov.f32    s3, r1
  vmov.f32    s4, r0
  vmov.f32    s5, r0
  vmov.f32    s6, r2

  vstm.f32    r3!, {s0 - s6}
  
  subs        r4, #1
  bne         1b

  vldmia.f32  sp!, {s0 - s6}
  ldmfd       sp!, {r0 - r4, pc}


================================================
FILE: pillars.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global draw_pillars
.global pillar_pos

.section .data
.include "assets/pillar.s"

.section .text
@ ------------------------------------------------------------------------------
@ Cubes on the sides
@ ------------------------------------------------------------------------------
.equ             PILLAR_COUNT, 24
pillar_pos:     .float 0.0
pillar_list:
  .float        -10.0,  0.0, -142.0
  .float          1.0,  6.0,    1.0
  .float        -10.0,  0.0, -130.0
  .float          1.0,  6.0,    1.0
  .float        -10.0,  0.0, -118.0
  .float          1.0,  6.0,    1.0
  .float        -10.0,  0.0, -106.0
  .float          1.0,  6.0,    1.0
  .float        -10.0,  0.0,  -94.0
  .float          1.0,  6.0,    1.0
  .float        -10.0,  0.0,  -82.0
  .float          1.0,  6.0,    1.0
  .float        -10.0,  0.0,  -70.0
  .float          1.0,  6.0,    1.0
  .float        -10.0,  0.0,  -58.0
  .float          1.0,  6.0,    1.0
  .float        -10.0,  0.0,  -46.0
  .float          1.0,  6.0,    1.0
  .float        -10.0,  0.0,  -34.0
  .float          1.0,  6.0,    1.0
  .float        -10.0,  0.0,  -22.0
  .float          1.0,  6.0,    1.0
  .float        -10.0,  0.0,  -10.0
  .float          1.0,  6.0,    1.0
  .float         10.0,  0.0, -142.0
  .float          1.0,  6.0,    1.0
  .float         10.0,  0.0, -130.0
  .float          1.0,  6.0,    1.0
  .float         10.0,  0.0, -118.0
  .float          1.0,  6.0,    1.0
  .float         10.0,  0.0, -106.0
  .float          1.0,  6.0,    1.0
  .float         10.0,  0.0,  -94.0
  .float          1.0,  6.0,    1.0
  .float         10.0,  0.0,  -82.0
  .float          1.0,  6.0,    1.0
  .float         10.0,  0.0,  -70.0
  .float          1.0,  6.0,    1.0
  .float         10.0,  0.0,  -58.0
  .float          1.0,  6.0,    1.0
  .float         10.0,  0.0,  -46.0
  .float          1.0,  6.0,    1.0
  .float         10.0,  0.0,  -34.0
  .float          1.0,  6.0,    1.0
  .float         10.0,  0.0,  -22.0
  .float          1.0,  6.0,    1.0
  .float         10.0,  0.0,  -10.0
  .float          1.0,  6.0,    1.0

@ ------------------------------------------------------------------------------
@ Renders the pillars on the sides
@ ------------------------------------------------------------------------------
draw_pillars:
  stmfd       sp!, {lr}
  ldr         r12, =PILLAR_COUNT
  ldr         r11, =pillar_list

  @ Reset model matrix
  ldr         r0, =mtx_id
  vldm.f32    r0, {s0 - s15}
  ldr         r0, =mtx_model
  vstm.f32    r0, {s0 - s15}

  @ Get player position
  ldr         r0, =player_speed
  vldr.f32    s3, [r0]
  ldr         r0, =pillar_pos
  vldr.f32    s4, [r0]
  vadd.f32    s4, s4, s3
  ldr         r1, =0x41400000
  vmov.f32    s5, r1
  vcmp.f32    s4, s5
  fmstat
  ldr         r1, =0x00000000
  vmovgt.f32  s4, r1
  vstr.f32    s4, [r0]

1:
  vldm.f32    r11!, {s0 - s2}
  vstmdb.f32  sp!, {s4}
  vadd.f32    s2, s2, s4
  bl          draw_pillar
  vldmia.f32  sp!, {s4}
  subs        r12, r12, #1
  bne         1b

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Draws a single pillar
@ ------------------------------------------------------------------------------
draw_pillar:
  stmfd       sp!, {lr}

  ldr         r0, =mtx_model
  bl          mat4_translate
  vldm.f32    r11!, {s0 - s2}
  bl          mat4_scale

  ldr         r0, =mtx_vp
  ldr         r1, =mtx_model
  ldr         r2, =mtx_mvp
  bl          mat4_mul_mat4

  ldr         r0, =pillar_vtx
  ldr         r1, =pillar_idx
  ldr         r2, =10
  ldr         r3, =mtx_mvp
  ldr         r4, =light_dir
  bl          gfx_draw_trgs

  ldmfd       sp!, {pc}


================================================
FILE: player.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global player_pos
.global player_health
.global player_rolling
.global player_speed
.global player_shake
.global player_score
.global player_high_score
.global player_wrenches
.global player_rockets
.global draw_player
.global player_damage
.global update_player
.global setup_player
.global reset_player_mov
.global monkey_timer
.global rock_timer

.section .data
@ ------------------------------------------------------------------------------
@ Player resources
@ ------------------------------------------------------------------------------
player_charge:      .long 0
player_health:      .long 200
player_wrenches:    .long 3
player_rockets:     .long 3
tristan_timer:      .long 0
monkey_timer:       .long 0
rock_timer:         .long 0

@ ------------------------------------------------------------------------------
@ Player movement
@ ------------------------------------------------------------------------------
player_tilt_z:      .float 0.0
player_tilt_x:      .float 0.0
player_rolling:     .long 0
player_shake:       .long 0
player_score:       .long 0
player_high_score:  .long 0
player_roll_dir:    .float 0.15
player_pos:         .float 0.0, 0.0, 0.0
player_speed:       .float 0.0
player_speed_mod:   .long 0

.section .text
@ ------------------------------------------------------------------------------
@ Initialises the player
@ ------------------------------------------------------------------------------
setup_player:
  stmfd       sp!, {r0 - r1, lr}

  ldr         r0, =player_charge
  ldr         r1, =0
  str         r1, [r0]

  ldr         r0, =player_health
  ldr         r1, =200
  str         r1, [r0]

  ldr         r0, =player_wrenches
  ldr         r1, =3
  str         r1, [r0]

  ldr         r0, =player_rockets
  ldr         r1, =3
  str         r1, [r0]

  ldr         r0, =player_score
  mov         r1, #0
  str         r1, [r0]

  ldr         r0, =player_speed_mod
  mov         r1, #0
  str         r1, [r0]

  ldr         r0, =tristan_timer
  mov         r1, #0
  str         r1, [r0]

  ldr         r0, =monkey_timer
  mov         r1, #0
  str         r1, [r0]

  ldr         r0, =rock_timer
  mov         r1, #130
  str         r1, [r0]

  ldr         r0, =player_pos
  mov         r1, #0
  vmov.f32    s0, r1
  vmov.f32    s1, r1
  vmov.f32    s2, r1
  vstm.f32    r0, {s0 - s2}

  ldmfd       sp!, {r0 - r1, pc}

@ ------------------------------------------------------------------------------
@ Renders the player's plane
@ ------------------------------------------------------------------------------
draw_player:
  stmfd       sp!, {lr}

  @ Reset model matrix
  ldr         r0, =mtx_id
  vldm.f32    r0, {s0 - s15}
  ldr         r0, =mtx_model
  vstm.f32    r0, {s0 - s15}
  mov         r1, #0
  vmov.f32    s0, r1
  ldr         r1, =0xc0000000
  vmov.f32    s1, r1
  ldr         r1, =0xc0e00000
  vmov.f32    s2, r1
  bl          mat4_translate

  @ Tilt around Z
  ldr         r0, =mtx_id
  vldm.f32    r0, {s0 - s15}
  ldr         r0, =mtx_temp
  vstm.f32    r0, {s0 - s15}
  ldr         r0, =player_tilt_z
  vldr.f32    s0, [r0]
  ldr         r0, =mtx_temp
  bl          mat4_rot_z
  ldr         r0, =mtx_model
  ldr         r1, =mtx_temp
  ldr         r2, =mtx_model
  bl          mat4_mul_mat4

  @ Tilt around X
  ldr         r0, =mtx_id
  vldm.f32    r0, {s0 - s15}
  ldr         r0, =mtx_temp
  vstm.f32    r0, {s0 - s15}
  ldr         r0, =player_tilt_x
  vldr.f32    s0, [r0]
  ldr         r0, =mtx_temp
  bl          mat4_rot_x
  ldr         r0, =mtx_model
  ldr         r1, =mtx_temp
  ldr         r2, =mtx_model
  bl          mat4_mul_mat4

  @ Compute proj * model
  ldr         r0, =mtx_proj
  ldr         r1, =mtx_model
  ldr         r2, =mtx_mp
  bl          mat4_mul_mat4

  @ Draw the plane
  ldr         r0, =ship_vtx
  ldr         r1, =ship_idx
  ldr         r2, =22
  ldr         r3, =mtx_mp
  ldr         r4, =light_dir
  bl          gfx_draw_trgs

  @ Draw wrenches
  ldr         r3, =player_wrenches
  ldr         r3, [r3]
  ldr         r1, =582
  ldr         r2, =410
  ldr         r0, =wrench

  tst         r3, r3
  beq         2f
1:
  bl          gfx_draw_image
  sub         r1, r1, #20
  subs        r3, r3, #1
  bne         1b
2:

  @ Draw rockets
  ldr         r3, =player_rockets
  ldr         r3, [r3]
  mov         r1, #34
  ldr         r2, =410
  ldr         r0, =rocket

  tst         r3, r3
  beq         2f
1:
  bl          gfx_draw_image
  add         r1, r1, #20
  subs        r3, r3, #1
  bne         1b
2:

  @ Draw health bar
  ldr         r0, =522
  ldr         r1, =450
  ldr         r5, =player_health
  ldr         r6, =0xFF0000cc
  bl          draw_bar

  @ Draw charge bar
  mov         r0, #10
  ldr         r1, =450
  ldr         r5, =player_charge
  ldr         r6, =0xFFFF4400
  bl          draw_bar

  @ Test if monkey should pop-up
  ldr         r0, =monkey_timer
  ldr         r1, [r0]
  subs        r1, r1, #1
  str         r1, [r0]

  ble         2f

  @ Draw monkey
  ldr         r0, =124
  ldr         r1, =420
  ldr         r5, =monkey
  ldr         r6, =5f
  bl          draw_pop_up

  ldmfd       sp!, {pc}

2:

  @ Test if Tristan should pop-up
  ldr         r0, =tristan_timer
  ldr         r1, [r0]
  subs        r1, r1, #1
  str         r1, [r0]

  ble         3f

  @ Draw Tristan barrel roll
  ldr         r0, =124
  ldr         r1, =420
  ldr         r5, =tristan
  ldr         r6, =4f
  bl          draw_pop_up

  ldmfd       sp!, {pc}

3:

  @ Test if Tristan should pop-up
  ldr         r0, =rock_timer
  ldr         r1, [r0]
  subs        r1, r1, #1
  str         r1, [r0]

  ldmlefd     sp!, {pc}

  @ Draw Tristan rock-and-roll
  ldr         r0, =124
  ldr         r1, =420
  ldr         r5, =tristan
  ldr         r6, =6f
  bl          draw_pop_up

  ldmfd       sp!, {pc}

4:
  .ascii "\nDo a barrel roll!"
  .byte  0x0
  .align 2

5:
  .ascii "\nCan't let you do that PiFox!"
  .byte  0x0
  .align 2

6:
  .ascii "I see 'em up ahead.\nLet's Rock and Roll!"
  .byte  0x0
  .align 2

@ ------------------------------------------------------------------------------
@ Rotates the camera around origin
@ ------------------------------------------------------------------------------
update_player:
  stmfd       sp!, {lr}

  bl          do_barrel_roll
  bl          do_speed
  bl          do_movement
  bl          do_repair
  bl          do_rocket
  bl          do_charge
  bl          do_matrices

  bl          do_speed_inc

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Continues a barrell roll
@ ------------------------------------------------------------------------------
do_barrel_roll:
  stmfd       sp!, {lr}

  @ If we are already rolling, continue
  ldr         r0, =player_rolling
  ldr         r1, [r0]
  tst         r1, r1
  bne         1f

  @ Roll when left/right is double clicked
  ldr         r2, =button_dclicked
  ldr         r2, [r2]
  tst         r2, #0xC0
  ldmeqfd     sp!, {pc}

  @ Charge must be full
  ldr         r3, =player_charge
  ldr         r4, [r3]
  cmp         r4, #200
  ldmltfd     sp!, {pc}
  mov         r4, #0
  str         r4, [r3]

  @ Play sounds
  bl          snd_play_boost

  @ Start the roll
  ldr         r3, =player_roll_dir
  vldr.f32    s0, [r3]
  vabs.f32    s0, s0
  tst         r2, #0x80
  vnegne.f32  s0, s0
  vstr.f32    s0, [r3]
  mov         r1, #1
  str         r1, [r0]
  ldmfd       sp!, {pc}
1:
  @ Update rotation
  ldr         r2, =player_roll_dir
  vldr.f32    s0, [r2]
  ldr         r2, =player_tilt_z
  vldr.f32    s1, [r2]
  vadd.f32    s1, s1, s0

  vcmp.f32    s0, #0
  fmstat
  bgt         2f
  ldr         r3, =0xc0c90fdb
  vmov.f32    s2, r3
  vcmp.f32    s1, s2
  fmstat
  movlt       r1, #0
  vmovlt.f32  s1, r1
  b           3f
2:
  ldr         r3, =0x40c90fdb
  vmov.f32    s2, r3
  vcmp.f32    s1, s2
  fmstat
  movgt       r1, #0
  vmovgt.f32  s1, r1
3:
  str         r1, [r0]
  vstr.f32    s1, [r2]
  ldr         r0, =0x3f800000        @ 1.0

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Updates the movement speed of the player
@ ------------------------------------------------------------------------------
do_speed:
  ldr         r0, =player_speed_mod
  vldr.f32    s0, [r0]
  fsitos      s0, s0

  ldr         r0, =0x3a378034        @ 0.0007
  vmov.f32    s1, r0
  vmul.f32    s0, s0, s1

  ldr         r0, =0x3f800000
  vmov.f32    s1, r0
  vadd.f32    s0, s0, s1

  ldr         r0, =0x40600000         @ 3.5
  vmov.f32    s1, r0
  ldr         r0, =player_rolling
  ldr         r0, [r0]
  tst         r0, r0
  vmulne.f32  s0, s0, s1

  ldr         r0, =0x40a00000
  vmov.f32    s1, r0
  vcmp.f32    s0, s1
  fmstat
  vmovgt.f32  s0, s1

  ldr         r0, =player_speed
  vstr.f32    s0, [r0]

  mov         pc, lr

@ ------------------------------------------------------------------------------
@ Increase player's speed modifier
@ ------------------------------------------------------------------------------
do_speed_inc:
  stmfd       sp!, {r0, r1}
  ldr         r0, =player_speed_mod
  ldr         r1, [r0]
  add         r1, r1, #1
  str         r1, [r0]
  ldmfd       sp!, {r0, r1}
  mov         pc, lr

@ ------------------------------------------------------------------------------
@ Updates the position of the player
@ ------------------------------------------------------------------------------
do_movement:
  @ Normal speed
  ldr         r0, =player_rolling
  ldr         r0, [r0]
  tst         r0, r0
  movne       pc, lr

  stmfd       sp!, {lr}

  @ Get button state
  ldr         r2, =button_pressed
  ldr         r2, [r2]

  @ Move player forward
  ldr         r0, =player_pos
  vldm.f32    r0, {s0 - s2}
  ldr         r3, =0x3f800000       @ 1.0
  vmov.f32    s4, r3
  tst         r2, #0x80
  vaddne.f32  s0, s4                @ right
  tst         r2, #0x40
  vsubne.f32  s0, s4                @ left
  tst         r2, #0x10
  vsubne.f32  s1, s4                @ up
  tst         r2, #0x20
  vaddne.f32  s1, s4                @ down
  bl          clamp

  vstm.f32    r0, {s0 - s2}

  @ Tilt left/right
  ldr         r3, =player_tilt_z
  vldr.f32    s0, [r3]
  ldr         r0, =0x3d23d70a       @ 0.04
  vmov.f32    s1, r0

  tst         r2, #0x80
  beq         1f
  vsub.f32    s0, s1
  ldr         r0, =0xbecccccd       @ -0.4
  vmov.f32    s2, r0
  vcmp.f32    s0, s2
  fmstat
  vmovlt.f32  s0, s2
  b           4f
1:
  tst         r2, #0x40
  beq         2f
  vadd.f32    s0, s1
  ldr         r0, =0x3ecccccd       @ 0.4
  vmov.f32    s2, r0
  vcmp.f32    s0, s2
  fmstat
  vmovgt.f32  s0, s2
  b           4f
2:
  vcmp.f32    s0, #0
  fmstat
  blt         3f
  vsub.f32    s0, s1
  ldr         r0, =0
  vmov.f32    s2, r0
  vcmp.f32    s0, s2
  fmstat
  vmovlt.f32  s0, s2
  b           4f
3:
  vadd.f32    s0, s1
  ldr         r0, =0
  vmov.f32    s2, r0
  vcmp.f32    s0, s2
  fmstat
  vmovgt.f32  s0, s2
4:
  vstr.f32    s0, [r3]

  @ Tilt up/down
  ldr         r3, =player_tilt_x
  vldr.f32    s0, [r3]
  ldr         r0, =0x3d23d70a       @ 0.04
  vmov.f32    s1, r0

  tst         r2, #0x20
  beq         1f
  vsub.f32    s0, s1
  ldr         r0, =0xbecccccd       @ -0.4
  vmov.f32    s2, r0
  vcmp.f32    s0, s2
  fmstat
  vmovlt.f32  s0, s2
  b           4f
1:
  tst         r2, #0x10
  beq         2f
  vadd.f32    s0, s1
  ldr         r0, =0x3dcccccd       @ 0.1
  vmov.f32    s2, r0
  vcmp.f32    s0, s2
  fmstat
  vmovgt.f32  s0, s2
  b           4f
2:
  vcmp.f32    s0, #0
  fmstat
  blt         3f
  vsub.f32    s0, s1
  ldr         r0, =0
  vmov.f32    s2, r0
  vcmp.f32    s0, s2
  fmstat
  vmovlt.f32  s0, s2
  b           4f
3:
  vadd.f32    s0, s1
  ldr         r0, =0
  vmov.f32    s2, r0
  vcmp.f32    s0, s2
  fmstat
  vmovgt.f32  s0, s2
4:
  vstr.f32    s0, [r3]

  ldmfd       sp!, {pc}

@-------------------------------------------------------------------------------
@ Handles repairs
@-------------------------------------------------------------------------------
do_repair:
  @ Repair when B was double clicked
  ldr         r0, =button_dclicked
  ldr         r0, [r0]
  tst         r0, #0x02
  moveq       pc, lr

  @ Check number of repairs left
  ldr         r0, =player_wrenches
  ldr         r1, [r0]
  tst         r1, r1
  moveq       pc, lr
  sub         r1, #1
  str         r1, [r0]

  @ Update health
  ldr         r0, =player_health
  ldr         r1, [r0]
  add         r1, #50
  cmp         r1, #200
  movgt       r1, #200
  str         r1, [r0]

  mov         pc, lr

@-------------------------------------------------------------------------------
@ Handles firing rockets
@-------------------------------------------------------------------------------
do_rocket:
  stmfd       sp!, {lr}

  @ Fire when B was clicked
  ldr         r0, =button_clicked
  ldr         r1, [r0]
  tst         r1, #0x02
  moveq       pc, lr

  @ Check number of rockets left
  ldr         r0, =player_rockets
  ldr         r1, [r0]
  tst         r1, r1
  moveq       pc, lr
  sub         r1, #1
  str         r1, [r0]

  @ Play rocket sound
  bl          snd_play_rocket

  @ Fire the rocket
  bl          spawn_rocket

  ldmfd       sp!, {pc}

@-------------------------------------------------------------------------------
@ Increses the charge of barrell roll
@-------------------------------------------------------------------------------
do_charge:
  @ Check for roll
  ldr         r0, =player_rolling
  ldr         r0, [r0]
  tst         r0, r0
  movne       pc, lr

  @ Update charge
  ldr         r0, =player_charge
  ldr         r1, [r0]
  cmp         r1, #200
  add         r1, #1
  strlt       r1, [r0]

  mov         pc, lr

@ ------------------------------------------------------------------------------
@ Computes mtx_view and mtx_vp
@ ------------------------------------------------------------------------------
do_matrices:
  stmfd       sp!, {lr}

  @ Clear the view matrix
  ldr         r0, =mtx_id
  vldm.f32    r0, {s0 - s15}
  ldr         r0, =mtx_view
  vstm.f32    r0, {s0 - s15}

  @ Shake the world
  ldr         r0, =player_shake
  ldr         r1, [r0]
  tst         r1, r1
  subne       r1, r1, #1
  str         r1, [r0]
  vmov.f32    s0, r1
  fsitos      s0, s0
  bl          sin
  ldr         r0, =0x42700000
  vmov.f32    s2, r0
  vmul.f32    s5, s0, s1
  vdiv.f32    s5, s5, s2

  @ Compute the view matrix (translation)
  ldr         r0, =player_pos
  vldm.f32    r0, {s0 - s2}
  vadd.f32    s1, s1, s5
  vabs.f32    s5, s5
  vsub.f32    s0, s5, s0
  ldr         r0, =mtx_view
  bl          mat4_translate

  @ Compute the proj * view matrix
  ldr         r0, =mtx_proj
  ldr         r1, =mtx_view
  ldr         r2, =mtx_vp
  bl          mat4_mul_mat4

  ldmfd       sp!, {pc}

@ ------------------------------------------------------------------------------
@ Clamps a coordinate, forcing it inside bounds on X and Y axis
@
@ Arguments:
@   s0 - x
@   s1 - y
@   s2 - z
@ Returns:
@   s0 - clamped x
@   s1 - clamped y
@   s2 - clamped z
@ Clobbers
@   none
@ ------------------------------------------------------------------------------
clamp:
  stmfd         sp!, {r0}
  vstmdb.f32    sp!, {s3}

  ldr           r0, =0x41080000     @ 8.5
  vmov.f32      s3, r0
  vcmp.f32      s0, s3
  fmstat
  vmovgt.f32    s0, s3

  ldr           r0, =0xc1080000     @ -8.5
  vmov.f32      s3, r0
  vcmp.f32      s0, s3
  fmstat
  vmovlt.f32    s0, s3

  ldr           r0, =0x3f000000     @ 0.5
  vmov.f32      s3, r0
  vcmp.f32      s1, s3
  fmstat
  vmovgt.f32    s1, s3

  ldr           r0, =0xc0900000     @ -4.5
  vmov.f32      s3, r0
  vcmp.f32      s1, s3
  fmstat
  vmovlt.f32    s1, s3

  vldmia.f32    sp!, {s3}
  ldmfd         sp!, {r0}
  mov           pc, lr

@-------------------------------------------------------------------------------
@ Draws health / charge bar
@
@ Arguments:
@   r0 - x
@   r1 - y
@   r5 - current amount address
@   r6 - colour
@ Returns:
@   none
@ Clobbers:
@   none
@-------------------------------------------------------------------------------
draw_bar:
  stmfd       sp!, {r0 - r6, lr}

  @ Draw frame
  mov         r2, #108
  mov         r3, #20
  ldr         r4, =0xFFFFFF00
  bl          gfx_draw_frame

  @ Draw current charge
  add         r0, #4
  add         r1, #4
  ldr         r2, [r5]
  lsr         r2, r2, #1
  sub         r3, r3, #8
  mov         r4, r6
  bl          gfx_draw_rect

  ldmfd       sp!, {r0 - r6, pc}

@-------------------------------------------------------------------------------
@ Draws character
@ Arguments:
@   r0 - x
@   r1 - y
@   r5 - picture address
@   r6 - message address
@ Returns:
@   none
@ Clobbers:
@   none
@-------------------------------------------------------------------------------
draw_pop_up:
  stmfd       sp!, {r0 - r6, lr}

  @ Draw frame
  mov         r2, #58
  mov         r3, #57
  ldr         r4, =0xFFFFFF00
  bl          gfx_draw_frame

  add         r2, r1, #3
  add         r1, r0, #3
  mov         r0, r5
  bl          gfx_draw_image

  mov         r0, r6
  add         r1, r1, #60
  add         r2, r2, #16
  ldr         r3, =0xFFFFFF00
  bl          printf

  ldmfd       sp!, {r0 - r6, pc}     

@ ------------------------------------------------------------------------------
@ Applies damage to the player
@ Arguments:
@   r0 - damage
@ Returns:
@   none
@ Clobbers:
@   none
@ ------------------------------------------------------------------------------
player_damage:
  stmfd       sp!, {r0 - r4, lr}

  ldr         r3, =player_speed_mod     @ Decrement score
  ldr         r4, [r3]
  subs        r4, r4, #300
  movlt       r4, #0
  str         r4, [r3]


  @ Play damage sound
  bl          snd_play_crash

  ldr         r1, =player_health    @ Decrement health
  ldr         r2, [r1]
  cmp         r2, #100
  blt         1f
  sub         r3, r2, r0
  cmp         r3, #100
  bgt         1f

  @ Pop-up Tristan
  ldr         r3, =tristan_timer
  mov         r4, #110
  str         r4, [r3]

  bl          snd_play_roll


1:
  sub         r2, r2, r0
  cmp         r2, #0
  movle       r2, #0
  str         r2, [r1]

  ldr         r1, =player_shake
  mov         r2, #40
  str         r2, [r1]

  ldmfd       sp!, {r0 - r4, pc}

@-------------------------------------------------------------------------------
@ Resets player movement
@-------------------------------------------------------------------------------
reset_player_mov:
  stmfd       sp!, {r0 -r1, lr}
  vstmdb.f32  sp!, {s0}

  mov         r0, #0
  vmov.f32    s0, r0

  ldr         r1, =player_tilt_z
  vstm.f32    r1, {s0}

  ldr         r1, =player_tilt_x
  vstm.f32    r1, {s0}    

  ldr         r1, =player_rolling
  str         r0, [r1]

  ldr         r1, =player_shake
  str         r0, [r1]

  ldr         r1, =player_speed
  vstm.f32    r1, {s0}

  ldr         r1, =player_roll_dir
  ldr         r0, =0x3E19999A         @ 0.15
  vmov.f32    s0, r0
  vstm.f32    r1, {s0}

  vldmia.f32  sp!, {s0}  
  ldmfd       sp!, {r0 - r1, pc}
  


================================================
FILE: ports.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.

@ ------------------------------------------------------------------------------
@ System timer
@ ------------------------------------------------------------------------------
.equ STIMER_CS,        0x20003000
.equ STIMER_CLO,       0x20003004
.equ STIMER_CHI,       0x20003008
.equ STIMER_C0,        0x2000300C
.equ STIMER_C1,        0x20003010
.equ STIMER_C2,        0x20003014
.equ STIMER_C3,        0x20003018

@ ------------------------------------------------------------------------------
@ Interrupt register
@ ------------------------------------------------------------------------------
.equ IRQ_PENDING,      0x2000B200
.equ IRQ_GPU_PENDING1, 0x2000B204
.equ IRQ_GPU_PENDING2, 0x2000B208
.equ IRQ_FIQ,          0x2000B20C
.equ IRQ_EN1,          0x2000B210
.equ IRQ_EN2,          0x2000B214
.equ IRQ_ENB,          0x2000B218
.equ IRQ_DS1,          0x2000B21C
.equ IRQ_DS2,          0x2000B220
.equ IRQ_DSB,          0x2000B224

@ ------------------------------------------------------------------------------
@ ARM timer
@ ------------------------------------------------------------------------------
.equ TIMER_LOD,        0x2000B400
.equ TIMER_VAL,        0x2000B404
.equ TIMER_CTL,        0x2000B408
.equ TIMER_CLI,        0x2000B40C
.equ TIMER_RIS,        0x2000B410
.equ TIMER_MIS,        0x2000B414
.equ TIMER_RLD,        0x2000B418
.equ TIMER_DIV,        0x2000B41C
.equ TIMER_CNT,        0x2000B420

@ ------------------------------------------------------------------------------
@ Mailbox Ports
@ ------------------------------------------------------------------------------
.equ MBOX_BASE,        0x2000B880
.equ MBOX_READ,        0x2000B880
.equ MBOX_POLL,        0x2000B890
.equ MBOX_SENDER,      0x2000B894
.equ MBOX_STATUS,      0x2000B898
.equ MBOX_CONFIG,      0x2000B89C
.equ MBOX_WRITE,       0x2000B8A0

@ ------------------------------------------------------------------------------
@ GPIO Ports
@ ------------------------------------------------------------------------------
.equ GPIO_FSEL0,       0x20200000
.equ GPIO_FSEL1,       0x20200004
.equ GPIO_FSEL2,       0x20200008
.equ GPIO_FSEL3,       0x2020000C
.equ GPIO_FSEL4,       0x20200010
.equ GPIO_FSEL5,       0x20200014
.equ GPIO_SET0,        0x2020001C
.equ GPIO_SET1,        0x20200020
.equ GPIO_CLR0,        0x20200028
.equ GPIO_CLR1,        0x2020002C
.equ GPIO_LEV0,        0x20200034
.equ GPIO_LEV1,        0x20200038
.equ GPIO_EDS0,        0x20200040
.equ GPIO_EDS1,        0x20200044
.equ GPIO_REN0,        0x2020004C
.equ GPIO_REN1,        0x20200050
.equ GPIO_FEN0,        0x20200058
.equ GPIO_FEN1,        0x2020005C
.equ GPIO_HEN0,        0x20200064
.equ GPIO_HEN1,        0x20200068
.equ GPIO_LEN0,        0x20200070
.equ GPIO_LEN1,        0x20200074
.equ GPIO_AREN0,       0x2020007C
.equ GPIO_AREN1,       0x20200080
.equ GPIO_AFEN0,       0x20200088
.equ GPIO_AFEN1,       0x2020008C
.equ GPIO_PUD,         0x20200094
.equ GPIO_UDCLK0,      0x20200098
.equ GPIO_UDCLK1,      0x2020009C

@ ------------------------------------------------------------------------------
@ PL011 UART Ports
@ ------------------------------------------------------------------------------
.equ UART_DR,          0x20201000
.equ UART_RSECR,       0x20201004
.equ UART_FR,          0x20201018
.equ UART_ILPR,        0x20201020
.equ UART_IBRD,        0x20201024
.equ UART_FBRC,        0x20201028
.equ UART_LCRH,        0x2020102C
.equ UART_CR,          0x20201030
.equ UART_IFLS,        0x20201034
.equ UART_IMSC,        0x20201038
.equ UART_RIS,         0x2020103C
.equ UART_MIS,         0x20201040
.equ UART_ICR,         0x20201044
.equ UART_DMACR,       0x20201048
.equ UART_ITCR,        0x20201080
.equ UART_ITIP,        0x20201084
.equ UART_ITOP,        0x20201088
.equ UART_TDR,         0x2020108C

@ ------------------------------------------------------------------------------
@ Clock manager
@ ------------------------------------------------------------------------------
.equ CM_PWMCTL,        0x201010A0
.equ CM_PWMDIV,        0x201010A4

@ ------------------------------------------------------------------------------
@ Direct Memory Access
@ ------------------------------------------------------------------------------
.equ DMA0_CS,          0x20007000
.equ DMA0_CONBLK,      0x20007004
.equ DMA_INT_STATUS,   0x20007FE0
.equ DMA_ENABLE,       0x20007FF0

@ ------------------------------------------------------------------------------
@ Pulse Width modulator
@ ------------------------------------------------------------------------------
.equ PWM_CTL,          0x2020C000
.equ PWM_STA,          0x2020C004
.equ PWM_DMAC,         0x2020C008
.equ PWM_RNG1,         0x2020C010
.equ PWM_DAT1,         0x2020C014
.equ PWM_FIF1,         0x2020C018
.equ PWM_RNG2,         0x2020C020
.equ PWM_DAT2,         0x2020C024


================================================
FILE: printf.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved..global printf
.global printf

.section .text
@ ------------------------------------------------------------------------------
@ Print formatted string.
@ The format string may contain the following sequences:
@   %[n]x - print hexadecimal, pad to n chars
@   %[n]d - print decimal, pad to n chars
@   %s    - print string
@ Arguments are taken from the stack and they must be pushed from the right to
@ the left. Stack cleanup is the responsibility of the caller.
@
@ Arguments:
@   r0    - address of the output format string
@   stack - number(s)/string(s) to be printed, in reversed order
@ Return:
@   None
@ Clobbers:
@   None
@ ------------------------------------------------------------------------------
printf:
  stmfd   sp!, {r0 - r12, lr} @ r0 will store address of the format string
                              @ r1 will store beginning of the output string
  add     r4, sp, #56
  mov     r3, sp

  ldr     lr, =1f
1:
  mov     r2, #0
  ldrb    r1, [r0], #1        @ put next char in r1
  tst     r1, r1
  beq     4f                  @ exit if r2 == '\0'
  cmp     r1, #37
  beq     2f
  strb    r1, [sp, #-1]!      @ store character on stack
  b       1b
2:
  ldrb    r1, [r0], #1
  cmp     r1, #100            @ d = decimal
  beq     printf_d
  cmp     r1, #115            @ s = string
  beq     printf_s
  cmp     r1, #120            @ x = hexadecimal
  beq     printf_x
  cmp     r1, #48
  blo     3f
  cmp     r1, #57
  bhi     3f

  @ Charater is a digit
  subs    r2, r1, #48
  b       2b
3:
  mov     r2, #37
  strb    r2, [sp, #-1]!
  strb    r1, [sp, #-1]!
  tst     r1, r1
  bne     1b
4:
  mov     r1, #0
  strb    r1, [sp, #-1]!      @ Store a null terminator
  @ Reverse buffer
  mov     r4, sp
  sub     r5, r3, #1
5:
  ldrb    r1, [r4]
  ldrb    r2, [r5]
  strb    r1, [r5], #-1
  strb    r2, [r4], #1
  cmp     r4, r5
  blo     5b

  @ Print to stdout with a syscall
  mov     r4, r3
  mov     r0, sp
  ldr     r1, [r3, #4]
  ldr     r2, [r3, #8]
  ldr     r3, [r3, #12]
  bl      gfx_draw_text

  mov     sp, r4
  ldmfd   sp!, {r0 - r12, pc}

@ ------------------------------------------------------------------------------
@ Decimal helper for printf
@ Arguments:
@   r5 - Number to print
@   r2 - Max size of buffer
@ ------------------------------------------------------------------------------
printf_d:
  ldr     r5, [r4], #4
  ldr     r7, =429496730

  sub     r10, sp, #1

  cmp     r5, #0                @ check sign
  bge     1f
  mov     r6, #45               @ r6 = '-'
  neg     r5, r5
1:
  cmp     r2, #0
  bne     3f                    @ if r2 != 0 then write the digits with padding
2:
  @ Handles the case where padding is not required i.e. r2 = 0
  mov     r9, r5
  sub     r5, r5, r5, lsr #30
  umull   r8, r5, r7, r5

  mov     r8, #10
  mul     r8, r5, r8
  sub     r9, r9, r8
  add     r9, #48
  strb    r9, [sp, #-1]!
  cmp     r5, #0
  bgt     2b
  b       5f
3:
  mov     r11, #32              @ r11 = ' '
4:
  @ Handles padding
  mov     r9, r5
  sub     r5, r5, r5, lsr #30
  umull   r8, r5, r7, r5

  mov     r8, #10
  mul     r8, r5, r8
  sub     r9, r9, r8
  add     r9, #48
  strb    r9, [sp, #-1]!
  sub     r2, r2, #1
  cmp     r2, #0
  beq     5f
  cmp     r5, #0
  bgt     4b
5:
  @ Puts minus if negative
  cmp     r6, #45
  bne     6f
  strb    r6, [sp, #-1]!
  sub     r2, r2, #1
6:
  @ Padds with spaces if necessary
  cmp     r2, #0
  ble     7f
  strb    r11, [sp, #-1]!
  sub     r2, r2, #1
  b       6b
7:
  mov     r11, sp
8:
  @ Reverses the buffer
  ldrb    r8, [r10]
  ldrb    r9, [r11]
  strb    r8, [r11], #1
  strb    r9, [r10], #-1
  cmp     r10, r11
  bgt     8b

  mov     pc, lr

@ ------------------------------------------------------------------------------
@ Hexadecimal helper for printf
@ Arguments:
@   r5 - Number to print
@ ------------------------------------------------------------------------------
printf_x:
  ldr     r5, [r4], #4

  tst     r2, r2
  bne     2f

  mov     r6, r5
1:
  add     r2, #1
  lsrs    r6, r6, #4
  bne     1b
2:
  sub     sp, r2

  @ Unroll for first digit
  and     r6, r5, #0xF
  lsr     r5, r5, #4
  cmp     r6, #10
  addlo   r6, #48
  addhs   r6, #55
  strb    r6, [sp]
  mov     r7, #1
  subs    r2, r2, #1
  beq     4f
3:
  @ Write the rest of the digits with padding
  and     r6, r5, #0xF
  cmp     r6, #10
  addlo   r6, #48
  addhs   r6, #55
  lsr     r5, r5, #4
  strb    r6, [sp, r7]
  add     r7, r7, #1
  subs    r2, r2, #1
  bne     3b
4:
  mov     pc, lr

@ ------------------------------------------------------------------------------
@ String helper for printf
@ Arguments:
@   r5 - Address of the string
@ ------------------------------------------------------------------------------
printf_s:
  ldr     r5, [r4], #4
1:
  ldrb    r6, [r5], #1
  cmp     r6, #0
  beq     2f
  strb    r6, [sp, #-1]!
  b       1b
2:
  mov pc, lr


================================================
FILE: rockets.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global draw_rockets
.global spawn_rocket
.global reset_rockets

.section .data
@-------------------------------------------------------------------------------
@ Rockets
@-------------------------------------------------------------------------------
.equ          ROCKET_COUNT, 5
rocket_list:
  .rept ROCKET_COUNT
    .float 0.0, 0.0, 0.0   @ (x, y, z)
    .float 0.25            @ speed
    .float 0.0             @ rotation
    .long 0                @ active
  .endr

.section .text
@-------------------------------------------------------------------------------
@ Draws rockets
@-------------------------------------------------------------------------------
draw_rockets:
  stmfd       sp!, {lr}

  ldr         r12, =ROCKET_COUNT
  ldr         r11, =rocket_list  
  ldr         r10, =0x40a00000      
  vmov.f32    s31, r10              @ radius = 5

1:
  vldmia.f32  r11!, {s0 - s4}
  ldmia       r11!, {r9}

  tst         r9, r9
  beq         3f

  vmov.f32  s11, s0
  vmov.f32  s12, s1
  vmov.f32  s13, s2
  mov       r0, r9
  bl        collide_objects
  bl        collide_enemies
  mov       r9, r0

  @ Update position
  vsub.f32    s2, s2, s3     

  @ Update rotation
  ldr         r0, =0x3C23D70A       @ r0 = 0.01
  vmov.f32    s5, r0
  vadd.f32    s4, s4, s5

  ldr         r0, =0xc3480000
  vmov.f32    s5, r0                @ s5 = -200  
  vcmp.f32    s2, s5
  fmstat
  movle       r9, #0

3:
  stmdb       r11!, {r9}  
  vstmdb.f32  r11!, {s0 - s4}

  tst         r9, r9 
  blne        draw_rocket
  add         r11, r11, #24
  subs        r12, r12, #1
  bne         1b

  ldmfd       sp!, {pc}

@-------------------------------------------------------------------------------
@ Spawn rocket
@-------------------------------------------------------------------------------
spawn_rocket:
  stmfd       sp!, {lr}

  ldr         r12, =ROCKET_COUNT
  ldr         r11, =rocket_list

1:
  vldmia.f32  r11!, {s0 - s4}
  ldmia       r11!, {r9}

  @ do not overwrite an existing rocket
  tst         r9, r9
  bne         2f

  ldr         r0, =player_pos
  vldmia.f32  r0, {s0 - s2}

  @ set z
  ldr         r0, =0xC1200000       
  vmov.f32    s2, r0                @ z = -10

  @ set y
  ldr         r0, =0xc0000000
  vmov.f32    s5, r0
  vsub.f32    s1, s1, s5            @ y = p.y + 2

  @ set speed
  ldr         r0, =0x3f800000
  vmov.f32    s3, r0                @ speed = 0.25

  @ set rotation
  ldr         r0, =0
  vmov.f32    s4, r0                

  @ activate
  mov         r9, #1

  stmdb       r11!, {r9} 
  vstmdb.f32  r11!, {s0 - s4}
  ldmfd       sp!, {pc}

2:
  stmdb       r11!, {r9} 
  vstmdb.f32  r11!, {s0 - s4}

  add         r11, r11, #24
  subs        r12, r12, #1
  bne         1b

  ldmfd       sp!, {pc}

@-------------------------------------------------------------------------------
@ Draws a single rocket
@ Arguments:
@   s0 - s4: Rocket attributes
@ Returns:
@   none
@ Clobbers:
@   s0 - s31, r0 -r4
@-------------------------------------------------------------------------------
draw_rocket:
  stmfd       sp!, {lr}

  @ Clear model matrix
  ldr         r0, =mtx_id
  vldm.f32    r0, {s16 - s31}
  ldr         r0, =mtx_model
  vstm.f32    r0, {s16 - s31}
  ldr         r0, =mtx_temp
  vstm.f32    r0, {s16 - s31}

  @ Translate
  vneg.f32    s1, s1 
  ldr         r0, =mtx_model
  bl          mat4_translate

  @ Rotate
  vmov.f32   s0, s4            @ s0 = angle
  ldr        r0, =mtx_temp
  bl         mat4_rot_z

  ldr        r0, =mtx_model
  ldr        r1, =mtx_temp
  ldr        r2, =mtx_model
  bl         mat4_mul_mat4

  @ Compute mvp
  ldr        r0, =mtx_vp
  ldr        r1, =mtx_model    @ v' = MVP * v
  ldr        r2, =mtx_mvp      
  bl         mat4_mul_mat4

  ldr        r0, =rocket_vtx
  ldr        r1, =rocket_idx
  ldr        r2, =30
  ldr        r3, =mtx_mvp
  ldr        r4, =light_dir
  bl         gfx_draw_trgs

  ldmfd      sp!, {pc}

@-------------------------------------------------------------------------------
@ Collide rocket at (s11, s12, s13) with all objects
@ Arguments:
@   s11 - x
@   s12 - y
@   s13 - z
@   s31 - radius
@ Returns:
@
@ Clobbers:
@-------------------------------------------------------------------------------
collide_objects:
  stmfd       sp!, {r0 - r12, lr}
  vstmdb.f32  sp!, {s0 - s7}

  ldr         r12, =OBJECT_COUNT
  ldr         r11, =object_list

1:
  vldm.f32    r11, {s0 - s6}  

  @ Check x
  vsub.f32    s7, s11, s0
  vabs.f32    s7, s7
  vcmp.f32    s7, s31
  fmstat
  bgt         2f

  @ Check y
  vsub.f32    s7, s12, s1
  vabs.f32    s7, s7
  vcmp.f32    s7, s31
  fmstat
  bgt         2f

  @ Check z
  vsub.f32    s7, s13, s2
  vabs.f32    s7, s7
  vcmp.f32    s7, s31
  fmstat
  bgt         2f

  @ Cause damage & update score if needed
  vmov.f32    r9, s5
  cmp         r9, #2
  moveq       r9, #0
  movgt       r9, #1
  ldreq       r1, =player_score
  ldreq       r2, [r1]
  addeq       r2, r2, #5
  streq       r2, [r1]
  vmoveq.f32  s2, r9
  vmov.f32    s5, r9

2:
  vstm.f32    r11!, {s0 - s6}
  subs        r12, r12, #1
  bne         1b

  vldmia.f32  sp!, {s0 - s7}
  ldmfd       sp!, {r0 - r12, pc}

@-------------------------------------------------------------------------------
@ Collide rocket at (s11, s12, s13) with all enemies
@ Arguments:
@   s11 - x
@   s12 - y
@   s13 - z
@   s31 - radius
@ Returns:
@   r0 - 0 if collision happened
@ Clobbers:
@-------------------------------------------------------------------------------
collide_enemies:
  stmfd       sp!, {r1 - r12, lr}
  vstmdb.f32  sp!, {s0 - s8}

  ldr         r12, =ENEMY_COUNT
  ldr         r11, =enemies

1:
  vldm.f32    r11, {s0 - s7}  

  @ Check x
  vsub.f32    s8, s11, s0
  vabs.f32    s8, s8
  vcmp.f32    s8, s31
  fmstat
  bgt         2f

  @ Check y
  vsub.f32    s8, s12, s1
  vabs.f32    s8, s8
  vcmp.f32    s8, s31
  fmstat
  bgt         2f

  @ Check z
  vsub.f32    s8, s13, s2
  vabs.f32    s8, s8
  vcmp.f32    s8, s31
  fmstat
  bgt         2f

  @ Cause damage & update score if needed
  vmov.f32    r9, s6
  subs        r9, r9, #2
  movlt       r9, #0
  ldr         r1, =player_score
  ldr         r2, [r1]
  addlt       r2, r2, #50
  addge       r2, r2, #100
  str         r2, [r1]
  vmovle.f32  s2, r9
  vmov.f32    s6, r9
  mov         r0, #0

2:
  vstm.f32    r11!, {s0 - s7}
  subs        r12, r12, #1
  bne         1b

  vldmia.f32  sp!, {s0 - s8}
  ldmfd       sp!, {r1 - r12, pc}

@-------------------------------------------------------------------------------
@ Resets rockets
@-------------------------------------------------------------------------------
reset_rockets:
  stmfd       sp!, {r0 - r3, lr}
  vstmdb.f32  sp!, {s0 - s5}

  ldr         r3, =ROCKET_COUNT
  ldr         r2, =rocket_list
  ldr         r1, =0x3E800000    @ 0.25
  mov         r0, #0

1:
  vldm.f32    r2, {s0 - s5}

  vmov.f32    s0, r0
  vmov.f32    s1, r0
  vmov.f32    s2, r0
  vmov.f32    s3, r1
  vmov.f32    s4, r0
  vmov.f32    s5, r0

  vstm.f32    r2!, {s0 - s5}

  subs        r3, #1
  bne         1b

  vldmia.f32  sp!, {s0 - s5}
  ldmfd       sp!, {r0 - r3, pc}


================================================
FILE: sound.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global setup_sound
.global update_sound

.include "ports.s"

@ ------------------------------------------------------------------------------
@ Macro that simplifies adding new sounds
@ ------------------------------------------------------------------------------
.macro sound name
  .long 0                    @ Play flag
  .long \name\()_start       @ Address of current chunk
  .long \name\()_start       @ Start address of sample
  .long \name\()_end         @ End address of sample
  .global snd_play_\name
  snd_play_\name:            @ Start playing
    push  {r0}
    mov   r0, #1
    str   r0, [pc, #-32]
    pop   {r0}
    mov   pc, lr
  .global snd_stop_\name
  snd_stop_\name:
    push  {r0}
    mov   r0, #0
    str   r0, [pc, #-52]
    pop   {r0}
    mov   pc, lr
.endm

.section .text
@ ------------------------------------------------------------------------------
@ Initialises the sound module, setting up GPIO 40 and 45 to use PWM and
@ programming DMA channel 1 with two control blocks chained together that
@ write data to the GPIO ports
@
@ Arguments:
@   none
@ Returns:
@   none
@ Clobbers:
@   r0 - r3
@ ------------------------------------------------------------------------------
setup_sound:
  @ Copy first chunk of background music into buffers
  ldr         r0, =corneria_start
  ldr         r1, =dma_buffer_0
  ldr         r2, =0x2000
  mov         r3, #0
1:
  ldrb        r3, [r0], #1
  str         r3, [r1], #4
  subs        r2, r2, #1
  bne         1b

  ldr         r1, =dma_buffer_1
  ldr         r2, =0x2000
1:
  ldrb        r3, [r0], #1
  str         r3, [r1], #4
  subs        r2, r2, #1
  bne         1b

  @ Set GPIO 40 & 45 to PWM
  ldr         r0, =GPIO_FSEL4
  ldr         r1, [r0]
  ldr         r2, =0x00038007
  bic         r1, r1, r2
  ldr         r2, =0x00020004
  orr         r1, r1, r2
  str         r1, [r0]

  @ Setup clock
  ldr         r0, =CM_PWMDIV
  ldr         r1, =0x5A002000
  str         r1, [r0]
  ldr         r0, =CM_PWMCTL
  ldr         r1, =0x5A000016
  str         r1, [r0]

  @ Setup PWM
  ldr         r1, =0x00002C48
  ldr         r0, =PWM_RNG1
  str         r1, [r0]
  ldr         r0, =PWM_RNG2
  str         r1, [r0]
  ldr         r0, =PWM_CTL
  ldr         r1, =0x00002161
  str         r1, [r0]

  @ Setup PWM to use DMA
  ldr         r0, =PWM_DMAC
  ldr         r1, =0x80000001
  str         r1, [r0]

  @ Enable DMA0
  ldr         r0, =DMA_ENABLE
  ldr         r1, =0x00000001
  str         r1, [r0]

  @ Set DMA0 control block
  ldr         r0, =DMA0_CONBLK
  adr         r1, DMA_CTRL_1
  str         r1, [r0]

  @ Start DMA0
  ldr         r0, =DMA0_CS
  ldr         r1, =0x00000001
  str         r1, [r0]

  mov         pc, lr

@ ------------------------------------------------------------------------------
@ Should be called when DMA triggers an interrupt, but unfortunately the
@ hardware seems incapable of triggering it. Fortunately, we can poll for the
@ interrupt flag in the DMA interrupt status register and call the function
@ ourselves
@
@ Arguments:
@   none
@ Clobbers:
@   none
@ Returns:
@   none
@ ------------------------------------------------------------------------------
update_sound:
  stmfd       sp!, {r0 - r10, lr}

  ldr         r0, =DMA_INT_STATUS
  ldr         r1, [r0]
  tst         r1, r1
  ldmeqfd     sp!, {r0 - r10, pc}

  ldr         r0, =DMA0_CS
  ldr         r1, =0x00000005
  str         r1, [r0]

  @ Swap buffers
  ldr         r1, =buffer_index
  ldr         r0, [r1]
  add         r0, r0, #1
  and         r0, r0, #1
  str         r0, [r1]

  @ Find target buffer
  tst         r0, r0
  ldrne       r0, =dma_buffer_0
  ldreq       r0, =dma_buffer_1

  @ Copy background sound
  ldr         r1, =corneria_start
  ldr         r2, =corneria_ptr
  ldr         r3, [r2]
  ldr         r4, =corneria_end
  sub         r4, r4, #0x2000
  add         r3, r3, #0x2000
  cmp         r3, r4
  movge       r3, r1
  str         r3, [r2]

  ldr         r4, =0x2000
  mov         r2, r0
1:
  ldrb        r5, [r3], #1
  lsl         r5, r5, #3
  str         r5, [r2], #4
  subs        r4, r4, #1
  bne         1b

  @ Play sounds
  ldr         r9, =sounds
  mov         r10, #9
1:
  ldr         r5, [r9]
  tst         r5, r5
  beq         3f

  ldr         r5, [r9, #4]
  ldr         r3, =0x2000
  mov         r7, r0
2:
  ldrb        r6, [r5], #1
  sub         r6, r6, #0x7F
  ldr         r8, [r7]
  add         r8, r8, r6, lsl #3
  str         r8, [r7], #4

  subs        r3, r3, #1
  bne         2b

  ldr         r3, [r9, #8]
  ldr         r4, [r9, #12]
  cmp         r5, r4
  movge       r5, r3
  str         r5, [r9, #4]
  movge       r5, #0
  strge       r5, [r9]
3:
  add         r9, #56
  subs        r10, r10, #1
  bne         1b

  ldmfd       sp!, {r0 - r10, pc}


.ltorg
.section .text
@ ------------------------------------------------------------------------------
@ DMA control structures - chained after each other
@ ------------------------------------------------------------------------------
.align 5
DMA_CTRL_0:
  .long 0x00050141    @ Attributes
  .long dma_buffer_0  @ Source address
  .long 0x7E20C018    @ Destination Address
  .long 0x8000        @ Transfer length
  .long 0
  .long DMA_CTRL_1

.align 5
DMA_CTRL_1:
  .long 0x00050141    @ Attributes
  .long dma_buffer_1  @ Source address
  .long 0x7E20C018    @ Destination Address
  .long 0x8000        @ Transfer length
  .long 0
  .long DMA_CTRL_0


.align 4
@ ------------------------------------------------------------------------------
@ DMA buffers
@ ------------------------------------------------------------------------------
dma_buffer_0:
  .space 0x8000, 0
dma_buffer_1:
  .space 0x8000, 0


.align 2
@ ------------------------------------------------------------------------------
@ Sound effect states
@ ------------------------------------------------------------------------------
buffer_index:
  .long 1

sounds:
  sound bullet
  sound roll
  sound rock
  sound crash
  sound fail
  sound boost
  sound rocket
  sound cantlet
  sound pickup

.section .data
@ ------------------------------------------------------------------------------
@ Background music
@ ------------------------------------------------------------------------------
corneria_start: .incbin "assets/corneria.bin"
corneria_end:
corneria_ptr:   .long corneria_start

@ ------------------------------------------------------------------------------
@ Short sounds effects
@ ------------------------------------------------------------------------------
bullet_start:   .incbin "assets/laser.bin"
bullet_end:
roll_start:     .incbin "assets/roll.bin"
roll_end:
rock_start:     .incbin "assets/rocknroll.bin"
rock_end:
crash_start:    .incbin "assets/crash.bin"
crash_end:
fail_start:     .incbin "assets/fail.bin"
fail_end:
boost_start: 	  .incbin "assets/boost.bin"
boost_end:
rocket_start:   .incbin "assets/rocketsound.bin"
rocket_end:
cantlet_start:  .incbin "assets/cantletyou.bin"
cantlet_end:
pickup_start:   .incbin "assets/pickup.bin"
pickup_end:


================================================
FILE: test.s
================================================
@ This file is part of the Team 28 Project
@ Licensing information can be found in the LICENSE file
@ (C) 2014 The Team 28 Authors. All rights reserved.
.global run_tests

.section .data
@-------------------------------------------------------------------------------
@ Sin and Cos test cases
@-------------------------------------------------------------------------------
angle_input:
  .float -777.0, -10.0, -6.283, -3.6, -3.1415, -3.0, -2.8, -2.5, -2.2, -2.0
  .float -1.8, -1.57075, -1.047197, -0.785375, -0.5
  .float  0.0
  .float  777.0,  10.0,  6.283,  3.6, 3.1415,  3.0,  2.8,  2.5,  2.2,  2.0
  .float  1.8, 1.57075,  1.047197,  0.785375,  0.5

@-------------------------------------------------------------------------------
@ Sin output destination
@-------------------------------------------------------------------------------
sin_output:
  .float  2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0
  .float  2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0
  .float  2.0, 2.0, 2.0, 2.0

@-------------------------------------------------------------------------------
@ Cos output destination
@-------------------------------------------------------------------------------
cos_output:
  .float  2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0
  .float  2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0
  .float  2.0, 2.0, 2.0, 2.0

.section .text
@-------------------------------------------------------------------------------
@ Runs test functions
@ Arguments:
@   None
@ Returns:
@   None
@ Clobbers:
@   None
@-------------------------------------------------------------------------------
run_tests:
  stmfd       sp!, {r0 - r2, lr}

  @ test sin
  ldr         r0, =angle_input
  mov         r1, #30
  ldr         r2, =sin_output
  bl          sin_test

  @ test cos
  ldr         r0, =angle_input
  mov         r1, #30
  ldr         r2, =cos_output
  bl          cos_test

  ldmfd       sp!, {r0 - r2, pc}

@ ------------------------------------------------------------------------------
@ Sin function test
@ Arguments:
@   r0 - address of inputs
@   r1 - number of inputs
@   r2 - destination for outputs
@ Returns:
@  None
@ Clobbers:
@  None
@ ------------------------------------------------------------------------------
sin_test:
  stmfd       sp!, {r0 - r4, lr}
  vstmdb      sp!, {s0 - s1}
  mov         r3, #0

1:
  cmp         r3, r1
  bge         2f

  ldr         r4, [r0]
  vmov.f32    s0, r4
  bl          sin
  vstm.f32    r2, {s1}

  add         r0, r0, #4
  add         r2, r2, #4
  add         r3, r3, #1
  b           1b

2:
  vldmia.f32  sp!, {s0 - s1}
  ldmfd       sp!, {r0 - r4, pc}

@ ------------------------------------------------------------------------------
@ Cos function test
@ Arguments:
@   r0 - address of inputs
@   r1 - number of inputs
@   r2 - destination for outputs
@ Returns:
@  None
@ Clobbers:
@  None
@ ------------------------------------------------------------------------------
cos_test:
  stmfd       sp!, {r0 - r4, lr}
  vstmdb      sp!, {s0 - s1}
  mov         r3, #0

1:
  cmp         r3, r1
  bge         2f

  ldr         r4, [r0]
  vmov.f32    s0, r4
  bl          cos
  vstm.f32    r2, {s1}

  add         r0, r0, #4
  add         r2, r2, #4
  add         r3, r3, #1
  b           1b

2:
  vldmia.f32  sp!, {s0 - s1}
  ldmfd       sp!, {r0 - r4, pc}


================================================
FILE: toolchains/arm-none-eabi.cmake
================================================
#   Copyright (c) 2013, Brian Sidebotham
#   All rights reserved.

#   Redistribution and use in source and binary forms, with or without
#   modification, are permitted provided that the following conditions are met:

#   1. Redistributions of source code must retain the above copyright notice,
#       this list of conditions and the following disclaimer.

#   2. Redistributions in binary form must reproduce the above copyright notice,
#       this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.

#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
#   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
#   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
#   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
#   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
#   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
#   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
#   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#   POSSIBILITY OF SUCH DAMAGE.

# A CMake toolchain file so we can cross-compile for the Rapsberry-Pi bare-metal

# usage
# cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain-arm-none-eabi.cmake ../

include( CMakeForceCompiler )

# The Generic system name is used for embedded targets (targets without OS) in
# CMake
set( CMAKE_SYSTEM_NAME          Generic )
set( CMAKE_SYSTEM_PROCESSOR     BCM2385 )

# Set a toolchain path. You only need to set this if the toolchain isn't in
# your system path. Don't forget a trailing path separator!
set( TC_PATH "" )

# The toolchain prefix for all toolchain executables
set( CROSS_COMPILE arm-none-eabi- )

# specify the cross compiler. Force the compiler so it doesn't perform checks.
# We don't want to attempt to test the compiler when we're using a custom
# linker script for example, or we're not linking against a c library as it'll
# fail. See: http://www.cmake.org/Wiki/CMake_Cross_Compiling
cmake_force_c_compiler( ${TC_PATH}${CROSS_COMPILE}gcc GNU )
cmake_force_cxx_compiler( ${TC_PATH}${CROSS_COMPILE}g++ GNU )

# We must set the OBJCOPY setting into cache so that it's available to the
# whole project. Otherwise, this does not get set into the CACHE and therefore
# the build doesn't know what the OBJCOPY filepath is
set( CMAKE_OBJCOPY      ${TC_PATH}${CROSS_COMPILE}objcopy
    CACHE FILEPATH "The toolchain objcopy command " FORCE )

Download .txt
gitextract_441jipqv/

├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── assets/
│   ├── enemy.s
│   ├── pillar.s
│   ├── rock.s
│   ├── rocket.s
│   └── ship.s
├── bullets.s
├── enemies.s
├── game.s
├── gfx.s
├── imager.py
├── input.s
├── kernel.ld
├── kernel.s
├── math.s
├── mbox.s
├── objects.s
├── pillars.s
├── player.s
├── ports.s
├── printf.s
├── rockets.s
├── sound.s
├── test.s
└── toolchains/
    └── arm-none-eabi.cmake
Download .txt
SYMBOL INDEX (1 symbols across 1 files)

FILE: imager.py
  function main (line 16) | def main():
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (187K chars).
[
  {
    "path": ".gitignore",
    "chars": 194,
    "preview": "# CMake\nbuild/\n\n# Object files\n*.o\n*.ko\n*.obj\n*.elf\n\n# Libraries\n*.lib\n*.a\n\n# Shared objects (inc. Windows DLLs)\n*.dll\n*"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 2042,
    "preview": "# This file is part of the Team 28 Project\n# Licensing information can be found in the LICENSE file\n# (C) 2014 The Team "
  },
  {
    "path": "LICENSE",
    "chars": 1542,
    "preview": "Copyright (c) 2014, Licker Nandor, Ilija Radosavovic, David Avedissian, Nic Prettejohn\nAll rights reserved.\n\nRedistribut"
  },
  {
    "path": "README.md",
    "chars": 2664,
    "preview": "PiFox\n=====\n\nVideo of the game in action: https://www.youtube.com/watch?v=-5n9IxSQH1M\n\nDeveloped as an extension to a fi"
  },
  {
    "path": "assets/enemy.s",
    "chars": 1600,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "assets/pillar.s",
    "chars": 1509,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "assets/rock.s",
    "chars": 3257,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "assets/rocket.s",
    "chars": 2622,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "assets/ship.s",
    "chars": 1954,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "bullets.s",
    "chars": 4713,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "enemies.s",
    "chars": 12140,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "game.s",
    "chars": 12944,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "gfx.s",
    "chars": 31023,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "imager.py",
    "chars": 1433,
    "preview": "#!/usr/bin/python2\n\n\"\"\"\nThis file is part of the Team 28 Project\nLicensing information can be found in the LICENSE file\n"
  },
  {
    "path": "input.s",
    "chars": 3376,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "kernel.ld",
    "chars": 1812,
    "preview": "SECTIONS\n{\n  /**\n   * Start address of the kernel.\n   *\n   * In QEMU, this is hardcoded to 0x100000 (64Kb).\n   * On the "
  },
  {
    "path": "kernel.s",
    "chars": 4932,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "math.s",
    "chars": 23436,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "mbox.s",
    "chars": 1878,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "objects.s",
    "chars": 9797,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "pillars.s",
    "chars": 3831,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "player.s",
    "chars": 19212,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "ports.s",
    "chars": 4975,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "printf.s",
    "chars": 5027,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "rockets.s",
    "chars": 7246,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "sound.s",
    "chars": 7175,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "test.s",
    "chars": 3392,
    "preview": "@ This file is part of the Team 28 Project\n@ Licensing information can be found in the LICENSE file\n@ (C) 2014 The Team "
  },
  {
    "path": "toolchains/arm-none-eabi.cmake",
    "chars": 2738,
    "preview": "#   Copyright (c) 2013, Brian Sidebotham\n#   All rights reserved.\n\n#   Redistribution and use in source and binary forms"
  }
]

About this extraction

This page contains the full source code of the ICTeam28/PiFox GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (174.3 KB), approximately 66.8k tokens, and a symbol index with 1 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!