CS107e library header files

#ifndef ASSERT_H
#define ASSERT_H

/*
 * Interface for assertions.  The `assert` macro tests the given expression and
 * if it is false, prints a diagnostic message and halts execution.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include "mango.h"
#include "uart.h"

#define STRINGIFY_IMPL(x) #x
#define AS_STRING(x) STRINGIFY_IMPL(x)

#define assert(EXPR) \
    do { \
        if (!(EXPR)) { \
            uart_start_error(); \
            uart_putstring("File " __FILE__ ", line " AS_STRING(__LINE__) ", in function "); \
            uart_putstring(__func__); \
            uart_putstring("() : Assertion '" #EXPR "' failed.\n"); \
            uart_end_error(); \
            mango_abort(); \
        } \
    } while (0);

#define error(STR) \
    do { \
        uart_start_error(); \
        uart_putstring("File " __FILE__ ", line " AS_STRING(__LINE__) ", in function "); \
        uart_putstring(__func__); \
        uart_putstring("()\nERROR: "); \
        uart_putstring(STR); \
        uart_end_error(); \
        mango_abort(); \
    } while (0);

#endif

#ifndef BACKTRACE_H
#define BACKTRACE_H

/*
 * Functions for harvesting a debugging backtrace from the call stack.
 *
 * Students implement this module in assignment 4.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include <stdint.h>

/*
 * Type: `frame_t`
 *
 * This struct stores the information for a function that has a frame on the
 * call stack. The `resume_addr` is taken from saved ra in the callee frame.
 * The return address in the caller's sequence of instructions where control
 * will resume after the callee returns. The type `uintptr_t` is an
 * unsigned int/long of the appropriate bitwidth to store an address. The
 * `uintptr_t` type is used for an address that you intend to treat numerically.
 */
typedef struct {
    uintptr_t resume_addr;
} frame_t;

/*
 * `backtrace_gather_frames`
 *
 * Gathers a backtrace by harvesting frames from the current call stack and
 * storing into the frame array `f`.
 *
 * A backtrace is the sequence of currently active function calls. Each element
 * in the array `f` is of type `frame_t` (struct described above). Each struct
 * represents a caller who has a frame on the stack.
 *
 * The `max_frames` argument specifies the maximum number of frames to harvest.
 * If the current call stack contains more than `max_frames`, only the
 * `max_frames` topmost calls are stored into the array.
 *
 * The function returns the count of frames written to `f`.
 *
 * @param f            array in which to write stack frames
 * @param max_frames   maximum number of frames that can be stored in array f
 * @return             count of frames written to array f
 */
int backtrace_gather_frames(frame_t f[], int max_frames);

/*
 * `backtrace_print_frames`
 *
 * Given an array of frames as filled in by a call to backtrace_gather_frames(),
 * this function prints one line per frame zero-indexed using format:
 *
 *      #0  0x40000124 at <label+offset>
 *      #1  0x4000004c at <label+offset>
 *      #2  0x400001d8 at <label+offset>
 *
 * If symbols are available, the label is the name of the function and offset.
 * The offset is the number of bytes from function start to resume_addr.
 * If symbols are not available,  the label is the offset of the address
 * within the .text section. See `symtab_label_for_addr` in symtab.h
 *
 *     #2 0x400001d8 at <.text+2816>
 *
 * @param f     array of stack frames
 * @param n     number of frames in array f
 */
void backtrace_print_frames(frame_t f[], int n);

/*
 * `backtrace_print`
 *
 * Convenience function that calls `backtrace_gather_frames` and
 * `backtrace_print_frames` to display current call stack.
 */
void backtrace_print(void);

#endif

#ifndef CONSOLE_H
#define CONSOLE_H

/*
 * Interface to a text console displayed on the screen.
 *
 * Students implement this module in assignment 6.
 *
 * Author: Pat Hanrahan <hanrahan@cs.stanford.edu>
 * Author: Philip Levis <pal@cs.stanford.edu>
 */

#include "gl.h"

/*
 * `console_init`: required initialization for console
 *
 * Initialize the console. The console text begins empty and
 * the cursor is in the home position (upper left corner).
 *
 * @param nrows       the requested number of rows in characters
 * @param ncols       the requested number of columns in characters
 * @param foreground  foreground color used for text
 * @param background  background color
 *
 * A subsequent call to console_init after the first does a reinitialization.
 */
void console_init(int nrows, int ncols, color_t foreground, color_t background);

/*
 * `console_clear`
 *
 * Clear all console text and move the cursor to home position.
 */
void console_clear(void);

/*
 * `console_printf`
 *
 * Print the formatted string to the console starting at current cursor
 * position. The arguments to this function are the same as `printf`.
 * When processing characters, interpret the following special characters:
 *
 * '\n' :  newline (move cursor down to the beginning of next line)
 * '\b' :  backspace (move cursor backwards one position)
 * '\f' :  form feed (clear all contents and move cursor to home position)
 *
 * @param format    format for output string. May contain ordinary characters
 *                  and format conversions
 * @param ...       variable arguments to be converted
 * @return          count of characters written to the console
 */
int console_printf(const char *format, ...) __attribute__((format(printf, 1, 2)));

#endif

#ifndef DE_H
#define DE_H

/*
 * Functions for interacting with Display Engine peripheral.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */


/*
 * `de_init` : Required initializion for DisplayEngine
 *
 * The DE is configured to display a framebuffer of the specified
 * width and height on a screen of the specified dimensions.
 * DE should be initialized only after HDMI hardware has been configured
 * via hdmi_init(). If the requested fb dimensions do not fit on the
 * screen, an error is raised.
 *
 * @param fb_width      width of framebuffer in pixels
 * @param fb_height     height of framebuffer in pixels
 * @param screen_width  width of hdmi screen in pixels
 * @param screen_height height of hdmi screen in pixels
 */
void de_init(int fb_width, int fb_height, int screen_width, int screen_height);

/*
 * `de_set_active_framebuffer` :
 *
 * Change screen to show contents of framebuffer memory at `addr`.
 * Framebuffer is assumed to be valid memory of size/shape matching how
 * DE was configured during de_init().
 *
 * @param addr      base address of framebuffer to display
 */
void de_set_active_framebuffer(void *addr);

#endif

#ifndef FB_H
#define FB_H

/*
 * Low-level framebuffer routines. These provide a hardware
 * abstraction for displaying to screen that a graphics library can
 * then use to support drawing primitives.
 *
 * Students implement this module for assignment 6.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

typedef enum { FB_SINGLEBUFFER = 0, FB_DOUBLEBUFFER = 1 } fb_mode_t;

/*
 * `fb_init` : Required initialization for framebuffer
 *
 * Initialize the framebuffer.
 *
 * @param width  the requested width in pixels of the framebuffer
 * @param height the requested height in pixels of the framebuffer
 * @param mode   whether the framebuffer should be
 *                      single buffered (FB_SINGLEBUFFER)
 *                      or double buffered (FB_DOUBLEBUFFER)
 *
 * The depth of the framebuffer is always 4 bytes; ech pixel is
 * 32-bit value in format BGRA.
 *
 * If the requested size can be successfully accommodated by the underlying
 * hardware, the function returns normally. Otherwise an assert will be raised
 * within de/hdmi to report the problem.
 *
 * A subsequent call to fb_init after the first does a reinitialization.
 * All previous framebuffer memory is deallocated and the framebuffer is
 * re-initialized for the requested configuration.
 */
void fb_init(int width, int height, fb_mode_t mode);

/*
 * `fb_get_width`
 *
 * Get the current width in pixels of the framebuffer.
 *
 * @return    the width in pixels
 */
int fb_get_width(void);

/*
 * `fb_get_height`
 *
 * Get the current height in pixels of the framebuffer.
 *
 * @return    the height in pixels
 */
int fb_get_height(void);

/*
 * `fb_get_depth`
 *
 * Get the current depth in bytes of a single pixel.
 * In our case, depth is always 4.
 *
 * @return    the depth in bytes
 */
int fb_get_depth(void);

/*
 * `fb_get_draw_buffer`
 *
 * Get the start address of the framebuffer memory into which the
 * client can draw pixels. The address returned is the start of an
 * array of bytes of capacity width*height*depth.
 *
 * If in single buffering mode, there is only one buffer in use, so the
 * returned address does not change. That buffer is on-screen at all
 * times. The client draws to that one buffer, and all updates are
 * immediately displayed.
 *
 * In double buffering mode, there are two buffers: the one
 * currently on-screen and a second buffer that is off-screen. The
 * address returned by `fb_get_draw_buffer` corresponds to the buffer
 * that is currently off-screen. The off-screen buffer is sometimes
 * called the draw buffer. The client will do all drawing to the off-screen
 * buffer and when ready, calls `fb_swap_buffer` to exchange the
 * on-screen and off-screen buffers. The swap brings the updated
 * drawing on-screen in one smooth update.
 *
 * Note the address is returned as `void*`. Client should store into
 * a properly typed pointer so as to access the pixel data according
 * to their desired scheme (1-d, 2-d, etc.)
 *
 * @return    the address of the current draw (off-screen) buffer
 */
void* fb_get_draw_buffer(void);

/*
 * `fb_swap_buffer`
 *
 * Fast exchange of front (on-screen) and back (off-screen) buffers.
 * The buffer that was previously off-screen is now displayed on-screen
 * and buffer that was on-screen moves off-screen.
 *
 * If not in double buffering mode, there is only one buffer and this
 * function has no effect.
 */
void fb_swap_buffer(void);

#endif

#ifndef FONT_H
#define FONT_H

/*
 * This module provides a font.
 *
 * Author: Philip Levis <pal@cs.stanford.edu>
 */

#include <stdbool.h>
#include <stddef.h>

/*
 * A font contains a glyph for each ASCII character to be drawn.
 * A glyph is a bitmap image for drawing the character.
 *
 * The font is fixed width; every glyph has the same width and
 * same height. A glyph is an image with font_get_glyph_height() rows
 * and font_get_glyph_width() columns.
 *
 * Each pixel of the glyph image is represented as one byte.
 * The byte for an "on" pixel is 0xFF, and the byte for
 * "off" pixel is 0x00.
 */

/*
 * `font_get_glpyh_height`
 *
 * Get the height in pixels of each glyph in this font.
 *
 * @return    the height in pixels
 */
int font_get_glyph_height(void);

/*
 * `font_get_glpyh_width`
 *
 * Get the width in pixels of each glyph in this font.
 *
 * @return    the width in pixels
 */
int font_get_glyph_width(void);

/*
 * `font_get_glpyh_size`
 *
 * Get the total number of bytes needed to store the glyph image
 * for one character. This is equal to the product of
 * (glyph height * glyph width).
 *
 * @return    the size in bytes of a glyph image
 */
int font_get_glyph_size(void);

/*
 * `font_get_glpyh`
 *
 * Fill in the glyph image for character `ch` into `buf`.
 * `buf` is an array of bytes of length font_get_glyph_size().
 * Each 'on' pixel has value 0xff, 'off' pixel has value 0x0.
 *
 * @param ch    the requested character
 * @param buf   the buffer in which to place the glyph image
 * @param buflen the length of `buf`.
 *              `buflen` should be equal to value returned by font_get_glyph_size()
 *
 * @return      returns true when successfully filled `buf`, false otherwise.
 *              Failure is when `ch` is not available in this font or `buflen`
 *              does not equal the value returned by font_get_glyph_size()
 */
bool font_get_glyph(char ch, unsigned char buf[], size_t buflen);

#endif

#ifndef GL_H
#define GL_H

/*
 * Functions for a simple graphics library that draws
 * pixels, text, rectangles, lines, and triangles. Builds
 * on the fb module to configure and access the framebuffer.
 * Attempting to use both fb and gl simultaneously is discouraged.
 *
 * Students implement this module in assignment 6.
 *
 * Author: Philip Levis <pal@cs.stanford.edu>
 */

#include "fb.h"

typedef enum {
    GL_SINGLEBUFFER = FB_SINGLEBUFFER,
    GL_DOUBLEBUFFER = FB_DOUBLEBUFFER }
gl_mode_t;

/*
 * `gl_init` : Required initialization for graphics library
 *
 * Initialize the graphic library. This function will call `fb_init` in turn
 * to initialize the framebuffer.
 *
 * @param width  the requested width in pixels of the framebuffer
 * @param height the requested height in pixels of the framebuffer
 * @param mode   whether the framebuffer should be
 *                  single buffered (GL_SINGLEBUFFER)
 *                  or double buffered (GL_DOUBLEBUFFER)
 *
 * A subsequent call to gl_init after the first does a reinitialization.
 */
void gl_init(int width, int height, gl_mode_t mode);

/*
 * `gl_get_width`
 *
 * Get the current width in pixels of the framebuffer.
 *
 * @return    the width in pixels
 */
int gl_get_width(void);

/*
 * `gl_get_height`
 *
 * Get the current height in pixels of the framebuffer.
 *
 * @return    the height in pixels
 */
int gl_get_height(void);

/*
 * `color_t`
 *
 * Define a type for color. We use BGRA colors, where each color
 * component R, B, G, or A is a single unsigned byte. The least
 * signficant byte is the B component, and A is most significant.
 */
typedef unsigned int color_t;

/*
 * Define some common colors ...
 *
 * Note that colors are BGRA, where B is the first byte in memory
 * and the least significant byte in the unsigned word.
 */
#define GL_BLACK    0xFF000000
#define GL_WHITE    0xFFFFFFFF
#define GL_RED      0xFFFF0000
#define GL_GREEN    0xFF00FF00
#define GL_BLUE     0xFF0000FF
#define GL_CYAN     0xFF00FFFF
#define GL_MAGENTA  0xFFFF00FF
#define GL_YELLOW   0xFFFFFF00
#define GL_AMBER    0xFFFFBF00
#define GL_ORANGE   0xFFFF3F00
#define GL_PURPLE   0xFF7F00FF
#define GL_INDIGO   0xFF000040
#define GL_CAYENNE  0xFF400000
#define GL_MOSS     0xFF004000
#define GL_SILVER   0xFFBBBBBB

/*
 * `gl_color`
*
 * Returns a color composed of the specified red, green, and
 * blue components. The alpha component of the color will be
 * set to 0xff (fully opaque).
 *
 * @param r  the red component of the color
 * @param g  the green component of the color
 * @param b  the blue component of the color
 *
 * @return   the color as a single value of type color_t
 */
color_t gl_color(unsigned char r, unsigned char g, unsigned char b);

/*
 * `gl_clear`
 *
 * Clear all the pixels in the framebuffer to the given color.
 *
 * @param c  the color drawn into the framebuffer
 */
void gl_clear(color_t c);

/*
 * `gl_swap_buffer`
 *
 * If in double-buffered mode, all gl drawing takes place in the
 * off-screen buffer and updated drawing is not brought on-screen until
 * a call is made to `gl_swap_buffer` to exchange the on-screen
 * and off-screen buffers.
 *
 * If not in double-buffer mode, all drawing takes place on-screen and
 * the `gl_swap_buffer` function has no effect.
 */
void gl_swap_buffer(void);

/*
 * `gl_draw_pixel`
 *
 * Draw a single pixel at location x,y in color c.
 * If the location is outside the bounds of framebuffer, it is not drawn.
 *
 * @param x  the x location of the pixel
 * @param y  the y location of the pixel
 * @param c  the color of the pixel
 */
void gl_draw_pixel(int x, int y, color_t c);

/*
 * `gl_read_pixel`
 *
 * Return the color of the pixel at location x,y. Returns 0 if the
 * location is outside the bounds of the framebuffer.
 *
 * @param x  the x location of the pixel
 * @param y  the y location of the pixel
 *
 * @return   the color at that location
 */
color_t gl_read_pixel(int x, int y);

/*
 * `gl_draw_char`
 *
 * Draw a single character at location x,y in color c.
 * Only those pixels of the character that lie within the bounds
 * of the framebuffer are drawn. Any pixel that lies outside is
 * clipped (i.e. not drawn). Only the "on" pixels of the character
 * are drawn, all "off" pixels are left as-is.
 *
 * @param x   the x location of the upper left corner of the character glyph
 * @param y   the y location of the upper left corner of the character glyph
 * @param ch  the character to be drawn, e.g. 'a'. If this character has no glyph
 *            in the current font, nothing is drawn (refer to font_get_glyph())
 * @param c   the color of the character
 */
void gl_draw_char(int x, int y, char ch, color_t c);

/*
 * `gl_draw_string`
 *
 * Draw a string at location x,y in color c. The characters are drawn
 * left to right in a single line. Only the pixels of the characters
 * that lie within the bounds of the framebuffer are drawn. Any pixel
 * that lies outside is clipped (i.e. not drawn). Only the "on" pixels of
 * the characters are drawn, all "off" pixels are left as-is.
 *
 * @param x    the x location of the upper left corner of the first char of string
 * @param y    the y location of the upper left corner of the first char of string
 * @param str  the null-terminated string to be drawn
 * @param c    the color of the string
 */
void gl_draw_string(int x, int y, const char* str, color_t c);

/*
 * `gl_get_char_height`
 *
 * Get the height in pixels of a single character glyph.
 *
 * @return the height of character glyph in pixels
 */
int gl_get_char_height(void);

/*
 * `gl_get_char_width`
 *
 * Get the width in pixels of a single character glyph.
 *
 * @return the width of character glyph in pixels
 */
int gl_get_char_width(void);

/*
 * `gl_draw_rect`
 *
 * Draw a filled rectangle at location x,y with size w,h filled with color c.
 * All pixels in the rectangle that lie within the bounds of the
 * framebuffer are drawn. Any pixel that lies outside is clipped (i.e. not drawn).
 *
 * @param x  the x location of the upper left corner of the rectangle
 * @param y  the y location of the upper left corner of the rectangle
 * @param w  the width of the rectangle
 * @param h  the height of the rectangle
 * @param c  the color of the rectangle
 */
void gl_draw_rect(int x, int y, int w, int h, color_t c);

/*
 * `gl_draw_line`: optional extension
 *
 * Draw a line segment from location x1,y1 to location x2,y2 of color c.
 * All pixels along the line that lie within the bounds of the framebuffer
 * are drawn. Any pixel that lies outside is clipped (i.e. not drawn).
 *
 * @param x1  the x location of vertex 1
 * @param y1  the y location of vertex 1
 * @param x2  the x location of vertex 2
 * @param y2  the y location of vertex 2
 * @param c   the color of the line
 *
 * This function is NOT part of the core requirements.
 * You can leave this function unimplemented if not doing the extension.
 */
void gl_draw_line(int x1, int y1, int x2, int y2, color_t c);

/*
 * `gl_draw_triangle`: optional extension
 *
 * Draw a filled triangle connecting the three vertices filled with color c.
 * All pixels within the triangle that lie within the bounds of the
 * framebuffer are drawn. Any pixel that lies outside is clipped (i.e. not drawn).
 *
 * @param x1  the x location of vertex 1
 * @param y1  the y location of vertex 1
 * @param x2  the x location of vertex 2
 * @param y2  the y location of vertex 2
 * @param x3  the x location of vertex 3
 * @param y3  the y location of vertex 3
 * @param c   the color of the triangle
 *
 * This function is NOT part of the core requirements.
 * You can leave this function unimplemented if not doing the extension.
 */
void gl_draw_triangle(int x1, int y1, int x2, int y2, int x3, int y3, color_t c);

#endif

#ifndef GPIO_H
#define GPIO_H

/*
 * Functions for controlling Mango Pi GPIO.
 *
 * Students implement this module in assignment 2.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include <stdbool.h>

#define GPIO_INVALID_REQUEST  -1  // return value for invalid request

/*
 * Type: `gpio_id_t`
 *
 * `gpio_id_t` is an enumerated type used to refer to a gpio pin.
 * A C enumeration is effectively an (unsigned) int.
 * See the enumeration below for symbolic names/constants for
 * the valid ids on the Mango Pi.
 */
typedef enum _mango_ids gpio_id_t;

/*
 * `gpio_init`
 *
 * Initialize the GPIO code module. For assignment 2, this does nothing.
 * However, all peripheral modules require an init, so it is included
 * for consistency's sake.
 */
void gpio_init(void);

/*
 * `gpio_id_is_valid`
 *
 * Returns true if `pin` refers to a valid GPIO pin id, false otherwse.
 * See enumeration `gpio_id_t` below for list of valid ids.
 */
bool gpio_id_is_valid(gpio_id_t pin);

/*
 * `gpio_set_function`
 *
 * Set a GPIO function for GPIO with id `pin`. The configuration for
 * pins other than `pin` should not be changed.
 *
 * @param pin       the id of the GPIO pin
 * @param function  the GPIO function to set for the pin
 *
 * If `pin` or `function` is not valid, does nothing.
 */
void gpio_set_function(gpio_id_t pin, unsigned int function);

/*
 * `gpio_get_function`
 *
 * Get the GPIO function for GPIO with id `pin`.
 *
 * @param pin the id of the GPIO pin
 * @return    the current GPIO function of the specified pin
 *
 * If `pin` is not valid, returns GPIO_INVALID_REQUEST.
 */
unsigned int gpio_get_function(gpio_id_t pin);

/*
 * `gpio_set_input`, `gpio_set_output`
 *
 * Convenience functions for setting a pin to GPIO_FN_INPUT or
 * GPIO_FN_OUTPUT. The implementation calls `gpio_set_function`.
 *
 * @param pin      the id of the GPIO pin to set function
 */
void gpio_set_input(gpio_id_t pin);
void gpio_set_output(gpio_id_t pin);

/*
 * `gpio_read`
 *
 * Read current value for GPIO with id `pin`.
 * Returns 1 if pin is high, 0 if low.
 *
 * @param pin   the id of the GPIO pin
 * @return      the value of the specified pin
 *
 * If `pin` is not valid, returns GPIO_INVALID_REQUEST.
 */
int gpio_read(gpio_id_t pin);

/*
 * `gpio_write`
 *
 * Set value of GPIO with id `pin` to high (1) or low (0).
 * This function assumes the pin is already in output mode.
 * Values for pins other than `pin` should not be changed.
 *
 * @param pin   the id of the GPIO pin
 * @param val   1 to set pin high, 0 to set pin low
 *
 * If `pin` is not valid, does nothing.
 */
void gpio_write(gpio_id_t pin, int val);

/*
 * Enumeration of ids for GPIO pins on the Mango Pi
 *
 * Each GPIO pin has a symbolic id in the enumeration below.
 *
 * The GPIO pins are organized into six groups: PB PC PD PE PF PG
 * (These are called "ports" in the user manual)
 * The pins within a group are indexed by number starting from 0. Each 
 * group has a differing number of pins. For example, the PB group has 
 * 13 pins indexed from PB0 to PB12. The PC group has 8 pins PC0 to PC7.
 *
 * Values are assigned to each pin id in such a way to use a single hex constant
 * that mashes together the group and pin index as shown below
 *       constant 0xNnn  N = which group,  nn = pin index within group
 */
enum _mango_ids {
    GPIO_PB0 = 0x000, // start of B group, enum vals assigned in sequence from here
    GPIO_ID_FIRST = GPIO_PB0,
    GPIO_PB1,
    GPIO_PB2,
    GPIO_PB3,
    GPIO_PB4,
    GPIO_PB5,
    GPIO_PB6,
    GPIO_PB7,
    GPIO_PB8,
    GPIO_PB9,
    GPIO_PB10,
    GPIO_PB11,
    GPIO_PB12,
    GPIO_PB_LAST_INDEX = GPIO_PB12 - GPIO_PB0,
    GPIO_PC0 = 0x100, // start of C group
    GPIO_PC1,
    GPIO_PC2,
    GPIO_PC3,
    GPIO_PC4,
    GPIO_PC5,
    GPIO_PC6,
    GPIO_PC7,
    GPIO_PC_LAST_INDEX = GPIO_PC7 - GPIO_PC0,
    GPIO_PD0 = 0x200, // start of D group
    GPIO_PD1,
    GPIO_PD2,
    GPIO_PD3,
    GPIO_PD4,
    GPIO_PD5,
    GPIO_PD6,
    GPIO_PD7,
    GPIO_PD8,
    GPIO_PD9,
    GPIO_PD10,
    GPIO_PD11,
    GPIO_PD12,
    GPIO_PD13,
    GPIO_PD14,
    GPIO_PD15,
    GPIO_PD16,
    GPIO_PD17,
    GPIO_PD18,
    GPIO_PD19,
    GPIO_PD20,
    GPIO_PD21,
    GPIO_PD22,
    GPIO_PD_LAST_INDEX = GPIO_PD22 - GPIO_PD0,
    GPIO_PE0 = 0x300, // start of E group
    GPIO_PE1,
    GPIO_PE2,
    GPIO_PE3,
    GPIO_PE4,
    GPIO_PE5,
    GPIO_PE6,
    GPIO_PE7,
    GPIO_PE8,
    GPIO_PE9,
    GPIO_PE10,
    GPIO_PE11,
    GPIO_PE12,
    GPIO_PE13,
    GPIO_PE14,
    GPIO_PE15,
    GPIO_PE16,
    GPIO_PE17,
    GPIO_PE_LAST_INDEX = GPIO_PE17 - GPIO_PE0,
    GPIO_PF0 = 0x400, // start of F group
    GPIO_PF1,
    GPIO_PF2,
    GPIO_PF3,
    GPIO_PF4,
    GPIO_PF5,
    GPIO_PF6,
    GPIO_PF_LAST_INDEX = GPIO_PF6 - GPIO_PF0,
    GPIO_PG0 = 0x500, // start of G group
    GPIO_PG1,
    GPIO_PG2,
    GPIO_PG3,
    GPIO_PG4,
    GPIO_PG5,
    GPIO_PG6,
    GPIO_PG7,
    GPIO_PG8,
    GPIO_PG9,
    GPIO_PG10,
    GPIO_PG11,
    GPIO_PG12,
    GPIO_PG13,
    GPIO_PG14,
    GPIO_PG15,
    GPIO_PG16,
    GPIO_PG17,
    GPIO_PG18,
    GPIO_PG_LAST_INDEX = GPIO_PG18 - GPIO_PG0,
    GPIO_ID_LAST = GPIO_PG18,
};

#define GPIO_UART_TX GPIO_PB8
#define GPIO_UART_RX GPIO_PB9

/*
 * Enumeration for GPIO functions
 *
 * The enumerated values below establish symbolic names for each of the
 * available GPIO pin functions. Each pin function corresponds to
 * a particular "mode" of operation.  For example, setting a pin's
 * function to GPIO_FN_INPUT configures the pin to be used as an input.
 */
enum {
    GPIO_FN_INPUT  = 0,
    GPIO_FN_OUTPUT = 1,
    GPIO_FN_ALT2   = 2,
    GPIO_FN_ALT3   = 3,
    GPIO_FN_ALT4   = 4,
    GPIO_FN_ALT5   = 5,
    GPIO_FN_ALT6   = 6,
    GPIO_FN_ALT7   = 7,
    GPIO_FN_ALT8   = 8,
    // 9 -13 are reserved
    GPIO_FN_INTERRUPT = 14,
    GPIO_FN_DISABLED = 15,
};

#endif

#ifndef GPIO_EXTRA_H
#define GPIO_EXTRA_H

/*
 * Functions for controlling extra GPIO features (pull state and name to id conversion)
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include "gpio.h"

/*
 * The functions below control the pull state of a GPIO pin via its
 * internal resistor. Setting the pull state is useful for when you
 * have an input pin that would otherwise be floating. A pin configured
 * to pull-up defaults to 1. If configured to pull-down defaults to 0.
 */

/*
 * `gpio_set_pullup`
 *
 * Set pull state of GPIO with id `pin` to pull-up.
 *
 * @param pin   the id of the GPIO pin
 *
 * If `pin` is not valid, does nothing.
 */
void gpio_set_pullup(gpio_id_t pin);

/*
 *
 * `gpio_set_pulldown`
 *
 * Set pull state of GPIO with id `pin` to pull-down.
 *
 * @param pin   the id of the GPIO pin
 *
 * If `pin` is not valid, does nothing.
 */
void gpio_set_pulldown(gpio_id_t pin);

/*
 * `gpio_set_pullnone`
 *
 * Disables any pull state of GPIO with id `pin` (value will float).
 *
 * @param pin   the id of the GPIO pin
 *
 * If `pin` is not valid, does nothing.
 */
void gpio_set_pullnone(gpio_id_t pin);

/*
 * `gpio_get_name_for_id`
 *
 * Translate a pin id into name string suitable for display:
 * e.g. GPIO_PB4 -> "PB4"
 *
 * @param pin      the id of the GPIO pin
 * @return         name string
 *
 * If `pin` is not valid, returns GPIO_INVALID_REQUEST.
 */
const char *gpio_get_name_for_id(gpio_id_t pin);

/*
 * `gpio_get_id_for_name`
 *
 * Translate a string name into pin id:
 * e.g. "PB4" -> GPIO_PB4
 *
 * @param name  name string
 * @return      the id of the GPIO pin
 *
 * If `name` is not valid, returns GPIO_INVALID_REQUEST.
 */
gpio_id_t gpio_get_id_for_name(const char *name);

#endif

#ifndef GPIO_INTERRUPT_H
#define GPIO_INTERRUPT_H

/*
 * Module to configure GPIO interrupt processing for Mango Pi.
 * Because all of the gpios within one group share the same interrupt
 * source, an additional level of indirection is needed to dispatch
 * those interrupts to individual handlers per specific gpio.
 * This module manages that additional layer of dispatch.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include <stdbool.h>
#include "gpio.h"
#include "interrupts.h"

/*
 * `gpio_interrupt_init`: Required initialization for module
 *
 * Initialize the GPIO interrupt module. The init function must be
 * called before any calls to other functions in this module.
 * The init function coordinates with the top-level interrupts module
 * to register a handler to receive events on all GPIO interrupt sources
 * (i.e. for all groups). The handler will re-dispatch each interrupt
 * to the per-gpio handler registered with this module.
 *
 * It is safe to call init more than once for this module. A re-init
 * retains all handlers that were previously registered.
 *
 * It is an error to attempt to initialize this module without first
 * initialized the top-level interrupts (i.e. required to call `interrupts_init`).
 */
void gpio_interrupt_init(void);

/*
 * `gpio_interrupt_register_handler`
 *
 * Register a handler function to gpio with id `pin`. Each GPIO
 * can have one handler.
 *
 * @param pin       the id of the GPIO pin
 * @param fn        handler function to call when interrupt generated on pin
 * @param aux_data  client's data pointer to be passed as second argument
 *                  when calling handler function
 *
 * Raises an assert if `pin` does not refer to valid gpio id. `aux_data`
 * can be NULL if handler function has no need for auxiliary data. If `fn`
 * is NULL, removes any handler previously registered for `pin`.
 *
 * It is an error to register handler without first
 * initializing the module (i.e. required to call `gpio_interrupt_init`).
 */
void gpio_interrupt_register_handler(gpio_id_t pin, handlerfn_t fn, void *aux_data);

/*
 * Enumeration for GPIO interrupt events
 *
 * The enumerated values below establish symbolic names for the different
 * GPIO events that can trigger an interrupt.
 */
typedef enum {
    GPIO_INTERRUPT_POSITIVE_EDGE = 0,
    GPIO_INTERRUPT_NEGATIVE_EDGE = 1,
    GPIO_INTERRUPT_HIGH_LEVEL = 2,
    GPIO_INTERRUPT_LOW_LEVEL = 3,
    GPIO_INTERRUPT_DOUBLE_EDGE = 4,
} gpio_event_t;

/*
 * `gpio_interrupt_config`
 *
 * Config interrupt to trigger on `event` for GPIO with id `pin`.
 * The boolean parameter `debounce` controls whether to apply debounce
 * circuit to filter/coalesce sequence of rapid-fire events. If true, debounce
 * filters to ~1 event/ms. If events expected to be more rapid (e.g. PS/2),
 * set debounce to false to not miss events.
 *
 * @param pin        the id of the GPIO pin
 * @param event      which event, see gpio_event_t enumeration
 * @param debounce   whether to apply debounce filter
 *
 * Raises an assert if `pin` or `event` is not valid.
 */
void gpio_interrupt_config(gpio_id_t pin, gpio_event_t event, bool debounce);

/*
 * `gpio_interrupt_enable`
 *
 * Enable interrupts for GPIO with id `pin`.
 *
 * @param pin      the id of the GPIO pin
 *
 * Raises an assert if `pin` is not valid.
 */
void gpio_interrupt_enable(gpio_id_t pin);

/*
 * `gpio_interrupt_disable`
 *
 * Disable interrupts for GPIO with id `pin`.
 *
 * @param pin      the id of the GPIO pin
 *
 * Raises an assert if `pin` is not valid.
 */
void gpio_interrupt_disable(gpio_id_t pin);

/*
 * `gpio_interrupt_clear`
 *
 * Clears any pending interrupt for GPIO with id `pin`.
 *
 * @param pin      the id of the GPIO pin
 *
 * Raises an assert if `pin` is not valid.
 */
void gpio_interrupt_clear(gpio_id_t pin);

#endif

#ifndef HDMI_H
#define HDMI_H

/*
 * Functions for controlling display hardware.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */


/*
 * `hdmi_resolution_id_t` : enum of available HDMI resolutions
 *
 * These are the available resolutions that the HDMI display
 * hardware can be configured for.
 */
typedef enum  {
    HDMI_1080P,     /* 1920 x 1080 */
    HDMI_HD,        /* 1280 x  720 */
    HDMI_SVGA,      /*  800 x  600 */
    HDMI_INVALID
} hdmi_resolution_id_t;

/*
 * `hdmi_init` : Required initializion for HDMI hardware driver
 *
 * Initializes the HDMI hardware driver and configures output screen
 * for the specified resolution.
 *
 * @param res   requested resolution id chosen from enum above
 */
void hdmi_init(hdmi_resolution_id_t res);

/*
 * `hdmi_best_match` : Return the resolution id that is the best
 * match for the desired width x height.
 *
 * Returns the resolution id of best match or HDMI_INVALID if
 * the size cannot be accommodated.
 *
 * @return      resolution id chosen from enum above
 */
hdmi_resolution_id_t hdmi_best_match(int width, int height);

/*
 * `hdmi_get_screen_width`
 *
 * Get the width of screen in pixels.
 *
 * @return    the width in pixels
 */
int hdmi_get_screen_width(void);

/*
 * `hdmi_get_screen_height`
 *
 * Get the height of screen in pixels.
 *
 * @return    the height in pixels
 */
int hdmi_get_screen_height(void);

#endif

#ifndef HSTIMER_H
#define HSTIMER_H

/*
 * Functions for Mango Pi high-speed timer.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include <stdbool.h>

/*
 * Type: `hstimer_id_t`
 *
 * The hstimer peripheral has two available timers: HSTIMER0 and HSTIMER1.
 */
typedef enum  { HSTIMER0 = 0, HSTIMER1 = 1 } hstimer_id_t;

/*
 * `hstimer_init` : required module initialization
 *
 * Initialize the hstimer peripheral `timer` for use as a "countdown" timer.
 * The number of microsecs to count is specified  as the `us_interval`
 * argument. A request to init (or re-init) hstimer will configure
 * the countdown interval and place the timer into a disabled state.
 * Use `hstimer_enable` to begin counting down when ready.
 *
 * When an hstimer is enabled, its counter is decremented once per microsec.
 * When counter reaches zero, it will generate an interrupt. Clearing the
 * interrupt will restart the timer, causing it to countdown again from the
 * initial interval value. The cycle repeats until timer is disabled.
 *
 * The `timer` argument specifies which hstimer id to operate on. A hstimer id
 * that is not valid will be ignored.
 *
 * @param timer         which hstimer to config (either HSTIMER0 or HSTIMER1)
 * @param us_interval   count of usecs per timer interval
 */
void hstimer_init(hstimer_id_t timer, long us_interval);

/*
 * `hstimer_enable`
 *
 * Enable `timer`. Countdown will start/resume.
 *
 * @param timer         which hstimer to enable (either HSTIMER0 or HSTIMER1)
 */
void hstimer_enable(hstimer_id_t timer);

/*
 * `hstimer_disable`
 *
 * Disable `timer`. Suspends counting down.
 *
 * @param timer         which hstimer to disable (either HSTIMER0 or HSTIMER1)
 */
void hstimer_disable(hstimer_id_t timer);

/*
 * `hstimer_interrupt_clear`
 *
 * Clear any pending hstimer interrupt for `timer`. Countdown will restart
 * from the initial interval.
 *
 * @param timer         which hstimer to enable (either HSTIMER0 or HSTIMER1)
 */
void hstimer_interrupt_clear(hstimer_id_t timer);

#endif

#ifndef INTERRUPTS_H
#define INTERRUPTS_H

/*
 * Module to configure interrupts for Mango Pi.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include <stdint.h>

typedef enum _interrupt_sources interrupt_source_t;

/*
 * `interrupts_init`: Required initialization for interrupts
 *
 * Initialize interrupts module and configure to a clean state.
 * After init, the state of the interrupt system will be:
 *
 *    - top-level trap handler installed and active
 *    - all interrupt sources are disabled
 *    - interrupts are globally disabled
 *
 * This module init should be called once (and only once)
 * before any calls to other functions in the interrupts module.
 * Calling the init function a second time will raise an error.
 * Without more specific initialization semantics and structure,
 * this is the safe approach that avoids having to debug why a
 * source suddenly stopped receiving interrupts after a re-init
 * silently wiped the settings from previous configuration.
 */
void interrupts_init(void);

/*
 * `interrupts_global_enable`
 *
 * Turn on interrupts system-wide. An interrupt generated on an
 * interrupt source that is enabled will call the registered handler.
 */
void interrupts_global_enable(void);

/*
 * `interrupts_global_disable`
 *
 * Turn off interrupts system-wide. No interrupts will be generated.
 * Does not remove registered handlers or disable interrupt sources,
 * only temporarily suspends interrupt generation. To resume
 * generating interrupts, call `interrupts_global_enable`.
 */
void interrupts_global_disable(void);

/*
 * `interrupts_enable_source`
 *
 * Enable a particular interrupt source. The source itself must
 * be configured to generate interrupts (and global interrupts must be
 * enabled) for a registered handler to be called.
 *
 * @param source    which interrupt source (see enumeration values below)
 *
 * An error is raised if `source` is not valid.
 */
void interrupts_enable_source(interrupt_source_t source);

/*
 * `interrupts_disable_source`
 *
 * Disable a particular interrupt source. Interrupts for this source
 * will not trigger a handler and will remain pending (until cleared).
 *
 * @param source    which interrupt source (see enumeration values below)
 *
 * An error is raised if `source` is not valid.
 */
void interrupts_disable_source(interrupt_source_t source);

/*
 * `handlerfn_t`
 *
 * This typedef gives a nickname to the type of function pointer used as
 * a handler callback. A handler is registered to an interrupt source. When
 * an interrupt is generated by that source, the handler is called to
 * process it. The one argument to the handler is the client's
 * auxiliary data pointer (can be NULL if not used).
 */
typedef void (*handlerfn_t)(void *);

/*
 * `interrupts_register_handler`
 *
 * Register the handler function for a given interrupt source. Each interrupt
 * source can have one handler: further dispatch should be managed by
 * the handler itself. Registering a handler does not enable the source:
 * this must be done separately through `interrupts_enable_source`.
 * These are separate because otherwise there can be impossible-to-solve
 * technical challenges such as
 *   - receiving an interrupt before `interrupts_register_handler` completes,
 *   - handling an interrupt that was pending from a different use of the source,
 *   - changing the handler as one part of a larger atomic action.
 *
 * @param source    which interrupt source (see enumeration values below)
 * @param fn        handler function to call when interrupt generated on source
 * @param aux_data  client's data pointer to be passed as second argument
 *                  when calling handler function
 *
 * An error is raised if `source` is not valid. `aux_data` can be NULL if
 * handler function has no need for auxiliary data. If `fn` is NULL, this
 * removes any handler previously registered for `source`.
 */
void interrupts_register_handler(interrupt_source_t source, handlerfn_t fn, void *aux_data);

/*
 * 'interrupt_source_t` enumeration
 *
 * Below are interrupt sources for which this module can enable, disable,
 * and register a handler. Interrupt source numbers are assigned in
 * table 3-9 p.204-210 of the D1-H User Manual.
 */
enum _interrupt_sources {
    INTERRUPT_SOURCE_UART0 = 18,
    INTERRUPT_SOURCE_UART1 = 19,
    INTERRUPT_SOURCE_UART2 = 20,
    INTERRUPT_SOURCE_UART3 = 21,
    INTERRUPT_SOURCE_UART4 = 22,
    INTERRUPT_SOURCE_UART5 = 23,
    INTERRUPT_SOURCE_TWI0 = 25,
    INTERRUPT_SOURCE_TWI1 = 26,
    INTERRUPT_SOURCE_TWI2 = 27,
    INTERRUPT_SOURCE_TWI3 = 28,
    INTERRUPT_SOURCE_SPI0 = 31,
    INTERRUPT_SOURCE_SPI1 = 32,
    INTERRUPT_SOURCE_HSTIMER0 = 71,
    INTERRUPT_SOURCE_HSTIMER1 = 72,
    INTERRUPT_SOURCE_GPIOB = 85,
    INTERRUPT_SOURCE_GPIOC = 87,
    INTERRUPT_SOURCE_GPIOD = 89,
    INTERRUPT_SOURCE_GPIOE = 91,
    INTERRUPT_SOURCE_GPIOF = 93,
    INTERRUPT_SOURCE_GPIOG = 95,
};

#endif

#ifndef KEYBOARD_H
#define KEYBOARD_H

/*
 * Module to read keys typed on a PS/2 keyboard.
 *
 * Students implement this module in assignment 5.
 *
 * Author: Philip Levis <pal@cs.stanford.edu>
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include "gpio.h"
#include "ps2_keys.h"


// gpio pins for connecting keyboard clock/data lines
#define KEYBOARD_CLOCK GPIO_PG12
#define KEYBOARD_DATA GPIO_PB7

/*
 * Type: `key_action_t`
 *
 * This struct represents a single action on a PS2 key.
 * The action is either a press or release of the specified key
 * (identified by its keycode).
 */
typedef struct {
    enum { KEY_PRESS, KEY_RELEASE } what;
    unsigned char keycode;
} key_action_t;

/*
 * Type: `keyboard_modifiers_t`
 *
 * This enum type represents the state of the modifier keys.
 * A single bit flag is used for each modifier. The bits
 * taken together as a bitset indicate the state of all modifier keys.
 * A bit can be set/clear/read within the bitset by using the individual
 * bit flag as a mask.
 */
typedef enum {
    KEYBOARD_MOD_SHIFT = 1 << 0,
    KEYBOARD_MOD_ALT = 1 << 1,
    KEYBOARD_MOD_CTRL = 1 << 2,
    KEYBOARD_MOD_CAPS_LOCK = 1 << 3,
} keyboard_modifiers_t;

/*
 * Type: `key_event_t`
 *
 * This struct represents a single key event, which packages
 * the raw key action with additional data about keyboard state.
 */
typedef struct {
    key_action_t action;                // see struct declared above
    ps2_key_t key;                      // entry taken from ps2_keys table (see ps2_keys.h)
    keyboard_modifiers_t modifiers;     // modifiers in effect, composed of above bit flags
} key_event_t;


/*
 * `keyboard_init`: Required initialization for keyboard.
 *
 * The keyboard must first be initialized before any key events can be read.
 * The two arguments are the ids of the GPIOs connected to the PS2 clock and data lines.
 *
 * @param clock    the gpio connected to the clock line of keyboard
 * @param data     the gpio connected to the data line of keyboard
 */
void keyboard_init(gpio_id_t clock, gpio_id_t data);

/*
 * `keyboard_read_next`: Top level keyboard interface.
 *
 * This function reads (blocking) the next key typed on the keyboard.
 * The character returned reflects the current keyboard modifiers in effect.
 *
 * Return values in the range 0 - 0x7f indicate the typed key is an ordinary
 * Ascii character. For a typed key not associated with an Ascii character,
 * such an arrow or function key, the function returns a value >= 0x90. The
 * value assigned to each non-Ascii key is given in the list of `ps2_special_chars`
 * in the `ps2_keys.h` header file.
 *
 * This function calls `keyboard_read_event` to receive a key press event.
 *
 * @return      Ascii value of typed char or function code for non-ascii key
 */
unsigned char keyboard_read_next(void);

/*
 * `keyboard_read_event`: Mid level keyboard interface.
 *
 * The function reads (blocking) the next key event.
 * Returns a `key_event_t` struct that represents the key event.
 * A key event is a press or release of a single non-modifier key.
 * The returned struct includes the ps2 key that was pressed or
 * released and the keyboard modifiers in effect.
 *
 * `keyboard_read_event` does not return until it observes a key
 * event for a non-modifier key. Pressing or releasing a modifier
 * key changes the state of the modifiers for other key events
 * but does not produce its own key event. For example, if the user
 * presses Shift, press 'a', release Shift, and release 'a', this
 * comprises four key actions (four calls to `keyboard_read_sequence`)
 * but only two key events (press 'a' and release `a`). The key
 * event returned for press 'a' has the shift modifier on, the key event
 * returned for release 'a' has shift modifier off.
 *
 * This function calls `keyboard_read_sequence` to read a sequence.
 *
 * @return      key_event_t struct containing key event information
 */
key_event_t keyboard_read_event(void);

/*
 * `keyboard_read_sequence`: Low level keyboard interface.
 *
 * Reads a sequence of scancode bytes corresponding to the press or
 * release of a single key.  Returns a `key_action_t` struct that
 * represents the key action for the sequence read. Reads 1, 2, or 3
 * scancodes:
 *    1 byte:  ordinary key press
 *    2 bytes: ordinary key release or extended key press
 *    3 bytes: extended key release
 *
 * The `keycode` field of the returned key_action_t stores the last byte
 * of the sequence. This keycode identifies the PS2 key that was acted upon.
 *
 * This function calls `keyboard_read_scancode` to read each scancode.
 *
 * @return      key_action_t struct containing key action for sequence
 */
key_action_t keyboard_read_sequence(void);

/*
 * `keyboard_read_scancode`: Bottom level keyboard interface.
 *
 * Calls into ps2 module to read (blocking) a single scancode from
 * the PS2 keyboard device.
 *
 * @return      scancode read from keyboard
 */
unsigned char keyboard_read_scancode(void);

/*
 * `keyboard_use_interrupts` : reference only
 *
 * Change keyboard from default polling behavior to instead configure interrupts
 * for gpio events. After setting keyboard to use interrupts, client must
 * also globally enable interrupts at system level. This switchable feature is
 * specific to reference module. The student's keyboard module is initially
 * polling-only (assign5) and later changed to interrupt-only (assign7).
 */
void keyboard_use_interrupts(void);

#endif

#ifndef MALLOC_H
#define MALLOC_H

/*
 * Functions for dynamic allocation.
 *
 * Students implement this module in assignment 4.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include <stddef.h> // for size_t

/*
 * `malloc`
 *
 * Service a dynamic allocation request. Returns the
 * address of a memory block of at least `nybtes` contiguous
 * bytes or NULL if the request cannot be satisifed.
 * The block address is guaranteed to be aligned to an 8-byte boundary.
 * If `nbytes` is 0, malloc returns either NULL or a unique
 * pointer to an allocated block of some minimum size.
 *
 * @param nbytes    requested size in bytes
 * @return          address of memory block of requested size
 *                  or NULL if request cannot be satisifed
 */
void *malloc(size_t nbytes);

/*
 * `free`
 *
 * Deallocate the memory block at address `ptr`.
 *
 * The `ptr` argument is expected to be an address that was
 * previously returned by malloc and has not yet been freed.
 * If this precondition is not satisified, the behavior is undefined.
 * If `ptr` is NULL, the operation does nothing.
 *
 * @param ptr       address of memory block to deallocate
 */
void free(void *ptr);

/*
 * `sbrk`
 *
 * Return the address of the previous end of the heap segment
 * and enlarge the segment by the specified number of bytes.
 * The call `sbrk(0)` returns the address of the end of segment
 * with no change in size. You may assume that no calls to
 * sbrk with a non-zero argument will come from outside clients,
 * calls to enlarge the segment will only come from within
 * your heap allocator implementation.
 *
 * @param nbytes    the number of bytes to extend the segment end by
 * @return          address of previous end of segment (before enlarge)
 */
void *sbrk(size_t nbytes);

#endif

#ifndef MANGO_H
#define MANGO_H

/*
 * Utility functions for Mango Pi.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */
#include "gpio.h"

/*
 * The blue act led on-board the Mango Pi is controlled via GPIO PD18.
 */
 #define MANGO_ACTLED GPIO_PD18

/*
 * Enumeration for LED state.
 */
enum led_state_t {
    LED_OFF = 0,
    LED_ON,
    LED_TOGGLE
};

/*
 * `mango_actled`
 *
 * Change state of the act led.
 *
 * @param state      state change for led
 *
 * If state is not valid, function does nothing.
 */
void mango_actled(enum led_state_t state);

/*
 * `mango_reboot`
 *
 * Halts current program execution and reboots the Pi.
 */
void mango_reboot(void) __attribute__ ((noreturn));

/*
 * `mango_abort`
 *
 * Goes into an infininte loop that flashes an SOS
 * pattern on the act led. This function does not return.
 */
void mango_abort(void) __attribute__ ((noreturn));

#endif

#ifndef MOUSE_H
#define MOUSE_H

/*
 * Module to read mouse events from a PS/2 mouse.
 *
 * Students implement this module as an extension to assignment 7.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include "gpio.h"
#include <stdbool.h>

// gpio pins for connecting mouse clock/data lines
#define MOUSE_CLOCK GPIO_PD21
#define MOUSE_DATA GPIO_PD22

/*
 * Each event corresponds to a particular user action with
 * mouse (press/release button, move/drag). This enum
 * identifes the four types of mouse actions.
 */
typedef enum {
    MOUSE_BUTTON_PRESS = 0,
    MOUSE_BUTTON_RELEASE = 1,
    MOUSE_MOVED = 2,
    MOUSE_DRAGGED = 3,
} mouse_action_t;

/*
 * This struct captures the information associated with a mouse event.
 * For information on the value and purpose of the fields, refer to
 * the documentation for function `mouse_read_event` below.
 */
typedef struct {
    mouse_action_t action;
    int dx, dy;
    bool x_overflow, y_overflow;
    bool left, middle, right;
} mouse_event_t;


/*
 * `mouse_init`: Required initialization for mouse.
 *
 * The mouse must first be initialized before any mouse events can be read.
 * The two arguments are the ids of the GPIOs connected to the PS2 clock and data lines.
 * `mouse_init` resets the mouse and enables data reporting mode.
 *
 * @param clock    the gpio connected to the clock line of mouse
 * @param data     the gpio connected to the data line of mouse
 */
void mouse_init(gpio_id_t clock, gpio_id_t data);

/*
 * `mouse_read_event`
 *
 * The function reads (blocking) the next event from the mouse.
 * Returns a `mouse_event_t` struct that represents the mouse state/action.
 * The dx, dy fields are delta values, i.e. relative change in x and y
 * when mouse is moved/dragged. A delta value is within the range -255 to +255.
 * The x_overflow/y_overflow fields indicate an overflow occurred in
 * computing the delta value. If actual change dx or dy exceeds
 * representable range (> 255), the overflow field is true, false otherwise.
 * The left/middle/right fields give the state of the mouse buttons.
 * The field is true if the corresponding mouse button is down, false otherwise.
 *
 * @return         mouse_event_t struct containing event data
 */
mouse_event_t mouse_read_event(void);

#endif

#ifndef PRINTF_H
#define PRINTF_H

/*
 * Functions for printing formatted strings.
 *
 * Students implement this module in assignment 3
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include <stdarg.h>
#include <stddef.h>

/*
 * The functions vnsprintf, snprintf, and printf construct a
 * formatted output from an input format string and arguments.
 * These functions support same conversion options in format
 * string, but differ slightly in how the function is called
 * or what it does with the output string, e.g., whether it is
 * sent to the UART peripheral (printf) or written into the
 * destination buffer (snprintf, vsnprintf).
 *
 * The supported format conversions are
 *   %c    single character
 *   %s    string
 *   %d    signed decimal integer
 *   %x    unsigned hexadecimal integer (hex letters in lowercase)
 *   %p    pointer (printed as a hex address)
 *   %%    used to output a single percent character
 *
 * The %d and %x formats support an optional field width.
 *
 * The fancier printf features (padding with spaces, justification
 * left/right, precision, etc.) are not supported.
 * All format conversions other than the supported ones listed above
 * are considered invalid. The function's behavior for an invalid
 * format conversion is undefined.
 */

/*
 * `vsnprintf`
 *
 * Constructs a formatted output string from an input string and a va_list
 * of arguments. Writes output string to the destination buffer.
 *
 * @param buf       destination buffer where to write output string
 * @param bufsize   size of destination buffer (output truncated to fit if needed)
 * @param format    format for output string. May contain ordinary characters
 *                  (copied to destination as-is) and format conversions (next
 *                  argument converted and copied to destination)
 * @param args      list of arguments to be converted
 * @return          count of characters written if entire formatted string
 *                  fits in destination; otherwise count of characters it would
 *                  have written if there were space.
 */
int vsnprintf(char *buf, size_t bufsize, const char *format, va_list args);

/*
 * `snprintf`
 *
 * Constructs a formatted output string from an input string and variable
 * arguments. Writes output string to the destination buffer.
 *
 * @param buf       destination buffer where to write output string
 * @param bufsize   size of destination buffer (output truncated to fit if needed)
 * @param format    format for output string. May contain ordinary characters
 *                  and format conversions
 * @param ...       variable arguments to be converted
 * @return          count of characters written if entire formatted string
 *                  fits in destination; otherwise count of characters it would
 *                  have written if there were space.
 */
int snprintf(char *buf, size_t bufsize, const char *format, ...) __attribute__((format(printf, 3, 4)));

/*
 * `printf`
 *
 * Constructs a formatted output string from an input string and arguments.
 * Writes output string to UART.
 *
 * @param format    format for output string. May contain ordinary characters
 *                  and format conversions
 * @param ...       variable arguments to be converted
 * @return          count of characters written to UART
 */
int printf(const char *format, ...) __attribute__((format(printf, 1, 2)));

#endif

#ifndef PS2_H
#define PS2_H

/*
 * Module that communicates with a PS2 device such as a keyboard
 * or mouse.
 *
 * Students implement this module in assignments 5 and 7.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include <stdbool.h>
#include "gpio.h"

/*
 * Type: `ps2_device_t`
 *
 * This typedef gives a nickname to the struct that will be used to represent a
 * single PS2 device. The internal details of the struct (e.g. type and names
 * of struct fields) will be given in the file ps2.c; those details are private
 * to the implementation and not shared in the public interface.
 * Clients of the ps2 module are not privy to the details of ps2_device_t,
 * nor should they be. A client simply holds on to the pointer returned by
 * `ps2_new` and sends that pointer as an argument to `ps2_read`.
 */
typedef struct ps2_device ps2_device_t;

/*
 *  `ps2_new`
 *
 * Initializes a new PS2 device connected to specified clock and data GPIO
 * pins and returns pointer to device.
 *
 * To configure a new PS2 device in your code:
 *
 *     ps2_device_t *dev = ps2_new(KEYBOARD_CLK, KEYBOARD_DATA);
 *
 * Notice that this interface is different from the module_init exposed by
 * other library modules. The new operation can be called more than once
 * in order to create multiple PS2 devices, such as one for a keyboard and
 * another for a mouse. When calling ps2_new, the client saves the
 * returned pointer to use as argument in when calling ps2_read.
 * This is how the client indicates which PS2 device is to be read.
 *
 * @param clock     the id of gpio connected to the clock line of the PS2 device
 * @param data      the id of gpio connected to the data line of the PS2 device
 * @return          pointer to new PS2 device or NULL if failed to create
 *
 * Although `ps2_new` configures the requested clock and data GPIOs
 * to use the internal pull-up resistors, it is recommended to choose GPIO
 * pins whose default state is already pull-up. This avoid timing issues
 * if the device attempts to handshake with the Pi before `ps2_new` has
 * been called.
 */
ps2_device_t *ps2_new(gpio_id_t clock, gpio_id_t data);

/*
 * `ps2_read`
 *
 * Read (blocking) a single scancode from the specifed PS2 device.
 * Bits are read on the falling edge of the clock.
 *
 * Reads 11 bits: 1 start bit, 8 data bits, 1 parity bit, and 1 stop bit
 *
 * Discards and restarts the scancode if:
 *   (lab5) The start bit is incorrect
 *   (assign5) or if parity or stop bit is incorrect
 *
 * Returns the 8 data bits of a well-formed PS2 scancode.
 * Will not return until it reads a complete and valid scancode.
 *
 * @param dev     PS2 device from which to read
 * @return        scancode read from PS2 device
 */
unsigned char ps2_read(ps2_device_t *dev);

/*
 * `ps2_write`: optional extension
 *
 * Write a command scancode to the specifed PS2 device.
 * You do not need to implement this function unless you are
 * doing the mouse extension for assign7.
 *
 * @param dev       PS2 device to which to write
 * @param command   scancode to write
 * @return          true if successful write, false otherwise
 */
bool ps2_write(ps2_device_t *dev, unsigned char command);

/*
 * `ps2_use_interrupts` : reference only
 *
 * The default behavior of `ps2_read` is to read scancode bits
 * by polling the clock GPIO and waiting for a falling edge. An
 * alternate mode would be to register for event detection
 * on the clock GPIO to be notified of each falling edge and use
 * an interrupt handler to read a bit. The interrupt handler would
 * enqueue a scancode to an internal queue to be later
 * dequeued by`ps2_read`.
 *
 * The reference implementation of this module has a switch to
 * change the mode for a ps2 device. The default is read-by-polling.
 * After creating a device with `ps2_new`, the client may call
 8 `ps2_use_interrupts(device)` to change that device into
 * read-by-interrupt mode.
 *
 * The client must also globally enable interrupts at system level for
 * interrupts to be generated.
 *
 * The switchable mode is specific to the reference implementation.
 * The student's ps2 module does not have this function.
 * The initial implementation by student is polling-only (assign5) and
 * later changed to interrupt-only (assign7).
 */
void ps2_use_interrupts(ps2_device_t *dev);

#endif

#ifndef PS2_KEYS_H
#define PS2_KEYS_H

/*
 * This module declares constants for interacting with a PS/2
 * keyboard, including an array that serves as a lookup table
 * to access information about the keys and the characters they produce.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 * Author: Philip Levis <pal@cs.stanford.edu>
 */

#define PS2_CODE_RELEASE 0xF0   // Scancodes used in the PS/2 protocol
#define PS2_CODE_EXTENDED 0xE0

/*
 * Type: `ps2_key_t`
 *
 * A ps2_key_t struct has two fields, `ch` and `other_ch`.
 * These fields correspond to the unmodified and modified character produced by
 * a given PS2 key. For example, the A key produces `{ 'a', 'A' }` and the
 * Four key produces `{ '4', '$' }`. Keys such as Tab that are unchanged
 * by the modifier have same `ch` and `other_ch`, e.g. `{'\t', '\t'}`.
 * A non-ASCII key produces a char with special value >= 0x90 as
 * assigned in the enumeration of special characters below.
 */
typedef struct {
    unsigned char ch;
    unsigned char other_ch;
} ps2_key_t;

/*
 * Global constant array: `ps2_keys`
 *
 * Each entry in the array corresponds to one physical key on the keyboard.
 * Each key has an assigned PS/2 scancode. The array is organized
 * in order of scancode. The scancode is used as an index to access
 * the characters produced by that key.
 * As an example, pressing the A key on a PS2 keyboard generates scancode `0x1c`.
 * Accessing the array element at that index `ps2_keys[0x1c]` retrieves the ps2_key_t
 * `{'a', 'A'}`. These are the unmodified and modified characters produced by the A key.
 */
extern ps2_key_t const ps2_keys[];

/*
 * Enumeration of special chars
 *
 * Regular chars have ASCII characters values that are all <= 0x7F,
 * we assign special character codes >= 0x90 to be produced by
 * the non-ASCII keys.
 */
enum {
    PS2_KEY_NONE = 0,
    PS2_KEY_SHIFT = 0x90,
    PS2_KEY_ALT,    // enum values assigned in increasing sequence from here
    PS2_KEY_CTRL,
    PS2_KEY_CAPS_LOCK,
    PS2_KEY_ENTER,
    PS2_KEY_ESC,
    PS2_KEY_F1,
    PS2_KEY_F2,
    PS2_KEY_F3,
    PS2_KEY_F4,
    PS2_KEY_F5,
    PS2_KEY_F6,
    PS2_KEY_F7,
    PS2_KEY_F8,
    PS2_KEY_F9,
    PS2_KEY_F10,
    PS2_KEY_F11,
    PS2_KEY_F12,
    PS2_KEY_NUM_LOCK,
    PS2_KEY_HOME,
    PS2_KEY_PAGE_UP,
    PS2_KEY_PAGE_DOWN,
    PS2_KEY_INSERT,
    PS2_KEY_DELETE,
    PS2_KEY_END,
    PS2_KEY_SCROLL_LOCK,
    PS2_KEY_ARROW_UP,
    PS2_KEY_ARROW_DOWN,
    PS2_KEY_ARROW_LEFT,
    PS2_KEY_ARROW_RIGHT
};

#endif

#ifndef RAND_H
#define RAND_H

/*
 * Module to generate random numbers.
 *
 * Author: Philip Levis <pal@cs.stanford.edu>
 * Author: Pat Hanrahan <hanrahan@cs.stanford.edu>
 */

/*
 * `rand`
 *
 * Generate a 32-bit random number. Uses a psuedo-random
 * generator with a fixed seed.
 *
 * @return   pseudo-random value in the range 0 to UINT_MAX
 */
unsigned int rand(void);

#endif

#ifndef RINGBUFFER_H
#define RINGBUFFER_H

/*
 * This module defines a ring buffer data structure that provides
 * a fixed-length FIFO (first-in-first-out) queue of int elements.
 *
 * The queue is designed to allow concurrent access by 1 reader (rb_dequeue)
 * and 1 writer (rb_enqueue). The writer is typically the interrupt handler,
 * which is enqueuing data to be dequeued by the main program, the reader.
 *
 * Author: Philip Levis <pal@cs.stanford.edu>
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include <stdbool.h>

/*
 * Type: `rb_t`
 *
 * This struct holds the information for a ring buffer.
 */
typedef struct ringbuffer rb_t;

/*
 * `rb_new`
 *
 * Initializes a new empty ring buffer and returns a pointer to it.
 *
 * @return      pointer to new ring buffer or NULL if failed to create
 *
 * To set up a ring buffer in your code:
 *
 *     rb_t *rb = rb_new();
 *
 * Notice that this interface is slightly different from the _init exposed by
 * other library modules. This _new pattern allows a user to have multiple ring
 * buffers, like objects in Java. It also means that users of this
 * module don't need to know the implementation details (like size) of rb_t,
 * since they just keep a pointer.
 */
rb_t *rb_new(void);

/*
 * `rb_empty`
 *
 * Check if a ring buffer is currently empty.
 *
 * @param rb    the ring buffer to check
 * @return      true if rb is empty, false otherwise
 */
bool rb_empty(rb_t *rb);

/*
 *  `rb_full`
 *
 * Check if a ring buffer is currently full. When full, existing
 * elements must first be dequeued before further elements can
 * be enqueued.
 *
 * @param rb    the ring buffer to check
 * @return      true if rb is full, false otherwise
 */
bool rb_full(rb_t *rb);

/*
 * `rb_enqueue`
 *
 * Add an element to the back of a ring buffer. If the ring buffer
 * is full, no changes are made and false is returned.
 *
 * @param rb    the ring buffer to enqueue to
 * @param elem  the element to enqueue
 * @return      true if elem was successfully enqueued, false otherwise
 */
bool rb_enqueue(rb_t *rb, int elem);

/*
 * `rb_dequeue`
 *
 * If the ring buffer is not empty, remove frontmost element,
 * store into *p_elem, and return true.  p_elem should be the address
 * of a valid memory location into which to store the dequeued value.
 * If the ring buffer is empty, no changes are made to either the ring
 * buffer or *p_elem and the return value is false.
 *
 * @param rb        the ring buffer to dequeue from
 * @param p_elem    address at which to store the dequeued element
 * @return          true if an element was written to *p_elem, false otherwise
 */
bool rb_dequeue(rb_t *rb, int *p_elem);

#endif

#ifndef SHELL_H
#define SHELL_H

/*
 * Interface to a simple shell module.
 *
 * Students implement this module in assignment 5.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

 #include <stddef.h>

/*
 * Type: `input_fn_t`
 *
 * This typedef gives a nickname to the type of function pointer used as the
 * the shell input function.  A input_fn_t function takes no arguments and
 * returns a value of type unsigned char. `keyboard_read_next` is an example
 * of a possible shell input function.
 */
typedef unsigned char (*input_fn_t)(void);

/*
 * Type: `formatted_fn_t`
 *
 * This typedef gives a nickname to the type of function pointer used as the
 * the shell output function.  A formatted_fn_t function has one fixed
 * parameter (format string) and variable arguments to follow. The return
 * value is of type int. `printf` is an example of a possible shell
 *  output function.
 */
typedef int (*formatted_fn_t)(const char *format, ...) __attribute__((format(printf, 1, 2)));

/*
 * `shell_init`: Required initialization for shell
 *
 * The two arguments are function pointers. The `read_fn` is a function
 * to read input. The shell calls this function to get the next
 * input character. The `print_fn` is a function to print output.
 * The shell calls this function to output formatted text.
 * The client's choice of read function controls where shell reads
 * input and cclient's hoice of print function controls where shell
 * output is displayed.
 *
 * Example usage:
 *   `shell_init(keyboard_read_next, printf)`
 *   `shell_init(keyboard_read_next, console_printf)`
 *
 * @param read_fn    function to read input char
 * @param print_fn   function to output formatted text
 */
void shell_init(input_fn_t read_fn, formatted_fn_t print_fn);

/*
 * `shell_bell`: audio/visual beep
 *
 * Sends an Ascii BEL character '\a' over the uart. This gives
 * an audio or visual beep depending on your terminal settings.
 * https://en.wikipedia.org/wiki/Bell_character
 */
void shell_bell(void);

/*
 * `shell_readline`: read function of shell read-eval-print loop
 *
 * Reads a single line of input from the user.
 *
 * Reads characters entered by user and stores them into `buf`.
 * Updates display to show current line as user edits.
 * Stops reading when user enters Return ('\n'). The ending newline
 * is discarded (not written to `buf`).  If user enters more
 * characters than fit in `buf` (`bufsize` - 1), those excess characters
 * are rejected (call `shell_bell`) and not written to `buf`.  A
 * null-terminator is written to the end of the contents in `buf`.
 * The basic shell discards any non-ASCII characters (i.e. ch > 0x7f).
 * If doing the extension, some of these non-ASCII codes are implemented
 * by the shell to access features for line editing and command history.
 *
 * When the user types backspace (\b):
 *   If there are any characters currently in the buffer, deletes the last one.
 *   Otherwise, calls `shell_bell`.
 *   Backspace through a tab character will not be tested (printing a tab
 *   outputs a variable number of spaces, handling of backspace does does
 *   not have to account for this)
 *
 * @param buf       destination buffer to store characters
 * @param bufsize   size of the buffer
 */
void shell_readline(char buf[], size_t bufsize);

/*
 * `shell_evaluate`: eval function of shell read-eval-print loop
 *
 * Parses line and execute command.
 *
 * Parsing proceeds as follows:
 *   - Divide the line into an array of tokens. A token consists
 *     of a sequence of non-space chars.  Ignore/skip all whitespace
 *     in between tokens as well as leading and trailing whitespace.
 *     Whitespace includes space, tab, and newline.
 *   - The first token is the name of the command to execute, the
 *     subsequent tokens are the arguments to the command.
 *   - If line contains no tokens (string is empty or all whitespace),
 *     no command is executed and -1 is returned.
 * After parsing, execute the command:
 *   - Find the function pointer associated with the command name.
 *   - If no such command is found, give error:
 *           error: no such command 'binky'.
 *   - Otherwise, execute the function, passing array of arguments.
 *
 * Returns the result of the call to the command function, or -1 if no
 * command was executed.
 *
 * @param line       command line to parse and execute
 * @return           result returned by command function or -1 if none
 */
int shell_evaluate(const char *line);

/*
 * `shell_run`: Top level shell interface.
 *
 * Main function of the shell module. Must be preceded by calls
 * to `shell_init` and `keyboard_init`.
 *
 * Enters a read-eval-print loop that repeatedly cycles between `shell_readline`
 * and `shell_evaluate`. This function never returns.
 */
void shell_run(void);

#endif

#ifndef SHELL_COMMANDS_H
#define SHELL_COMMANDS_H

/*
 * Interface for the shell commands.
 *
 * Students implement these functions in assignment 5.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

/*
 * Type: `command_fn_t`
 *
 * This typedef gives a nickname to the type of function pointer used as the
 * a shell command.  A command_fn_t function has two parameters, the array
 * of tokens and its count. The return value is of type int.
 */
typedef int (*command_fn_t)(int argc, const char *argv[]);

/*
 * Type: `command_t`
 *
 * This typedef defines the type for each entry in the command table.
 * A command_t stores the info for one command, including the one-word name,
 * usage, help description, and function pointer to execute the command.
 */
typedef struct {
    const char *name;
    const char *usage;
    const char *description;
    command_fn_t fn;
} command_t;

/*
 * `cmd_echo`
 *
 * Usage: echo [args]
 * Description: print arguments
 *
 * Example:
 *   Pi> echo Leland Stanford Junior University
 *   Leland Stanford Junior University
 *
 * Print arguments. Returns 0 (success).
*/
int cmd_echo(int argc, const char *argv[]);

/*
 * `cmd_help`
 *
 * Usage: help [cmd]
 * Description: print command usage and description
 *
 * If no argument, print a list of all available commands with
 * usage and description. If an argument is given, print
 * usage and description for named command. If no such
 * named command, prints an error message.
 *
 * Examples:
 *   Pi> help
 *   help [cmd]         print command usage and description
 *   echo [args]        print arguments
 *   reboot             reboot the Mango Pi
 *   peek [addr]        print contents of memory at address
 *   poke [addr] [val]  store value into memory at address
 *   Pi> help reboot
 *   reboot             reboot the Mango Pi
 *   Pi> help please
 *   error: no such command 'please'
 *
 * Ignores any arguments after the first. Returns 0 on success,
 * nonzero on error (command not found).
 */
int cmd_help(int argc, const char *argv[]);

/*
 * `cmd_clear`
 *
 * Usage: clear
 * Description: clear screen (if your terminal supports it)
 *
 * Example:
 *   Pi> clear
 *
 * Ignores all arguments. Sends a formfeed \f character to your terminal.
 * A terminal program such as minicom that supports formfeed will
 * respond by clearing the screen and putting cursor in the upper row.
 * If your terminal does not support formfeed, cmd_clear will
 * have no effect. The function returns 0 in all cases.
 */
int cmd_clear(int argc, const char *argv[]);

/*
 * `cmd_reboot`
 *
 * Usage: reboot
 * Description: reboot the Pi
 *
 * Example:
 *   Pi> reboot
 *   Rebooting...
 *
 * Ignores all arguments. Prints informative message and reboots Pi.
 * This function does not return; the program exits on reboot.
 */
int cmd_reboot(int argc, const char *argv[]);

/*
 * `cmd_peek`
 *
 * Usage: peek [addr]
 * Description: print contents of memory at address
 *
 * Retrieve 4-byte value at memory address and print it as an
 * unsigned hex value, e.g. %08x.
 * If the address argument is prefixed with 0x, it is interpreted as hex,
 * otherwise interpreted as decimal. Address must be 4-byte aligned.
 * If address is missing or invalid, prints an error message.
 *
 * Examples:
 *   Pi> peek 0x40000000
 *   0x40000000:  30047073
 *   Pi> peek
 *   error: peek expects 1 argument [addr]
 *   Pi> peek fred
 *   error: peek cannot convert 'fred'
 *   Pi> peek 13
 *   error: peek address must be 4-byte aligned
 *
 * Ignores any arguments after the first. Returns 0 on success, nonzero on error.
 */
int cmd_peek(int argc, const char *argv[]);

/*
 * `cmd_poke`
 *
 * Usage: poke [addr] [val]
 * Description: store value into memory at address
 *
 * Writes 4-byte value to memory at address.
 * If address or value is prefixed with 0x, it is interpreted as hex,
 * otherwise interpreted as decimal. Address must be 4-byte aligned.
 * If address or value is missing or invalid, prints an error message.
 *
 * Examples:
 *   Pi> poke 0x40000000 3
 *   Pi> peek 0x40000000
 *   0x40000000:  00000003
 *   Pi> poke 0x40000000
 *   error: poke expects 2 arguments [addr] and [val]
 *   Pi> poke bob 3
 *   error: poke cannot convert 'bob'
 *   Pi> poke 11 0
 *   error: poke address must be 4-byte aligned
 *
 * Ignores any arguments after the second. Returns 0 on success, nonzero on error.
 */
int cmd_poke(int argc, const char *argv[]);

#endif

#ifndef STRINGS_H
#define STRINGS_H

/*
 * Functions for handling strings.
 *
 * Students implement this module in assignment 3
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include <stddef.h>

/*
 * `memset`
 *
 * Write `n` bytes of value `val` to the memory area `dst`.
 * Due to historical artifact, `val` parameter has type `int` even
 * though the argument is treated as `unsigned char`. The
 * least significant byte of `val` is copied `n` times.
 *
 * @param dst   address of memory location
 * @param val   the byte value to replicate
 * @param n     the number of bytes to write
 * @return      argument `dst`
 */
void *memset(void *dst, int val, size_t n);

/*
 * `memcpy`
 *
 * Copy `n` bytes of data from the memory area `src` to the
 * memory area `dst`. It is a client error to call memcpy on
 * `dst` and `src` areas that overlap. In such a case,
 * the behavior is undefined.
 *
 * @param dst   address of memory location area to write
 * @param src   address of memory location area to read
 * @param n     the number of bytes to copy
 * @return      argument `dst`
 */
void *memcpy(void *dst, const void *src, size_t n);

/*
 * `strlen`
 *
 * Compute the length of string `str`.
 *
 * @param str   null-terminated string
 * @return      the count of characters preceding terminating '\0' character
 */
size_t strlen(const char *str);

/*
 * `strcmp`
 *
 * Lexicographically compare the null-terminated strings `s1` and `s2`.
 *
 * The function result indicates whether string `s1` is less than (negative),
 * equal to (zero), or greater than (positive) string `s2`.
 * Comparison is done as unsigned characters.
 *
 * @param s1, s2    null-terminated strings
 * @return          negative, zero, or positive result of comparing `s1` to `s2`
 */
int strcmp(const char *s1, const char *s2);

/*
 * `strtonum`
 *
 * Convert the digit characters in `str` to the corresponding unsigned long
 * value. If `str` begins with the prefix "0x", the characters of `str` will
 * be interpreted as hexadecimal digits (base 16); otherwise the characters
 * are interpreted as decimal digits (base 10). No other bases are supported.
 * The hex letter digits are accepted in both upper and lowercase.
 *
 * `strtonum` processes the characters of `str`, stopping at the first
 * character that is not a valid digit in the base or at the terminating
 * '\0' (whichever comes first).  The function is not required to support
 * leading spaces or a plus/minus sign. Such characters can be
 * treated as invalid and stop the conversion.
 *
 * The argument `endptr` is an output parameter optionally used to
 * communicate back to the caller what characters remain in `str` after having
 * "consumed" the digit characters. A caller can pass NULL for `endptr`
 * if they do not need this information.
 *
 * If `endptr` is not NULL, *endptr is updated to point to the character
 * in `str` where conversion stopped. This is either the address of the
 * first invalid character in `str` or the address of the terminating '\0'
 * if all characters in `str` are valid digits.
 *
 * The function result is the converted value or 0 if the first character of
 * `str` is not a valid digit and no conversion was possible.
 *
 * The function only needs to handle converting numbers that are within
 * the range representable as `unsigned long`.
 *
 * @param str       null-terminated string to convert
 * @param endptr    output parameter, will point to character after conversion end
 * @return          result of numeric conversion (or 0 if first char is invalid)
 */
unsigned long strtonum(const char *str, const char **endptr);

/*
 * `strlcat`
 *
 * Size-bounded string concatenation. Append the null-terminated string `src`
 * to the end of `dst`. `strlcat` appends at most `dstsize - strlen(dst) - 1`
 * bytes, and null-terminates `dst`. If `dst` and `src` overlap,
 * the behavior is undefined.
 *
 * The function result is the initial length of `dst` plus the length of
 * `src`, i.e. the final length of `dst` if there were space to append
 * all of `src`.
 *
 * If there is no null terminator within the first `dstsize` characters of
 * `dst`, either `dstsize` is incorrect or `dst` is not a proper string.
 * In such cases, nothing is written to `dst` and the return result is
 * `dstsize + strlen(src)`
 *
 * @param dst       destination buffer containing null-terminated string
 * @param str       null-terminated string to append to destination
 * @param dstsize   size of the dst buffer
 * @return          final length of dst if there were space to append all of src
 */
size_t strlcat(char *dst, const char *src, size_t dstsize);

#endif

#ifndef SYMTAB_H
#define SYMTAB_H

/*
 * Functions to operate on symbol table (if available):
 *    - find symbol for name
 *    - find symbol for address
 *    - label for address (used by printf %pL format)
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

typedef struct {
    const char *name;   // symbol name
    uintptr_t addr;     // symbol start address
    size_t size;        // symbol size in bytes
} symbol_t;

/*
 * `symtab_symbol_for_name`
 *
 * Search for a symbol matching `name`. If match found, store
 * symbol data into parameter `*p_symbol`.
 *
 * @param name      name of function to search for
 * @param p_symbol  pointer to where to store symbol data
 * @return          true if found, false otherwise
 */
bool symtab_symbol_for_name(const char *name, symbol_t *p_symbol);

/*
 * `symtab_symbol_for_addr`
 *
 * Search for a symbol that contains `addr`. If match found, store
 * symbol data into parameter `*p_symbol`.
 *
 * @param addr      address to search for
 * @param p_symbol  pointer to where to store symbol data
 * @return          true if found, false otherwise
 */
bool symtab_symbol_for_addr(uintptr_t addr, symbol_t *p_symbol);

/*
 * `symtab_label_for_addr`
 *
 * Determines label for instruction at address and write label
 * to `buf`, truncated to `bufsize`. If the symbol name is available,
 * label is of "<symbol_name+offset>". Without symbol names, the
 * basic label is "<.text+offset>" for an address within the text
 * section and "<>" otherwise.
 *
 * @param buf       destination buffer where to write label
 * @param bufsize   size of destination buffer (label truncated to fit if needed)
 * @param addr      addr to provide label for
 */
void symtab_label_for_addr(char *buf, size_t bufsize, uintptr_t addr);

#endif

#ifndef TIMER_H
#define TIMER_H

/*
 * Hardware abstraction for a timer.
 *
 * Students implement this module in assignment 2.
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#define TICKS_PER_USEC 24 // 24 ticks counted per one microsecond

/*
 * `timer_init`
 *
 * Initialize the timer. For assignment 2, this does nothing.
 * However, all peripheral modules require an init, so it is
 * included for consistency's sake.
 */
void timer_init(void);

/*
 * `timer_get_ticks`
 *
 * Returns the current system tick count. The tick count is initialized
 * to zero at boot and increments at constant rate 24Mhz until the
 * processor resets.
 *
 * @return  system tick count
 */
unsigned long timer_get_ticks(void);

/*
 * `timer_delay_us`
 *
 * Delay the program for `usec` microseconds in a busy loop.
 *
 * @param usec  the number of microseconds to delay
 */
void timer_delay_us(int usec);

/*
 * `timer_delay_ms`
 *
 * Delay for `msec` milliseconds.
 *
 * @param msec  the number of milliseconds to delay
 */
void timer_delay_ms(int msec);

/*
 * `timer_delay`
 *
 * Delay for `sec` seconds.
 *
 * @param sec   the number of seconds to delay
 */
void timer_delay(int sec);

#endif

#ifndef UART_H
#define UART_H

/*
 * Hardware abstraction for a serial port (UART).
 *
 * Author: Julie Zelenski <zelenski@cs.stanford.edu>
 */

#include <stdbool.h>
#include "gpio.h"
#include "interrupts.h"

// gpio pins for connecting uart TX/RX lines
#define UART_TX_GPIO GPIO_PB8
#define UART_RX_GPIO GPIO_PB9

/*
 * `uart_init`: Required initialization for module
 *
 * Initialize the UART module. A single call to `uart_init`
 * should made as part of the program initialization, before any
 * calls to other uart_ functions.  It is allowed, although unusual,
 * to call `uart_init` again to reset the UART state. A reset
 * will discard any pending data in the send/receive buffer.
 *
 * The uart requires exclusive use of GPIO pins PB8 (transmit)
 * and PB9 (receive).  Once the uart is initialized and in use,
 * your code should not further access these two pins.
 */
void uart_init(void);

/*
 * `uart_getchar`
 *
 * Obtains the next input character from the serial port and returns it.
 * If receive buffer is empty, will block until next character is received.
 *
 * @return    the character read or EOF if error or at end of input
 */
int uart_getchar(void);

/*
 * `uart_putchar`
 *
 * Outputs a character to the serial port.
 * If send buffer is full, will block until space available.
 *
 * @param ch   the character to write to the serial port
 * @return     the character written
 */
int uart_putchar(int ch);

/*
 * `uart_flush`
 *
 * Flushes any output characters pending in the send buffer.
 */
void uart_flush(void);

/*
 * `uart_haschar`
 *
 * Returns whether there is a character in the receive buffer.
 *
 * @return      true if character ready to be read, false otherwise
 */
bool uart_haschar(void);

/*
 * `uart_putstring`
 *
 * Outputs a string to the serial port by calling `uart_putchar`
 * on each character.
 *
 * @param str  the string to output
 * @return     the count of characters written or EOF if error
 */
int uart_putstring(const char *str);

/*
 * `uart_send`
 *
 * Outputs raw byte to the serial port. `uart_send` outputs the raw byte
 * with no translation (unlike `uart_putchar` which adds processing for
 * converting end-of-line markers). To send text character, use
 * `uart_putchar`; if raw binary data, use `uart_send`.
 *
 * @param byte   the byte to write to the serial port
 */
void uart_send(unsigned char byte);

/*
 * `uart_recv`
 *
 * Obtain raw byte from the serial port. `uart_recv` returns the raw
 * byte with no translation (unlike uart_getchar which adds processing
 * for converting end-of-line and end-of-file markers). To read text
 * character, use `uart_getchar`; if raw binary data, use `uart_recv`.
 *
 * @return   the byte read from the serial port
 */
unsigned char uart_recv(void);

/*
 * `uart_use_interrupts`
 *
 * @param handler   the handler function to call
 * @param client_data  to write to the serial port
 */
void uart_use_interrupts(handlerfn_t handler, void *client_data);

/*
 * `uart_start_error`,`uart_end_error`
 *
 * Output ANSI color code for red+bold on start error, restore to
 * normal on end error. Used by assert/error to highlight error
 * messages.
 */
void uart_start_error(void);
void uart_end_error(void);

#endif