James Heavey / Mbed 2 deprecated 2665-Breakout-Game

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
jamesheavey
Date:
Tue Jan 05 01:14:11 2021 +0000
Commit message:
test

Changed in this revision

Ball/Ball-test.h Show annotated file Show diff for this revision Revisions of this file
Ball/Ball.cpp Show annotated file Show diff for this revision Revisions of this file
Ball/Ball.h Show annotated file Show diff for this revision Revisions of this file
BreakoutEngine/BreakoutEngine.cpp Show annotated file Show diff for this revision Revisions of this file
BreakoutEngine/BreakoutEngine.h Show annotated file Show diff for this revision Revisions of this file
Brick/Brick-test.h Show annotated file Show diff for this revision Revisions of this file
Brick/Brick.cpp Show annotated file Show diff for this revision Revisions of this file
Brick/Brick.h Show annotated file Show diff for this revision Revisions of this file
FXOS8700CQ/FXOS8700CQ.cpp Show annotated file Show diff for this revision Revisions of this file
FXOS8700CQ/FXOS8700CQ.h Show annotated file Show diff for this revision Revisions of this file
Gamepad/Gamepad.cpp Show annotated file Show diff for this revision Revisions of this file
Gamepad/Gamepad.h Show annotated file Show diff for this revision Revisions of this file
Laser/Laser-test.h Show annotated file Show diff for this revision Revisions of this file
Laser/Laser.cpp Show annotated file Show diff for this revision Revisions of this file
Laser/Laser.h Show annotated file Show diff for this revision Revisions of this file
Life_Powerup/Life_Powerup.cpp Show annotated file Show diff for this revision Revisions of this file
Life_Powerup/Life_Powerup.h Show annotated file Show diff for this revision Revisions of this file
N5110/Bitmap.cpp Show annotated file Show diff for this revision Revisions of this file
N5110/Bitmap.h Show annotated file Show diff for this revision Revisions of this file
N5110/N5110.cpp Show annotated file Show diff for this revision Revisions of this file
N5110/N5110.h Show annotated file Show diff for this revision Revisions of this file
Paddle/Paddle.cpp Show annotated file Show diff for this revision Revisions of this file
Paddle/Paddle.h Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/FATFileSystem/ChaN/ccsbcs.cpp Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/FATFileSystem/ChaN/diskio.cpp Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/FATFileSystem/ChaN/diskio.h Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/FATFileSystem/ChaN/ff.cpp Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/FATFileSystem/ChaN/ff.h Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/FATFileSystem/ChaN/ffconf.h Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/FATFileSystem/ChaN/integer.h Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/FATFileSystem/FATDirHandle.cpp Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/FATFileSystem/FATDirHandle.h Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/FATFileSystem/FATFileHandle.cpp Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/FATFileSystem/FATFileHandle.h Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/FATFileSystem/FATFileSystem.cpp Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/FATFileSystem/FATFileSystem.h Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/FATFileSystem/MemFileSystem.h Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/SDFileSystem.cpp Show annotated file Show diff for this revision Revisions of this file
SDFileSystem/SDFileSystem.h Show annotated file Show diff for this revision Revisions of this file
Sprites/Sprites.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
tests.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Ball/Ball-test.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,49 @@
+#ifndef BALL_TEST_H
+#define BALL_TEST_H
+
+/**
+ * \brief Check that Ball object goes to correct position when moved
+ * 
+ * \returns true if all the tests passed
+ */
+bool Ball_test_movement()
+{
+    // Initialise Ball object with a size of 2, and speed of 2
+    Ball _ball;
+    _ball.init(2, 2);
+
+    // Set the position to 0, 0
+    Vector2D initial_pos = {0, 0};
+    _ball.set_pos(initial_pos);
+
+    // Read the position
+    Vector2D read_pos_1 = _ball.get_pos();
+    printf("%f, %f\n", read_pos_1.x, read_pos_1.y);
+
+    // Set the velocity to -2, 3
+    Vector2D velocity = {-2, 3};
+    _ball.set_velocity(velocity);
+
+    // Update the position
+    _ball.update();
+
+    // Read the position
+    Vector2D read_pos_2 = _ball.get_pos();
+    printf("%f, %f\n", read_pos_2.x, read_pos_2.y);
+    
+    // Now check that both the positions are as expected
+    bool success_flag = true;
+    
+    // Fail the test if the initial position is wrong
+    if (read_pos_1.x != 0 || read_pos_1.y != 0) {
+        success_flag = false;
+    }
+    
+    // Fail the test if the final position is wrong
+    if (read_pos_2.x != -2 || read_pos_2.y != 3) {
+        success_flag = false;
+    }
+
+    return success_flag;
+}
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Ball/Ball.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,72 @@
+#include "Ball.h"
+
+Ball::Ball()
+{
+
+}
+
+Ball::~Ball()
+{
+
+}
+
+void Ball::init(int size,int speed, int x)
+{
+    _size = size; // size of the ball
+
+    _x = x;   // init position of the ball will be at the centre of the paddle
+    _y = HEIGHT - _size/2 - 3; // fixed y init position, just above the paddle
+
+    srand(time(NULL));
+    int direction = rand() % 2; // randomise initial direction.
+
+    // 2 directions, both directed upwards (N) from the paddle
+    if (direction == 0) {
+        _velocity.x = speed; // direction = E
+        _velocity.y = -speed; // direction = N
+    } else {
+        _velocity.x = -speed; // direction = W
+        _velocity.y = -speed; // direction = N
+    }
+}
+
+
+void Ball::draw(N5110 &lcd)  // draw the ball at the specified location
+{
+    lcd.drawRect(_x,_y,_size,_size,FILL_BLACK);
+}
+
+
+void Ball::update()  // update the ball's position, based on its current velocity
+{
+    _x += _velocity.x;
+    _y += _velocity.y;
+}
+
+
+void Ball::set_velocity(Vector2D v) // set the velocity in its x/y directions (used for collision velocity correction)
+{
+    _velocity.x = v.x;
+    _velocity.y = v.y;
+}
+
+
+Vector2D Ball::get_velocity() // retrieve the ball's current velocities
+{
+    Vector2D v = {_velocity.x,_velocity.y};
+    return v;
+}
+
+
+Vector2D Ball::get_pos() // retrieve the ball's current coordinates
+{
+    Vector2D p = {_x,_y};
+    return p;
+}
+
+
+void Ball::set_pos(Vector2D p)  // set the ball's coordinates (used for collision velocity correction)
+{
+    _x = p.x;
+    _y = p.y;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Ball/Ball.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,73 @@
+#ifndef BALL_H
+#define BALL_H
+
+#include "mbed.h"
+#include "N5110.h"
+#include "Gamepad.h"
+#include "Paddle.h"
+
+#define BALL_SIZE 2
+#define BALL_SPEED 2
+
+/** Ball Class
+@author James Heavey, University of Leeds
+@brief Controls the ball in the Breakout game
+@date May 2019
+*/
+
+class Ball
+{ 
+
+public:
+    
+    /** Constructor declaration */
+    Ball();
+    
+    /** Destructor declaration */
+    ~Ball();
+    
+    /** Initialise Ball attributes
+    * @param size @details initialises _size
+    * @param speed @details initialises _speed
+    * @param x @details initialises the _x coordinate over the centre of the paddle
+    */
+    void init(int size,int speed,int x);
+    
+    /** Draws the Ball at at its current coordinates on the LCD
+    * @param lcd @details a N5110 pointer
+    */
+    void draw(N5110 &lcd);
+    
+    /** Update the Laser's x and y coordinates based on its velocity */
+    void update();
+
+    /** Set the Ball's x and y velocities 
+    * @param v @details a vector of the x and y velocities
+    */
+    void set_velocity(Vector2D v);
+    
+    /** Set the Ball's x and y coordinates
+    * @param p @details a vector of the x and y velocities
+    */
+    void set_pos(Vector2D p); 
+    
+    /** Retrieves the Ball's x and y velocities 
+    * @returns a vector of the x and y velocities
+    */
+    Vector2D get_velocity();
+    
+    /** Retrieves the Ball's x and y velocities 
+    * @returns a vector of the x and y velocities
+    */
+    Vector2D get_pos();
+    
+    
+
+private:
+
+    Vector2D _velocity;
+    int _size;
+    int _x;
+    int _y;
+};
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BreakoutEngine/BreakoutEngine.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,552 @@
+#include "BreakoutEngine.h"
+
+BreakoutEngine::BreakoutEngine()
+{
+
+}
+
+BreakoutEngine::~BreakoutEngine()
+{
+
+}
+
+void BreakoutEngine::init(int paddle_width,int paddle_height,int ball_size,int speed)
+{
+    // initialise the game parameters
+    _paddle_width = paddle_width;
+    _paddle_height = paddle_height;
+    _ball_size = ball_size; // size of the ball
+    _speed = speed; // speed of the ball
+    _number_left = 18; // number of bricks that remain on screen
+    _index = 0;  // index of which laser to move on screen at any time, starts at 0
+    _cool_time = 0.0f; // time remaining before laser can be fired again, starts at 0
+    _score = 0; // initialised as 0
+    _prev_score = 0;  // initialised as 0 for the first round
+    _multiplier = 0;  // initialised as 1 for the first round
+
+    // y position of the paddle on screen - HEIGHT is defined in N5110.h
+    _paddley = HEIGHT - GAP - 1;
+
+    // initialises paddles and ball in middle
+    _paddle.init(_paddley,_paddle_height,_paddle_width);
+    _ball.init(_ball_size,_speed,_paddle.get_pos().x + (PADDLE_WIDTH/2));
+
+    // initialises all the bricks in their set x/y positions
+    _brick11.init(3,GAP_TOP+1,3);
+    _brick12.init(16,GAP_TOP+1,3);
+    _brick13.init(29,GAP_TOP+1,3);
+    _brick14.init(42,GAP_TOP+1,3);
+    _brick15.init(55,GAP_TOP+1,3);
+    _brick16.init(68,GAP_TOP+1,3);
+
+    _brick21.init(3,GAP_TOP+BRICK_HEIGHT+2,2);
+    _brick22.init(16,GAP_TOP+BRICK_HEIGHT+2,2);
+    _brick23.init(29,GAP_TOP+BRICK_HEIGHT+2,2);
+    _brick24.init(42,GAP_TOP+BRICK_HEIGHT+2,2);
+    _brick25.init(55,GAP_TOP+BRICK_HEIGHT+2,2);
+    _brick26.init(68,GAP_TOP+BRICK_HEIGHT+2,2);
+
+    _brick31.init(3,GAP_TOP+1+((BRICK_HEIGHT+1)*2),1);
+    _brick32.init(16,GAP_TOP+1+((BRICK_HEIGHT+1)*2),1);
+    _brick33.init(29,GAP_TOP+1+((BRICK_HEIGHT+1)*2),1);
+    _brick34.init(42,GAP_TOP+1+((BRICK_HEIGHT+1)*2),1);
+    _brick35.init(55,GAP_TOP+1+((BRICK_HEIGHT+1)*2),1);
+    _brick36.init(68,GAP_TOP+1+((BRICK_HEIGHT+1)*2),1);
+
+    // appends all bricks to a list so that they are easier to check for collisions
+    listofBricks.push_back(_brick11);
+    listofBricks.push_back(_brick12);
+    listofBricks.push_back(_brick13);
+    listofBricks.push_back(_brick14);
+    listofBricks.push_back(_brick15);
+    listofBricks.push_back(_brick16);
+    listofBricks.push_back(_brick21);
+    listofBricks.push_back(_brick22);
+    listofBricks.push_back(_brick23);
+    listofBricks.push_back(_brick24);
+    listofBricks.push_back(_brick25);
+    listofBricks.push_back(_brick26);
+    listofBricks.push_back(_brick31);
+    listofBricks.push_back(_brick32);
+    listofBricks.push_back(_brick33);
+    listofBricks.push_back(_brick34);
+    listofBricks.push_back(_brick35);
+    listofBricks.push_back(_brick36);
+
+    // initialises lasers off screen
+    _laser1.init();
+    _laser2.init();
+    _laser3.init();
+
+    // appends lasers to a list so that they are easier to check for collisions
+    listofLasers.push_back(_laser1);
+    listofLasers.push_back(_laser2);
+    listofLasers.push_back(_laser3);
+
+    _powerup.init();
+}
+
+
+void BreakoutEngine::reset_game()  // resets the game after victory screen
+{
+    reset_num_left();   // resets _number_left to 18
+    _paddle.recentre();  // recentres the paddle on screen
+    //printf("Paddle lives = %d", _paddle.get_lives());
+    _ball.init(_ball_size,_speed + _multiplier/2, _paddle.get_pos().x + (PADDLE_WIDTH/2)); // resets the ball position to above the paddle, and increases its speed depending on the _multiplier
+    _powerup.set_posx(-50);
+
+    int pointer = 0;  // tracks the rows, if 6 bricks are placed, it moves to the next row
+    for (it_R = listofBricks.begin(); it_R != listofBricks.end(); ++it_R) {
+        if (pointer <= 5) {  // 6 bricks in the first row
+            it_R -> set_posx((pointer * 13) + 3);
+        } else if (pointer <= 11) {
+            it_R -> set_posx(((pointer-6) * 13) + 3);  // pointer - 6 to offset it back to the first column of bricks
+        } else if (pointer <= 17) {
+            it_R -> set_posx(((pointer-12) * 13) + 3);  // pointer - 12 to offset it back to the first column of bricks
+        }
+
+        it_R -> reset_lives(_multiplier);  // increases the bricks number of lives by the multiplier + their _base_lives
+        pointer ++;  // increment the pointer
+        //printf("pointer = %d",pointer);
+    }
+    for (it_L = listofLasers.begin(); it_L != listofLasers.end(); ++it_L) {
+        it_L -> set_posx(-10);  // moves all the lasers off screen
+    }
+}
+
+
+void BreakoutEngine::read_input(Gamepad &pad)
+{
+    _d = pad.get_direction();
+    _mag = pad.get_mag();
+
+    if (pad.check_event(Gamepad::B_PRESSED) && _cool_time <= 0) {  // if B is pressed and it is not cooling down then fire a laser
+
+        Vector2D p_pos = _paddle.get_pos();
+        it_L = listofLasers.begin();
+        switch(_index) {     // switch between the lasers each time the button is pressed so that it doesnt just re position the same laser each time
+            case 0:
+                advance(it_L, 0);
+                it_L -> set_posx(p_pos.x+7);  // move laser 1 to middle of paddle
+                it_L -> set_posy(p_pos.y);
+                inc_index();
+                //printf("index = %d",_index);
+                break;
+            case 1:
+                advance(it_L, 1);
+                it_L -> set_posx(p_pos.x+7);  // move laser 2 to middle of paddle
+                it_L -> set_posy(p_pos.y);
+                inc_index();
+                //printf("index = %d",_index);
+                break;
+            case 2:
+                advance(it_L, 2);
+                it_L -> set_posx(p_pos.x+7);  // move laser 3 to middle of paddle
+                it_L -> set_posy(p_pos.y);
+                //printf("index = %d",_index);
+                reset_index();
+                //printf("index = %d",_index);
+                break;
+        }
+
+        it_L = listofLasers.end();
+
+        _cool_time = 0.75f;  // reset cool time to 0.75 seconds
+    } else {
+        _cool_time -= 0.125; // reduce cool down time by 1/8 of a second as fps is 8  
+    }
+
+}
+
+
+void BreakoutEngine::draw(N5110 &lcd)
+{
+    lcd.drawRect(0,GAP_TOP - 2,WIDTH,HEIGHT - GAP_TOP + 2,FILL_TRANSPARENT); // draw the game screen dimensions
+    print_scores(lcd);  // print the score above the screen
+    _paddle.draw(lcd);  // draw the paddle
+    _ball.draw(lcd);  // draw the ball
+
+    for (it_R = listofBricks.begin(); it_R != listofBricks.end(); ++it_R) {  // draw all teh bricks
+        it_R->draw(lcd);
+    }
+    for (it_L = listofLasers.begin(); it_L != listofLasers.end(); ++it_L) {  // draw all the lasers
+        it_L->draw(lcd);
+    }
+
+    check_life_powerup();  // check if power up can be drawn, if so move on screen when random condition is met
+
+    _powerup.draw(lcd);   // draw the powerup
+}
+
+
+void BreakoutEngine::update(Gamepad &pad)
+{
+    check_loss(pad);  // check if the ball has gone above max y
+    
+    _paddle.update(_d,_mag);  // update paddle position
+    _ball.update();   // update ball position
+    _powerup.update();  // update powerup position
+
+    for (it_L = listofLasers.begin(); it_L != listofLasers.end(); ++it_L) { // update all the lasers' positions
+        it_L->update();
+    }
+
+    lives_leds(pad);  // update the LEDs with the remaining lives
+
+    check_wall_collisions(pad);  // check ball and laser collisions with walls and ceiling
+    check_paddle_collisions(pad);  // check ball collision with paddle
+    check_brick_collisions(pad);  // check ball collision with bricks
+    check_laser_collisions(pad);  // check all laser collisions with all bricks
+    check_powerup_collisions(pad);  // check powerup collision with paddle
+}
+
+
+void BreakoutEngine::lives_leds(Gamepad &pad)  // converst the number of lives left into LEDs
+{
+    if (_paddle.get_lives() == 0) {  
+        pad.leds_off();  // all LEDs off
+    }
+
+    else if (_paddle.get_lives() == 1) {
+        pad.leds_off();
+        pad.led(1,1);   // 1 LED on
+    }
+
+    else if (_paddle.get_lives() == 2) {
+        pad.leds_off();
+        pad.led(1,1);
+        pad.led(2,1); // 2 LEDs on
+    }
+
+    else if (_paddle.get_lives() == 3) {
+        pad.leds_off();
+        pad.led(1,1);
+        pad.led(2,1);
+        pad.led(3,1);  // 3 LEDs on
+    }
+
+    else if (_paddle.get_lives() == 4) {
+        pad.leds_on();
+        pad.led(5,0);
+        pad.led(6,0); // 4 LEDs on
+    }
+
+    else if (_paddle.get_lives() == 5) {
+        pad.leds_on();
+        pad.led(6,0);  // 5 LEDs on
+    }
+
+    else if (_paddle.get_lives() == 6) {
+        pad.leds_on();  // all LEDs on
+        //printf("all LEDs on \n");
+    }
+}
+
+
+void BreakoutEngine::check_wall_collisions(Gamepad &pad)  // checks ball and laser collisions with the walls and ceiling
+{
+    // read current ball attributes
+    Vector2D ball_pos = _ball.get_pos();
+    Vector2D ball_velocity = _ball.get_velocity();
+    
+    // bounce off the ceiling
+    if (ball_pos.y <= GAP_TOP - 1) {  //  1 due to 1 pixel boundary
+        ball_pos.y = GAP_TOP - 1;  
+        ball_velocity.y = -ball_velocity.y;
+        pad.tone(750.0,0.1);
+    }
+    
+    // bounce off the left
+    else if (ball_pos.x <= 1) {  //  1 due to 1 pixel boundary
+        ball_pos.x = 1;  // bounce off walls 
+        ball_velocity.x = -ball_velocity.x;
+        pad.tone(750.0,0.1);
+    }
+
+    // bounce off the right
+    else if (ball_pos.x + _ball_size >= (WIDTH-1) ) { // bottom pixel is 47
+        // hit bottom
+        ball_pos.x = (WIDTH-1) - _ball_size;  // stops ball going off screen
+        ball_velocity.x = -ball_velocity.x;
+        // audio feedback
+        pad.tone(750.0,0.1);
+    }
+
+    // update ball parameters
+    _ball.set_velocity(ball_velocity);
+    _ball.set_pos(ball_pos);
+
+    for (it_L = listofLasers.begin(); it_L != listofLasers.end(); ++it_L) { // checks every laser
+        if (
+            (it_L -> get_y() <= GAP_TOP - 2) // if it hits the ceiling 
+        ) {    
+            it_L -> set_posx(-10);  // set its x coordinate off screen
+        }
+    }
+
+}
+
+
+void BreakoutEngine::check_paddle_collisions(Gamepad &pad)
+{
+    // read current ball attributes
+    Vector2D ball_pos = _ball.get_pos();
+    Vector2D ball_velocity = _ball.get_velocity();
+
+    // read the paddle position
+    Vector2D paddle_pos = _paddle.get_pos();
+
+    // check if ball overlaps with the paddle
+    if (
+        (ball_pos.x >= paddle_pos.x) && //left
+        (ball_pos.x <= paddle_pos.x + _paddle_width) && //right
+        (ball_pos.y >= _paddley) && //bottom
+        (ball_pos.y <= _paddley + _paddle_height)  //top
+    ) {    
+        pad.tone(1000.0,0.1);
+        ball_pos.y = _paddley + _paddle_height - 1;
+        ball_velocity.y = -ball_velocity.y;
+        
+        // below is a method to control the ball reflect angle based on where it hits the paddle
+        // however cannot be used as it does not work with collisions due to varying angles and speeds
+        // would work with greater pixel density in the screen
+
+//        if (ball_pos.x == paddle_pos.x + PADDLE_WIDTH/2) {     // check ballxpos in relation to paddle xpos. translate the distance from the centre to an angle between 30 and 60 degrees in that direction
+//            ball_pos.y = _paddley + _paddle_height - 1;
+//            ball_velocity.y = -ball_velocity.y;
+//        }
+//        else if (ball_pos.x <= paddle_pos.x + PADDLE_WIDTH/2) {
+//            float ang = 40*(((paddle_pos.x + PADDLE_WIDTH/2)-ball_pos.x)/(PADDLE_WIDTH/2 - paddle_pos.x)) + 30;  //converts the distance from the centre to an angle between 30 and 60
+//            if (ball_velocity.x > 0) {
+//                ball_velocity.x = -ball_velocity.x;
+//            }
+//           ball_pos.y = _paddley + _paddle_heigh - 1;
+//           ball_velocity.y = -tan(ang);
+//       }
+//       else if (ball_pos.x >= paddle_pos.x + PADDLE_WIDTH/2) {
+//           float ang = 40*(((paddle_pos.x + PADDLE_WIDTH/2)-ball_pos.x)/(PADDLE_WIDTH/2 - paddle_pos.x)) + 30;  //converts the distance from the centre to an angle between 30 and 60
+//           if (ball_velocity.x < 0) {
+//               ball_velocity.x = -ball_velocity.x;
+//           }
+//           ball_pos.y = _paddley + _paddle_height - 1;
+//           ball_velocity.y = -tan(ang);
+//       }
+    }
+
+    // write new attributes
+    _ball.set_velocity(ball_velocity);
+    _ball.set_pos(ball_pos);
+}
+
+void BreakoutEngine::check_brick_collisions(Gamepad &pad)
+{
+    // read current ball attributes
+    Vector2D ball_pos = _ball.get_pos();
+    Vector2D ball_velocity = _ball.get_velocity();
+
+    for (it_R = listofBricks.begin(); it_R != listofBricks.end(); ++it_R) {
+        if (
+            (ball_pos.x >= it_R -> get_x()) && //left
+            (ball_pos.x <= it_R -> get_x() + BRICK_WIDTH) && //right
+            (ball_pos.y >= it_R -> get_y()) && //bottom
+            (ball_pos.y <= it_R -> get_y() + BRICK_HEIGHT)  //top
+        ) {    
+            if (ball_velocity.y < 0) {  // if velocity is negative, it has hit the bottom, therefore correct its pos to the bottom face
+                ball_pos.y = it_R -> get_y() + BRICK_HEIGHT;
+            } else {  // else it has hit the top, correct its position to the top face
+                ball_pos.y = it_R -> get_y();   
+            }
+            ball_velocity.y = -ball_velocity.y;  // invert the balls y velocity
+            // audio feedback
+            pad.tone(1000.0,0.1);  
+            it_R -> hit();  
+            if(it_R-> hit() == true) {  // hit the brick, remove 2 lives from it and check if destroyed
+                it_R -> set_posx(-100);  // if destroyed, move off screen
+                dec_num_left();  // decrement the number of bricks left on screen
+            }
+        }
+    }
+    // write new attributes
+    _ball.set_velocity(ball_velocity);
+    _ball.set_pos(ball_pos);
+}
+
+
+void BreakoutEngine::check_laser_collisions(Gamepad &pad)
+{
+    for (it_L = listofLasers.begin(); it_L != listofLasers.end(); ++it_L) {   
+        for (it_R = listofBricks.begin(); it_R != listofBricks.end(); ++it_R) {  // check every laser against every brick
+            if (
+                (it_L -> get_x() >= it_R -> get_x()) && //left
+                (it_L -> get_x() <= it_R -> get_x() + BRICK_WIDTH) && //right
+                (it_L -> get_y() >= it_R -> get_y()) && //bottom
+                (it_L -> get_y() <= it_R -> get_y() + BRICK_HEIGHT)  //top
+            ) {  
+                it_L -> set_posx(-10);    // if they overlap/collide, move the laser off screen
+                // audio feedback
+                pad.tone(1000.0,0.1);  
+                if(it_R-> hit() == true) {   // remove a life from the brick, if its destroyed, move it off screen
+                    it_R -> set_posx(-100);
+                    dec_num_left();  // decrement the number of bricks remaining on screen
+                }
+            }
+        }
+    }
+}
+
+
+void BreakoutEngine::check_powerup_collisions(Gamepad &pad)  // checks powerup collisions with the paddle and the bottom of the screen
+{
+    // read the current position of the paddle
+    Vector2D paddle_pos = _paddle.get_pos();  
+    
+    if (
+        (_powerup.get_x() >= paddle_pos.x) && //left
+        (_powerup.get_x() + 8 <= paddle_pos.x + _paddle_width) && //right
+        (_powerup.get_y() + 8 >= paddle_pos.y) // top
+    ) {   
+        pad.tone(2500.0,0.1);
+        _powerup.set_posx(-50);   // if the paddle and power up overlap, move the powerup off screen
+        _paddle.inc_life();   // increment the numebr of lives left on the paddle
+    }
+
+    else if (
+        (_powerup.get_y() +9 >= HEIGHT)  // bottom of screen
+    ) {    
+        _powerup.set_posx(-50);   // if the powerup reaches the bottom of the screen, move it off screen
+    }
+}
+
+
+bool BreakoutEngine::check_loss(Gamepad &pad)  // check if the ball has hit the bottom of the screen
+{
+    // read the current ball position
+    Vector2D ball_pos = _ball.get_pos();
+    
+    if (ball_pos.y > HEIGHT) {  // if it has gone off screen
+        _paddle.lose_life();   // decrement the number of lives left
+
+        _ball.init(_ball_size,_speed+_multiplier/2,_paddle.get_pos().x + (PADDLE_WIDTH/2));  // re initialise the ball above the paddle
+        pad.tone(1500.0,0.5);
+        return true;     // return true, to be used to flash the screen in the main
+    } else {
+        return false;    
+    }
+}
+
+
+void BreakoutEngine::set_paddle_motion(bool tilt, float sens)  // sets the motion options for the paddle
+{
+    if (tilt == true) {
+        _paddle.set_tilt();  // sets tilt
+    } else {
+        _paddle.set_joy();  // sets joystick
+    }
+    _paddle.set_sens(sens);  // sets sensitivity
+}
+
+
+void BreakoutEngine::print_scores(N5110 &lcd)
+{
+    int score = _prev_score + (18 - get_num_left())*100 * (_multiplier + 1);  // current score depends on number left, the multiplier and the previous score
+    _score = score; // set the member variable _score
+    
+    char buffer[14];
+    sprintf(buffer,"%2d",score); // put score in buffer
+    lcd.printString("SCORE: ",2,0);
+    lcd.printString(buffer,WIDTH/2 -2,0);  // print buffer on LCD
+}
+
+
+void BreakoutEngine::check_life_powerup()
+{
+    srand(time(0));  // Initialize random number generator.
+    int r1 = (rand() % 25) + 1; 
+    int r2 = (rand() % 25) + 1;   // 2 random number between 1 and 26
+    int rx = (rand() % 60) + 11;   // generate a random x coordinate for the powerup to be place
+    //printf("r1 = %d",r1);
+    //printf("r2 = %d",r2);
+    //printf("rx = %d",rx);
+    if ((get_paddle_lives() < 6) & (r1 == r2)) {   // if lives are not max, and the random condition is met
+        _powerup.set_posx(rx);   // set the powerup x
+        _powerup.set_posy(HEIGHT/2);  // set the y at the middle of the screen
+    }
+}
+
+
+int BreakoutEngine::get_prev_score()
+{
+    return _prev_score;  // return the previous score
+}
+
+
+void BreakoutEngine::set_prev_score(int prev_score)
+{
+    _prev_score = prev_score;  // set the member variable _prev_score
+}
+
+
+int BreakoutEngine::get_num_left()
+{
+    return _number_left;  // return the number of bricks left on screen
+}
+
+
+void BreakoutEngine::dec_num_left()
+{
+    _number_left -= 1;  // decrement the member variable _number_left when a brick is destroyed
+}
+
+
+void BreakoutEngine::reset_num_left()
+{
+    _number_left = 18;  // reset the number left to 18 when the game is reset
+}
+
+
+int BreakoutEngine::get_score()
+{
+    return _score;  // return the member variable _score
+}
+
+
+void BreakoutEngine::inc_mult()
+{
+    _multiplier ++;   // increment the _multiplier when continue is selected upon victory
+}
+
+
+int BreakoutEngine::get_mult()
+{
+    return _multiplier;   // retrieve the multiplier value
+}
+
+
+void BreakoutEngine::reset_mult()
+{
+    _multiplier = 0;    // reset the multiplier to 0
+}
+
+
+void BreakoutEngine::inc_index()
+{
+    _index ++;    // increment the laser index
+}
+
+
+void BreakoutEngine::reset_index()
+{
+    _index = 0;  // reset the laser index
+}
+
+
+void BreakoutEngine::reset_paddle_lives()
+{
+    _paddle.reset_lives();  // resets paddle lives
+}
+
+
+int BreakoutEngine::get_paddle_lives()
+{
+    return _paddle.get_lives();    // return the number of lives left
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BreakoutEngine/BreakoutEngine.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,198 @@
+#ifndef BREAKOUTENGINE_H
+#define BREAKOUTENGINE_H
+
+#include "mbed.h"
+#include "N5110.h"
+#include "Gamepad.h"
+#include "Ball.h"
+#include "Paddle.h"
+#include "Brick.h"
+#include "Laser.h"
+#include "Life_Powerup.h"
+
+#include <list>
+#include <iterator>
+#include <ctime>    // For time()
+#include <cstdlib>  // For srand() and rand()
+
+#define GAP_TOP 10
+#define GAP 2
+
+/** BreakoutEngine Class
+@author James Heavey, University of Leeds
+@brief Controls the Breakout game
+@date May 2019
+*/
+
+class BreakoutEngine
+{
+
+public:
+
+    /** Constructor declaration */
+    BreakoutEngine();
+    
+    /** Destructor declaration */
+    ~BreakoutEngine();
+    
+    /** Initialises game variables
+    * @param paddle_width @details integer width of paddle
+    * @param paddle_height @details integer height of paddle
+    * @param ball_size @details integer diameter of ball in pixels
+    * @param speed @details integer initial speed of ball
+    */
+    void init(int paddle_width,int paddle_height,int ball_size,int speed);
+    
+    /** Reads inputs from the Gamepad
+    * @param pad @details a Gamepad pointer
+    */
+    void read_input(Gamepad &pad);
+    
+    /** Updates the Game attributes
+    * @param pad @details a Gamepad pointer
+    */
+    void update(Gamepad &pad);
+    
+    /** Draws the objects' at their respective coordinates on the LCD
+    * @param lcd @details a N5110 pointer
+    */
+    void draw(N5110 &lcd);
+    
+    /** Updates the Gamepad LEDs with lives remaining
+    * @param pad @details a Gamepad pointer
+    */
+    void lives_leds(Gamepad &pad);
+    
+    /** Sets the variable _prev_score to current score
+    * @param prev_score @details the new previous score which is the score achieved at victory
+    */
+    void set_prev_score(int prev_score);
+    
+    /** Increments the laser index */
+    void inc_index();
+    
+    /** Resets the laser index to 0 */
+    void reset_index();
+    
+    /** Returns the game (and all relevant objects) to initialised state  */
+    void reset_game();
+    
+    /** Sets the variable _number_left to 18 */
+    void reset_num_left();
+    
+    /** Increment the multiplier if continue is selected upon victory */
+    void inc_mult();
+    
+    /** Resets the multiplier to 0
+    * @returns _prev_score
+    */
+    void reset_mult();
+    
+    /** Sets the paddle motion options for use in game
+    * @param tilt @details a bool that sets tilt if true and joystick if false
+    * @param sens @details a float that is derived from the pot, it multiplies the velocity of the paddle
+    */
+    void set_paddle_motion(bool tilt, float sens);
+    
+    /** Sets the powerup to a random x position on screen if conditions are met */
+    void check_life_powerup();
+    
+    /** Resets the paddle lives to 6, used to reset the game  */
+    void reset_paddle_lives();
+    
+    /** Decrements the number of bricks left on screen when one is destroyed  */
+    void dec_num_left();
+    
+    /** Returns the number of Bricks remaining on screen
+    * @returns _number_left
+    */
+    int get_num_left();
+    
+    /** Returns the score achieved on victory in the previous iteration of the game
+    * @returns _prev_score
+    */
+    int get_prev_score();
+    
+    /** Returns the number of lives remaining 
+    * @returns the paddle variable _lives
+    */
+    int get_paddle_lives();
+    
+    /** Returns the current multiplier value
+    * @returns _multiplier
+    */
+    int get_mult();
+    
+    /** Checks if the ball goes past the screen y boundary
+    * @param pad @details a Gamepad pointer
+    * @returns a bool; true if ball is off screen, false
+    */
+    bool check_loss(Gamepad &pad);
+    
+    /** Returns the current score to print to LCD in the game
+    * @returns _score
+    */
+    int get_score();
+
+private:
+
+    void check_wall_collisions(Gamepad &pad);  
+    void check_paddle_collisions(Gamepad &pad);
+    void check_brick_collisions(Gamepad &pad);
+    void check_laser_collisions(Gamepad &pad);
+    void check_powerup_collisions(Gamepad &pad);
+    void print_scores(N5110 &lcd);
+
+    int _paddle_width;
+    int _paddle_height;
+    int _ball_size;
+    int _speed;
+    int _index;
+    int _multiplier;
+    int _paddley;
+    int _number_left;
+    int _prev_score;
+    int _score;
+    double _cool_time;
+    
+    Direction _d;
+    float _mag;
+
+    std::list<Laser> listofLasers;
+    std::list<Laser>::iterator it_L;
+
+    std::list<Brick> listofBricks;
+    std::list<Brick>::iterator it_R;
+    
+    Paddle _paddle;
+
+    Ball _ball;
+
+    Brick _brick11;
+    Brick _brick12;
+    Brick _brick13;
+    Brick _brick14;
+    Brick _brick15;
+    Brick _brick16;
+    Brick _brick21;
+    Brick _brick22;
+    Brick _brick23;
+    Brick _brick24;
+    Brick _brick25;
+    Brick _brick26;
+    Brick _brick31;
+    Brick _brick32;
+    Brick _brick33;
+    Brick _brick34;
+    Brick _brick35;
+    Brick _brick36;
+
+    Laser _laser1;
+    Laser _laser2;
+    Laser _laser3;
+
+    Life_Powerup _powerup;
+
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Brick/Brick-test.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,45 @@
+#ifndef BRICK_TEST_H
+#define BRICK_TEST_H
+
+/**
+ * \brief Check that Brick object goes to correct position when moved
+ *
+ * \returns true if all the tests passed
+ */
+bool Brick_test_movement()
+{
+    // Initialise Brick object at (2,2) with 2 lives
+    Brick _brick;
+    _brick.init(2, 2, 2);
+    int read_pos1x = _brick.get_x();
+    int read_pos1y = _brick.get_y();
+
+    // Read the position
+    printf("%f, %f\n", read_pos1x, read_pos1y);
+
+    // Set the x position to 10, 0
+    _brick.set_posx(10);
+
+    // Update the position
+    _brick.update();
+
+    int read_pos2x = _brick.get_x();
+    int read_pos2y = _brick.get_y();
+
+    // Read the position
+    printf("%f, %f\n", read_pos2x, read_pos2y);
+
+    // Now check that both the positions are as expected
+    bool success_flag = true;
+
+    // Fail the test if the initial position is wrong
+    if (read_pos1x != 0 || read_pos1y != 0) {
+        success_flag = false;
+    }
+    if (read_pos2x != 10 || read_pos2y != 0) {
+        success_flag = false;
+    }
+
+    return success_flag;
+}
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Brick/Brick.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,71 @@
+#include "Brick.h"
+
+Brick::Brick()
+{
+
+}
+
+Brick::~Brick()
+{
+
+}
+
+void Brick::init(int x,int y, int lives) // initialise the Brick object at the specified x, y coordinate and with the specified number of lives
+{
+    _x = x;    // x value on screen is fixed and must be defined at initialisation
+    _y = y;    // y value on screen is fixed and must be defined at initialisation
+    _height = BRICK_HEIGHT; // set brick height and width defined in Brick.h
+    _width = BRICK_WIDTH;
+    _lives = lives;     // lives set at initialisation, will be decremented upon collisions
+    _base_lives = lives;    // _base_lives variable used to reset the bricks to the number of lives specified at init
+
+}
+
+
+void Brick::draw(N5110 &lcd) // draw the brick on the LCD
+{
+    if (_x >= 0) {    // only draw if on screen (more efficient?)
+        if (_lives <= 2) {
+            lcd.drawRect(_x,_y,_width,_height,FILL_TRANSPARENT);   // if lives < 3 then draw a hollow brick
+        } else if (_lives <= 4) {
+            lcd.drawRect(_x,_y,_width,_height,FILL_BLACK);
+            lcd.drawRect(_x + 3, _y + 1,_width-6,_height-2,FILL_WHITE);   // if lives < 5 then fill up the brick a little bit (less hollow)
+        } else {
+            lcd.drawRect(_x,_y,_width,_height,FILL_BLACK);   // if lives >= 5 then draw a fully black brick
+        }
+    }
+}
+
+
+bool Brick::hit()  // decrement the number of lives by one (triggered by collisions), returns a bool
+{
+    _lives = _lives - 1; // decrement lives
+    if (_lives <= 0) {
+        return true;     // if lives <=0 brick is destroyed and will need to be moved off screen
+    } else {
+        return false;    // else the brick is still alive and can remain where it is
+    }
+}
+
+
+void Brick::reset_lives(int inc)  // reset the lives after victory, takes inc
+{
+    _lives = _base_lives + inc;   // lives goes back to what it was at init plus 1 for every victory screen (inc = _multiplier in the breakout engine)
+}
+
+int Brick::get_x()   // return the brick's x coordinate (broken up into individual coordinates so it is easier to work with for list iterators)
+{
+    return _x;
+}
+
+
+int Brick::get_y()   // return the brick's y coordinate
+{
+    return _y;
+}
+
+
+void Brick::set_posx(int x) // set the brick's x coordinate (used to move it off and on screen). y is unneccessary
+{
+    _x = x;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Brick/Brick.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,74 @@
+#ifndef BRICK_H
+#define BRICK_H
+
+#include "mbed.h"
+#include "N5110.h"
+#include "Gamepad.h"
+
+#define BRICK_WIDTH 12
+#define BRICK_HEIGHT 4
+
+/** Brick Class
+@author James Heavey, University of Leeds
+@brief Controls the bricks in the Breakout game
+@date May 2019
+*/
+
+class Brick
+{
+public:
+
+    /** Constructor declaration */
+    Brick();
+    
+    /** Destructor declaration */
+    ~Brick();
+    
+    /** Initialise Brick attributes
+    * @param x @details initialises x coordinate
+    * @param y @details initialises y coordinate
+    * @param lives @details initialises the number of lives
+    */
+    void init(int x,int y, int lives);
+    
+    /** Draws the Brick on the LCD, at current coordinates with a fill that is dependant on _lives
+    * @param lcd @details a N5110 pointer
+    */
+    void draw(N5110 &lcd);
+    
+    /** Sets the Brick's x coordinate
+    * @param x @details set the  variable _x to the new local x
+    */
+    void set_posx(int x);
+    
+    /** Resets the lives after victory
+    * @param inc @details additional lives added on t o the _base_lives (additional lives come from _multiplier)
+    */
+    void reset_lives(int inc);
+    
+    /** Decrements the  variable _lives
+    * @returns a bool; true if brick is destroyed, false if not
+    */
+    bool hit();
+    
+    /** Retrieves the Brick's x coordinate
+    * @returns integer _x coordinate
+    */
+    int get_x();
+    
+    /** Retrieves the Brick's y coordinate
+    * @returns integer _y coordinate
+    */
+    int get_y();
+
+private:
+
+    int _height;
+    int _width;
+    int _x;
+    int _y;
+    int _lives;
+    int _base_lives;
+
+};
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FXOS8700CQ/FXOS8700CQ.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,176 @@
+/* FXOS8700CQ Library
+
+Sample code from ELEC2645 - demonstrates how to create a library
+for the K64F on-board accelerometer and magnetometer
+
+(c) Craig A. Evans, University of Leeds, Jan 2017
+
+*/ 
+
+#include "FXOS8700CQ.h"
+
+// constructor is called when the object is created - use it to set pins and frequency
+FXOS8700CQ::FXOS8700CQ(PinName sda, PinName scl)
+{
+    i2c = new I2C(sda,scl);        // create new I2C instance and initialise
+}
+
+// destructor is called when the object goes out of scope
+FXOS8700CQ::~FXOS8700CQ()
+{
+    delete i2c;        // free memory
+}
+
+// based on 13.4 in datasheet - 200 Hz hybrid mode (both acc and mag)
+void FXOS8700CQ::init()
+{
+    // i2c fast-mode - 10.1.1 data sheet
+    i2c->frequency(400000);       // I2C Fast Mode - 400kHz
+
+    // the device has an ID number so we check the value to ensure the correct
+    // drive is on the i2c bus
+    char data = read_byte_from_reg(FXOS8700CQ_WHO_AM_I);
+    if (data != FXOS8700CQ_WHO_AM_I_VAL) { // if correct ID not found, hang and flash error message
+        error("Incorrect ID!");
+    }
+
+    // write 0000 0000 = 0x00 to accelerometer control register 1 to place
+    // FXOS8700CQ into standby
+    // [7-1] = 0000 000
+    // [0]: active=0
+    data = 0x00;
+    send_byte_to_reg(data,FXOS8700CQ_CTRL_REG1);
+
+    // write 0001 1111 = 0x1F to magnetometer control register 1
+    // [7]: m_acal=0: auto calibration disabled
+    // [6]: m_rst=0: no one-shot magnetic reset
+    // [5]: m_ost=0: no one-shot magnetic measurement
+    // [4-2]: m_os=111=7: 8x oversampling (for 200Hz) to reduce magnetometer noise
+    // [1-0]: m_hms=11=3: select hybrid mode with accel and magnetometer active
+    data = 0x1F;
+    send_byte_to_reg(data,FXOS8700CQ_M_CTRL_REG1);
+
+    // write 0010 0000 = 0x20 to magnetometer control register 2
+    // [7]: reserved
+    // [6]: reserved
+    // [5]: hyb_autoinc_mode=1 to map the magnetometer registers to follow
+    // the accelerometer registers
+    // [4]: m_maxmin_dis=0 to retain default min/max latching even though not used
+    // [3]: m_maxmin_dis_ths=0
+    // [2]: m_maxmin_rst=0
+    // [1-0]: m_rst_cnt=00 to enable magnetic reset each cycle
+    data = 0x20;
+    send_byte_to_reg(data,FXOS8700CQ_M_CTRL_REG2);
+
+    // write 0000 0001= 0x01 to XYZ_DATA_CFG register
+    // [7]: reserved
+    // [6]: reserved
+    // [5]: reserved
+    // [4]: hpf_out=0
+    // [3]: reserved
+    // [2]: reserved
+    // [1-0]: fs=01 for accelerometer range of +/-4g range with 0.488mg/LSB
+    data = 0x01;
+    send_byte_to_reg(data,FXOS8700CQ_XYZ_DATA_CFG);
+
+    // write 0000 1101 = 0x0D to accelerometer control register 1
+    // [7-6]: aslp_rate=00
+    // [5-3]: dr=001 for 200Hz data rate (when in hybrid mode)
+    // [2]: lnoise=1 for low noise mode
+    // [1]: f_read=0 for normal 16 bit reads
+    // [0]: active=1 to take the part out of standby and enable sampling
+    data = 0x0D;
+    send_byte_to_reg(data,FXOS8700CQ_CTRL_REG1);
+
+}
+
+Data FXOS8700CQ::get_values()
+{
+    // 13 bytes - status plus 6 channels (2 bytes each)
+    // x,y,z for accelerometer and magnetometer
+    char data[FXOS8700CQ_READ_LEN];
+    read_bytes_from_reg(FXOS8700CQ_STATUS,FXOS8700CQ_READ_LEN,data);
+
+    // copy the 14 bit accelerometer byte data into 16 bit words
+    int acc_x = (int16_t)(((data[1] << 8) | data[2]))>> 2;
+    int acc_y = (int16_t)(((data[3] << 8) | data[4]))>> 2;
+    int acc_z = (int16_t)(((data[5] << 8) | data[6]))>> 2;
+
+    // copy the magnetometer byte data into 16 bit words
+    int mag_x = (int16_t) (data[7] << 8) | data[8];
+    int mag_y = (int16_t) (data[9] << 8) | data[10];
+    int mag_z = (int16_t) (data[11] << 8) | data[12];
+
+    Data values;  // struct to hold values
+
+    // 0.488 mg/LSB in 4 g mode (8.1 data sheet)
+    values.ax = 0.488e-3*acc_x;
+    values.ay = 0.488e-3*acc_y;
+    values.az = 0.488e-3*acc_z;
+
+    // the magnetometer sensitivity is fixed at 0.1 μT/LSB
+    values.mx = 0.1e-6*mag_x;
+    values.my = 0.1e-6*mag_y;
+    values.mz = 0.1e-6*mag_z;
+
+    return values;
+}
+
+void FXOS8700CQ::send_byte_to_reg(char byte,char reg)
+{
+    char data[2];
+    data[0] = reg;
+    data[1] = byte;
+    // send the register address, followed by the data
+    int nack = i2c->write(FXOS8700CQ_ADDR,data,2);
+    if (nack)
+        error("No acknowledgement received!");  // if we don't receive acknowledgement, send error message
+
+}
+
+// reads a byte from a specific register
+char FXOS8700CQ::read_byte_from_reg(char reg)
+{
+    int nack = i2c->write(FXOS8700CQ_ADDR,&reg,1,true);  // send the register address to the slave
+    // true as need to send repeated start condition (5.10.1 datasheet)
+    // http://www.i2c-bus.org/repeated-start-condition/
+    if (nack)
+        error("No acknowledgement received!");  // if we don't receive acknowledgement, send error message
+
+    char rx;
+    nack = i2c->read(FXOS8700CQ_ADDR,&rx,1);  // read a byte from the register and store in buffer
+    if (nack)
+        error("No acknowledgement received!");  // if we don't receive acknowledgement, send error message
+
+    return rx;
+}
+
+// reads a series of bytes, starting from a specific register
+void FXOS8700CQ::read_bytes_from_reg(char reg,int number_of_bytes,char bytes[])
+{
+    int nack = i2c->write(FXOS8700CQ_ADDR,&reg,1,true);  // send the slave write address and the configuration register address
+    // true as need to send repeated start condition (5.10.1 datasheet)
+    // http://www.i2c-bus.org/repeated-start-condition/
+
+    if (nack)
+        error("No acknowledgement received!");  // if we don't receive acknowledgement, send error message
+
+    nack = i2c->read(FXOS8700CQ_ADDR,bytes,number_of_bytes);  // read bytes
+    if (nack)
+        error("No acknowledgement received!");  // if we don't receive acknowledgement, send error message
+
+}
+
+float FXOS8700CQ::get_roll_angle()
+{
+    Data values = get_values();
+    float roll_rad = atan2(values.ay,values.az);
+    return roll_rad*(360/3.14159265);
+}
+
+float FXOS8700CQ::get_pitch_angle()
+{
+    Data values = get_values();
+    float pitch_rad = atan2(-values.ax,sqrt((values.ay * values.ay) + (values.az * values.az)));
+    return pitch_rad*(360/3.14159265);
+} 
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FXOS8700CQ/FXOS8700CQ.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,87 @@
+/** @file FXOS8700CQ.h
+
+@ brief FXOS8700CQ Library
+
+@author Dr Craig A. Evans
+@brief (c) University of Leeds, Jan 2017
+
+@code
+
+#include "mbed.h"
+#include "FXOS8700CQ.h"
+
+// create object and specifiy pins
+FXOS8700CQ device(I2C_SDA,I2C_SCL);
+
+int main()
+{
+    // call initialisation method
+    device.init();
+
+    while (1) {
+        
+        // poll the sensor and get the values, storing in a struct
+        Data values = device.get_values();
+        
+        // print each struct member over serial
+        printf("ax = %f ay = %f az = %f | mx = %f my = %f mz = %f\n"
+               ,values.ax, values.ay, values.az
+               ,values.mx, values.my, values.mz);
+        
+        wait(0.5);
+    }
+}
+
+@endcode
+
+*/
+
+#ifndef FXOS8700CQ_H
+#define FXOS8700CQ_H
+
+#include "mbed.h"
+
+// mbed API uses 8-bit addresses so need to left-shift 7-bit addresses by 1
+#define FXOS8700CQ_ADDR   (0x1D << 1)    // for K64F board
+// values from 13.2 datasheet
+#define FXOS8700CQ_STATUS 0x00
+#define FXOS8700CQ_WHO_AM_I 0x0D
+#define FXOS8700CQ_XYZ_DATA_CFG 0x0E
+#define FXOS8700CQ_CTRL_REG1 0x2A
+#define FXOS8700CQ_M_CTRL_REG1 0x5B
+#define FXOS8700CQ_M_CTRL_REG2 0x5C
+#define FXOS8700CQ_WHO_AM_I_VAL 0xC7
+#define FXOS8700CQ_READ_LEN 13
+
+#define PI 3.14159265359f
+#define RAD2DEG 57.2957795131f
+
+struct Data {
+    float ax;
+    float ay;
+    float az;
+    float mx;
+    float my;
+    float mz;
+};
+
+class FXOS8700CQ
+{
+
+public:
+    FXOS8700CQ(PinName sda, PinName scl);
+    ~FXOS8700CQ();
+    void init();
+    Data get_values();
+    float get_pitch_angle();
+    float get_roll_angle();
+
+private:
+    I2C* i2c;
+
+    void send_byte_to_reg(char byte,char reg);
+    char read_byte_from_reg(char reg);
+    void read_bytes_from_reg(char reg,int number_of_bytes,char bytes[]);
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Gamepad/Gamepad.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,354 @@
+#include "Gamepad.h"
+
+#include "mbed.h"
+
+//////////// constructor/destructor ////////////
+Gamepad::Gamepad()
+    :
+    _led1(new PwmOut(PTA1)),
+    _led2(new PwmOut(PTA2)),
+    _led3(new PwmOut(PTC2)),
+    _led4(new PwmOut(PTC3)),
+    _led5(new PwmOut(PTC4)),
+    _led6(new PwmOut(PTD3)),
+
+    _button_A(new InterruptIn(PTB9)),
+    _button_B(new InterruptIn(PTD0)),
+    _button_X(new InterruptIn(PTC17)),
+    _button_Y(new InterruptIn(PTC12)),
+    _button_L(new InterruptIn(PTB18)),
+    _button_R(new InterruptIn(PTB3)),
+    _button_back(new InterruptIn(PTB19)),
+    _button_start(new InterruptIn(PTC5)),
+    _button_joystick(new InterruptIn(PTC16)),
+
+    _vert(new AnalogIn(PTB10)),
+    _horiz(new AnalogIn(PTB11)),
+
+    _buzzer(new PwmOut(PTC10)),
+    _pot(new AnalogIn(PTB2)),
+
+    _timeout(new Timeout()),
+
+    _event_state(0),
+
+    _x0(0),
+    _y0(0)
+{}
+
+Gamepad::~Gamepad()
+{
+    delete _led1,_led2,_led3,_led4,_led5,_led6;
+    delete _button_A,_button_B,_button_joystick,_vert,_horiz;
+    delete _button_X,_button_Y,_button_back,_button_start;
+    delete _button_L,_button_R, _buzzer, _pot, _timeout;
+}
+
+///////////////// public methods /////////////////
+
+void Gamepad::init()
+{
+    leds_off();
+    init_buttons();
+
+    // read centred values of joystick
+    _x0 = _horiz->read();
+    _y0 = _vert->read();
+
+    // clear all flags
+    _event_state = 0;
+}
+
+void Gamepad::leds_off()
+{
+    leds(0.0);
+}
+
+void Gamepad::leds_on()
+{
+    leds(1.0);
+}
+
+void Gamepad::leds(float val) const
+{
+    if (val < 0.0f) {
+        val = 0.0f;
+    }
+    if (val > 1.0f) {
+        val = 1.0f;
+    }
+
+    // leds are active-low, so subtract from 1.0
+    // 0.0 corresponds to fully-off, 1.0 to fully-on
+    val = 1.0f - val;
+
+    _led1->write(val);
+    _led2->write(val);
+    _led3->write(val);
+    _led4->write(val);
+    _led5->write(val);
+    _led6->write(val);
+}
+
+void Gamepad::led(int n,float val) const
+{
+    // ensure they are within vlaid range
+    if (val < 0.0f) {
+        val = 0.0f;
+    }
+    if (val > 1.0f) {
+        val = 1.0f;
+    }
+    
+    switch (n) {
+        
+        // check for valid LED number and set value
+
+        case 1:
+            _led1->write(1.0f-val);   // active-low so subtract from 1
+            break;
+        case 2:
+            _led2->write(1.0f-val);   // active-low so subtract from 1
+            break;
+        case 3:
+            _led3->write(1.0f-val);   // active-low so subtract from 1
+            break;
+        case 4:
+            _led4->write(1.0f-val);   // active-low so subtract from 1
+            break;
+        case 5:
+            _led5->write(1.0f-val);   // active-low so subtract from 1
+            break;
+        case 6:
+            _led6->write(1.0f-val);   // active-low so subtract from 1
+            break;
+
+    }
+}
+
+float Gamepad::read_pot() const
+{
+    return _pot->read();
+}
+
+void Gamepad::tone(float frequency, float duration)
+{
+    _buzzer->period(1.0f/frequency);
+    _buzzer->write(0.5);  // 50% duty cycle - square wave
+    _timeout->attach(callback(this, &Gamepad::tone_off), duration );
+}
+
+bool Gamepad::check_event(GamepadEvent const id)
+{
+    // Check whether event flag is set
+    if (_event_state[id]) {
+        _event_state.reset(id);  // clear flag
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void Gamepad::reset_flags()
+{
+    // reset all button flags
+    _event_state.reset(A_PRESSED);
+    _event_state.reset(B_PRESSED);
+    _event_state.reset(X_PRESSED);
+    _event_state.reset(Y_PRESSED);
+    _event_state.reset(START_PRESSED);
+    _event_state.reset(BACK_PRESSED);
+    _event_state.reset(L_PRESSED);
+    _event_state.reset(R_PRESSED);
+}
+
+// this method gets the magnitude of the joystick movement
+float Gamepad::get_mag()
+{
+    Polar p = get_polar();
+    return p.mag;
+}
+
+// this method gets the angle of joystick movement (0 to 360, 0 North)
+float Gamepad::get_angle()
+{
+    Polar p = get_polar();
+    return p.angle;
+}
+
+Direction Gamepad::get_direction()
+{
+    float angle = get_angle();  // 0 to 360, -1 for centred
+
+    Direction d;
+    // partition 360 into segments and check which segment the angle is in
+    if (angle < 0.0f) {
+        d = CENTRE;   // check for -1.0 angle
+    } else if (angle < 22.5f) {  // then keep going in 45 degree increments
+        d = N;
+    } else if (angle < 67.5f) {
+        d = NE;
+    } else if (angle < 112.5f) {
+        d = E;
+    } else if (angle < 157.5f) {
+        d = SE;
+    } else if (angle < 202.5f) {
+        d = S;
+    } else if (angle < 247.5f) {
+        d = SW;
+    } else if (angle < 292.5f) {
+        d = W;
+    } else if (angle < 337.5f) {
+        d = NW;
+    } else {
+        d = N;
+    }
+
+    return d;
+}
+
+///////////////////// private methods ////////////////////////
+
+void Gamepad::tone_off()
+{
+    // called after timeout
+    _buzzer->write(0.0);
+}
+
+void Gamepad::init_buttons()
+{
+    // turn on pull-downs as other side of button is connected to 3V3
+    // button is 0 when not pressed and 1 when pressed
+    _button_A->mode(PullDown);
+    _button_B->mode(PullDown);
+    _button_X->mode(PullDown);
+    _button_Y->mode(PullDown);
+    _button_back->mode(PullDown);
+    _button_start->mode(PullDown);
+    _button_L->mode(PullDown);
+    _button_R->mode(PullDown);
+    _button_joystick->mode(PullDown);
+    // therefore setup rising edge interrupts
+    _button_A->rise(callback(this,&Gamepad::a_isr));
+    _button_B->rise(callback(this,&Gamepad::b_isr));
+    _button_X->rise(callback(this,&Gamepad::x_isr));
+    _button_Y->rise(callback(this,&Gamepad::y_isr));
+    _button_L->rise(callback(this,&Gamepad::l_isr));
+    _button_R->rise(callback(this,&Gamepad::r_isr));
+    _button_start->rise(callback(this,&Gamepad::start_isr));
+    _button_back->rise(callback(this,&Gamepad::back_isr));
+    _button_joystick->rise(callback(this,&Gamepad::joy_isr));
+}
+
+// button interrupts ISRs
+// Each of these simply sets the appropriate event bit in the _event_state
+// variable
+void Gamepad::a_isr()
+{
+    _event_state.set(A_PRESSED);
+}
+void Gamepad::b_isr()
+{
+    _event_state.set(B_PRESSED);
+}
+void Gamepad::x_isr()
+{
+    _event_state.set(X_PRESSED);
+}
+void Gamepad::y_isr()
+{
+    _event_state.set(Y_PRESSED);
+}
+void Gamepad::l_isr()
+{
+    _event_state.set(L_PRESSED);
+}
+void Gamepad::r_isr()
+{
+    _event_state.set(R_PRESSED);
+}
+void Gamepad::back_isr()
+{
+    _event_state.set(BACK_PRESSED);
+}
+void Gamepad::start_isr()
+{
+    _event_state.set(START_PRESSED);
+}
+void Gamepad::joy_isr()
+{
+    _event_state.set(JOY_PRESSED);
+}
+
+// get raw joystick coordinate in range -1 to 1
+// Direction (x,y)
+// North     (0,1)
+// East      (1,0)
+// South     (0,-1)
+// West      (-1,0)
+Vector2D Gamepad::get_coord()
+{
+    // read() returns value in range 0.0 to 1.0 so is scaled and centre value
+    // substracted to get values in the range -1.0 to 1.0
+    float x = 2.0f*( _horiz->read() - _x0 );
+    float y = 2.0f*( _vert->read()  - _y0 );
+
+    // Note: the x value here is inverted to ensure the positive x is to the
+    // right. This is simply due to how the potentiometer on the joystick
+    // I was using was connected up. It could have been corrected in hardware
+    // by swapping the power supply pins. Instead it is done in software so may
+    // need to be changed depending on your wiring setup
+
+    Vector2D coord = {-x,y};
+    return coord;
+}
+
+// This maps the raw x,y coord onto a circular grid.
+// See:  http://mathproofs.blogspot.co.uk/2005/07/mapping-square-to-circle.html
+Vector2D Gamepad::get_mapped_coord()
+{
+    Vector2D coord = get_coord();
+
+    // do the transformation
+    float x = coord.x*sqrt(1.0f-pow(coord.y,2.0f)/2.0f);
+    float y = coord.y*sqrt(1.0f-pow(coord.x,2.0f)/2.0f);
+
+    Vector2D mapped_coord = {x,y};
+    return mapped_coord;
+}
+
+// this function converts the mapped coordinates into polar form
+Polar Gamepad::get_polar()
+{
+    // get the mapped coordinate
+    Vector2D coord = get_mapped_coord();
+
+    // at this point, 0 degrees (i.e. x-axis) will be defined to the East.
+    // We want 0 degrees to correspond to North and increase clockwise to 359
+    // like a compass heading, so we need to swap the axis and invert y
+    float x = coord.y;
+    float y = coord.x;
+
+    float mag = sqrt(x*x+y*y);  // pythagoras
+    float angle = RAD2DEG*atan2(y,x);
+    // angle will be in range -180 to 180, so add 360 to negative angles to
+    // move to 0 to 360 range
+    if (angle < 0.0f) {
+        angle+=360.0f;
+    }
+
+    // the noise on the ADC causes the values of x and y to fluctuate slightly
+    // around the centred values. This causes the random angle values to get
+    // calculated when the joystick is centred and untouched. This is also when
+    // the magnitude is very small, so we can check for a small magnitude and then
+    // set the angle to -1. This will inform us when the angle is invalid and the
+    // joystick is centred
+
+    if (mag < TOL) {
+        mag = 0.0f;
+        angle = -1.0f;
+    }
+
+    Polar p = {mag,angle};
+    return p;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Gamepad/Gamepad.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,197 @@
+#ifndef GAMEPAD_H
+#define GAMEPAD_H
+
+#include <bitset>
+#include "FXOS8700CQ.h"
+
+// Forward declaration of the classes that we use from the mbed library
+// This avoids the need for us to include the huge mbed.h header inside our
+// own library API
+namespace mbed
+{
+class AnalogIn;
+class InterruptIn;
+class PwmOut;
+class Timeout;
+}
+
+#define TOL 0.1f
+#define RAD2DEG 57.2957795131f
+
+/** Enum for direction */
+enum Direction {
+    CENTRE,  /**< joystick centred */
+    N,       /**< pushed North (0)*/
+    NE,      /**< pushed North-East (45) */
+    E,       /**< pushed East (90) */
+    SE,      /**< pushed South-East (135) */
+    S,       /**< pushed South (180) */
+    SW,      /**< pushed South-West (225) */
+    W,       /**< pushed West (270) */
+    NW       /**< pushed North-West (315) */
+};
+
+/** Vector 2D struct */
+struct Vector2D {
+    float x; /**< float for x value */
+    float y; /**< float for y value */
+};
+
+/** Polar coordinate struct */
+struct Polar {
+    float mag;  /**< float for magnitude */
+    float angle; /**< float for angle (in degrees) */
+};
+
+/** Gamepad Class
+@brief Library for interfacing with ELEC2645 Gamepad PCB, University of Leeds
+@author Dr Craig A. Evans
+@author Dr Alex Valanvanis
+@date Febraury 2017
+*/
+class Gamepad
+{
+    public:
+    /** Gamepad events 
+ * @brief List of events that can be registered on the gamepad
+ */
+enum GamepadEvent {
+    A_PRESSED,     ///< Button A has been pressed
+    B_PRESSED,     ///< Button B has been pressed
+    X_PRESSED,     ///< Button X has been pressed
+    Y_PRESSED,     ///< Button Y has been pressed
+    L_PRESSED,     ///< Button L has been pressed
+    R_PRESSED,     ///< Button R has been pressed
+    BACK_PRESSED,  ///< Button "Back" has been pressed
+    START_PRESSED, ///< Button "Start" has been pressed
+    JOY_PRESSED,   ///< Joystick button has been pressed
+    N_EVENTS       ///< A dummy flag that marks the end of the list
+};
+private:
+    mbed::PwmOut *_led1;
+    mbed::PwmOut *_led2;
+    mbed::PwmOut *_led3;
+    mbed::PwmOut *_led4;
+    mbed::PwmOut *_led5;
+    mbed::PwmOut *_led6;
+
+    mbed::InterruptIn *_button_A;
+    mbed::InterruptIn *_button_B;
+    mbed::InterruptIn *_button_X;
+    mbed::InterruptIn *_button_Y;
+    mbed::InterruptIn *_button_L;
+    mbed::InterruptIn *_button_R;
+    mbed::InterruptIn *_button_back;
+    mbed::InterruptIn *_button_start;
+    mbed::InterruptIn *_button_joystick;
+
+    mbed::AnalogIn *_vert;
+    mbed::AnalogIn *_horiz;
+
+    mbed::PwmOut   *_buzzer;
+    mbed::AnalogIn *_pot;
+
+    mbed::Timeout *_timeout;
+
+    std::bitset<N_EVENTS> _event_state; ///< A binary list of buttons that has been pressed
+
+    // centred x,y values
+    float _x0;
+    float _y0;
+
+public:
+
+    /** Constructor */
+    Gamepad();
+
+    /** Destructor */
+    ~Gamepad();
+
+    /** Initialise all peripherals and configure interrupts */
+    void init();
+
+    /** Turn all LEDs on */
+    void leds_on();
+
+    /** Turn all LEDs off */
+    void leds_off();
+
+    /** Set all LEDs to duty-cycle
+    *@param value in range 0.0 to 1.0
+    */
+    void leds(float val) const;
+
+    /** Set LED to duty-cycle
+    *@param led number (0 to 5)
+    *@param value in range 0.0 to 1.0
+    */
+    void led(int n,float val) const;
+
+    /** Read potentiometer
+    *@returns potentiometer value in range 0.0 to 1.0
+    */
+    float read_pot() const;
+
+    /** Play tone on piezo
+    * @param frequency in Hz
+    * @param duration of tone in seconds
+    */
+    void tone(float frequency, float duration);
+
+    /**
+     * @brief Check whether an event flag has been set and clear it
+     * @param id[in] The ID of the event to test
+     * @return true if the event occurred
+     */
+    bool check_event(GamepadEvent const id);
+    
+    /** Reset all button flags */
+    void reset_flags();
+
+    /** Get magnitude of joystick movement
+    * @returns value in range 0.0 to 1.0
+    */
+
+    float get_mag();
+
+    /** Get angle of joystick movement
+    * @returns value in range 0.0 to 359.9. 0.0 corresponds to N, 180.0 to S. -1.0 is central
+    */
+    float get_angle();
+
+    /** Gets joystick direction
+    * @returns an enum: CENTRE, N, NE, E, SE, S, SW, W, NW,
+    */
+    Direction get_direction();    // N,NE,E,SE etc.
+
+    /** Gets raw cartesian co-ordinates of joystick
+    * @returns a struct with x,y members, each in the range 0.0 to 1.0
+    */
+    Vector2D get_coord();         // cartesian co-ordinates x,y
+
+    /** Gets cartesian coordinates mapped to circular grid
+    * @returns a struct with x,y members, each in the range 0.0 to 1.0
+    */
+    Vector2D get_mapped_coord();  // x,y mapped to circle
+
+    /** Gets polar coordinates of the joystick
+    * @returns a struct contains mag and angle
+    */
+    Polar get_polar();            // mag and angle in struct form
+
+private:
+    void init_buttons();
+    void tone_off();
+
+    void a_isr();
+    void b_isr();
+    void x_isr();
+    void y_isr();
+    void l_isr();
+    void r_isr();
+    void back_isr();
+    void start_isr();
+    void joy_isr();
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Laser/Laser-test.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,48 @@
+#ifndef LASER_TEST_H
+#define LASER_TEST_H
+
+/**
+ * \brief Check that Laser object goes to correct position when moved
+ * 
+ * \returns true if all the tests passed
+ */
+bool Laser_test_movement()
+{
+    // Initialise Laser object at -10,0 with velocity of y as -2
+    Laser _laser;
+    _laser.init();
+    
+    int read_pos1x = _laser.get_x();
+    int read_pos1y = _laser.get_y();
+    
+     // Read the position
+    printf("%f, %f\n", read_pos1x, read_pos1y);
+
+    // Set the x position to 10, 0
+    _laser.set_posx(10);
+
+    // Update the position
+    _laser.update();
+
+    int read_pos2x = _laser.get_x();
+    int read_pos2y = _laser.get_y();
+    
+     // Read the position
+    printf("%f, %f\n", read_pos2x, read_pos2y);
+    
+    // Now check that both the positions are as expected
+    bool success_flag = true;
+    
+    // Fail the test if the initial position is wrong
+    if (read_pos1x != 0 || read_pos1y != 0) {
+        success_flag = false;
+    }
+    if (read_pos2x != 10 || read_pos2y != 0) {
+        success_flag = false;
+    }
+    
+    
+
+    return success_flag;
+}
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Laser/Laser.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,51 @@
+#include "Laser.h"
+
+Laser::Laser()
+{
+
+}
+
+Laser::~Laser()
+{
+
+}
+
+void Laser::init()  // initialises the lasers off screen
+{
+    _x = -10;  
+    _y = 0;
+
+    _speed_y = -2; // -2 speed in the y direction, doesnt change even off screen
+}
+
+void Laser::draw(N5110 &lcd)
+{
+    if (_x >= 0) {  // only draw if on screen (more efficient?)
+        lcd.drawRect(_x,_y,2,6,FILL_BLACK);
+    }
+}
+
+void Laser::update() // updates the laser's y coordinate according to its speed
+{
+    _y += _speed_y;
+}
+
+int Laser::get_x()  // retrieves the lasers's x coordinate
+{
+    return _x;
+}
+
+int Laser::get_y()  // retrieves the lasers's y coordinate
+{
+    return _y;
+}
+
+void Laser::set_posx(int x)  // sets the laser's x coordinate
+{
+    _x = x;
+}
+
+void Laser::set_posy(int y)  // sets the lasers's y coordinate
+{
+    _y = y;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Laser/Laser.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,63 @@
+#ifndef LASER_H
+#define LASER_H
+
+#include "mbed.h"
+#include "N5110.h"
+#include "Gamepad.h"
+#include "Paddle.h"
+
+/** Laser Class
+@author James Heavey, University of Leeds
+@brief Controls the Laser in the Breakout game
+@date May 2019
+*/
+class Laser
+{
+
+public:
+
+    /** Constructor declaration */
+    Laser();
+    
+    /** Deconstructor declaration */
+    ~Laser();
+    
+    /** Initialises the laser off screen with a set velocity */
+    void init();
+    
+    /** Draws the Laser at at its current coordinates on the LCD
+    * @param lcd @details a N5110 pointer
+    */
+    void draw(N5110 &lcd);
+    
+    /** Update the Laser's y coordinate based on its velocity */
+    void update();
+    
+    /** Sets the Laser's x coordinate
+    * @param x @details set the  variable _x to the new local x
+    */
+    void set_posx(int x);
+    
+    /** Sets the Laser's x coordinate
+    * @param y @details set the  variable _y to the new local y
+    */
+    void set_posy(int y);
+    
+    /** Retrieve the Laser's current x coordinate
+    * @returns variable _x as an integer
+    */
+    int get_x();
+    
+    /** Retrieve the Laser's current y coordinate
+    * @returns variable _y as an integer
+    */
+    int get_y();
+
+private:
+
+    int _speed_y;
+    int _x;
+    int _y;
+    
+};
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Life_Powerup/Life_Powerup.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,57 @@
+#include "Life_Powerup.h"
+
+Life_Powerup::Life_Powerup()
+{
+
+}
+
+Life_Powerup::~Life_Powerup()
+{
+
+}
+
+void Life_Powerup::init()  // initialises the Life_Powerup off screen
+{
+    _x = -10;  
+    _y = 0;
+    _speed_y = 1; // -2 speed in the y direction, doesnt change even off screen
+}
+
+
+void Life_Powerup::draw(N5110 &lcd)
+{   
+    if (_x >= 0) {  // only draw if on screen (more efficient?)
+        Bitmap powerup(powerup_data, 9, 9);  // assign the powerup sprite data
+        powerup.render(lcd,_x,_y);
+    }
+}
+
+
+void Life_Powerup::update() // updates the Life_Powerup's y coordinate according to its speed
+{
+    _y += _speed_y;
+}
+
+
+int Life_Powerup::get_x()  // retrieves the Life_Powerups's x coordinate
+{
+    return _x;
+}
+
+
+int Life_Powerup::get_y()  // retrieves the Life_Powerups's y coordinate
+{
+    return _y;
+}
+
+
+void Life_Powerup::set_posx(int x)  // sets the Life_Powerup's x coordinate
+{
+    _x = x;
+}
+
+
+void Life_Powerup::set_posy(int y)  // sets the Life_Powerups's y coordinate
+{
+    _y = y;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Life_Powerup/Life_Powerup.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,75 @@
+#ifndef LIFE_POWERUP_H
+#define LIFE_POWERUP_H
+
+#include "mbed.h"
+#include "N5110.h"
+#include "Gamepad.h"
+#include "Paddle.h"
+#include "Bitmap.h"
+
+static int powerup_data[] = 
+{
+    0, 0, 0, 1, 1, 1, 0, 0, 0, 
+    0, 1, 1, 0, 0, 0, 1, 1, 0, 
+    0, 1, 0, 1, 0, 1, 0, 1, 0, 
+    1, 0, 1, 1, 1, 1, 1, 0, 1, 
+    1, 0, 1, 1, 1, 1, 1, 0, 1, 
+    1, 0, 0, 1, 1, 1, 0, 0, 1, 
+    0, 1, 0, 0, 1, 0, 0, 1, 0, 
+    0, 1, 1, 0, 0, 0, 1, 1, 0, 
+    0, 0, 0, 1, 1, 1, 0, 0, 0
+};
+
+/** Life_Powerup Class
+@author James Heavey, University of Leeds
+@brief Controls the Life_Powerup in the Breakout game
+@date May 2019
+*/
+
+class Life_Powerup
+{
+public:
+
+    /** Constructor declaration */
+    Life_Powerup();
+    
+    /** Destructor declaration */
+    ~Life_Powerup();
+    
+    /** Initialises the powerup off screen with a set velocity */
+    void init();
+    
+    /** Draws the powerup at at its current coordinates on the LCD
+    * @param lcd @details a N5110 pointer
+    */
+    void draw(N5110 &lcd);
+    
+    /** Update the powerup's y coordinate based on its velocity */
+    void update();
+    
+    /** Sets the powerups's x coordinate
+    * @param x @details set the variable _x to the new local x
+    */
+    void set_posx(int x);
+    
+    /** Sets the powerups's x coordinate
+    * @param y @details set the variable _y to the new local y
+    */
+    void set_posy(int y);
+    
+    /** Retrieve the powerup's current x coordinate
+    * @returns variable _x as an integer
+    */
+    int get_x();
+    
+    /** Retrieve the powerup's current y coordinate
+    * @returns variable _y as an integer
+    */
+    int get_y();
+
+private:
+    int _speed_y;
+    int _x;
+    int _y;
+};
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/N5110/Bitmap.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,96 @@
+#include "Bitmap.h"
+
+#include <iostream>
+
+#include "N5110.h"
+
+Bitmap::Bitmap(int const               *contents,
+               unsigned int const       height,
+               unsigned int const       width)
+    :
+    _contents(std::vector<int>(height*width)),
+    _height(height),
+    _width(width)
+{
+    // Perform a quick sanity check of the dimensions
+    if (_contents.size() != height * width) {
+        std::cerr << "Contents of bitmap has size " << _contents.size()
+                  << " pixels, but its dimensions were specified as "
+                  << width << " * " << height << " = " << width * height << std::endl;
+    }
+
+    for(unsigned int i = 0; i < height*width; ++i) _contents[i] = contents[i];
+}
+
+/**
+ * @returns the value of the pixel at the given position
+ */
+int Bitmap::get_pixel(unsigned int const row,
+                      unsigned int const column) const
+{
+    // First check that row and column indices are within bounds
+    if(column >= _width || row >= _height)
+    {
+        std::cerr << "The requested pixel with index " << row << "," << column
+                  << "is outside the bitmap dimensions: " << _width << ","
+                  << _height << std::endl;
+    }
+
+    // Now return the pixel value, using row-major indexing
+    return _contents[row * _width + column];
+}
+
+/**
+ * @brief Prints the contents of the bitmap to the terminal
+ */
+void Bitmap::print() const
+{
+    for (unsigned int row = 0; row < _height; ++row)
+    {
+        // Print each element of the row
+        for (unsigned int column = 0; column < _width; ++column)
+        {
+            int pixel = get_pixel(row, column);
+            std::cout << pixel;
+        }
+
+        // And then terminate with a new-line character
+        std::cout << std::endl;
+    }
+}
+
+/**
+ * @brief Renders the contents of the bitmap onto an N5110 screen
+ *
+ * @param[in] lcd The screen to use for rendering
+ * @param[in] x0  The horizontal position in pixels at which to render the bitmap
+ * @param[in] y0  The vertical position in pixels at which to render the bitmap
+ *
+ * @details Note that x0, y0 gives the location of the top-left of the bitmap on
+ *          the screen.
+ *          This function only updates the buffer on the screen.  You still need
+ *          to refresh the screen in order to actually see the bitmap.
+ */
+void Bitmap::render(N5110 &lcd,
+                    unsigned int const x0,
+                    unsigned int const y0) const
+{
+    // Loop through each row of the bitmap image
+    for (unsigned int bitmap_row = 0; bitmap_row < _height; ++bitmap_row)
+    {
+        // Row index on the screen for rendering the row of pixels
+        unsigned int screen_row = y0 + bitmap_row;
+                
+        // Render each pixel in the row
+        for (unsigned int bitmap_col = 0; bitmap_col < _width; ++bitmap_col)
+        {
+            // Column index on the screen for rendering this pixel
+            int screen_col = x0 + bitmap_col;
+
+            // Find the required value of the pixel at the given location within
+            // the bitmap data and then write it to the LCD screen
+            int pixel = get_pixel(bitmap_row, bitmap_col);
+            lcd.setPixel(screen_col, screen_row, pixel);
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/N5110/Bitmap.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,65 @@
+#ifndef BITMAP_H
+#define BITMAP_H
+
+#include <vector>
+
+// Forward declarations
+class N5110;
+
+/**
+ * @brief  A black & white bitmap that can be rendered on an N5110 screen
+ * @author Alex Valavanis <a.valavanis@leeds.ac.uk>
+ * 
+ * @code
+  // First declare the pixel map data using '1' for black,
+  // or '0' for white pixels
+  static int sprite_data[] = {
+    0,0,1,0,0,
+    0,1,1,1,0,
+    0,0,1,0,0,
+    0,1,1,1,0,
+    1,1,1,1,1,
+    1,1,1,1,1,
+    1,1,0,1,1,
+    1,1,0,1,1
+  };
+
+  // Instantiate the Bitmap object using the data above
+  Bitmap sprite(sprite_data, 8, 5); // Specify rows and columns in sprite
+  
+  // We can render the bitmap wherever we want on the screen
+  sprite.render(lcd, 20, 6); // x and y locations for rendering
+  sprite.render(lcd, 30, 10);
+  
+  // We can also print its values to the terminal
+  sprite.print();
+ * @endcode
+ */
+class Bitmap
+{
+private:
+    /**
+     * @brief The contents of the drawing, with pixels stored in row-major order
+     * @details '1' represents a black pixel; '0' represents white
+     */
+    std::vector<int> _contents;
+    
+    unsigned int _height; ///< The height of the drawing in pixels
+    unsigned int _width;  ///< The width of the drawing in pixels
+    
+public:
+    Bitmap(int const          *contents,
+           unsigned int const  height,
+           unsigned int const  width);
+
+    int get_pixel(unsigned int const row,
+                  unsigned int const column) const;
+
+    void print() const;
+
+    void render(N5110 &lcd,
+                unsigned int const x0,
+                unsigned int const y0) const;
+};
+
+#endif // BITMAP_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/N5110/N5110.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,497 @@
+#include "mbed.h"
+#include "N5110.h"
+
+// overloaded constructor includes power pin - LCD Vcc connected to GPIO pin
+// this constructor works fine with LPC1768 - enough current sourced from GPIO
+// to power LCD. Doesn't work well with K64F.
+N5110::N5110(PinName const pwrPin,
+             PinName const scePin,
+             PinName const rstPin,
+             PinName const dcPin,
+             PinName const mosiPin,
+             PinName const sclkPin,
+             PinName const ledPin)
+    :
+    _spi(new SPI(mosiPin,NC,sclkPin)), // create new SPI instance and initialise
+    _led(new PwmOut(ledPin)),
+    _pwr(new DigitalOut(pwrPin)),
+    _sce(new DigitalOut(scePin)),
+    _rst(new DigitalOut(rstPin)),
+    _dc(new DigitalOut(dcPin))
+{}
+
+// overloaded constructor does not include power pin - LCD Vcc must be tied to +3V3
+// Best to use this with K64F as the GPIO hasn't sufficient output current to reliably
+// drive the LCD.
+N5110::N5110(PinName const scePin,
+             PinName const rstPin,
+             PinName const dcPin,
+             PinName const mosiPin,
+             PinName const sclkPin,
+             PinName const ledPin)
+    :
+    _spi(new SPI(mosiPin,NC,sclkPin)), // create new SPI instance and initialise
+    _led(new PwmOut(ledPin)),
+    _pwr(NULL), // pwr not needed so null it to be safe
+    _sce(new DigitalOut(scePin)),
+    _rst(new DigitalOut(rstPin)),
+    _dc(new DigitalOut(dcPin))
+{}
+
+N5110::~N5110()
+{
+    delete _spi;
+
+    if(_pwr) {
+        delete _pwr;
+    }
+
+    delete _led;
+    delete _sce;
+    delete _rst;
+    delete _dc;
+}
+
+// initialise function - powers up and sends the initialisation commands
+void N5110::init()
+{
+    turnOn();     // power up
+    reset();      // reset LCD - must be done within 100 ms
+    initSPI();    
+    
+    setContrast(0.55);  // this may need tuning (say 0.4 to 0.6)
+    setBias(3);   // datasheet - 48:1 mux - don't mess with if you don't know what you're doing! (0 to 7)
+    setTempCoefficient(0); // datasheet - may need increasing (range 0 to 3) at very low temperatures
+    normalMode();  // normal video mode by default
+    
+    clearRAM();      // RAM is undefined at power-up so clear to be sure
+    clear();   // clear buffer
+    setBrightness(0.5);
+}
+
+// sets normal video mode (black on white)
+void N5110::normalMode()
+{
+    sendCommand(0b00100000);   // basic instruction
+    sendCommand(0b00001100);  // normal video mode- datasheet
+}
+
+// sets normal video mode (white on black)
+void N5110::inverseMode()
+{
+    sendCommand(0b00100000);   // basic instruction
+    sendCommand(0b00001101);   // inverse video mode - datasheet
+}
+
+// function to power up the LCD and backlight - only works when using GPIO to power
+void N5110::turnOn()
+{
+    if (_pwr != NULL) {
+        _pwr->write(1);  // apply power
+    }
+}
+
+// function to power down LCD
+void N5110::turnOff()
+{
+    clear(); // clear buffer
+    refresh();
+    setBrightness(0.0);  // turn backlight off
+    clearRAM();   // clear RAM to ensure specified current consumption
+    // send command to ensure we are in basic mode
+    
+    sendCommand(0b00100000); // basic mode
+    sendCommand(0b00001000); // clear display
+    sendCommand(0b00100001); // extended mode
+    sendCommand(0b00100100); // power down
+    
+    // if we are powering the LCD using the GPIO then make it low to turn off
+    if (_pwr != NULL) {
+        wait_ms(10);  // small delay and then turn off the power pin
+        _pwr->write(0);  // turn off power
+    }
+
+}
+
+// function to change LED backlight brightness
+void N5110::setBrightness(float brightness)
+{
+    // check whether brightness is within range
+    if (brightness < 0.0f)
+        brightness = 0.0f;
+    if (brightness > 1.0f)
+        brightness = 1.0f;
+    // set PWM duty cycle
+    _led->write(brightness);
+}
+
+void N5110::setContrast(float contrast) {
+    
+    // enforce limits
+    if (contrast > 1.0f)
+        contrast = 1.0f;
+    else if (contrast < 0.0f)
+        contrast = 0.0;
+    
+    // convert to char in range 0 to 127 (i.e. 6 bits)
+    char ic = char(contrast*127.0f);
+    
+    sendCommand(0b00100001);  // extended instruction set
+    sendCommand(0b10000000 | ic);   // set Vop (which controls contrast)
+    sendCommand(0b00100000);  // back to basic instruction set
+}
+
+void N5110::setTempCoefficient(char tc) {
+    
+    // enforce limits
+    if (tc>3) {
+        tc=3;
+    }
+    
+    // temperature coefficient may need increasing at low temperatures
+
+    sendCommand(0b00100001);  // extended instruction set
+    sendCommand(0b00000100 | tc);
+    sendCommand(0b00100000);  // back to basic instruction set
+}
+    
+void N5110::setBias(char bias) {
+    
+    // from data sheet
+    // bias      mux rate
+    // 0        1:100
+    // 1        1:80
+    // 2        1:65
+    // 3        1:48   (default)
+    // 4        1:40/1:34
+    // 5        1:24
+    // 6        1:18/1:16
+    // 7        1:10/1:9/1:8
+    
+    // enforce limits
+    if (bias>7) {
+        bias=7;
+    }
+        
+    sendCommand(0b00100001);  // extended mode instruction
+    sendCommand(0b00010000 | bias);  
+    sendCommand(0b00100000); // end of extended mode instruction
+}
+
+// pulse the active low reset line
+void N5110::reset()
+{
+    _rst->write(0);  // reset the LCD
+    _rst->write(1);
+}
+
+// function to initialise SPI peripheral
+void N5110::initSPI()
+{
+    _spi->format(8,1);    // 8 bits, Mode 1 - polarity 0, phase 1 - base value of clock is 0, data captured on falling edge/propagated on rising edge
+    _spi->frequency(4000000);  // maximum of screen is 4 MHz
+}
+
+// send a command to the display
+void N5110::sendCommand(unsigned char command)
+{
+    _dc->write(0);  // set DC low for command
+    _sce->write(0); // set CE low to begin frame
+    _spi->write(command);  // send command
+    _dc->write(1);  // turn back to data by default
+    _sce->write(1); // set CE high to end frame (expected for transmission of single byte)
+}
+
+// send data to the display at the current XY address
+// dc is set to 1 (i.e. data) after sending a command and so should
+// be the default mode.
+void N5110::sendData(unsigned char data)
+{
+    _sce->write(0);   // set CE low to begin frame
+    _spi->write(data);
+    _sce->write(1);  // set CE high to end frame (expected for transmission of single byte)
+}
+
+// this function writes 0 to the 504 bytes to clear the RAM
+void N5110::clearRAM()
+{
+    _sce->write(0);  //set CE low to begin frame
+    for(int i = 0; i < WIDTH * HEIGHT; i++) { // 48 x 84 bits = 504 bytes
+        _spi->write(0x00);  // send 0's
+    }
+    _sce->write(1); // set CE high to end frame
+}
+
+// function to set the XY address in RAM for subsequenct data write
+void N5110::setXYAddress(unsigned int const x,
+                         unsigned int const y)
+{
+    if (x<WIDTH && y<HEIGHT) {  // check within range
+        sendCommand(0b00100000);  // basic instruction
+        sendCommand(0b10000000 | x);  // send addresses to display with relevant mask
+        sendCommand(0b01000000 | y);
+    }
+}
+
+// These functions are used to set, clear and get the value of pixels in the display
+// Pixels are addressed in the range of 0 to 47 (y) and 0 to 83 (x).  The refresh()
+// function must be called after set and clear in order to update the display
+void N5110::setPixel(unsigned int const x,
+                     unsigned int const y,
+                     bool const         state)
+{
+    if (x<WIDTH && y<HEIGHT) {  // check within range
+        // calculate bank and shift 1 to required position in the data byte
+        if(state) buffer[x][y/8] |= (1 << y%8);
+        else      buffer[x][y/8] &= ~(1 << y%8);
+    }
+}
+
+void N5110::clearPixel(unsigned int const x,
+                       unsigned int const y)
+{
+    if (x<WIDTH && y<HEIGHT) {  // check within range
+        // calculate bank and shift 1 to required position (using bit clear)
+        buffer[x][y/8] &= ~(1 << y%8);
+    }
+}
+
+int N5110::getPixel(unsigned int const x,
+                    unsigned int const y) const
+{
+    if (x<WIDTH && y<HEIGHT) {  // check within range
+        // return relevant bank and mask required bit
+
+        int pixel = (int) buffer[x][y/8] & (1 << y%8);
+
+        if (pixel)
+            return 1;
+        else
+            return 0;
+    }
+
+    return 0;
+
+}
+
+// function to refresh the display
+void N5110::refresh()
+{
+    setXYAddress(0,0);  // important to set address back to 0,0 before refreshing display
+    // address auto increments after printing string, so buffer[0][0] will not coincide
+    // with top-left pixel after priting string
+
+    _sce->write(0);  //set CE low to begin frame
+
+    for(int j = 0; j < BANKS; j++) {  // be careful to use correct order (j,i) for horizontal addressing
+        for(int i = 0; i < WIDTH; i++) {
+            _spi->write(buffer[i][j]);  // send buffer
+        }
+    }
+    _sce->write(1); // set CE high to end frame
+
+}
+
+// fills the buffer with random bytes.  Can be used to test the display.
+// The rand() function isn't seeded so it probably creates the same pattern everytime
+void N5110::randomiseBuffer()
+{
+    int i,j;
+    for(j = 0; j < BANKS; j++) {  // be careful to use correct order (j,i) for horizontal addressing
+        for(i = 0; i < WIDTH; i++) {
+            buffer[i][j] = rand()%256;  // generate random byte
+        }
+    }
+
+}
+
+// function to print 5x7 font
+void N5110::printChar(char const          c,
+                      unsigned int const  x,
+                      unsigned int const  y)
+{
+    if (y<BANKS) {  // check if printing in range of y banks
+
+        for (int i = 0; i < 5 ; i++ ) {
+            int pixel_x = x+i;
+            if (pixel_x > WIDTH-1)  // ensure pixel isn't outside the buffer size (0 - 83)
+                break;
+            buffer[pixel_x][y] = font5x7[(c - 32)*5 + i];
+            // array is offset by 32 relative to ASCII, each character is 5 pixels wide
+        }
+
+    }
+}
+
+// function to print string at specified position
+void N5110::printString(const char         *str,
+                        unsigned int const  x,
+                        unsigned int const  y)
+{
+    if (y<BANKS) {  // check if printing in range of y banks
+
+        int n = 0 ; // counter for number of characters in string
+        // loop through string and print character
+        while(*str) {
+
+            // writes the character bitmap data to the buffer, so that
+            // text and pixels can be displayed at the same time
+            for (int i = 0; i < 5 ; i++ ) {
+                int pixel_x = x+i+n*6;
+                if (pixel_x > WIDTH-1) // ensure pixel isn't outside the buffer size (0 - 83)
+                    break;
+                buffer[pixel_x][y] = font5x7[(*str - 32)*5 + i];
+            }
+            str++;  // go to next character in string
+            n++;    // increment index
+        }
+    }
+}
+
+// function to clear the screen buffer
+void N5110::clear()
+{
+    memset(buffer,0,sizeof(buffer));
+}
+
+// function to plot array on display
+void N5110::plotArray(float const array[])
+{
+    for (int i=0; i<WIDTH; i++) {  // loop through array
+        // elements are normalised from 0.0 to 1.0, so multiply
+        // by 47 to convert to pixel range, and subtract from 47
+        // since top-left is 0,0 in the display geometry
+        setPixel(i,47 - int(array[i]*47.0f),true);
+    }
+
+}
+
+// function to draw circle
+void N5110:: drawCircle(unsigned int const x0,
+                        unsigned int const y0,
+                        unsigned int const radius,
+                        FillType const     fill)
+{
+    // from http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
+    int x = radius;
+    int y = 0;
+    int radiusError = 1-x;
+
+    while(x >= y) {
+
+        // if transparent, just draw outline
+        if (fill == FILL_TRANSPARENT) {
+            setPixel( x + x0,  y + y0,true);
+            setPixel(-x + x0,  y + y0,true);
+            setPixel( y + x0,  x + y0,true);
+            setPixel(-y + x0,  x + y0,true);
+            setPixel(-y + x0, -x + y0,true);
+            setPixel( y + x0, -x + y0,true);
+            setPixel( x + x0, -y + y0,true);
+            setPixel(-x + x0, -y + y0,true);
+        } else {  // drawing filled circle, so draw lines between points at same y value
+
+            int type = (fill==FILL_BLACK) ? 1:0;  // black or white fill
+
+            drawLine(x+x0,y+y0,-x+x0,y+y0,type);
+            drawLine(y+x0,x+y0,-y+x0,x+y0,type);
+            drawLine(y+x0,-x+y0,-y+x0,-x+y0,type);
+            drawLine(x+x0,-y+y0,-x+x0,-y+y0,type);
+        }
+
+        y++;
+        if (radiusError<0) {
+            radiusError += 2 * y + 1;
+        } else {
+            x--;
+            radiusError += 2 * (y - x) + 1;
+        }
+    }
+
+}
+
+void N5110::drawLine(unsigned int const x0,
+                     unsigned int const y0,
+                     unsigned int const x1,
+                     unsigned int const y1,
+                     unsigned int const type)
+{
+    // Note that the ranges can be negative so we have to turn the input values
+    // into signed integers first
+    int const y_range = static_cast<int>(y1) - static_cast<int>(y0);
+    int const x_range = static_cast<int>(x1) - static_cast<int>(x0);
+
+    // if dotted line, set step to 2, else step is 1
+    unsigned int const step = (type==2) ? 2:1;
+
+    // make sure we loop over the largest range to get the most pixels on the display
+    // for instance, if drawing a vertical line (x_range = 0), we need to loop down the y pixels
+    // or else we'll only end up with 1 pixel in the x column
+    if ( abs(x_range) > abs(y_range) ) {
+
+        // ensure we loop from smallest to largest or else for-loop won't run as expected
+        unsigned int const start = x_range > 0 ? x0:x1;
+        unsigned int const stop =  x_range > 0 ? x1:x0;
+
+        // loop between x pixels
+        for (unsigned int x = start; x<= stop ; x+=step) {
+            // do linear interpolation
+            int const dx = static_cast<int>(x)-static_cast<int>(x0);
+            unsigned int const y = y0 + y_range * dx / x_range;
+
+            // If the line type is '0', this will clear the pixel
+            // If it is '1' or '2', the pixel will be set
+            setPixel(x,y, type);
+        }
+    } else {
+
+        // ensure we loop from smallest to largest or else for-loop won't run as expected
+        unsigned int const start = y_range > 0 ? y0:y1;
+        unsigned int const stop =  y_range > 0 ? y1:y0;
+
+        for (unsigned int y = start; y<= stop ; y+=step) {
+            // do linear interpolation
+            int const dy = static_cast<int>(y)-static_cast<int>(y0);
+            unsigned int const x = x0 + x_range * dy / y_range;
+
+            // If the line type is '0', this will clear the pixel
+            // If it is '1' or '2', the pixel will be set
+            setPixel(x,y, type);
+        }
+    }
+
+}
+
+void N5110::drawRect(unsigned int const x0,
+                     unsigned int const y0,
+                     unsigned int const width,
+                     unsigned int const height,
+                     FillType const     fill)
+{
+    if (fill == FILL_TRANSPARENT) { // transparent, just outline
+        drawLine(x0,y0,x0+(width-1),y0,1);  // top
+        drawLine(x0,y0+(height-1),x0+(width-1),y0+(height-1),1);  // bottom
+        drawLine(x0,y0,x0,y0+(height-1),1);  // left
+        drawLine(x0+(width-1),y0,x0+(width-1),y0+(height-1),1);  // right
+    } else { // filled rectangle
+        int type = (fill==FILL_BLACK) ? 1:0;  // black or white fill
+        for (int y = y0; y<y0+height; y++) {  // loop through rows of rectangle
+            drawLine(x0,y,x0+(width-1),y,type);  // draw line across screen
+        }
+    }
+}
+
+void N5110::drawSprite(int x0,
+                       int y0,
+                       int nrows,
+                       int ncols,
+                       int *sprite)
+{
+    for (int i = 0; i < nrows; i++) {
+        for (int j = 0 ; j < ncols ; j++) {
+
+            int pixel = *((sprite+i*ncols)+j);
+            setPixel(x0+j,y0+i, pixel);
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/N5110/N5110.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,534 @@
+#ifndef N5110_H
+#define N5110_H
+
+#include "mbed.h"
+
+// number of pixels on display
+#define WIDTH 84
+#define HEIGHT 48
+#define BANKS 6
+
+/// Fill types for 2D shapes
+enum FillType {
+    FILL_TRANSPARENT, ///< Transparent with outline
+    FILL_BLACK,       ///< Filled black
+    FILL_WHITE,       ///< Filled white (no outline)
+};
+
+/** N5110 Class
+@brief Library for interfacing with Nokia 5110 LCD display (https://www.sparkfun.com/products/10168) using the hardware SPI on the mbed.
+@brief The display is powered from a GPIO pin meaning it can be controlled via software.  The LED backlight is also software-controllable (via PWM pin).
+@brief Can print characters and strings to the display using the included 5x7 font.
+@brief The library also implements a screen buffer so that individual pixels on the display (84 x 48) can be set, cleared and read.
+@brief The library can print primitive shapes (lines, circles, rectangles)
+@brief Acknowledgements to Chris Yan's Nokia_5110 Library.
+
+@brief Revision 1.3
+
+@author Craig A. Evans
+@date   7th February 2017
+
+@code
+
+#include "mbed.h"
+#include "N5110.h"
+
+//      rows,cols
+int sprite[8][5] =   {
+    { 0,0,1,0,0 },
+    { 0,1,1,1,0 },
+    { 0,0,1,0,0 },
+    { 0,1,1,1,0 },
+    { 1,1,1,1,1 },
+    { 1,1,1,1,1 },
+    { 1,1,0,1,1 },
+    { 1,1,0,1,1 },
+};
+
+//    VCC,SCE,RST,D/C,MOSI,SCLK,LED
+//N5110 lcd(p7,p8,p9,p10,p11,p13,p21);  // LPC1768 - pwr from GPIO
+N5110 lcd(p8,p9,p10,p11,p13,p21);  // LPC1768 - powered from +3V3 - JP1 in 2/3 position
+//N5110 lcd(PTC9,PTC0,PTC7,PTD2,PTD1,PTC11);  // K64F - pwr from 3V3
+
+int main()
+{
+    // first need to initialise display
+    lcd.init();
+    
+    // change set contrast in range 0.0 to 1.0
+    // 0.4 appears to be a good starting point
+    lcd.setContrast(0.4);
+
+    while(1) {
+
+        // these are default settings so not strictly needed
+        lcd.normalMode();      // normal colour mode
+        lcd.setBrightness(0.5); // put LED backlight on 50%
+
+        lcd.clear();
+        // x origin, y origin, rows, cols, sprite
+        lcd.drawSprite(20,6,8,5,(int *)sprite);
+        lcd.refresh();
+        wait(5.0);
+
+        lcd.clear(); // clear buffer at start of every loop
+        // can directly print strings at specified co-ordinates (must be less than 84 pixels to fit on display)
+        lcd.printString("Hello, World!",0,0);
+
+        char buffer[14];  // each character is 6 pixels wide, screen is 84 pixels (84/6 = 14)
+        // so can display a string of a maximum 14 characters in length
+        // or create formatted strings - ensure they aren't more than 14 characters long
+        int temperature = 27;
+        int length = sprintf(buffer,"T = %2d C",temperature); // print formatted data to buffer
+        // it is important the format specifier ensures the length will fit in the buffer
+        if (length <= 14)  // if string will fit on display (assuming printing at x=0)
+            lcd.printString(buffer,0,1);           // display on screen
+
+        float pressure = 1012.3;  // same idea with floats
+        length = sprintf(buffer,"P = %.2f mb",pressure);
+        if (length <= 14)
+            lcd.printString(buffer,0,2);
+
+        // can also print individual characters at specified place
+        lcd.printChar('X',5,3);
+
+        // draw a line across the display at y = 40 pixels (origin top-left)
+        for (int i = 0; i < WIDTH; i++) {
+            lcd.setPixel(i,40,true);
+        }
+        // need to refresh display after setting pixels or writing strings
+        lcd.refresh();
+        wait(5.0);
+
+        // can check status of pixel using getPixel(x,y);
+        lcd.clear();  // clear buffer
+        lcd.setPixel(2,2,true);  // set random pixel in buffer
+        lcd.refresh();
+        wait(1.0);
+
+        int pixel_to_test = lcd.getPixel(2,2);
+
+        if ( pixel_to_test ) {
+            lcd.printString("2,2 is set",0,4);
+        }
+
+        // this one shouldn't be set
+        lcd.setPixel(3,3,false);  // clear random pixel in buffer
+        lcd.refresh();
+        pixel_to_test = lcd.getPixel(3,3);
+
+        if ( pixel_to_test == 0 ) {
+            lcd.printString("3,3 is clear",0,5);
+        }
+
+        lcd.refresh();
+        wait(4.0);
+
+        lcd.clear();            // clear buffer
+        lcd.inverseMode();      // invert colours
+        lcd.setBrightness(1.0); // put LED backlight on full
+
+        float array[84];
+
+        for (int i = 0; i < 84; i++) {
+            array[i] = 0.5 + 0.5*sin(i*2*3.14/84);
+        }
+
+        // can also plot graphs - 84 elements only
+        // values must be in range 0.0 - 1.0
+        lcd.plotArray(array);
+        lcd.refresh();
+        wait(5.0);
+
+        lcd.clear();
+        lcd.normalMode();      // normal colour mode back
+        lcd.setBrightness(0.5); // put LED backlight on 50%
+
+        // example of drawing lines
+        for (int x = 0; x < WIDTH ; x+=10) {
+            // x0,y0,x1,y1,type 0-white,1-black,2-dotted
+            lcd.drawLine(0,0,x,HEIGHT,2);
+        }
+        lcd.refresh();  // refresh after drawing shapes
+        wait(5.0);
+
+
+        lcd.clear();
+        // example of how to draw circles
+        lcd.drawCircle(WIDTH/2,HEIGHT/2,20,FILL_BLACK);  // x,y,radius,black fill
+        lcd.drawCircle(WIDTH/2,HEIGHT/2,10,FILL_WHITE);  // x,y,radius,white fill
+        lcd.drawCircle(WIDTH/2,HEIGHT/2,30,FILL_TRANSPARENT);  // x,y,radius,transparent with outline
+        lcd.refresh();  // refresh after drawing shapes
+        wait(5.0);
+
+        lcd.clear();
+        // example of how to draw rectangles
+        //          origin x,y,width,height,type
+        lcd.drawRect(10,10,50,30,FILL_BLACK);  // filled black rectangle
+        lcd.drawRect(15,15,20,10,FILL_WHITE);  // filled white rectange (no outline)
+        lcd.drawRect(2,2,70,40,FILL_TRANSPARENT);    // transparent, just outline
+        lcd.refresh();  // refresh after drawing shapes
+        wait(5.0);
+
+    }
+}
+
+
+@endcode
+*/
+class N5110
+{
+private:
+// objects
+    SPI         *_spi;
+    PwmOut      *_led;
+    DigitalOut  *_pwr;
+    DigitalOut  *_sce;
+    DigitalOut  *_rst;
+    DigitalOut  *_dc;
+
+// variables
+    unsigned char buffer[84][6];  // screen buffer - the 6 is for the banks - each one is 8 bits;
+
+public:
+    /** Create a N5110 object connected to the specified pins
+    *
+    * @param pwr  Pin connected to Vcc on the LCD display (pin 1)
+    * @param sce  Pin connected to chip enable (pin 3)
+    * @param rst  Pin connected to reset (pin 4)
+    * @param dc   Pin connected to data/command select (pin 5)
+    * @param mosi Pin connected to data input (MOSI) (pin 6)
+    * @param sclk Pin connected to serial clock (SCLK) (pin 7)
+    * @param led  Pin connected to LED backlight (must be PWM) (pin 8)
+    *
+    */
+    N5110(PinName const pwrPin,
+          PinName const scePin,
+          PinName const rstPin,
+          PinName const dcPin,
+          PinName const mosiPin,
+          PinName const sclkPin,
+          PinName const ledPin);
+
+    /** Create a N5110 object connected to the specified pins (Vcc to +3V3)
+    *
+    * @param sce  Pin connected to chip enable (pin 3)
+    * @param rst  Pin connected to reset (pin 4)
+    * @param dc   Pin connected to data/command select (pin 5)
+    * @param mosi Pin connected to data input (MOSI) (pin 6)
+    * @param sclk Pin connected to serial clock (SCLK) (pin 7)
+    * @param led  Pin connected to LED backlight (must be PWM) (pin 8)
+    *
+    */
+    N5110(PinName const scePin,
+          PinName const rstPin,
+          PinName const dcPin,
+          PinName const mosiPin,
+          PinName const sclkPin,
+          PinName const ledPin);
+
+    /**
+     * Free allocated memory when object goes out of scope
+     */
+    ~N5110();
+
+    /** Initialise display
+    *
+    *   Powers up the display and turns on backlight (50% brightness default).
+    *   Sets the display up in horizontal addressing mode and with normal video mode.
+    */
+    void init();
+
+    /** Turn off
+    *
+    *   Powers down the display and turns of the backlight.
+    *   Needs to be reinitialised before being re-used.
+    */
+    void turnOff();
+
+    /** Clear
+    *
+    *   Clears the screen buffer.
+    */
+    void clear();
+
+    /** Set screen constrast
+    *   @param constrast - float in range 0.0 to 1.0 (0.40 to 0.60 is usually a good value)
+    */
+    void setContrast(float contrast);
+    
+    /** Turn on normal video mode (default)
+    *  Black on white
+    */
+    void normalMode();
+
+    /** Turn on inverse video mode (default)
+    *  White on black
+    */
+    void inverseMode();
+
+    /** Set Brightness
+    *
+    *   Sets brightness of LED backlight.
+    *   @param brightness - float in range 0.0 to 1.0
+    */
+    void setBrightness(float const brightness);
+
+    /** Print String
+    *
+    *   Prints a string of characters to the screen buffer. String is cut-off after the 83rd pixel.
+    *   @param x - the column number (0 to 83)
+    *   @param y - the row number (0 to 5) - the display is split into 6 banks - each bank can be considered a row
+    */
+    void printString(char const         *str,
+                     unsigned int const  x,
+                     unsigned int const  y);
+
+    /** Print Character
+    *
+    *   Sends a character to the screen buffer.  Printed at the specified location. Character is cut-off after the 83rd pixel.
+    *   @param  c - the character to print. Can print ASCII as so printChar('C').
+    *   @param x - the column number (0 to 83)
+    *   @param y - the row number (0 to 5) - the display is split into 6 banks - each bank can be considered a row
+    */
+    void printChar(char const         c,
+                   unsigned int const x,
+                   unsigned int const y);
+
+    /**
+    * @brief Set a Pixel
+    *
+    * @param x     The x co-ordinate of the pixel (0 to 83)
+    * @param y     The y co-ordinate of the pixel (0 to 47)
+    * @param state The state of the pixel [true=black (default), false=white]
+    *
+    * @details This function sets the state of a pixel in the screen buffer.
+    *          The third parameter can be omitted,
+    */
+    void setPixel(unsigned int const x,
+                  unsigned int const y,
+                  bool const         state = true);
+
+    /**
+    *  @brief Clear a Pixel
+    *
+    *   @param  x - the x co-ordinate of the pixel (0 to 83)
+    *   @param  y - the y co-ordinate of the pixel (0 to 47)
+    *
+    *   @details This function clears pixel in the screen buffer
+    *
+    *   @deprecated Use setPixel(x, y, false) instead
+    */
+    void clearPixel(unsigned int const x,
+                    unsigned int const y)
+    __attribute__((deprecated("Use setPixel(x,y,false) instead")));
+
+    /** Get a Pixel
+    *
+    *   This function gets the status of a pixel in the screen buffer.
+    *   @param  x - the x co-ordinate of the pixel (0 to 83)
+    *   @param  y - the y co-ordinate of the pixel (0 to 47)
+    *   @returns
+    *       0           - pixel is clear
+    *       1    - pixel is set
+    */
+    int getPixel(unsigned int const x,
+                 unsigned int const y) const;
+
+    /** Refresh display
+    *
+    *   This functions sends the screen buffer to the display.
+    */
+    void refresh();
+
+    /** Randomise buffer
+    *
+    *   This function fills the buffer with random data.  Can be used to test the display.
+    *   A call to refresh() must be made to update the display to reflect the change in pixels.
+    *   The seed is not set and so the generated pattern will probably be the same each time.
+    *   TODO: Randomise the seed - maybe using the noise on the AnalogIn pins.
+    */
+    void randomiseBuffer();
+
+    /** Plot Array
+    *
+    *   This function plots a one-dimensional array in the buffer.
+    *   @param array[] - y values of the plot. Values should be normalised in the range 0.0 to 1.0. First 84 plotted.
+    */
+    void plotArray(float const array[]);
+
+    /** Draw Circle
+    *
+    *   This function draws a circle at the specified origin with specified radius in the screen buffer
+    *   Uses the midpoint circle algorithm.
+    *   @see http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
+    *   @param  x0     - x-coordinate of centre
+    *   @param  y0     - y-coordinate of centre
+    *   @param  radius - radius of circle in pixels
+    *   @param  fill   - fill-type for the shape
+    */
+    void drawCircle(unsigned int const x0,
+                    unsigned int const y0,
+                    unsigned int const radius,
+                    FillType const     fill);
+
+    /** Draw Line
+    *
+    *   This function draws a line between the specified points using linear interpolation.
+    *   @param  x0 - x-coordinate of first point
+    *   @param  y0 - y-coordinate of first point
+    *   @param  x1 - x-coordinate of last point
+    *   @param  y1 - y-coordinate of last point
+    *   @param  type - 0 white,1 black,2 dotted
+    */
+    void drawLine(unsigned int const x0,
+                  unsigned int const y0,
+                  unsigned int const x1,
+                  unsigned int const y1,
+                  unsigned int const type);
+
+    /** Draw Rectangle
+    *
+    *   This function draws a rectangle.
+    *   @param  x0 - x-coordinate of origin (top-left)
+    *   @param  y0 - y-coordinate of origin (top-left)
+    *   @param  width - width of rectangle
+    *   @param  height - height of rectangle
+    *   @param  fill   - fill-type for the shape
+    */
+    void drawRect(unsigned int const x0,
+                  unsigned int const y0,
+                  unsigned int const width,
+                  unsigned int const height,
+                  FillType const     fill);
+
+    /** Draw Sprite
+    *
+    *   This function draws a sprite as defined in a 2D array
+    *   @param  x0 - x-coordinate of origin (top-left)
+    *   @param  y0 - y-coordinate of origin (top-left)
+    *   @param  nrows - number of rows in sprite
+    *   @param  ncols - number of columns in sprite
+    *   @param  sprite - 2D array representing the sprite
+    */
+    void drawSprite(int x0,
+                    int y0,
+                    int nrows,
+                    int ncols,
+                    int *sprite);
+
+
+private:
+// methods
+    void setXYAddress(unsigned int const x,
+                      unsigned int const y);
+    void initSPI();
+    void turnOn();
+    void reset();
+    void clearRAM();
+    void sendCommand(unsigned char command);
+    void sendData(unsigned char data);
+    void setTempCoefficient(char tc);  // 0 to 3
+    void setBias(char bias);  // 0 to 7
+};
+
+const unsigned char font5x7[480] = {
+    0x00, 0x00, 0x00, 0x00, 0x00,// (space)
+    0x00, 0x00, 0x5F, 0x00, 0x00,// !
+    0x00, 0x07, 0x00, 0x07, 0x00,// "
+    0x14, 0x7F, 0x14, 0x7F, 0x14,// #
+    0x24, 0x2A, 0x7F, 0x2A, 0x12,// $
+    0x23, 0x13, 0x08, 0x64, 0x62,// %
+    0x36, 0x49, 0x55, 0x22, 0x50,// &
+    0x00, 0x05, 0x03, 0x00, 0x00,// '
+    0x00, 0x1C, 0x22, 0x41, 0x00,// (
+    0x00, 0x41, 0x22, 0x1C, 0x00,// )
+    0x08, 0x2A, 0x1C, 0x2A, 0x08,// *
+    0x08, 0x08, 0x3E, 0x08, 0x08,// +
+    0x00, 0x50, 0x30, 0x00, 0x00,// ,
+    0x08, 0x08, 0x08, 0x08, 0x08,// -
+    0x00, 0x60, 0x60, 0x00, 0x00,// .
+    0x20, 0x10, 0x08, 0x04, 0x02,// /
+    0x3E, 0x51, 0x49, 0x45, 0x3E,// 0
+    0x00, 0x42, 0x7F, 0x40, 0x00,// 1
+    0x42, 0x61, 0x51, 0x49, 0x46,// 2
+    0x21, 0x41, 0x45, 0x4B, 0x31,// 3
+    0x18, 0x14, 0x12, 0x7F, 0x10,// 4
+    0x27, 0x45, 0x45, 0x45, 0x39,// 5
+    0x3C, 0x4A, 0x49, 0x49, 0x30,// 6
+    0x01, 0x71, 0x09, 0x05, 0x03,// 7
+    0x36, 0x49, 0x49, 0x49, 0x36,// 8
+    0x06, 0x49, 0x49, 0x29, 0x1E,// 9
+    0x00, 0x36, 0x36, 0x00, 0x00,// :
+    0x00, 0x56, 0x36, 0x00, 0x00,// ;
+    0x00, 0x08, 0x14, 0x22, 0x41,// <
+    0x14, 0x14, 0x14, 0x14, 0x14,// =
+    0x41, 0x22, 0x14, 0x08, 0x00,// >
+    0x02, 0x01, 0x51, 0x09, 0x06,// ?
+    0x32, 0x49, 0x79, 0x41, 0x3E,// @
+    0x7E, 0x11, 0x11, 0x11, 0x7E,// A
+    0x7F, 0x49, 0x49, 0x49, 0x36,// B
+    0x3E, 0x41, 0x41, 0x41, 0x22,// C
+    0x7F, 0x41, 0x41, 0x22, 0x1C,// D
+    0x7F, 0x49, 0x49, 0x49, 0x41,// E
+    0x7F, 0x09, 0x09, 0x01, 0x01,// F
+    0x3E, 0x41, 0x41, 0x51, 0x32,// G
+    0x7F, 0x08, 0x08, 0x08, 0x7F,// H
+    0x00, 0x41, 0x7F, 0x41, 0x00,// I
+    0x20, 0x40, 0x41, 0x3F, 0x01,// J
+    0x7F, 0x08, 0x14, 0x22, 0x41,// K
+    0x7F, 0x40, 0x40, 0x40, 0x40,// L
+    0x7F, 0x02, 0x04, 0x02, 0x7F,// M
+    0x7F, 0x04, 0x08, 0x10, 0x7F,// N
+    0x3E, 0x41, 0x41, 0x41, 0x3E,// O
+    0x7F, 0x09, 0x09, 0x09, 0x06,// P
+    0x3E, 0x41, 0x51, 0x21, 0x5E,// Q
+    0x7F, 0x09, 0x19, 0x29, 0x46,// R
+    0x46, 0x49, 0x49, 0x49, 0x31,// S
+    0x01, 0x01, 0x7F, 0x01, 0x01,// T
+    0x3F, 0x40, 0x40, 0x40, 0x3F,// U
+    0x1F, 0x20, 0x40, 0x20, 0x1F,// V
+    0x7F, 0x20, 0x18, 0x20, 0x7F,// W
+    0x63, 0x14, 0x08, 0x14, 0x63,// X
+    0x03, 0x04, 0x78, 0x04, 0x03,// Y
+    0x61, 0x51, 0x49, 0x45, 0x43,// Z
+    0x00, 0x00, 0x7F, 0x41, 0x41,// [
+    0x02, 0x04, 0x08, 0x10, 0x20,// "\"
+    0x41, 0x41, 0x7F, 0x00, 0x00,// ]
+    0x04, 0x02, 0x01, 0x02, 0x04,// ^
+    0x40, 0x40, 0x40, 0x40, 0x40,// _
+    0x00, 0x01, 0x02, 0x04, 0x00,// `
+    0x20, 0x54, 0x54, 0x54, 0x78,// a
+    0x7F, 0x48, 0x44, 0x44, 0x38,// b
+    0x38, 0x44, 0x44, 0x44, 0x20,// c
+    0x38, 0x44, 0x44, 0x48, 0x7F,// d
+    0x38, 0x54, 0x54, 0x54, 0x18,// e
+    0x08, 0x7E, 0x09, 0x01, 0x02,// f
+    0x08, 0x14, 0x54, 0x54, 0x3C,// g
+    0x7F, 0x08, 0x04, 0x04, 0x78,// h
+    0x00, 0x44, 0x7D, 0x40, 0x00,// i
+    0x20, 0x40, 0x44, 0x3D, 0x00,// j
+    0x00, 0x7F, 0x10, 0x28, 0x44,// k
+    0x00, 0x41, 0x7F, 0x40, 0x00,// l
+    0x7C, 0x04, 0x18, 0x04, 0x78,// m
+    0x7C, 0x08, 0x04, 0x04, 0x78,// n
+    0x38, 0x44, 0x44, 0x44, 0x38,// o
+    0x7C, 0x14, 0x14, 0x14, 0x08,// p
+    0x08, 0x14, 0x14, 0x18, 0x7C,// q
+    0x7C, 0x08, 0x04, 0x04, 0x08,// r
+    0x48, 0x54, 0x54, 0x54, 0x20,// s
+    0x04, 0x3F, 0x44, 0x40, 0x20,// t
+    0x3C, 0x40, 0x40, 0x20, 0x7C,// u
+    0x1C, 0x20, 0x40, 0x20, 0x1C,// v
+    0x3C, 0x40, 0x30, 0x40, 0x3C,// w
+    0x44, 0x28, 0x10, 0x28, 0x44,// x
+    0x0C, 0x50, 0x50, 0x50, 0x3C,// y
+    0x44, 0x64, 0x54, 0x4C, 0x44,// z
+    0x00, 0x08, 0x36, 0x41, 0x00,// {
+    0x00, 0x00, 0x7F, 0x00, 0x00,// |
+    0x00, 0x41, 0x36, 0x08, 0x00,// }
+    0x08, 0x08, 0x2A, 0x1C, 0x08,// ->
+    0x08, 0x1C, 0x2A, 0x08, 0x08 // <-
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Paddle/Paddle.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,125 @@
+#include "Paddle.h"
+
+FXOS8700CQ accelerometer(I2C_SDA,I2C_SCL);
+    
+Paddle::Paddle()
+{
+
+}
+
+Paddle::~Paddle()
+{
+
+}
+
+void Paddle::init(int y,int height,int width)
+{
+    _x = WIDTH/2 - width/2;  // initialised in the centre of the screen
+    _y = y;  // fixed at the bottom of the screen, initialised by _paddley in engine
+    _height = height; // paddle height, defined at init
+    _width = width; // paddle width, defined at init
+    _sens = 0.5; // default sensitivity of tilt/joystick
+    _speed = 4;  // default speed = _speed * _sens
+    _lives = 6;  // number of lives = number of LEDs
+    _tilt = false;  //used to choose between motion methods
+
+}
+
+
+void Paddle::draw(N5110 &lcd)
+{
+    lcd.drawRect(_x,_y,_width,_height,FILL_BLACK);   // draw paddle at specified coordinates
+}
+
+
+void Paddle::update(Direction d,float mag)   // update the paddle's properties
+{
+    if (_tilt == false) {
+        _speed = int(mag*10.0f);  // scale is arbitrary
+
+        // update x value depending on direction of movement
+        // West is decrement as origin is at the top-left so decreasing moves left
+        if (d == W) {
+            _x-=_speed * _sens;   // adjust the x coordinate accordingly. Sensitivity scales the speed of the paddle
+        } else if (d == E) {
+            _x+=_speed * _sens;
+        }
+
+        // censure that the paddle doesn't go off screen
+        if (_x < 1) {
+            _x = 1;
+        }
+        if (_x > WIDTH - _width - 1) {
+            _x = WIDTH - _width - 1;
+        }
+    } else if (_tilt == true) {
+        accelerometer.init();     // initialise the accelerometer
+        Data values = accelerometer.get_values();     // retrieve a struct of values
+        float roll_rad = atan2(values.ay,values.az);  // use values to calculate the roll angle in radians
+        _speed = int((roll_rad*(360/3.14159265))*0.3f);  // convert to degrees and scale
+        _x -= _speed * _sens;  // adjust the x coordinate accordingly
+    }
+
+    // ensure that the paddle doesn't go off screen
+    if (_x < 1) {
+        _x = 1;
+    }
+    if (_x > WIDTH - _width - 1) {
+        _x = WIDTH - _width - 1;
+    }
+}
+
+
+int Paddle::get_lives()  // returns the integer _lives
+{
+    return _lives;
+}
+
+
+void Paddle::lose_life()  // decrements the member variable _lives
+{
+    _lives--;
+}
+
+
+void Paddle::inc_life()  // increment the member variable _lives
+{
+    _lives++;
+}
+
+
+void Paddle::reset_lives()
+{
+    _lives = 6;
+}
+
+
+Vector2D Paddle::get_pos()  // returns the vector of the paddle's current position
+{
+    Vector2D p = {_x,_y};
+    return p;
+}
+
+
+void Paddle::set_tilt()    // sets the paddle motion option to tilt i.e. the member variable _tilt = true
+{
+    _tilt = true;
+}
+
+
+void Paddle::set_joy()     // sets the paddle motion option to joystick
+{
+    _tilt = false;
+}
+
+
+void Paddle::recentre()    // moves the paddle back to the centre of the screen (used after victory screen to restart)
+{
+    _x = WIDTH/2 - PADDLE_WIDTH/2;  // centre of the screen
+}
+
+
+void Paddle::set_sens(float sens)  // sets the member variable _sens to an input
+{
+    _sens = sens;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Paddle/Paddle.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,90 @@
+#ifndef PADDLE_H
+#define PADDLE_H
+
+#include "mbed.h"
+#include "N5110.h"
+#include "Gamepad.h"
+
+#define PADDLE_WIDTH 15
+#define PADDLE_HEIGHT 2
+
+/** Paddle Class
+@author James Heavey, University of Leeds
+@brief Controls the paddle in the Breakout game
+@date May 2019
+*/
+
+class Paddle
+{
+public:
+    
+    /** Constructor declaration */
+    Paddle();
+    
+    /** Destructor declaration */
+    ~Paddle();
+    
+    /** Initialise Paddle attributes
+    * @param y @details initialises the _y value at the correct value on screen
+    * @param height @details sets the height of the paddle
+    * @param width @details sets the width of the paddle
+    */
+    void init(int y,int height,int width);
+    
+    /** Draws the Paddle at at its current coordinates on the LCD
+    * @param lcd @details a N5110 pointer
+    */
+    void draw(N5110 &lcd);
+    
+    /** Update the Paddle's velocity according to the Gamepad input, then update coordinates accordingly
+    * @param pad @details a Gamepad pointer
+    */
+    void update(Direction d,float mag);
+    
+    /** Decrement the variable _lives */
+    void lose_life();
+    
+    /** Increment the variable _lives */
+    void inc_life();
+    
+    /** Set the variable _lives to 6 */
+    void reset_lives();
+    
+    /** Retrieves the Paddle's lives remaining 
+    * @returns the variable _lives
+    */
+    int get_lives();
+    
+    /** Sets the varible _tilt to true */
+    void set_tilt();
+    
+    /** Set the variable _tilt to false */
+    void set_joy();
+    
+    /** Set the variable _sens 
+    * @param sens @details sets _sens to local variable sens
+    */
+    void set_sens(float sens);
+    
+    /** Set the _x coordinate to the middle of the screen */
+    void recentre();
+    
+    /** Retrieves the Paddle's x and y velocities 
+    * @returns a vector of the x and y velocities
+    */
+    Vector2D get_pos();
+
+private:
+
+    int _height;
+    int _width;
+    int _x;
+    int _y;
+    int _speed;
+    int _score;
+    int _lives;
+    float _sens;
+    bool _tilt;
+
+};
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/FATFileSystem/ChaN/ccsbcs.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,348 @@
+/*------------------------------------------------------------------------*/
+/* Unicode - Local code bidirectional converter  (C)ChaN, 2015            */
+/* (SBCS code pages)                                                      */
+/*------------------------------------------------------------------------*/
+/*  437   U.S.
+/   720   Arabic
+/   737   Greek
+/   771   KBL
+/   775   Baltic
+/   850   Latin 1
+/   852   Latin 2
+/   855   Cyrillic
+/   857   Turkish
+/   860   Portuguese
+/   861   Icelandic
+/   862   Hebrew
+/   863   Canadian French
+/   864   Arabic
+/   865   Nordic
+/   866   Russian
+/   869   Greek 2
+*/
+
+#include "ff.h"
+
+
+#if _CODE_PAGE == 437
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP437(0x80-0xFF) to Unicode conversion table */
+	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+	0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+	0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+	0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+	0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 720
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP720(0x80-0xFF) to Unicode conversion table */
+	0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627,
+	0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+	0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+	0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A,
+	0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 737
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP737(0x80-0xFF) to Unicode conversion table */
+	0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
+	0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
+	0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+	0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+	0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E,
+	0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 771
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP771(0x80-0xFF) to Unicode conversion table */
+	0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+	0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+	0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+	0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x0104, 0x0105, 0x010C, 0x010D,
+	0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+	0x0118, 0x0119, 0x0116, 0x0117, 0x012E, 0x012F, 0x0160, 0x0161, 0x0172, 0x0173, 0x016A, 0x016B, 0x017D, 0x017E, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 775
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP775(0x80-0xFF) to Unicode conversion table */
+	0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5,
+	0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4,
+	0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D,
+	0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+	0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019,
+	0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 850
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP850(0x80-0xFF) to Unicode conversion table */
+	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+	0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
+	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+	0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+	0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
+	0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 852
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP852(0x80-0xFF) to Unicode conversion table */
+	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,
+	0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,
+	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+	0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580,
+	0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4,
+	0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 855
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP855(0x80-0xFF) to Unicode conversion table */
+	0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,
+	0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,
+	0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+	0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580,
+	0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116,
+	0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 857
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP857(0x80-0xFF) to Unicode conversion table */
+	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5,
+	0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F,
+	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+	0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+	0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4,
+	0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 860
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP860(0x80-0xFF) to Unicode conversion table */
+	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2,
+	0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3,
+	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+	0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+	0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+	0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 861
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP861(0x80-0xFF) to Unicode conversion table */
+	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5,
+	0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,
+	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+	0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+	0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+	0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 862
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP862(0x80-0xFF) to Unicode conversion table */
+	0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+	0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+	0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+	0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+	0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 863
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP863(0x80-0xFF) to Unicode conversion table */
+	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0,
+	0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192,
+	0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+	0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+	0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2219,
+	0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 864
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP864(0x80-0xFF) to Unicode conversion table */
+	0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518,
+	0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000,
+	0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5,
+	0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F,
+	0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9,
+	0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9,
+	0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1,
+	0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0x0000
+};
+
+#elif _CODE_PAGE == 865
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP865(0x80-0xFF) to Unicode conversion table */
+	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+	0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,
+	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+	0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+	0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+	0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 866
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP866(0x80-0xFF) to Unicode conversion table */
+	0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+	0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+	0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+	0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+	0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+	0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 869
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {	/*  CP869(0x80-0xFF) to Unicode conversion table */
+	0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389,
+	0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF,
+	0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510,
+	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0A30, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3,
+	0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580,
+	0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384,
+	0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0
+};
+
+#endif
+
+
+#if !_TBLDEF || !_USE_LFN
+#error This file is not needed at current configuration. Remove from the project.
+#endif
+
+
+
+
+WCHAR ff_convert (	/* Converted character, Returns zero on error */
+	WCHAR	chr,	/* Character code to be converted */
+	UINT	dir		/* 0: Unicode to OEM code, 1: OEM code to Unicode */
+)
+{
+	WCHAR c;
+
+
+	if (chr < 0x80) {	/* ASCII */
+		c = chr;
+
+	} else {
+		if (dir) {		/* OEM code to Unicode */
+			c = (chr >= 0x100) ? 0 : Tbl[chr - 0x80];
+
+		} else {		/* Unicode to OEM code */
+			for (c = 0; c < 0x80; c++) {
+				if (chr == Tbl[c]) break;
+			}
+			c = (c + 0x80) & 0xFF;
+		}
+	}
+
+	return c;
+}
+
+
+
+
+WCHAR ff_wtoupper (	/* Returns upper converted character */
+	WCHAR chr		/* Unicode character to be upper converted */
+)
+{
+	static const WCHAR lower[] = {	/* Lower case characters to be converted */
+	/* Latin Supplement */			0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,
+	/* Latin Extended-A */			0x101,0x103,0x105,0x107,0x109,0x10B,0x10D,0x10F,0x111,0x113,0x115,0x117,0x119,0x11B,0x11D,0x11F,0x121,0x123,0x125,0x127,0x129,0x12B,0x12D,0x12F,0x131,0x133,0x135,0x137,0x13A,0x13C,0x13E,0x140,0x142,0x144,0x146,0x148,0x14B,0x14D,0x14F,0x151,0x153,0x155,0x157,0x159,0x15B,0x15D,0x15F,0x161,0x163,0x165,0x167,0x169,0x16B,0x16D,0x16F,0x171,0x173,0x175,0x177,0x17A,0x17C,0x17E,
+	/* Latin Extended-B */			0x183,0x185,0x188,0x18C,0x192,0x199,0x1A1,0x1A3,0x1A8,0x1AD,0x1B0,0x1B4,0x1B6,0x1B9,0x1BD,0x1C6,0x1C9,0x1CC,0x1CE,0x1D0,0x1D2,0x1D4,0x1D6,0x1D8,0x1DA,0x1DC,0x1DD,0x1DF,0x1E1,0x1E3,0x1E5,0x1E7,0x1E9,0x1EB,0x1ED,0x1EF,0x1F3,0x1F5,0x1FB,0x1FD,0x1FF,0x201,0x203,0x205,0x207,0x209,0x20B,0x20D,0x20F,0x211,0x213,0x215,0x217,
+	/* Greek, Coptic */				0x3B1,0x3B2,0x3B3,0x3B4,0x3B5,0x3B6,0x3B7,0x3B8,0x3B9,0x3BA,0x3BB,0x3BC,0x3BD,0x3BE,0x3BF,0x3C0,0x3C1,0x3C3,0x3C4,0x3C5,0x3C6,0x3C7,0x3C8,0x3C9,0x3CA,0x3CB,0x3CC,0x3CD,0x3CE,0x3E3,0x3E5,0x3E7,0x3E9,0x3EB,
+	/* Cyrillic */					0x430,0x431,0x432,0x433,0x434,0x435,0x436,0x437,0x438,0x439,0x43A,0x43B,0x43C,0x43D,0x43E,0x43F,0x440,0x441,0x442,0x443,0x444,0x445,0x446,0x447,0x448,0x449,0x44A,0x44B,0x44C,0x44D,0x44E,0x44F,0x452,0x453,0x454,0x455,0x456,0x457,0x458,0x459,0x45A,0x45B,0x45C,0x45E,0x45F,0x461,0x463,0x465,0x467,0x469,0x46B,0x46D,0x46F,0x471,0x473,0x475,0x477,0x479,0x47B,0x47D,0x47F,0x481,0x491,0x493,0x495,0x497,0x499,0x49B,0x49D,0x49F,0x4A1,0x4A3,0x4A5,0x4A7,0x4A9,0x4AB,0x4AD,0x4AF,0x4B1,0x4B3,0x4B5,0x4B7,0x4B9,0x4BB,0x4BD,0x4BF,0x4C2,0x4C4,0x4C8,0x4D1,0x4D3,0x4D5,0x4D7,0x4D9,0x4DB,0x4DD,0x4DF,0x4E1,0x4E3,0x4E5,0x4E7,0x4E9,0x4EB,0x4ED,0x4EF,0x4F1,0x4F3,0x4F5,0x4F9,
+	/* Armenian */					0x561,0x562,0x563,0x564,0x565,0x566,0x567,0x568,0x569,0x56A,0x56B,0x56C,0x56D,0x56E,0x56F,0x570,0x571,0x572,0x573,0x574,0x575,0x576,0x577,0x578,0x579,0x57A,0x57B,0x57C,0x57D,0x57E,0x57F,0x580,0x581,0x582,0x583,0x584,0x585,0x586,
+	/* Latin Extended Additional */	0x1E01,0x1E03,0x1E05,0x1E07,0x1E09,0x1E0B,0x1E0D,0x1E0F,0x1E11,0x1E13,0x1E15,0x1E17,0x1E19,0x1E1B,0x1E1D,0x1E1F,0x1E21,0x1E23,0x1E25,0x1E27,0x1E29,0x1E2B,0x1E2D,0x1E2F,0x1E31,0x1E33,0x1E35,0x1E37,0x1E39,0x1E3B,0x1E3D,0x1E3F,0x1E41,0x1E43,0x1E45,0x1E47,0x1E49,0x1E4B,0x1E4D,0x1E4F,0x1E51,0x1E53,0x1E55,0x1E57,0x1E59,0x1E5B,0x1E5D,0x1E5F,0x1E61,0x1E63,0x1E65,0x1E67,0x1E69,0x1E6B,0x1E6D,0x1E6F,0x1E71,0x1E73,0x1E75,0x1E77,0x1E79,0x1E7B,0x1E7D,0x1E7F,0x1E81,0x1E83,0x1E85,0x1E87,0x1E89,0x1E8B,0x1E8D,0x1E8F,0x1E91,0x1E93,0x1E95,0x1E97,0x1E99,0x1E9B,0x1E9D,0x1E9F,0x1EA1,0x1EA3,0x1EA5,0x1EA7,0x1EA9,0x1EAB,0x1EAD,0x1EAF,0x1EB1,0x1EB3,0x1EB5,0x1EB7,0x1EB9,0x1EBB,0x1EBD,0x1EBF,0x1EC1,0x1EC3,0x1EC5,0x1EC7,0x1EC9,0x1ECB,0x1ECD,0x1ECF,0x1ED1,0x1ED3,0x1ED5,0x1ED7,0x1ED9,0x1EDB,0x1EDD,0x1EDF,0x1EE1,0x1EE3,0x1EE5,0x1EE7,0x1EE9,0x1EEB,0x1EED,0x1EEF,0x1EF1,0x1EF3,0x1EF5,0x1EF7,0x1EF9,
+	/* Number forms */				0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,0x217A,0x217B,0x217C,0x217D,0x217E,0x217F,
+	/* Full-width */				0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,0xFF58,0xFF59,0xFF5A
+	};
+	static const WCHAR upper[] = {	/* Upper case characters correspond to lower[] */
+									0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x178,
+									0x100,0x102,0x104,0x106,0x108,0x10A,0x10C,0x10E,0x110,0x112,0x114,0x116,0x118,0x11A,0x11C,0x11E,0x120,0x122,0x124,0x126,0x128,0x12A,0x12C,0x12E,0x130,0x132,0x134,0x136,0x139,0x13B,0x13D,0x13F,0x141,0x143,0x145,0x147,0x14A,0x14C,0x14E,0x150,0x152,0x154,0x156,0x158,0x15A,0x15C,0x15E,0x160,0x162,0x164,0x166,0x168,0x16A,0x16C,0x16E,0x170,0x172,0x174,0x176,0x179,0x17B,0x17D,
+									0x182,0x184,0x187,0x18B,0x191,0x198,0x1A0,0x1A2,0x1A7,0x1AC,0x1AF,0x1B3,0x1B5,0x1B8,0x1BC,0x1C4,0x1C7,0x1CA,0x1CD,0x1CF,0x1D1,0x1D3,0x1D5,0x1D7,0x1D9,0x1DB,0x18E,0x1DE,0x1E0,0x1E2,0x1E4,0x1E6,0x1E8,0x1EA,0x1EC,0x1EE,0x1F1,0x1F4,0x1FA,0x1FC,0x1FE,0x200,0x202,0x204,0x206,0x208,0x20A,0x20C,0x20E,0x210,0x212,0x214,0x216,
+									0x391,0x392,0x393,0x394,0x395,0x396,0x397,0x398,0x399,0x39A,0x39B,0x39C,0x39D,0x39E,0x39F,0x3A0,0x3A1,0x3A3,0x3A4,0x3A5,0x3A6,0x3A7,0x3A8,0x3A9,0x3AA,0x3AB,0x38C,0x38E,0x38F,0x3E2,0x3E4,0x3E6,0x3E8,0x3EA,
+									0x410,0x411,0x412,0x413,0x414,0x415,0x416,0x417,0x418,0x419,0x41A,0x41B,0x41C,0x41D,0x41E,0x41F,0x420,0x421,0x422,0x423,0x424,0x425,0x426,0x427,0x428,0x429,0x42A,0x42B,0x42C,0x42D,0x42E,0x42F,0x402,0x403,0x404,0x405,0x406,0x407,0x408,0x409,0x40A,0x40B,0x40C,0x40E,0x40F,0x460,0x462,0x464,0x466,0x468,0x46A,0x46C,0x46E,0x470,0x472,0x474,0x476,0x478,0x47A,0x47C,0x47E,0x480,0x490,0x492,0x494,0x496,0x498,0x49A,0x49C,0x49E,0x4A0,0x4A2,0x4A4,0x4A6,0x4A8,0x4AA,0x4AC,0x4AE,0x4B0,0x4B2,0x4B4,0x4B6,0x4B8,0x4BA,0x4BC,0x4BE,0x4C1,0x4C3,0x5C7,0x4D0,0x4D2,0x4D4,0x4D6,0x4D8,0x4DA,0x4DC,0x4DE,0x4E0,0x4E2,0x4E4,0x4E6,0x4E8,0x4EA,0x4EC,0x4EE,0x4F0,0x4F2,0x4F4,0x4F8,
+									0x531,0x532,0x533,0x534,0x535,0x536,0x537,0x538,0x539,0x53A,0x53B,0x53C,0x53D,0x53E,0x53F,0x540,0x541,0x542,0x543,0x544,0x545,0x546,0x547,0x548,0x549,0x54A,0x54B,0x54C,0x54D,0x54E,0x54F,0x550,0x551,0x552,0x553,0x554,0x555,0x556,
+									0x1E00,0x1E02,0x1E04,0x1E06,0x1E08,0x1E0A,0x1E0C,0x1E0E,0x1E10,0x1E12,0x1E14,0x1E16,0x1E18,0x1E1A,0x1E1C,0x1E1E,0x1E20,0x1E22,0x1E24,0x1E26,0x1E28,0x1E2A,0x1E2C,0x1E2E,0x1E30,0x1E32,0x1E34,0x1E36,0x1E38,0x1E3A,0x1E3C,0x1E3E,0x1E40,0x1E42,0x1E44,0x1E46,0x1E48,0x1E4A,0x1E4C,0x1E4E,0x1E50,0x1E52,0x1E54,0x1E56,0x1E58,0x1E5A,0x1E5C,0x1E5E,0x1E60,0x1E62,0x1E64,0x1E66,0x1E68,0x1E6A,0x1E6C,0x1E6E,0x1E70,0x1E72,0x1E74,0x1E76,0x1E78,0x1E7A,0x1E7C,0x1E7E,0x1E80,0x1E82,0x1E84,0x1E86,0x1E88,0x1E8A,0x1E8C,0x1E8E,0x1E90,0x1E92,0x1E94,0x1E96,0x1E98,0x1E9A,0x1E9C,0x1E9E,0x1EA0,0x1EA2,0x1EA4,0x1EA6,0x1EA8,0x1EAA,0x1EAC,0x1EAE,0x1EB0,0x1EB2,0x1EB4,0x1EB6,0x1EB8,0x1EBA,0x1EBC,0x1EBE,0x1EC0,0x1EC2,0x1EC4,0x1EC6,0x1EC8,0x1ECA,0x1ECC,0x1ECE,0x1ED0,0x1ED2,0x1ED4,0x1ED6,0x1ED8,0x1EDA,0x1EDC,0x1EDE,0x1EE0,0x1EE2,0x1EE4,0x1EE6,0x1EE8,0x1EEA,0x1EEC,0x1EEE,0x1EF0,0x1EF2,0x1EF4,0x1EF6,0x1EF8,
+									0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,0x216A,0x216B,0x216C,0x216D,0x216E,0x216F,
+									0xFF21,0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,0xFF28,0xFF29,0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,0xFF38,0xFF39,0xFF3A
+	};
+	UINT i, n, hi, li;
+
+
+	if (chr < 0x80) {	/* ASCII characters (acceleration) */
+		if (chr >= 0x61 && chr <= 0x7A) chr -= 0x20;
+
+	} else {			/* Non ASCII characters (table search) */
+		n = 12; li = 0; hi = sizeof lower / sizeof lower[0];
+		do {
+			i = li + (hi - li) / 2;
+			if (chr == lower[i]) break;
+			if (chr > lower[i]) li = i; else hi = i;
+		} while (--n);
+		if (n) chr = upper[i];
+	}
+
+	return chr;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/FATFileSystem/ChaN/diskio.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,117 @@
+/*-----------------------------------------------------------------------*/
+/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2014        */
+/*-----------------------------------------------------------------------*/
+/* If a working storage control module is available, it should be        */
+/* attached to the FatFs via a glue function rather than modifying it.   */
+/* This is an example of glue functions to attach various exsisting      */
+/* storage control modules to the FatFs module with a defined API.       */
+/*-----------------------------------------------------------------------*/
+
+#include "diskio.h"
+#include "mbed_debug.h"
+#include "FATFileSystem.h"
+
+using namespace mbed;
+
+/*-----------------------------------------------------------------------*/
+/* Get Drive Status                                                      */
+/*-----------------------------------------------------------------------*/
+
+DSTATUS disk_status (
+    BYTE pdrv        /* Physical drive nmuber to identify the drive */
+)
+{
+    debug_if(FFS_DBG, "disk_status on pdrv [%d]\n", pdrv);
+    return (DSTATUS)FATFileSystem::_ffs[pdrv]->disk_status();
+}
+
+/*-----------------------------------------------------------------------*/
+/* Inidialize a Drive                                                    */
+/*-----------------------------------------------------------------------*/
+
+DSTATUS disk_initialize (
+    BYTE pdrv        /* Physical drive nmuber to identify the drive */
+)
+{
+    debug_if(FFS_DBG, "disk_initialize on pdrv [%d]\n", pdrv);
+    return (DSTATUS)FATFileSystem::_ffs[pdrv]->disk_initialize();
+}
+
+/*-----------------------------------------------------------------------*/
+/* Read Sector(s)                                                        */
+/*-----------------------------------------------------------------------*/
+
+DRESULT disk_read (
+    BYTE pdrv,       /* Physical drive nmuber to identify the drive */
+    BYTE* buff,      /* Data buffer to store read data */
+    DWORD sector,    /* Sector address in LBA */
+    UINT count       /* Number of sectors to read */
+)
+{
+    debug_if(FFS_DBG, "disk_read(sector %d, count %d) on pdrv [%d]\n", sector, count, pdrv);
+    if (FATFileSystem::_ffs[pdrv]->disk_read((uint8_t*)buff, sector, count))
+        return RES_PARERR;
+    else
+        return RES_OK;
+}
+
+/*-----------------------------------------------------------------------*/
+/* Write Sector(s)                                                       */
+/*-----------------------------------------------------------------------*/
+
+#if _USE_WRITE
+DRESULT disk_write (
+    BYTE pdrv,           /* Physical drive nmuber to identify the drive */
+    const BYTE* buff,    /* Data to be written */
+    DWORD sector,        /* Sector address in LBA */
+    UINT count           /* Number of sectors to write */
+)
+{
+    debug_if(FFS_DBG, "disk_write(sector %d, count %d) on pdrv [%d]\n", sector, count, pdrv);
+    if (FATFileSystem::_ffs[pdrv]->disk_write((uint8_t*)buff, sector, count))
+        return RES_PARERR;
+    else
+        return RES_OK;
+}
+#endif
+
+/*-----------------------------------------------------------------------*/
+/* Miscellaneous Functions                                               */
+/*-----------------------------------------------------------------------*/
+
+#if _USE_IOCTL
+DRESULT disk_ioctl (
+    BYTE pdrv,        /* Physical drive nmuber (0..) */
+    BYTE cmd,         /* Control code */
+    void* buff        /* Buffer to send/receive control data */
+)
+{
+    debug_if(FFS_DBG, "disk_ioctl(%d)\n", cmd);
+    switch(cmd) {
+        case CTRL_SYNC:
+            if(FATFileSystem::_ffs[pdrv] == NULL) {
+                return RES_NOTRDY;
+            } else if(FATFileSystem::_ffs[pdrv]->disk_sync()) {
+                return RES_ERROR;
+            }
+            return RES_OK;
+        case GET_SECTOR_COUNT:
+            if(FATFileSystem::_ffs[pdrv] == NULL) {
+                return RES_NOTRDY;
+            } else {
+                DWORD res = FATFileSystem::_ffs[pdrv]->disk_sectors();
+                if(res > 0) {
+                    *((DWORD*)buff) = res; // minimum allowed
+                    return RES_OK;
+                } else {
+                    return RES_ERROR;
+                }
+            }
+        case GET_BLOCK_SIZE:
+            *((DWORD*)buff) = 1; // default when not known
+            return RES_OK;
+
+    }
+    return RES_PARERR;
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/FATFileSystem/ChaN/diskio.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,80 @@
+/*-----------------------------------------------------------------------/
+/  Low level disk interface modlue include file   (C)ChaN, 2014          /
+/-----------------------------------------------------------------------*/
+
+#ifndef _DISKIO_DEFINED
+#define _DISKIO_DEFINED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define _USE_WRITE	1	/* 1: Enable disk_write function */
+#define _USE_IOCTL	1	/* 1: Enable disk_ioctl fucntion */
+
+#include "integer.h"
+
+
+/* Status of Disk Functions */
+typedef BYTE	DSTATUS;
+
+/* Results of Disk Functions */
+typedef enum {
+	RES_OK = 0,		/* 0: Successful */
+	RES_ERROR,		/* 1: R/W Error */
+	RES_WRPRT,		/* 2: Write Protected */
+	RES_NOTRDY,		/* 3: Not Ready */
+	RES_PARERR		/* 4: Invalid Parameter */
+} DRESULT;
+
+
+/*---------------------------------------*/
+/* Prototypes for disk control functions */
+
+
+DSTATUS disk_initialize (BYTE pdrv);
+DSTATUS disk_status (BYTE pdrv);
+DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
+DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
+DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
+
+
+/* Disk Status Bits (DSTATUS) */
+
+#define STA_NOINIT		0x01	/* Drive not initialized */
+#define STA_NODISK		0x02	/* No medium in the drive */
+#define STA_PROTECT		0x04	/* Write protected */
+
+
+/* Command code for disk_ioctrl fucntion */
+
+/* Generic command (Used by FatFs) */
+#define CTRL_SYNC			0	/* Complete pending write process (needed at _FS_READONLY == 0) */
+#define GET_SECTOR_COUNT	1	/* Get media size (needed at _USE_MKFS == 1) */
+#define GET_SECTOR_SIZE		2	/* Get sector size (needed at _MAX_SS != _MIN_SS) */
+#define GET_BLOCK_SIZE		3	/* Get erase block size (needed at _USE_MKFS == 1) */
+#define CTRL_TRIM			4	/* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */
+
+/* Generic command (Not used by FatFs) */
+#define CTRL_POWER			5	/* Get/Set power status */
+#define CTRL_LOCK			6	/* Lock/Unlock media removal */
+#define CTRL_EJECT			7	/* Eject media */
+#define CTRL_FORMAT			8	/* Create physical format on the media */
+
+/* MMC/SDC specific ioctl command */
+#define MMC_GET_TYPE		10	/* Get card type */
+#define MMC_GET_CSD			11	/* Get CSD */
+#define MMC_GET_CID			12	/* Get CID */
+#define MMC_GET_OCR			13	/* Get OCR */
+#define MMC_GET_SDSTAT		14	/* Get SD status */
+
+/* ATA/CF specific ioctl command */
+#define ATA_GET_REV			20	/* Get F/W revision */
+#define ATA_GET_MODEL		21	/* Get model name */
+#define ATA_GET_SN			22	/* Get serial number */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/FATFileSystem/ChaN/ff.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,4691 @@
+/*----------------------------------------------------------------------------/
+/  FatFs - FAT file system module  R0.11a                (C)ChaN, 2015        /
+/-----------------------------------------------------------------------------/
+/ FatFs module is a free software that opened under license policy of
+/ following conditions.
+/
+/ Copyright (C) 2015, ChaN, all right reserved.
+/
+/ 1. Redistributions of source code must retain the above copyright notice,
+/    this condition and the following disclaimer.
+/
+/ This software is provided by the copyright holder and contributors "AS IS"
+/ and any warranties related to this software are DISCLAIMED.
+/ The copyright owner or contributors be NOT LIABLE for any damages caused
+/ by use of this software.
+/----------------------------------------------------------------------------*/
+
+
+#include "ff.h"			/* Declarations of FatFs API */
+#include "diskio.h"		/* Declarations of disk I/O functions */
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Definitions
+
+---------------------------------------------------------------------------*/
+
+#if _FATFS != 64180	/* Revision ID */
+#error Wrong include file (ff.h).
+#endif
+
+
+/* Reentrancy related */
+#if _FS_REENTRANT
+#if _USE_LFN == 1
+#error Static LFN work area cannot be used at thread-safe configuration
+#endif
+#define	ENTER_FF(fs)		{ if (!lock_fs(fs)) return FR_TIMEOUT; }
+#define	LEAVE_FF(fs, res)	{ unlock_fs(fs, res); return res; }
+#else
+#define	ENTER_FF(fs)
+#define LEAVE_FF(fs, res)	return res
+#endif
+
+#define	ABORT(fs, res)		{ fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
+
+
+/* Definitions of sector size */
+#if (_MAX_SS < _MIN_SS) || (_MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096) || (_MIN_SS != 512 && _MIN_SS != 1024 && _MIN_SS != 2048 && _MIN_SS != 4096)
+#error Wrong sector size configuration
+#endif
+#if _MAX_SS == _MIN_SS
+#define	SS(fs)	((UINT)_MAX_SS)	/* Fixed sector size */
+#else
+#define	SS(fs)	((fs)->ssize)	/* Variable sector size */
+#endif
+
+
+/* Timestamp feature */
+#if _FS_NORTC == 1
+#if _NORTC_YEAR < 1980 || _NORTC_YEAR > 2107 || _NORTC_MON < 1 || _NORTC_MON > 12 || _NORTC_MDAY < 1 || _NORTC_MDAY > 31
+#error Invalid _FS_NORTC settings
+#endif
+#define GET_FATTIME()	((DWORD)(_NORTC_YEAR - 1980) << 25 | (DWORD)_NORTC_MON << 21 | (DWORD)_NORTC_MDAY << 16)
+#else
+#define GET_FATTIME()	get_fattime()
+#endif
+
+
+/* File access control feature */
+#if _FS_LOCK
+#if _FS_READONLY
+#error _FS_LOCK must be 0 at read-only configuration
+#endif
+typedef struct {
+	FATFS *fs;		/* Object ID 1, volume (NULL:blank entry) */
+	DWORD clu;		/* Object ID 2, directory (0:root) */
+	WORD idx;		/* Object ID 3, directory index */
+	WORD ctr;		/* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
+} FILESEM;
+#endif
+
+
+
+/* DBCS code ranges and SBCS upper conversion tables */
+
+#if _CODE_PAGE == 932	/* Japanese Shift-JIS */
+#define _DF1S	0x81	/* DBC 1st byte range 1 start */
+#define _DF1E	0x9F	/* DBC 1st byte range 1 end */
+#define _DF2S	0xE0	/* DBC 1st byte range 2 start */
+#define _DF2E	0xFC	/* DBC 1st byte range 2 end */
+#define _DS1S	0x40	/* DBC 2nd byte range 1 start */
+#define _DS1E	0x7E	/* DBC 2nd byte range 1 end */
+#define _DS2S	0x80	/* DBC 2nd byte range 2 start */
+#define _DS2E	0xFC	/* DBC 2nd byte range 2 end */
+
+#elif _CODE_PAGE == 936	/* Simplified Chinese GBK */
+#define _DF1S	0x81
+#define _DF1E	0xFE
+#define _DS1S	0x40
+#define _DS1E	0x7E
+#define _DS2S	0x80
+#define _DS2E	0xFE
+
+#elif _CODE_PAGE == 949	/* Korean */
+#define _DF1S	0x81
+#define _DF1E	0xFE
+#define _DS1S	0x41
+#define _DS1E	0x5A
+#define _DS2S	0x61
+#define _DS2E	0x7A
+#define _DS3S	0x81
+#define _DS3E	0xFE
+
+#elif _CODE_PAGE == 950	/* Traditional Chinese Big5 */
+#define _DF1S	0x81
+#define _DF1E	0xFE
+#define _DS1S	0x40
+#define _DS1E	0x7E
+#define _DS2S	0xA1
+#define _DS2E	0xFE
+
+#elif _CODE_PAGE == 437	/* U.S. */
+#define _DF1S	0
+#define _EXCVT {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
+				0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+				0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+				0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+				0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 720	/* Arabic */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+				0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+				0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+				0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+				0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 737	/* Greek */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+				0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
+				0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+				0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+				0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 771	/* KBL */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+				0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+				0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \
+				0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+				0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF}
+
+#elif _CODE_PAGE == 775	/* Baltic */
+#define _DF1S	0
+#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \
+				0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+				0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+				0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \
+				0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 850	/* Latin 1 */
+#define _DF1S	0
+#define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \
+				0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \
+				0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \
+				0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \
+				0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 852	/* Latin 2 */
+#define _DF1S	0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \
+				0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \
+				0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+				0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \
+				0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
+
+#elif _CODE_PAGE == 855	/* Cyrillic */
+#define _DF1S	0
+#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \
+				0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
+				0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
+				0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \
+				0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 857	/* Turkish */
+#define _DF1S	0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \
+				0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
+				0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+				0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \
+				0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 860	/* Portuguese */
+#define _DF1S	0
+#define _EXCVT {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \
+				0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+				0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+				0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+				0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 861	/* Icelandic */
+#define _DF1S	0
+#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \
+				0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+				0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+				0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+				0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 862	/* Hebrew */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+				0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+				0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+				0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+				0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 863	/* Canadian-French */
+#define _DF1S	0
+#define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \
+				0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \
+				0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+				0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+				0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 864	/* Arabic */
+#define _DF1S	0
+#define _EXCVT {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
+				0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+				0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+				0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+				0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 865	/* Nordic */
+#define _DF1S	0
+#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
+				0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+				0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+				0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+				0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 866	/* Russian */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+				0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+				0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+				0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+				0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 869	/* Greek 2 */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+				0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \
+				0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+				0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+				0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \
+				0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \
+				0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1	/* ASCII (for only non-LFN cfg) */
+#if _USE_LFN
+#error Cannot use LFN feature without valid code page.
+#endif
+#define _DF1S	0
+
+#else
+#error Unknown code page
+
+#endif
+
+
+/* Character code support macros */
+#define IsUpper(c)	(((c)>='A')&&((c)<='Z'))
+#define IsLower(c)	(((c)>='a')&&((c)<='z'))
+#define IsDigit(c)	(((c)>='0')&&((c)<='9'))
+
+#if _DF1S		/* Code page is DBCS */
+
+#ifdef _DF2S	/* Two 1st byte areas */
+#define IsDBCS1(c)	(((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))
+#else			/* One 1st byte area */
+#define IsDBCS1(c)	((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)
+#endif
+
+#ifdef _DS3S	/* Three 2nd byte areas */
+#define IsDBCS2(c)	(((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))
+#else			/* Two 2nd byte areas */
+#define IsDBCS2(c)	(((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))
+#endif
+
+#else			/* Code page is SBCS */
+
+#define IsDBCS1(c)	0
+#define IsDBCS2(c)	0
+
+#endif /* _DF1S */
+
+
+/* Name status flags */
+#define NSFLAG		11		/* Index of name status byte in fn[] */
+#define NS_LOSS		0x01	/* Out of 8.3 format */
+#define NS_LFN		0x02	/* Force to create LFN entry */
+#define NS_LAST		0x04	/* Last segment */
+#define NS_BODY		0x08	/* Lower case flag (body) */
+#define NS_EXT		0x10	/* Lower case flag (ext) */
+#define NS_DOT		0x20	/* Dot entry */
+
+
+/* FAT sub-type boundaries (Differ from specs but correct for real DOS/Windows) */
+#define MIN_FAT16	4086U	/* Minimum number of clusters of FAT16 */
+#define	MIN_FAT32	65526U	/* Minimum number of clusters of FAT32 */
+
+
+/* FatFs refers the members in the FAT structures as byte array instead of
+/ structure members because the structure is not binary compatible between
+/ different platforms */
+
+#define BS_jmpBoot			0		/* x86 jump instruction (3) */
+#define BS_OEMName			3		/* OEM name (8) */
+#define BPB_BytsPerSec		11		/* Sector size [byte] (2) */
+#define BPB_SecPerClus		13		/* Cluster size [sector] (1) */
+#define BPB_RsvdSecCnt		14		/* Size of reserved area [sector] (2) */
+#define BPB_NumFATs			16		/* Number of FAT copies (1) */
+#define BPB_RootEntCnt		17		/* Number of root directory entries for FAT12/16 (2) */
+#define BPB_TotSec16		19		/* Volume size [sector] (2) */
+#define BPB_Media			21		/* Media descriptor (1) */
+#define BPB_FATSz16			22		/* FAT size [sector] (2) */
+#define BPB_SecPerTrk		24		/* Track size [sector] (2) */
+#define BPB_NumHeads		26		/* Number of heads (2) */
+#define BPB_HiddSec			28		/* Number of special hidden sectors (4) */
+#define BPB_TotSec32		32		/* Volume size [sector] (4) */
+#define BS_DrvNum			36		/* Physical drive number (1) */
+#define BS_NTres			37		/* Error flag (1) */
+#define BS_BootSig			38		/* Extended boot signature (1) */
+#define BS_VolID			39		/* Volume serial number (4) */
+#define BS_VolLab			43		/* Volume label (8) */
+#define BS_FilSysType		54		/* File system type (1) */
+#define BPB_FATSz32			36		/* FAT size [sector] (4) */
+#define BPB_ExtFlags		40		/* Extended flags (2) */
+#define BPB_FSVer			42		/* File system version (2) */
+#define BPB_RootClus		44		/* Root directory first cluster (4) */
+#define BPB_FSInfo			48		/* Offset of FSINFO sector (2) */
+#define BPB_BkBootSec		50		/* Offset of backup boot sector (2) */
+#define BS_DrvNum32			64		/* Physical drive number (1) */
+#define BS_NTres32			65		/* Error flag (1) */
+#define BS_BootSig32		66		/* Extended boot signature (1) */
+#define BS_VolID32			67		/* Volume serial number (4) */
+#define BS_VolLab32			71		/* Volume label (8) */
+#define BS_FilSysType32		82		/* File system type (1) */
+#define	FSI_LeadSig			0		/* FSI: Leading signature (4) */
+#define	FSI_StrucSig		484		/* FSI: Structure signature (4) */
+#define	FSI_Free_Count		488		/* FSI: Number of free clusters (4) */
+#define	FSI_Nxt_Free		492		/* FSI: Last allocated cluster (4) */
+#define MBR_Table			446		/* MBR: Partition table offset (2) */
+#define	SZ_PTE				16		/* MBR: Size of a partition table entry */
+#define BS_55AA				510		/* Signature word (2) */
+
+#define	DIR_Name			0		/* Short file name (11) */
+#define	DIR_Attr			11		/* Attribute (1) */
+#define	DIR_NTres			12		/* Lower case flag (1) */
+#define DIR_CrtTimeTenth	13		/* Created time sub-second (1) */
+#define	DIR_CrtTime			14		/* Created time (2) */
+#define	DIR_CrtDate			16		/* Created date (2) */
+#define DIR_LstAccDate		18		/* Last accessed date (2) */
+#define	DIR_FstClusHI		20		/* Higher 16-bit of first cluster (2) */
+#define	DIR_WrtTime			22		/* Modified time (2) */
+#define	DIR_WrtDate			24		/* Modified date (2) */
+#define	DIR_FstClusLO		26		/* Lower 16-bit of first cluster (2) */
+#define	DIR_FileSize		28		/* File size (4) */
+#define	LDIR_Ord			0		/* LFN entry order and LLE flag (1) */
+#define	LDIR_Attr			11		/* LFN attribute (1) */
+#define	LDIR_Type			12		/* LFN type (1) */
+#define	LDIR_Chksum			13		/* Checksum of corresponding SFN entry */
+#define	LDIR_FstClusLO		26		/* Must be zero (0) */
+#define	SZ_DIRE				32		/* Size of a directory entry */
+#define	LLEF				0x40	/* Last long entry flag in LDIR_Ord */
+#define	DDEM				0xE5	/* Deleted directory entry mark at DIR_Name[0] */
+#define	RDDEM				0x05	/* Replacement of the character collides with DDEM */
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Work Area
+
+---------------------------------------------------------------------------*/
+
+/* Remark: Uninitialized variables with static duration are guaranteed
+/  zero/null at start-up. If not, either the linker or start-up routine
+/  being used is not compliance with ANSI-C standard.
+*/
+
+#if _VOLUMES < 1 || _VOLUMES > 9
+#error Wrong _VOLUMES setting
+#endif
+static FATFS *FatFs[_VOLUMES];	/* Pointer to the file system objects (logical drives) */
+static WORD Fsid;				/* File system mount ID */
+
+#if _FS_RPATH && _VOLUMES >= 2
+static BYTE CurrVol;			/* Current drive */
+#endif
+
+#if _FS_LOCK
+static FILESEM Files[_FS_LOCK];	/* Open object lock semaphores */
+#endif
+
+#if _USE_LFN == 0			/* Non LFN feature */
+#define	DEFINE_NAMEBUF		BYTE sfn[12]
+#define INIT_BUF(dobj)		(dobj).fn = sfn
+#define	FREE_BUF()
+#else
+#if _MAX_LFN < 12 || _MAX_LFN > 255
+#error Wrong _MAX_LFN setting
+#endif
+#if _USE_LFN == 1			/* LFN feature with static working buffer */
+static WCHAR LfnBuf[_MAX_LFN + 1];
+#define	DEFINE_NAMEBUF		BYTE sfn[12]
+#define INIT_BUF(dobj)		{ (dobj).fn = sfn; (dobj).lfn = LfnBuf; }
+#define	FREE_BUF()
+#elif _USE_LFN == 2 		/* LFN feature with dynamic working buffer on the stack */
+#define	DEFINE_NAMEBUF		BYTE sfn[12]; WCHAR lbuf[_MAX_LFN + 1]
+#define INIT_BUF(dobj)		{ (dobj).fn = sfn; (dobj).lfn = lbuf; }
+#define	FREE_BUF()
+#elif _USE_LFN == 3 		/* LFN feature with dynamic working buffer on the heap */
+#define	DEFINE_NAMEBUF		BYTE sfn[12]; WCHAR *lfn
+#define INIT_BUF(dobj)		{ lfn = ff_memalloc((_MAX_LFN + 1) * 2); if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); (dobj).lfn = lfn; (dobj).fn = sfn; }
+#define	FREE_BUF()			ff_memfree(lfn)
+#else
+#error Wrong _USE_LFN setting
+#endif
+#endif
+
+#ifdef _EXCVT
+static const BYTE ExCvt[] = _EXCVT;	/* Upper conversion table for SBCS extended characters */
+#endif
+
+
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Functions
+
+---------------------------------------------------------------------------*/
+
+
+/*-----------------------------------------------------------------------*/
+/* String functions                                                      */
+/*-----------------------------------------------------------------------*/
+
+/* Copy memory to memory */
+static
+void mem_cpy (void* dst, const void* src, UINT cnt) {
+	BYTE *d = (BYTE*)dst;
+	const BYTE *s = (const BYTE*)src;
+
+#if _WORD_ACCESS == 1
+	while (cnt >= sizeof (int)) {
+		*(int*)d = *(int*)s;
+		d += sizeof (int); s += sizeof (int);
+		cnt -= sizeof (int);
+	}
+#endif
+	while (cnt--)
+		*d++ = *s++;
+}
+
+/* Fill memory */
+static
+void mem_set (void* dst, int val, UINT cnt) {
+	BYTE *d = (BYTE*)dst;
+
+	while (cnt--)
+		*d++ = (BYTE)val;
+}
+
+/* Compare memory to memory */
+static
+int mem_cmp (const void* dst, const void* src, UINT cnt) {
+	const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;
+	int r = 0;
+
+	while (cnt-- && (r = *d++ - *s++) == 0) ;
+	return r;
+}
+
+/* Check if chr is contained in the string */
+static
+int chk_chr (const char* str, int chr) {
+	while (*str && *str != chr) str++;
+	return *str;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Request/Release grant to access the volume                            */
+/*-----------------------------------------------------------------------*/
+#if _FS_REENTRANT
+static
+int lock_fs (
+	FATFS* fs		/* File system object */
+)
+{
+	return ff_req_grant(fs->sobj);
+}
+
+
+static
+void unlock_fs (
+	FATFS* fs,		/* File system object */
+	FRESULT res		/* Result code to be returned */
+)
+{
+	if (fs &&
+		res != FR_NOT_ENABLED &&
+		res != FR_INVALID_DRIVE &&
+		res != FR_INVALID_OBJECT &&
+		res != FR_TIMEOUT) {
+		ff_rel_grant(fs->sobj);
+	}
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* File lock control functions                                           */
+/*-----------------------------------------------------------------------*/
+#if _FS_LOCK
+
+static
+FRESULT chk_lock (	/* Check if the file can be accessed */
+	FATFS_DIR* dp,		/* Directory object pointing the file to be checked */
+	int acc			/* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */
+)
+{
+	UINT i, be;
+
+	/* Search file semaphore table */
+	for (i = be = 0; i < _FS_LOCK; i++) {
+		if (Files[i].fs) {	/* Existing entry */
+			if (Files[i].fs == dp->fs &&	 	/* Check if the object matched with an open object */
+				Files[i].clu == dp->sclust &&
+				Files[i].idx == dp->index) break;
+		} else {			/* Blank entry */
+			be = 1;
+		}
+	}
+	if (i == _FS_LOCK)	/* The object is not opened */
+		return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES;	/* Is there a blank entry for new object? */
+
+	/* The object has been opened. Reject any open against writing file and all write mode open */
+	return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
+}
+
+
+static
+int enq_lock (void)	/* Check if an entry is available for a new object */
+{
+	UINT i;
+
+	for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;
+	return (i == _FS_LOCK) ? 0 : 1;
+}
+
+
+static
+UINT inc_lock (	/* Increment object open counter and returns its index (0:Internal error) */
+	FATFS_DIR* dp,	/* Directory object pointing the file to register or increment */
+	int acc		/* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
+)
+{
+	UINT i;
+
+
+	for (i = 0; i < _FS_LOCK; i++) {	/* Find the object */
+		if (Files[i].fs == dp->fs &&
+			Files[i].clu == dp->sclust &&
+			Files[i].idx == dp->index) break;
+	}
+
+	if (i == _FS_LOCK) {				/* Not opened. Register it as new. */
+		for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;
+		if (i == _FS_LOCK) return 0;	/* No free entry to register (int err) */
+		Files[i].fs = dp->fs;
+		Files[i].clu = dp->sclust;
+		Files[i].idx = dp->index;
+		Files[i].ctr = 0;
+	}
+
+	if (acc && Files[i].ctr) return 0;	/* Access violation (int err) */
+
+	Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1;	/* Set semaphore value */
+
+	return i + 1;
+}
+
+
+static
+FRESULT dec_lock (	/* Decrement object open counter */
+	UINT i			/* Semaphore index (1..) */
+)
+{
+	WORD n;
+	FRESULT res;
+
+
+	if (--i < _FS_LOCK) {	/* Shift index number origin from 0 */
+		n = Files[i].ctr;
+		if (n == 0x100) n = 0;		/* If write mode open, delete the entry */
+		if (n) n--;					/* Decrement read mode open count */
+		Files[i].ctr = n;
+		if (!n) Files[i].fs = 0;	/* Delete the entry if open count gets zero */
+		res = FR_OK;
+	} else {
+		res = FR_INT_ERR;			/* Invalid index nunber */
+	}
+	return res;
+}
+
+
+static
+void clear_lock (	/* Clear lock entries of the volume */
+	FATFS *fs
+)
+{
+	UINT i;
+
+	for (i = 0; i < _FS_LOCK; i++) {
+		if (Files[i].fs == fs) Files[i].fs = 0;
+	}
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Move/Flush disk access window in the file system object               */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT sync_window (	/* FR_OK:succeeded, !=0:error */
+	FATFS* fs		/* File system object */
+)
+{
+	DWORD wsect;
+	UINT nf;
+	FRESULT res = FR_OK;
+
+
+	if (fs->wflag) {	/* Write back the sector if it is dirty */
+		wsect = fs->winsect;	/* Current sector number */
+		if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) {
+			res = FR_DISK_ERR;
+		} else {
+			fs->wflag = 0;
+			if (wsect - fs->fatbase < fs->fsize) {		/* Is it in the FAT area? */
+				for (nf = fs->n_fats; nf >= 2; nf--) {	/* Reflect the change to all FAT copies */
+					wsect += fs->fsize;
+					disk_write(fs->drv, fs->win, wsect, 1);
+				}
+			}
+		}
+	}
+	return res;
+}
+#endif
+
+
+static
+FRESULT move_window (	/* FR_OK(0):succeeded, !=0:error */
+	FATFS* fs,		/* File system object */
+	DWORD sector	/* Sector number to make appearance in the fs->win[] */
+)
+{
+	FRESULT res = FR_OK;
+
+
+	if (sector != fs->winsect) {	/* Window offset changed? */
+#if !_FS_READONLY
+		res = sync_window(fs);		/* Write-back changes */
+#endif
+		if (res == FR_OK) {			/* Fill sector window with new data */
+			if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) {
+				sector = 0xFFFFFFFF;	/* Invalidate window if data is not reliable */
+				res = FR_DISK_ERR;
+			}
+			fs->winsect = sector;
+		}
+	}
+	return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Synchronize file system and strage device                             */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT sync_fs (	/* FR_OK:succeeded, !=0:error */
+	FATFS* fs		/* File system object */
+)
+{
+	FRESULT res;
+
+
+	res = sync_window(fs);
+	if (res == FR_OK) {
+		/* Update FSInfo sector if needed */
+		if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) {
+			/* Create FSInfo structure */
+			mem_set(fs->win, 0, SS(fs));
+			ST_WORD(fs->win + BS_55AA, 0xAA55);
+			ST_DWORD(fs->win + FSI_LeadSig, 0x41615252);
+			ST_DWORD(fs->win + FSI_StrucSig, 0x61417272);
+			ST_DWORD(fs->win + FSI_Free_Count, fs->free_clust);
+			ST_DWORD(fs->win + FSI_Nxt_Free, fs->last_clust);
+			/* Write it into the FSInfo sector */
+			fs->winsect = fs->volbase + 1;
+			disk_write(fs->drv, fs->win, fs->winsect, 1);
+			fs->fsi_flag = 0;
+		}
+		/* Make sure that no pending write process in the physical drive */
+		if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK)
+			res = FR_DISK_ERR;
+	}
+
+	return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get sector# from cluster#                                             */
+/*-----------------------------------------------------------------------*/
+/* Hidden API for hacks and disk tools */
+
+DWORD clust2sect (	/* !=0:Sector number, 0:Failed (invalid cluster#) */
+	FATFS* fs,		/* File system object */
+	DWORD clst		/* Cluster# to be converted */
+)
+{
+	clst -= 2;
+	if (clst >= fs->n_fatent - 2) return 0;		/* Invalid cluster# */
+	return clst * fs->csize + fs->database;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Read value of a FAT entry                                */
+/*-----------------------------------------------------------------------*/
+/* Hidden API for hacks and disk tools */
+
+DWORD get_fat (	/* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x0FFFFFFF:Cluster status */
+	FATFS* fs,	/* File system object */
+	DWORD clst	/* FAT index number (cluster number) to get the value */
+)
+{
+	UINT wc, bc;
+	BYTE *p;
+	DWORD val;
+
+
+	if (clst < 2 || clst >= fs->n_fatent) {	/* Check if in valid range */
+		val = 1;	/* Internal error */
+
+	} else {
+		val = 0xFFFFFFFF;	/* Default value falls on disk error */
+
+		switch (fs->fs_type) {
+		case FS_FAT12 :
+			bc = (UINT)clst; bc += bc / 2;
+			if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;
+			wc = fs->win[bc++ % SS(fs)];
+			if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;
+			wc |= fs->win[bc % SS(fs)] << 8;
+			val = clst & 1 ? wc >> 4 : (wc & 0xFFF);
+			break;
+
+		case FS_FAT16 :
+			if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break;
+			p = &fs->win[clst * 2 % SS(fs)];
+			val = LD_WORD(p);
+			break;
+
+		case FS_FAT32 :
+			if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;
+			p = &fs->win[clst * 4 % SS(fs)];
+			val = LD_DWORD(p) & 0x0FFFFFFF;
+			break;
+
+		default:
+			val = 1;	/* Internal error */
+		}
+	}
+
+	return val;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Change value of a FAT entry                              */
+/*-----------------------------------------------------------------------*/
+/* Hidden API for hacks and disk tools */
+
+#if !_FS_READONLY
+FRESULT put_fat (	/* FR_OK(0):succeeded, !=0:error */
+	FATFS* fs,		/* File system object */
+	DWORD clst,		/* FAT index number (cluster number) to be changed */
+	DWORD val		/* New value to be set to the entry */
+)
+{
+	UINT bc;
+	BYTE *p;
+	FRESULT res;
+
+
+	if (clst < 2 || clst >= fs->n_fatent) {	/* Check if in valid range */
+		res = FR_INT_ERR;
+
+	} else {
+		switch (fs->fs_type) {
+		case FS_FAT12 :
+			bc = (UINT)clst; bc += bc / 2;
+			res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+			if (res != FR_OK) break;
+			p = &fs->win[bc++ % SS(fs)];
+			*p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
+			fs->wflag = 1;
+			res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+			if (res != FR_OK) break;
+			p = &fs->win[bc % SS(fs)];
+			*p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
+			fs->wflag = 1;
+			break;
+
+		case FS_FAT16 :
+			res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
+			if (res != FR_OK) break;
+			p = &fs->win[clst * 2 % SS(fs)];
+			ST_WORD(p, (WORD)val);
+			fs->wflag = 1;
+			break;
+
+		case FS_FAT32 :
+			res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
+			if (res != FR_OK) break;
+			p = &fs->win[clst * 4 % SS(fs)];
+			val |= LD_DWORD(p) & 0xF0000000;
+			ST_DWORD(p, val);
+			fs->wflag = 1;
+			break;
+
+		default :
+			res = FR_INT_ERR;
+		}
+	}
+
+	return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Remove a cluster chain                                 */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT remove_chain (	/* FR_OK(0):succeeded, !=0:error */
+	FATFS* fs,			/* File system object */
+	DWORD clst			/* Cluster# to remove a chain from */
+)
+{
+	FRESULT res;
+	DWORD nxt;
+#if _USE_TRIM
+	DWORD scl = clst, ecl = clst, rt[2];
+#endif
+
+	if (clst < 2 || clst >= fs->n_fatent) {	/* Check if in valid range */
+		res = FR_INT_ERR;
+
+	} else {
+		res = FR_OK;
+		while (clst < fs->n_fatent) {			/* Not a last link? */
+			nxt = get_fat(fs, clst);			/* Get cluster status */
+			if (nxt == 0) break;				/* Empty cluster? */
+			if (nxt == 1) { res = FR_INT_ERR; break; }	/* Internal error? */
+			if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }	/* Disk error? */
+			res = put_fat(fs, clst, 0);			/* Mark the cluster "empty" */
+			if (res != FR_OK) break;
+			if (fs->free_clust != 0xFFFFFFFF) {	/* Update FSINFO */
+				fs->free_clust++;
+				fs->fsi_flag |= 1;
+			}
+#if _USE_TRIM
+			if (ecl + 1 == nxt) {	/* Is next cluster contiguous? */
+				ecl = nxt;
+			} else {				/* End of contiguous clusters */ 
+				rt[0] = clust2sect(fs, scl);					/* Start sector */
+				rt[1] = clust2sect(fs, ecl) + fs->csize - 1;	/* End sector */
+				disk_ioctl(fs->drv, CTRL_TRIM, rt);				/* Erase the block */
+				scl = ecl = nxt;
+			}
+#endif
+			clst = nxt;	/* Next cluster */
+		}
+	}
+
+	return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Stretch or Create a cluster chain                      */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+DWORD create_chain (	/* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
+	FATFS* fs,			/* File system object */
+	DWORD clst			/* Cluster# to stretch, 0:Create a new chain */
+)
+{
+	DWORD cs, ncl, scl;
+	FRESULT res;
+
+
+	if (clst == 0) {		/* Create a new chain */
+		scl = fs->last_clust;			/* Get suggested start point */
+		if (!scl || scl >= fs->n_fatent) scl = 1;
+	}
+	else {					/* Stretch the current chain */
+		cs = get_fat(fs, clst);			/* Check the cluster status */
+		if (cs < 2) return 1;			/* Invalid value */
+		if (cs == 0xFFFFFFFF) return cs;	/* A disk error occurred */
+		if (cs < fs->n_fatent) return cs;	/* It is already followed by next cluster */
+		scl = clst;
+	}
+
+	ncl = scl;				/* Start cluster */
+	for (;;) {
+		ncl++;							/* Next cluster */
+		if (ncl >= fs->n_fatent) {		/* Check wrap around */
+			ncl = 2;
+			if (ncl > scl) return 0;	/* No free cluster */
+		}
+		cs = get_fat(fs, ncl);			/* Get the cluster status */
+		if (cs == 0) break;				/* Found a free cluster */
+		if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */
+			return cs;
+		if (ncl == scl) return 0;		/* No free cluster */
+	}
+
+	res = put_fat(fs, ncl, 0x0FFFFFFF);	/* Mark the new cluster "last link" */
+	if (res == FR_OK && clst != 0) {
+		res = put_fat(fs, clst, ncl);	/* Link it to the previous one if needed */
+	}
+	if (res == FR_OK) {
+		fs->last_clust = ncl;			/* Update FSINFO */
+		if (fs->free_clust != 0xFFFFFFFF) {
+			fs->free_clust--;
+			fs->fsi_flag |= 1;
+		}
+	} else {
+		ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1;
+	}
+
+	return ncl;		/* Return new cluster number or error code */
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Convert offset into cluster with link map table        */
+/*-----------------------------------------------------------------------*/
+
+#if _USE_FASTSEEK
+static
+DWORD clmt_clust (	/* <2:Error, >=2:Cluster number */
+	FIL* fp,		/* Pointer to the file object */
+	DWORD ofs		/* File offset to be converted to cluster# */
+)
+{
+	DWORD cl, ncl, *tbl;
+
+
+	tbl = fp->cltbl + 1;	/* Top of CLMT */
+	cl = ofs / SS(fp->fs) / fp->fs->csize;	/* Cluster order from top of the file */
+	for (;;) {
+		ncl = *tbl++;			/* Number of cluters in the fragment */
+		if (!ncl) return 0;		/* End of table? (error) */
+		if (cl < ncl) break;	/* In this fragment? */
+		cl -= ncl; tbl++;		/* Next fragment */
+	}
+	return cl + *tbl;	/* Return the cluster number */
+}
+#endif	/* _USE_FASTSEEK */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Set directory index                              */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_sdi (	/* FR_OK(0):succeeded, !=0:error */
+	FATFS_DIR* dp,		/* Pointer to directory object */
+	UINT idx		/* Index of directory table */
+)
+{
+	DWORD clst, sect;
+	UINT ic;
+
+
+	dp->index = (WORD)idx;	/* Current index */
+	clst = dp->sclust;		/* Table start cluster (0:root) */
+	if (clst == 1 || clst >= dp->fs->n_fatent)	/* Check start cluster range */
+		return FR_INT_ERR;
+	if (!clst && dp->fs->fs_type == FS_FAT32)	/* Replace cluster# 0 with root cluster# if in FAT32 */
+		clst = dp->fs->dirbase;
+
+	if (clst == 0) {	/* Static table (root-directory in FAT12/16) */
+		if (idx >= dp->fs->n_rootdir)	/* Is index out of range? */
+			return FR_INT_ERR;
+		sect = dp->fs->dirbase;
+	}
+	else {				/* Dynamic table (root-directory in FAT32 or sub-directory) */
+		ic = SS(dp->fs) / SZ_DIRE * dp->fs->csize;	/* Entries per cluster */
+		while (idx >= ic) {	/* Follow cluster chain */
+			clst = get_fat(dp->fs, clst);				/* Get next cluster */
+			if (clst == 0xFFFFFFFF) return FR_DISK_ERR;	/* Disk error */
+			if (clst < 2 || clst >= dp->fs->n_fatent)	/* Reached to end of table or internal error */
+				return FR_INT_ERR;
+			idx -= ic;
+		}
+		sect = clust2sect(dp->fs, clst);
+	}
+	dp->clust = clst;	/* Current cluster# */
+	if (!sect) return FR_INT_ERR;
+	dp->sect = sect + idx / (SS(dp->fs) / SZ_DIRE);					/* Sector# of the directory entry */
+	dp->dir = dp->fs->win + (idx % (SS(dp->fs) / SZ_DIRE)) * SZ_DIRE;	/* Ptr to the entry in the sector */
+
+	return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Move directory table index next                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_next (	/* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */
+	FATFS_DIR* dp,		/* Pointer to the directory object */
+	int stretch		/* 0: Do not stretch table, 1: Stretch table if needed */
+)
+{
+	DWORD clst;
+	UINT i;
+#if !_FS_READONLY
+	UINT c;
+#endif
+
+
+	i = dp->index + 1;
+	if (!(i & 0xFFFF) || !dp->sect)	/* Report EOT when index has reached 65535 */
+		return FR_NO_FILE;
+
+	if (!(i % (SS(dp->fs) / SZ_DIRE))) {	/* Sector changed? */
+		dp->sect++;					/* Next sector */
+
+		if (!dp->clust) {		/* Static table */
+			if (i >= dp->fs->n_rootdir)	/* Report EOT if it reached end of static table */
+				return FR_NO_FILE;
+		}
+		else {					/* Dynamic table */
+			if (((i / (SS(dp->fs) / SZ_DIRE)) & (dp->fs->csize - 1)) == 0) {	/* Cluster changed? */
+				clst = get_fat(dp->fs, dp->clust);				/* Get next cluster */
+				if (clst <= 1) return FR_INT_ERR;
+				if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+				if (clst >= dp->fs->n_fatent) {					/* If it reached end of dynamic table, */
+#if !_FS_READONLY
+					if (!stretch) return FR_NO_FILE;			/* If do not stretch, report EOT */
+					clst = create_chain(dp->fs, dp->clust);		/* Stretch cluster chain */
+					if (clst == 0) return FR_DENIED;			/* No free cluster */
+					if (clst == 1) return FR_INT_ERR;
+					if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+					/* Clean-up stretched table */
+					if (sync_window(dp->fs)) return FR_DISK_ERR;/* Flush disk access window */
+					mem_set(dp->fs->win, 0, SS(dp->fs));		/* Clear window buffer */
+					dp->fs->winsect = clust2sect(dp->fs, clst);	/* Cluster start sector */
+					for (c = 0; c < dp->fs->csize; c++) {		/* Fill the new cluster with 0 */
+						dp->fs->wflag = 1;
+						if (sync_window(dp->fs)) return FR_DISK_ERR;
+						dp->fs->winsect++;
+					}
+					dp->fs->winsect -= c;						/* Rewind window offset */
+#else
+					if (!stretch) return FR_NO_FILE;			/* If do not stretch, report EOT (this is to suppress warning) */
+					return FR_NO_FILE;							/* Report EOT */
+#endif
+				}
+				dp->clust = clst;				/* Initialize data for new cluster */
+				dp->sect = clust2sect(dp->fs, clst);
+			}
+		}
+	}
+
+	dp->index = (WORD)i;	/* Current index */
+	dp->dir = dp->fs->win + (i % (SS(dp->fs) / SZ_DIRE)) * SZ_DIRE;	/* Current entry in the window */
+
+	return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Reserve directory entry                          */
+/*-----------------------------------------------------------------------*/
+
+#if !_FS_READONLY
+static
+FRESULT dir_alloc (	/* FR_OK(0):succeeded, !=0:error */
+	FATFS_DIR* dp,		/* Pointer to the directory object */
+	UINT nent		/* Number of contiguous entries to allocate (1-21) */
+)
+{
+	FRESULT res;
+	UINT n;
+
+
+	res = dir_sdi(dp, 0);
+	if (res == FR_OK) {
+		n = 0;
+		do {
+			res = move_window(dp->fs, dp->sect);
+			if (res != FR_OK) break;
+			if (dp->dir[0] == DDEM || dp->dir[0] == 0) {	/* Is it a free entry? */
+				if (++n == nent) break;	/* A block of contiguous free entries is found */
+			} else {
+				n = 0;					/* Not a blank entry. Restart to search */
+			}
+			res = dir_next(dp, 1);		/* Next entry with table stretch enabled */
+		} while (res == FR_OK);
+	}
+	if (res == FR_NO_FILE) res = FR_DENIED;	/* No directory entry to allocate */
+	return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Load/Store start cluster number                  */
+/*-----------------------------------------------------------------------*/
+
+static
+DWORD ld_clust (	/* Returns the top cluster value of the SFN entry */
+	FATFS* fs,		/* Pointer to the fs object */
+	const BYTE* dir	/* Pointer to the SFN entry */
+)
+{
+	DWORD cl;
+
+	cl = LD_WORD(dir + DIR_FstClusLO);
+	if (fs->fs_type == FS_FAT32)
+		cl |= (DWORD)LD_WORD(dir + DIR_FstClusHI) << 16;
+
+	return cl;
+}
+
+
+#if !_FS_READONLY
+static
+void st_clust (
+	BYTE* dir,	/* Pointer to the SFN entry */
+	DWORD cl	/* Value to be set */
+)
+{
+	ST_WORD(dir + DIR_FstClusLO, cl);
+	ST_WORD(dir + DIR_FstClusHI, cl >> 16);
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};	/* Offset of LFN characters in the directory entry */
+
+
+static
+int cmp_lfn (			/* 1:matched, 0:not matched */
+	WCHAR* lfnbuf,		/* Pointer to the LFN working buffer to be compared */
+	BYTE* dir			/* Pointer to the directory entry containing the part of LFN */
+)
+{
+	UINT i, s;
+	WCHAR wc, uc;
+
+
+	if (LD_WORD(dir + LDIR_FstClusLO) != 0) return 0;	/* Check LDIR_FstClusLO */
+
+	i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13;	/* Offset in the LFN buffer */
+
+	for (wc = 1, s = 0; s < 13; s++) {		/* Process all characters in the entry */
+		uc = LD_WORD(dir + LfnOfs[s]);		/* Pick an LFN character */
+		if (wc) {
+			if (i >= _MAX_LFN || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++]))	/* Compare it */
+				return 0;					/* Not matched */
+			wc = uc;
+		} else {
+			if (uc != 0xFFFF) return 0;		/* Check filler */
+		}
+	}
+
+	if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i])	/* Last segment matched but different length */
+		return 0;
+
+	return 1;		/* The part of LFN matched */
+}
+
+
+
+static
+int pick_lfn (			/* 1:succeeded, 0:buffer overflow or invalid LFN entry */
+	WCHAR* lfnbuf,		/* Pointer to the LFN working buffer */
+	BYTE* dir			/* Pointer to the LFN entry */
+)
+{
+	UINT i, s;
+	WCHAR wc, uc;
+
+
+	if (LD_WORD(dir + LDIR_FstClusLO) != 0) return 0;	/* Check LDIR_FstClusLO */
+
+	i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13;	/* Offset in the LFN buffer */
+
+	for (wc = 1, s = 0; s < 13; s++) {		/* Process all characters in the entry */
+		uc = LD_WORD(dir + LfnOfs[s]);		/* Pick an LFN character */
+		if (wc) {
+			if (i >= _MAX_LFN) return 0;	/* Buffer overflow? */
+			lfnbuf[i++] = wc = uc;			/* Store it */
+		} else {
+			if (uc != 0xFFFF) return 0;		/* Check filler */
+		}
+	}
+
+	if (dir[LDIR_Ord] & LLEF) {				/* Put terminator if it is the last LFN part */
+		if (i >= _MAX_LFN) return 0;		/* Buffer overflow? */
+		lfnbuf[i] = 0;
+	}
+
+	return 1;		/* The part of LFN is valid */
+}
+
+
+#if !_FS_READONLY
+static
+void fit_lfn (
+	const WCHAR* lfnbuf,	/* Pointer to the LFN working buffer */
+	BYTE* dir,				/* Pointer to the LFN entry to be processed */
+	BYTE ord,				/* LFN order (1-20) */
+	BYTE sum				/* Checksum of the corresponding SFN */
+)
+{
+	UINT i, s;
+	WCHAR wc;
+
+
+	dir[LDIR_Chksum] = sum;			/* Set checksum */
+	dir[LDIR_Attr] = AM_LFN;		/* Set attribute. LFN entry */
+	dir[LDIR_Type] = 0;
+	ST_WORD(dir + LDIR_FstClusLO, 0);
+
+	i = (ord - 1) * 13;				/* Get offset in the LFN working buffer */
+	s = wc = 0;
+	do {
+		if (wc != 0xFFFF) wc = lfnbuf[i++];	/* Get an effective character */
+		ST_WORD(dir+LfnOfs[s], wc);	/* Put it */
+		if (!wc) wc = 0xFFFF;		/* Padding characters following last character */
+	} while (++s < 13);
+	if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLEF;	/* Bottom LFN part is the start of LFN sequence */
+	dir[LDIR_Ord] = ord;			/* Set the LFN order */
+}
+
+#endif
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create numbered name                                                  */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+void gen_numname (
+	BYTE* dst,			/* Pointer to the buffer to store numbered SFN */
+	const BYTE* src,	/* Pointer to SFN */
+	const WCHAR* lfn,	/* Pointer to LFN */
+	UINT seq			/* Sequence number */
+)
+{
+	BYTE ns[8], c;
+	UINT i, j;
+	WCHAR wc;
+	DWORD sr;
+
+
+	mem_cpy(dst, src, 11);
+
+	if (seq > 5) {	/* On many collisions, generate a hash number instead of sequential number */
+		sr = seq;
+		while (*lfn) {	/* Create a CRC */
+			wc = *lfn++;
+			for (i = 0; i < 16; i++) {
+				sr = (sr << 1) + (wc & 1);
+				wc >>= 1;
+				if (sr & 0x10000) sr ^= 0x11021;
+			}
+		}
+		seq = (UINT)sr;
+	}
+
+	/* itoa (hexdecimal) */
+	i = 7;
+	do {
+		c = (seq % 16) + '0';
+		if (c > '9') c += 7;
+		ns[i--] = c;
+		seq /= 16;
+	} while (seq);
+	ns[i] = '~';
+
+	/* Append the number */
+	for (j = 0; j < i && dst[j] != ' '; j++) {
+		if (IsDBCS1(dst[j])) {
+			if (j == i - 1) break;
+			j++;
+		}
+	}
+	do {
+		dst[j++] = (i < 8) ? ns[i++] : ' ';
+	} while (j < 8);
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Calculate checksum of an SFN entry                                    */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+BYTE sum_sfn (
+	const BYTE* dir		/* Pointer to the SFN entry */
+)
+{
+	BYTE sum = 0;
+	UINT n = 11;
+
+	do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);
+	return sum;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Find an object in the directory                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_find (	/* FR_OK(0):succeeded, !=0:error */
+	FATFS_DIR* dp			/* Pointer to the directory object linked to the file name */
+)
+{
+	FRESULT res;
+	BYTE c, *dir;
+#if _USE_LFN
+	BYTE a, ord, sum;
+#endif
+
+	res = dir_sdi(dp, 0);			/* Rewind directory object */
+	if (res != FR_OK) return res;
+
+#if _USE_LFN
+	ord = sum = 0xFF; dp->lfn_idx = 0xFFFF;	/* Reset LFN sequence */
+#endif
+	do {
+		res = move_window(dp->fs, dp->sect);
+		if (res != FR_OK) break;
+		dir = dp->dir;					/* Ptr to the directory entry of current index */
+		c = dir[DIR_Name];
+		if (c == 0) { res = FR_NO_FILE; break; }	/* Reached to end of table */
+#if _USE_LFN	/* LFN configuration */
+		a = dir[DIR_Attr] & AM_MASK;
+		if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) {	/* An entry without valid data */
+			ord = 0xFF; dp->lfn_idx = 0xFFFF;	/* Reset LFN sequence */
+		} else {
+			if (a == AM_LFN) {			/* An LFN entry is found */
+				if (dp->lfn) {
+					if (c & LLEF) {		/* Is it start of LFN sequence? */
+						sum = dir[LDIR_Chksum];
+						c &= ~LLEF; ord = c;	/* LFN start order */
+						dp->lfn_idx = dp->index;	/* Start index of LFN */
+					}
+					/* Check validity of the LFN entry and compare it with given name */
+					ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF;
+				}
+			} else {					/* An SFN entry is found */
+				if (!ord && sum == sum_sfn(dir)) break;	/* LFN matched? */
+				if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dir, dp->fn, 11)) break;	/* SFN matched? */
+				ord = 0xFF; dp->lfn_idx = 0xFFFF;	/* Reset LFN sequence */
+			}
+		}
+#else		/* Non LFN configuration */
+		if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dp->fn, 11)) /* Is it a valid entry? */
+			break;
+#endif
+		res = dir_next(dp, 0);		/* Next entry */
+	} while (res == FR_OK);
+
+	return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read an object from the directory                                     */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2
+static
+FRESULT dir_read (
+	FATFS_DIR* dp,		/* Pointer to the directory object */
+	int vol			/* Filtered by 0:file/directory or 1:volume label */
+)
+{
+	FRESULT res;
+	BYTE a, c, *dir;
+#if _USE_LFN
+	BYTE ord = 0xFF, sum = 0xFF;
+#endif
+
+	res = FR_NO_FILE;
+	while (dp->sect) {
+		res = move_window(dp->fs, dp->sect);
+		if (res != FR_OK) break;
+		dir = dp->dir;					/* Ptr to the directory entry of current index */
+		c = dir[DIR_Name];
+		if (c == 0) { res = FR_NO_FILE; break; }	/* Reached to end of table */
+		a = dir[DIR_Attr] & AM_MASK;
+#if _USE_LFN	/* LFN configuration */
+		if (c == DDEM || (!_FS_RPATH && c == '.') || (int)((a & ~AM_ARC) == AM_VOL) != vol) {	/* An entry without valid data */
+			ord = 0xFF;
+		} else {
+			if (a == AM_LFN) {			/* An LFN entry is found */
+				if (c & LLEF) {			/* Is it start of LFN sequence? */
+					sum = dir[LDIR_Chksum];
+					c &= ~LLEF; ord = c;
+					dp->lfn_idx = dp->index;
+				}
+				/* Check LFN validity and capture it */
+				ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF;
+			} else {					/* An SFN entry is found */
+				if (ord || sum != sum_sfn(dir))	/* Is there a valid LFN? */
+					dp->lfn_idx = 0xFFFF;		/* It has no LFN. */
+				break;
+			}
+		}
+#else		/* Non LFN configuration */
+		if (c != DDEM && (_FS_RPATH || c != '.') && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol)	/* Is it a valid entry? */
+			break;
+#endif
+		res = dir_next(dp, 0);				/* Next entry */
+		if (res != FR_OK) break;
+	}
+
+	if (res != FR_OK) dp->sect = 0;
+
+	return res;
+}
+#endif	/* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Register an object to the directory                                   */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT dir_register (	/* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */
+	FATFS_DIR* dp				/* Target directory with object name to be created */
+)
+{
+	FRESULT res;
+#if _USE_LFN	/* LFN configuration */
+	UINT n, nent;
+	BYTE sn[12], *fn, sum;
+	WCHAR *lfn;
+
+
+	fn = dp->fn; lfn = dp->lfn;
+	mem_cpy(sn, fn, 12);
+
+	if (_FS_RPATH && (sn[NSFLAG] & NS_DOT))		/* Cannot create dot entry */
+		return FR_INVALID_NAME;
+
+	if (sn[NSFLAG] & NS_LOSS) {			/* When LFN is out of 8.3 format, generate a numbered name */
+		fn[NSFLAG] = 0; dp->lfn = 0;			/* Find only SFN */
+		for (n = 1; n < 100; n++) {
+			gen_numname(fn, sn, lfn, n);	/* Generate a numbered name */
+			res = dir_find(dp);				/* Check if the name collides with existing SFN */
+			if (res != FR_OK) break;
+		}
+		if (n == 100) return FR_DENIED;		/* Abort if too many collisions */
+		if (res != FR_NO_FILE) return res;	/* Abort if the result is other than 'not collided' */
+		fn[NSFLAG] = sn[NSFLAG]; dp->lfn = lfn;
+	}
+
+	if (sn[NSFLAG] & NS_LFN) {			/* When LFN is to be created, allocate entries for an SFN + LFNs. */
+		for (n = 0; lfn[n]; n++) ;
+		nent = (n + 25) / 13;
+	} else {						/* Otherwise allocate an entry for an SFN  */
+		nent = 1;
+	}
+	res = dir_alloc(dp, nent);		/* Allocate entries */
+
+	if (res == FR_OK && --nent) {	/* Set LFN entry if needed */
+		res = dir_sdi(dp, dp->index - nent);
+		if (res == FR_OK) {
+			sum = sum_sfn(dp->fn);	/* Checksum value of the SFN tied to the LFN */
+			do {					/* Store LFN entries in bottom first */
+				res = move_window(dp->fs, dp->sect);
+				if (res != FR_OK) break;
+				fit_lfn(dp->lfn, dp->dir, (BYTE)nent, sum);
+				dp->fs->wflag = 1;
+				res = dir_next(dp, 0);	/* Next entry */
+			} while (res == FR_OK && --nent);
+		}
+	}
+#else	/* Non LFN configuration */
+	res = dir_alloc(dp, 1);		/* Allocate an entry for SFN */
+#endif
+
+	if (res == FR_OK) {				/* Set SFN entry */
+		res = move_window(dp->fs, dp->sect);
+		if (res == FR_OK) {
+			mem_set(dp->dir, 0, SZ_DIRE);	/* Clean the entry */
+			mem_cpy(dp->dir, dp->fn, 11);	/* Put SFN */
+#if _USE_LFN
+			dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT);	/* Put NT flag */
+#endif
+			dp->fs->wflag = 1;
+		}
+	}
+
+	return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Remove an object from the directory                                   */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY && !_FS_MINIMIZE
+static
+FRESULT dir_remove (	/* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
+	FATFS_DIR* dp				/* Directory object pointing the entry to be removed */
+)
+{
+	FRESULT res;
+#if _USE_LFN	/* LFN configuration */
+	UINT i;
+
+	i = dp->index;	/* SFN index */
+	res = dir_sdi(dp, (dp->lfn_idx == 0xFFFF) ? i : dp->lfn_idx);	/* Goto the SFN or top of the LFN entries */
+	if (res == FR_OK) {
+		do {
+			res = move_window(dp->fs, dp->sect);
+			if (res != FR_OK) break;
+			mem_set(dp->dir, 0, SZ_DIRE);	/* Clear and mark the entry "deleted" */
+			*dp->dir = DDEM;
+			dp->fs->wflag = 1;
+			if (dp->index >= i) break;	/* When reached SFN, all entries of the object has been deleted. */
+			res = dir_next(dp, 0);		/* Next entry */
+		} while (res == FR_OK);
+		if (res == FR_NO_FILE) res = FR_INT_ERR;
+	}
+
+#else			/* Non LFN configuration */
+	res = dir_sdi(dp, dp->index);
+	if (res == FR_OK) {
+		res = move_window(dp->fs, dp->sect);
+		if (res == FR_OK) {
+			mem_set(dp->dir, 0, SZ_DIRE);	/* Clear and mark the entry "deleted" */
+			*dp->dir = DDEM;
+			dp->fs->wflag = 1;
+		}
+	}
+#endif
+
+	return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get file information from directory entry                             */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2
+static
+void get_fileinfo (		/* No return code */
+	FATFS_DIR* dp,			/* Pointer to the directory object */
+	FILINFO* fno	 	/* Pointer to the file information to be filled */
+)
+{
+	UINT i;
+	TCHAR *p, c;
+	BYTE *dir;
+#if _USE_LFN
+	WCHAR w, *lfn;
+#endif
+
+	p = fno->fname;
+	if (dp->sect) {		/* Get SFN */
+		dir = dp->dir;
+		i = 0;
+		while (i < 11) {		/* Copy name body and extension */
+			c = (TCHAR)dir[i++];
+			if (c == ' ') continue;				/* Skip padding spaces */
+			if (c == RDDEM) c = (TCHAR)DDEM;	/* Restore replaced DDEM character */
+			if (i == 9) *p++ = '.';				/* Insert a . if extension is exist */
+#if _USE_LFN
+			if (IsUpper(c) && (dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY)))
+				c += 0x20;			/* To lower */
+#if _LFN_UNICODE
+			if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dir[i]))
+				c = c << 8 | dir[i++];
+			c = ff_convert(c, 1);	/* OEM -> Unicode */
+			if (!c) c = '?';
+#endif
+#endif
+			*p++ = c;
+		}
+		fno->fattrib = dir[DIR_Attr];				/* Attribute */
+		fno->fsize = LD_DWORD(dir + DIR_FileSize);	/* Size */
+		fno->fdate = LD_WORD(dir + DIR_WrtDate);	/* Date */
+		fno->ftime = LD_WORD(dir + DIR_WrtTime);	/* Time */
+	}
+	*p = 0;		/* Terminate SFN string by a \0 */
+
+#if _USE_LFN
+	if (fno->lfname) {
+		i = 0; p = fno->lfname;
+		if (dp->sect && fno->lfsize && dp->lfn_idx != 0xFFFF) {	/* Get LFN if available */
+			lfn = dp->lfn;
+			while ((w = *lfn++) != 0) {		/* Get an LFN character */
+#if !_LFN_UNICODE
+				w = ff_convert(w, 0);		/* Unicode -> OEM */
+				if (!w) { i = 0; break; }	/* No LFN if it could not be converted */
+				if (_DF1S && w >= 0x100)	/* Put 1st byte if it is a DBC (always false on SBCS cfg) */
+					p[i++] = (TCHAR)(w >> 8);
+#endif
+				if (i >= fno->lfsize - 1) { i = 0; break; }	/* No LFN if buffer overflow */
+				p[i++] = (TCHAR)w;
+			}
+		}
+		p[i] = 0;	/* Terminate LFN string by a \0 */
+	}
+#endif
+}
+#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Pattern matching                                                      */
+/*-----------------------------------------------------------------------*/
+#if _USE_FIND && _FS_MINIMIZE <= 1
+static
+WCHAR get_achar (		/* Get a character and advances ptr 1 or 2 */
+	const TCHAR** ptr	/* Pointer to pointer to the SBCS/DBCS/Unicode string */
+)
+{
+	WCHAR chr;
+
+#if !_LFN_UNICODE
+	chr = (BYTE)*(*ptr)++;					/* Get a byte */
+	if (IsLower(chr)) chr -= 0x20;			/* To upper ASCII char */
+	if (IsDBCS1(chr) && IsDBCS2(**ptr))		/* Get DBC 2nd byte if needed */
+		chr = chr << 8 | (BYTE)*(*ptr)++;
+#ifdef _EXCVT
+	if (chr >= 0x80) chr = ExCvt[chr - 0x80];	/* To upper SBCS extended char */
+#endif
+#else
+	chr = ff_wtoupper(*(*ptr)++);			/* Get a word and to upper */
+#endif
+	return chr;
+}
+
+
+static
+int pattern_matching (	/* 0:mismatched, 1:matched */
+	const TCHAR* pat,	/* Matching pattern */
+	const TCHAR* nam,	/* String to be tested */
+	int skip,			/* Number of pre-skip chars (number of ?s) */
+	int inf				/* Infinite search (* specified) */
+)
+{
+	const TCHAR *pp, *np;
+	WCHAR pc, nc;
+	int nm, nx;
+
+
+	while (skip--) {				/* Pre-skip name chars */
+		if (!get_achar(&nam)) return 0;	/* Branch mismatched if less name chars */
+	}
+	if (!*pat && inf) return 1;		/* (short circuit) */
+
+	do {
+		pp = pat; np = nam;			/* Top of pattern and name to match */
+		for (;;) {
+			if (*pp == '?' || *pp == '*') {	/* Wildcard? */
+				nm = nx = 0;
+				do {				/* Analyze the wildcard chars */
+					if (*pp++ == '?') nm++; else nx = 1;
+				} while (*pp == '?' || *pp == '*');
+				if (pattern_matching(pp, np, nm, nx)) return 1;	/* Test new branch (recurs upto number of wildcard blocks in the pattern) */
+				nc = *np; break;	/* Branch mismatched */
+			}
+			pc = get_achar(&pp);	/* Get a pattern char */
+			nc = get_achar(&np);	/* Get a name char */
+			if (pc != nc) break;	/* Branch mismatched? */
+			if (!pc) return 1;		/* Branch matched? (matched at end of both strings) */
+		}
+		get_achar(&nam);			/* nam++ */
+	} while (inf && nc);			/* Retry until end of name if infinite search is specified */
+
+	return 0;
+}
+#endif /* _USE_FIND && _FS_MINIMIZE <= 1 */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Pick a top segment and create the object name in directory form       */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT create_name (	/* FR_OK: successful, FR_INVALID_NAME: could not create */
+	FATFS_DIR* dp,			/* Pointer to the directory object */
+	const TCHAR** path	/* Pointer to pointer to the segment in the path string */
+)
+{
+#if _USE_LFN	/* LFN configuration */
+	BYTE b, cf;
+	WCHAR w, *lfn;
+	UINT i, ni, si, di;
+	const TCHAR *p;
+
+	/* Create LFN in Unicode */
+	for (p = *path; *p == '/' || *p == '\\'; p++) ;	/* Strip duplicated separator */
+	lfn = dp->lfn;
+	si = di = 0;
+	for (;;) {
+		w = p[si++];					/* Get a character */
+		if (w < ' ' || w == '/' || w == '\\') break;	/* Break on end of segment */
+		if (di >= _MAX_LFN)				/* Reject too long name */
+			return FR_INVALID_NAME;
+#if !_LFN_UNICODE
+		w &= 0xFF;
+		if (IsDBCS1(w)) {				/* Check if it is a DBC 1st byte (always false on SBCS cfg) */
+			b = (BYTE)p[si++];			/* Get 2nd byte */
+			w = (w << 8) + b;			/* Create a DBC */
+			if (!IsDBCS2(b))
+				return FR_INVALID_NAME;	/* Reject invalid sequence */
+		}
+		w = ff_convert(w, 1);			/* Convert ANSI/OEM to Unicode */
+		if (!w) return FR_INVALID_NAME;	/* Reject invalid code */
+#endif
+		if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal characters for LFN */
+			return FR_INVALID_NAME;
+		lfn[di++] = w;					/* Store the Unicode character */
+	}
+	*path = &p[si];						/* Return pointer to the next segment */
+	cf = (w < ' ') ? NS_LAST : 0;		/* Set last segment flag if end of path */
+#if _FS_RPATH
+	if ((di == 1 && lfn[di - 1] == '.') ||
+		(di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) {	/* Is this segment a dot entry? */
+		lfn[di] = 0;
+		for (i = 0; i < 11; i++)		/* Create dot name for SFN entry */
+			dp->fn[i] = (i < di) ? '.' : ' ';
+		dp->fn[i] = cf | NS_DOT;		/* This is a dot entry */
+		return FR_OK;
+	}
+#endif
+	while (di) {						/* Snip off trailing spaces and dots if exist */
+		w = lfn[di - 1];
+		if (w != ' ' && w != '.') break;
+		di--;
+	}
+	if (!di) return FR_INVALID_NAME;	/* Reject nul string */
+	lfn[di] = 0;						/* LFN is created */
+
+	/* Create SFN in directory form */
+	mem_set(dp->fn, ' ', 11);
+	for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ;	/* Strip leading spaces and dots */
+	if (si) cf |= NS_LOSS | NS_LFN;
+	while (di && lfn[di - 1] != '.') di--;	/* Find extension (di<=si: no extension) */
+
+	b = i = 0; ni = 8;
+	for (;;) {
+		w = lfn[si++];					/* Get an LFN character */
+		if (!w) break;					/* Break on end of the LFN */
+		if (w == ' ' || (w == '.' && si != di)) {	/* Remove spaces and dots */
+			cf |= NS_LOSS | NS_LFN; continue;
+		}
+
+		if (i >= ni || si == di) {		/* Extension or end of SFN */
+			if (ni == 11) {				/* Long extension */
+				cf |= NS_LOSS | NS_LFN; break;
+			}
+			if (si != di) cf |= NS_LOSS | NS_LFN;	/* Out of 8.3 format */
+			if (si > di) break;			/* No extension */
+			si = di; i = 8; ni = 11;	/* Enter extension section */
+			b <<= 2; continue;
+		}
+
+		if (w >= 0x80) {				/* Non ASCII character */
+#ifdef _EXCVT
+			w = ff_convert(w, 0);		/* Unicode -> OEM code */
+			if (w) w = ExCvt[w - 0x80];	/* Convert extended character to upper (SBCS) */
+#else
+			w = ff_convert(ff_wtoupper(w), 0);	/* Upper converted Unicode -> OEM code */
+#endif
+			cf |= NS_LFN;				/* Force create LFN entry */
+		}
+
+		if (_DF1S && w >= 0x100) {		/* Is this DBC? (always false at SBCS cfg) */
+			if (i >= ni - 1) {
+				cf |= NS_LOSS | NS_LFN; i = ni; continue;
+			}
+			dp->fn[i++] = (BYTE)(w >> 8);
+		} else {						/* SBC */
+			if (!w || chk_chr("+,;=[]", w)) {	/* Replace illegal characters for SFN */
+				w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */
+			} else {
+				if (IsUpper(w)) {		/* ASCII large capital */
+					b |= 2;
+				} else {
+					if (IsLower(w)) {	/* ASCII small capital */
+						b |= 1; w -= 0x20;
+					}
+				}
+			}
+		}
+		dp->fn[i++] = (BYTE)w;
+	}
+
+	if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM;	/* If the first character collides with DDEM, replace it with RDDEM */
+
+	if (ni == 8) b <<= 2;
+	if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03)	/* Create LFN entry when there are composite capitals */
+		cf |= NS_LFN;
+	if (!(cf & NS_LFN)) {						/* When LFN is in 8.3 format without extended character, NT flags are created */
+		if ((b & 0x03) == 0x01) cf |= NS_EXT;	/* NT flag (Extension has only small capital) */
+		if ((b & 0x0C) == 0x04) cf |= NS_BODY;	/* NT flag (Filename has only small capital) */
+	}
+
+	dp->fn[NSFLAG] = cf;	/* SFN is created */
+
+	return FR_OK;
+
+
+#else	/* Non-LFN configuration */
+	BYTE b, c, d, *sfn;
+	UINT ni, si, i;
+	const char *p;
+
+	/* Create file name in directory form */
+	for (p = *path; *p == '/' || *p == '\\'; p++) ;	/* Skip duplicated separator */
+	sfn = dp->fn;
+	mem_set(sfn, ' ', 11);
+	si = i = b = 0; ni = 8;
+#if _FS_RPATH
+	if (p[si] == '.') { /* Is this a dot entry? */
+		for (;;) {
+			c = (BYTE)p[si++];
+			if (c != '.' || si >= 3) break;
+			sfn[i++] = c;
+		}
+		if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
+		*path = &p[si];									/* Return pointer to the next segment */
+		sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT;	/* Set last segment flag if end of path */
+		return FR_OK;
+	}
+#endif
+	for (;;) {
+		c = (BYTE)p[si++];
+		if (c <= ' ' || c == '/' || c == '\\') break;	/* Break on end of segment */
+		if (c == '.' || i >= ni) {
+			if (ni != 8 || c != '.') return FR_INVALID_NAME;
+			i = 8; ni = 11;
+			b <<= 2; continue;
+		}
+		if (c >= 0x80) {				/* Extended character? */
+			b |= 3;						/* Eliminate NT flag */
+#ifdef _EXCVT
+			c = ExCvt[c - 0x80];		/* To upper extended characters (SBCS cfg) */
+#else
+#if !_DF1S
+			return FR_INVALID_NAME;		/* Reject extended characters (ASCII cfg) */
+#endif
+#endif
+		}
+		if (IsDBCS1(c)) {				/* Check if it is a DBC 1st byte (always false at SBCS cfg.) */
+			d = (BYTE)p[si++];			/* Get 2nd byte */
+			if (!IsDBCS2(d) || i >= ni - 1)	/* Reject invalid DBC */
+				return FR_INVALID_NAME;
+			sfn[i++] = c;
+			sfn[i++] = d;
+		} else {						/* SBC */
+			if (chk_chr("\"*+,:;<=>\?[]|\x7F", c))	/* Reject illegal chrs for SFN */
+				return FR_INVALID_NAME;
+			if (IsUpper(c)) {			/* ASCII large capital? */
+				b |= 2;
+			} else {
+				if (IsLower(c)) {		/* ASCII small capital? */
+					b |= 1; c -= 0x20;
+				}
+			}
+			sfn[i++] = c;
+		}
+	}
+	*path = &p[si];						/* Return pointer to the next segment */
+	c = (c <= ' ') ? NS_LAST : 0;		/* Set last segment flag if end of path */
+
+	if (!i) return FR_INVALID_NAME;		/* Reject nul string */
+	if (sfn[0] == DDEM) sfn[0] = RDDEM;	/* When first character collides with DDEM, replace it with RDDEM */
+
+	if (ni == 8) b <<= 2;
+	if ((b & 0x03) == 0x01) c |= NS_EXT;	/* NT flag (Name extension has only small capital) */
+	if ((b & 0x0C) == 0x04) c |= NS_BODY;	/* NT flag (Name body has only small capital) */
+
+	sfn[NSFLAG] = c;		/* Store NT flag, File name is created */
+
+	return FR_OK;
+#endif
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Follow a file path                                                    */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT follow_path (	/* FR_OK(0): successful, !=0: error code */
+	FATFS_DIR* dp,			/* Directory object to return last directory and found object */
+	const TCHAR* path	/* Full-path string to find a file or directory */
+)
+{
+	FRESULT res;
+	BYTE *dir, ns;
+
+
+#if _FS_RPATH
+	if (*path == '/' || *path == '\\') {	/* There is a heading separator */
+		path++;	dp->sclust = 0;				/* Strip it and start from the root directory */
+	} else {								/* No heading separator */
+		dp->sclust = dp->fs->cdir;			/* Start from the current directory */
+	}
+#else
+	if (*path == '/' || *path == '\\')		/* Strip heading separator if exist */
+		path++;
+	dp->sclust = 0;							/* Always start from the root directory */
+#endif
+
+	if ((UINT)*path < ' ') {				/* Null path name is the origin directory itself */
+		res = dir_sdi(dp, 0);
+		dp->dir = 0;
+	} else {								/* Follow path */
+		for (;;) {
+			res = create_name(dp, &path);	/* Get a segment name of the path */
+			if (res != FR_OK) break;
+			res = dir_find(dp);				/* Find an object with the sagment name */
+			ns = dp->fn[NSFLAG];
+			if (res != FR_OK) {				/* Failed to find the object */
+				if (res == FR_NO_FILE) {	/* Object is not found */
+					if (_FS_RPATH && (ns & NS_DOT)) {	/* If dot entry is not exist, */
+						dp->sclust = 0; dp->dir = 0;	/* it is the root directory and stay there */
+						if (!(ns & NS_LAST)) continue;	/* Continue to follow if not last segment */
+						res = FR_OK;					/* Ended at the root directroy. Function completed. */
+					} else {							/* Could not find the object */
+						if (!(ns & NS_LAST)) res = FR_NO_PATH;	/* Adjust error code if not last segment */
+					}
+				}
+				break;
+			}
+			if (ns & NS_LAST) break;			/* Last segment matched. Function completed. */
+			dir = dp->dir;						/* Follow the sub-directory */
+			if (!(dir[DIR_Attr] & AM_DIR)) {	/* It is not a sub-directory and cannot follow */
+				res = FR_NO_PATH; break;
+			}
+			dp->sclust = ld_clust(dp->fs, dir);
+		}
+	}
+
+	return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get logical drive number from path name                               */
+/*-----------------------------------------------------------------------*/
+
+static
+int get_ldnumber (		/* Returns logical drive number (-1:invalid drive) */
+	const TCHAR** path	/* Pointer to pointer to the path name */
+)
+{
+	const TCHAR *tp, *tt;
+	UINT i;
+	int vol = -1;
+#if _STR_VOLUME_ID		/* Find string drive id */
+	static const char* const str[] = {_VOLUME_STRS};
+	const char *sp;
+	char c;
+	TCHAR tc;
+#endif
+
+
+	if (*path) {	/* If the pointer is not a null */
+		for (tt = *path; (UINT)*tt >= (_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ;	/* Find ':' in the path */
+		if (*tt == ':') {	/* If a ':' is exist in the path name */
+			tp = *path;
+			i = *tp++ - '0'; 
+			if (i < 10 && tp == tt) {	/* Is there a numeric drive id? */
+				if (i < _VOLUMES) {	/* If a drive id is found, get the value and strip it */
+					vol = (int)i;
+					*path = ++tt;
+				}
+			}
+#if _STR_VOLUME_ID
+			 else {	/* No numeric drive number, find string drive id */
+				i = 0; tt++;
+				do {
+					sp = str[i]; tp = *path;
+					do {	/* Compare a string drive id with path name */
+						c = *sp++; tc = *tp++;
+						if (IsLower(tc)) tc -= 0x20;
+					} while (c && (TCHAR)c == tc);
+				} while ((c || tp != tt) && ++i < _VOLUMES);	/* Repeat for each id until pattern match */
+				if (i < _VOLUMES) {	/* If a drive id is found, get the value and strip it */
+					vol = (int)i;
+					*path = tt;
+				}
+			}
+#endif
+			return vol;
+		}
+#if _FS_RPATH && _VOLUMES >= 2
+		vol = CurrVol;	/* Current drive */
+#else
+		vol = 0;		/* Drive 0 */
+#endif
+	}
+	return vol;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Load a sector and check if it is an FAT boot sector                   */
+/*-----------------------------------------------------------------------*/
+
+static
+BYTE check_fs (	/* 0:Valid FAT-BS, 1:Valid BS but not FAT, 2:Not a BS, 3:Disk error */
+	FATFS* fs,	/* File system object */
+	DWORD sect	/* Sector# (lba) to check if it is an FAT boot record or not */
+)
+{
+	fs->wflag = 0; fs->winsect = 0xFFFFFFFF;	/* Invaidate window */
+	if (move_window(fs, sect) != FR_OK)			/* Load boot record */
+		return 3;
+
+	if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55)	/* Check boot record signature (always placed at offset 510 even if the sector size is >512) */
+		return 2;
+
+	if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146)		/* Check "FAT" string */
+		return 0;
+	if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146)	/* Check "FAT" string */
+		return 0;
+
+	return 1;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Find logical drive and check if the volume is mounted                 */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT find_volume (	/* FR_OK(0): successful, !=0: any error occurred */
+	FATFS** rfs,		/* Pointer to pointer to the found file system object */
+	const TCHAR** path,	/* Pointer to pointer to the path name (drive number) */
+	BYTE wmode			/* !=0: Check write protection for write access */
+)
+{
+	BYTE fmt, *pt;
+	int vol;
+	DSTATUS stat;
+	DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4];
+	WORD nrsv;
+	FATFS *fs;
+	UINT i;
+
+
+	/* Get logical drive number from the path name */
+	*rfs = 0;
+	vol = get_ldnumber(path);
+	if (vol < 0) return FR_INVALID_DRIVE;
+
+	/* Check if the file system object is valid or not */
+	fs = FatFs[vol];					/* Get pointer to the file system object */
+	if (!fs) return FR_NOT_ENABLED;		/* Is the file system object available? */
+
+	ENTER_FF(fs);						/* Lock the volume */
+	*rfs = fs;							/* Return pointer to the file system object */
+
+	if (fs->fs_type) {					/* If the volume has been mounted */
+		stat = disk_status(fs->drv);
+		if (!(stat & STA_NOINIT)) {		/* and the physical drive is kept initialized */
+			if (!_FS_READONLY && wmode && (stat & STA_PROTECT))	/* Check write protection if needed */
+				return FR_WRITE_PROTECTED;
+			return FR_OK;				/* The file system object is valid */
+		}
+	}
+
+	/* The file system object is not valid. */
+	/* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */
+
+	fs->fs_type = 0;					/* Clear the file system object */
+	fs->drv = LD2PD(vol);				/* Bind the logical drive and a physical drive */
+	stat = disk_initialize(fs->drv);	/* Initialize the physical drive */
+	if (stat & STA_NOINIT)				/* Check if the initialization succeeded */
+		return FR_NOT_READY;			/* Failed to initialize due to no medium or hard error */
+	if (!_FS_READONLY && wmode && (stat & STA_PROTECT))	/* Check disk write protection if needed */
+		return FR_WRITE_PROTECTED;
+#if _MAX_SS != _MIN_SS						/* Get sector size (multiple sector size cfg only) */
+	if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK
+		|| SS(fs) < _MIN_SS || SS(fs) > _MAX_SS) return FR_DISK_ERR;
+#endif
+	/* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */
+	bsect = 0;
+	fmt = check_fs(fs, bsect);					/* Load sector 0 and check if it is an FAT boot sector as SFD */
+	if (fmt == 1 || (!fmt && (LD2PT(vol)))) {	/* Not an FAT boot sector or forced partition number */
+		for (i = 0; i < 4; i++) {			/* Get partition offset */
+			pt = fs->win + MBR_Table + i * SZ_PTE;
+			br[i] = pt[4] ? LD_DWORD(&pt[8]) : 0;
+		}
+		i = LD2PT(vol);						/* Partition number: 0:auto, 1-4:forced */
+		if (i) i--;
+		do {								/* Find an FAT volume */
+			bsect = br[i];
+			fmt = bsect ? check_fs(fs, bsect) : 2;	/* Check the partition */
+		} while (!LD2PT(vol) && fmt && ++i < 4);
+	}
+	if (fmt == 3) return FR_DISK_ERR;		/* An error occured in the disk I/O layer */
+	if (fmt) return FR_NO_FILESYSTEM;		/* No FAT volume is found */
+
+	/* An FAT volume is found. Following code initializes the file system object */
+
+	if (LD_WORD(fs->win + BPB_BytsPerSec) != SS(fs))	/* (BPB_BytsPerSec must be equal to the physical sector size) */
+		return FR_NO_FILESYSTEM;
+
+	fasize = LD_WORD(fs->win + BPB_FATSz16);			/* Number of sectors per FAT */
+	if (!fasize) fasize = LD_DWORD(fs->win + BPB_FATSz32);
+	fs->fsize = fasize;
+
+	fs->n_fats = fs->win[BPB_NumFATs];					/* Number of FAT copies */
+	if (fs->n_fats != 1 && fs->n_fats != 2)				/* (Must be 1 or 2) */
+		return FR_NO_FILESYSTEM;
+	fasize *= fs->n_fats;								/* Number of sectors for FAT area */
+
+	fs->csize = fs->win[BPB_SecPerClus];				/* Number of sectors per cluster */
+	if (!fs->csize || (fs->csize & (fs->csize - 1)))	/* (Must be power of 2) */
+		return FR_NO_FILESYSTEM;
+
+	fs->n_rootdir = LD_WORD(fs->win + BPB_RootEntCnt);	/* Number of root directory entries */
+	if (fs->n_rootdir % (SS(fs) / SZ_DIRE))				/* (Must be sector aligned) */
+		return FR_NO_FILESYSTEM;
+
+	tsect = LD_WORD(fs->win + BPB_TotSec16);			/* Number of sectors on the volume */
+	if (!tsect) tsect = LD_DWORD(fs->win + BPB_TotSec32);
+
+	nrsv = LD_WORD(fs->win + BPB_RsvdSecCnt);			/* Number of reserved sectors */
+	if (!nrsv) return FR_NO_FILESYSTEM;					/* (Must not be 0) */
+
+	/* Determine the FAT sub type */
+	sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIRE);	/* RSV + FAT + FATFS_DIR */
+	if (tsect < sysect) return FR_NO_FILESYSTEM;		/* (Invalid volume size) */
+	nclst = (tsect - sysect) / fs->csize;				/* Number of clusters */
+	if (!nclst) return FR_NO_FILESYSTEM;				/* (Invalid volume size) */
+	fmt = FS_FAT12;
+	if (nclst >= MIN_FAT16) fmt = FS_FAT16;
+	if (nclst >= MIN_FAT32) fmt = FS_FAT32;
+
+	/* Boundaries and Limits */
+	fs->n_fatent = nclst + 2;							/* Number of FAT entries */
+	fs->volbase = bsect;								/* Volume start sector */
+	fs->fatbase = bsect + nrsv; 						/* FAT start sector */
+	fs->database = bsect + sysect;						/* Data start sector */
+	if (fmt == FS_FAT32) {
+		if (fs->n_rootdir) return FR_NO_FILESYSTEM;		/* (BPB_RootEntCnt must be 0) */
+		fs->dirbase = LD_DWORD(fs->win + BPB_RootClus);	/* Root directory start cluster */
+		szbfat = fs->n_fatent * 4;						/* (Needed FAT size) */
+	} else {
+		if (!fs->n_rootdir)	return FR_NO_FILESYSTEM;	/* (BPB_RootEntCnt must not be 0) */
+		fs->dirbase = fs->fatbase + fasize;				/* Root directory start sector */
+		szbfat = (fmt == FS_FAT16) ?					/* (Needed FAT size) */
+			fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
+	}
+	if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs))	/* (BPB_FATSz must not be less than the size needed) */
+		return FR_NO_FILESYSTEM;
+
+#if !_FS_READONLY
+	/* Initialize cluster allocation information */
+	fs->last_clust = fs->free_clust = 0xFFFFFFFF;
+
+	/* Get fsinfo if available */
+	fs->fsi_flag = 0x80;
+#if (_FS_NOFSINFO & 3) != 3
+	if (fmt == FS_FAT32				/* Enable FSINFO only if FAT32 and BPB_FSInfo == 1 */
+		&& LD_WORD(fs->win + BPB_FSInfo) == 1
+		&& move_window(fs, bsect + 1) == FR_OK)
+	{
+		fs->fsi_flag = 0;
+		if (LD_WORD(fs->win + BS_55AA) == 0xAA55	/* Load FSINFO data if available */
+			&& LD_DWORD(fs->win + FSI_LeadSig) == 0x41615252
+			&& LD_DWORD(fs->win + FSI_StrucSig) == 0x61417272)
+		{
+#if (_FS_NOFSINFO & 1) == 0
+			fs->free_clust = LD_DWORD(fs->win + FSI_Free_Count);
+#endif
+#if (_FS_NOFSINFO & 2) == 0
+			fs->last_clust = LD_DWORD(fs->win + FSI_Nxt_Free);
+#endif
+		}
+	}
+#endif
+#endif
+	fs->fs_type = fmt;	/* FAT sub-type */
+	fs->id = ++Fsid;	/* File system mount ID */
+#if _FS_RPATH
+	fs->cdir = 0;		/* Set current directory to root */
+#endif
+#if _FS_LOCK			/* Clear file lock semaphores */
+	clear_lock(fs);
+#endif
+
+	return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Check if the file/directory object is valid or not                    */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT validate (	/* FR_OK(0): The object is valid, !=0: Invalid */
+	void* obj		/* Pointer to the object FIL/FATFS_DIR to check validity */
+)
+{
+	FIL *fil = (FIL*)obj;	/* Assuming offset of .fs and .id in the FIL/FATFS_DIR structure is identical */
+
+
+	if (!fil || !fil->fs || !fil->fs->fs_type || fil->fs->id != fil->id || (disk_status(fil->fs->drv) & STA_NOINIT))
+		return FR_INVALID_OBJECT;
+
+	ENTER_FF(fil->fs);		/* Lock file system */
+
+	return FR_OK;
+}
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Public Functions
+
+---------------------------------------------------------------------------*/
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Mount/Unmount a Logical Drive                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mount (
+	FATFS* fs,			/* Pointer to the file system object (NULL:unmount)*/
+	const TCHAR* path,	/* Logical drive number to be mounted/unmounted */
+	BYTE opt			/* 0:Do not mount (delayed mount), 1:Mount immediately */
+)
+{
+	FATFS *cfs;
+	int vol;
+	FRESULT res;
+	const TCHAR *rp = path;
+
+
+	vol = get_ldnumber(&rp);
+	if (vol < 0) return FR_INVALID_DRIVE;
+	cfs = FatFs[vol];					/* Pointer to fs object */
+
+	if (cfs) {
+#if _FS_LOCK
+		clear_lock(cfs);
+#endif
+#if _FS_REENTRANT						/* Discard sync object of the current volume */
+		if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR;
+#endif
+		cfs->fs_type = 0;				/* Clear old fs object */
+	}
+
+	if (fs) {
+		fs->fs_type = 0;				/* Clear new fs object */
+#if _FS_REENTRANT						/* Create sync object for the new volume */
+		if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR;
+#endif
+	}
+	FatFs[vol] = fs;					/* Register new fs object */
+
+	if (!fs || opt != 1) return FR_OK;	/* Do not mount now, it will be mounted later */
+
+	res = find_volume(&fs, &path, 0);	/* Force mounted the volume */
+	LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Open or Create a File                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_open (
+	FIL* fp,			/* Pointer to the blank file object */
+	const TCHAR* path,	/* Pointer to the file name */
+	BYTE mode			/* Access mode and file open mode flags */
+)
+{
+	FRESULT res;
+	FATFS_DIR dj;
+	BYTE *dir;
+	DEFINE_NAMEBUF;
+#if !_FS_READONLY
+	DWORD dw, cl;
+#endif
+
+
+	if (!fp) return FR_INVALID_OBJECT;
+	fp->fs = 0;			/* Clear file object */
+
+	/* Get logical drive number */
+#if !_FS_READONLY
+	mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW;
+	res = find_volume(&dj.fs, &path, (BYTE)(mode & ~FA_READ));
+#else
+	mode &= FA_READ;
+	res = find_volume(&dj.fs, &path, 0);
+#endif
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		res = follow_path(&dj, path);	/* Follow the file path */
+		dir = dj.dir;
+#if !_FS_READONLY	/* R/W configuration */
+		if (res == FR_OK) {
+			if (!dir)	/* Default directory itself */
+				res = FR_INVALID_NAME;
+#if _FS_LOCK
+			else
+				res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0);
+#endif
+		}
+		/* Create or Open a file */
+		if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
+			if (res != FR_OK) {					/* No file, create new */
+				if (res == FR_NO_FILE)			/* There is no file to open, create a new entry */
+#if _FS_LOCK
+					res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;
+#else
+					res = dir_register(&dj);
+#endif
+				mode |= FA_CREATE_ALWAYS;		/* File is created */
+				dir = dj.dir;					/* New entry */
+			}
+			else {								/* Any object is already existing */
+				if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) {	/* Cannot overwrite it (R/O or FATFS_DIR) */
+					res = FR_DENIED;
+				} else {
+					if (mode & FA_CREATE_NEW)	/* Cannot create as new file */
+						res = FR_EXIST;
+				}
+			}
+			if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) {	/* Truncate it if overwrite mode */
+				dw = GET_FATTIME();
+				ST_DWORD(dir + DIR_CrtTime, dw);/* Set created time */
+				ST_DWORD(dir + DIR_WrtTime, dw);/* Set modified time */
+				dir[DIR_Attr] = 0;				/* Reset attribute */
+				ST_DWORD(dir + DIR_FileSize, 0);/* Reset file size */
+				cl = ld_clust(dj.fs, dir);		/* Get cluster chain */
+				st_clust(dir, 0);				/* Reset cluster */
+				dj.fs->wflag = 1;
+				if (cl) {						/* Remove the cluster chain if exist */
+					dw = dj.fs->winsect;
+					res = remove_chain(dj.fs, cl);
+					if (res == FR_OK) {
+						dj.fs->last_clust = cl - 1;	/* Reuse the cluster hole */
+						res = move_window(dj.fs, dw);
+					}
+				}
+			}
+		}
+		else {	/* Open an existing file */
+			if (res == FR_OK) {					/* Following succeeded */
+				if (dir[DIR_Attr] & AM_DIR) {	/* It is a directory */
+					res = FR_NO_FILE;
+				} else {
+					if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
+						res = FR_DENIED;
+				}
+			}
+		}
+		if (res == FR_OK) {
+			if (mode & FA_CREATE_ALWAYS)		/* Set file change flag if created or overwritten */
+				mode |= FA__WRITTEN;
+			fp->dir_sect = dj.fs->winsect;		/* Pointer to the directory entry */
+			fp->dir_ptr = dir;
+#if _FS_LOCK
+			fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0);
+			if (!fp->lockid) res = FR_INT_ERR;
+#endif
+		}
+
+#else				/* R/O configuration */
+		if (res == FR_OK) {					/* Follow succeeded */
+			dir = dj.dir;
+			if (!dir) {						/* Current directory itself */
+				res = FR_INVALID_NAME;
+			} else {
+				if (dir[DIR_Attr] & AM_DIR)	/* It is a directory */
+					res = FR_NO_FILE;
+			}
+		}
+#endif
+		FREE_BUF();
+
+		if (res == FR_OK) {
+			fp->flag = mode;					/* File access mode */
+			fp->err = 0;						/* Clear error flag */
+			fp->sclust = ld_clust(dj.fs, dir);	/* File start cluster */
+			fp->fsize = LD_DWORD(dir + DIR_FileSize);	/* File size */
+			fp->fptr = 0;						/* File pointer */
+			fp->dsect = 0;
+#if _USE_FASTSEEK
+			fp->cltbl = 0;						/* Normal seek mode */
+#endif
+			fp->fs = dj.fs;	 					/* Validate file object */
+			fp->id = fp->fs->id;
+		}
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read File                                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_read (
+	FIL* fp, 		/* Pointer to the file object */
+	void* buff,		/* Pointer to data buffer */
+	UINT btr,		/* Number of bytes to read */
+	UINT* br		/* Pointer to number of bytes read */
+)
+{
+	FRESULT res;
+	DWORD clst, sect, remain;
+	UINT rcnt, cc;
+	BYTE csect, *rbuff = (BYTE*)buff;
+
+
+	*br = 0;	/* Clear read byte counter */
+
+	res = validate(fp);							/* Check validity */
+	if (res != FR_OK) LEAVE_FF(fp->fs, res);
+	if (fp->err)								/* Check error */
+		LEAVE_FF(fp->fs, (FRESULT)fp->err);
+	if (!(fp->flag & FA_READ)) 					/* Check access mode */
+		LEAVE_FF(fp->fs, FR_DENIED);
+	remain = fp->fsize - fp->fptr;
+	if (btr > remain) btr = (UINT)remain;		/* Truncate btr by remaining bytes */
+
+	for ( ;  btr;								/* Repeat until all data read */
+		rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
+		if ((fp->fptr % SS(fp->fs)) == 0) {		/* On the sector boundary? */
+			csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));	/* Sector offset in the cluster */
+			if (!csect) {						/* On the cluster boundary? */
+				if (fp->fptr == 0) {			/* On the top of the file? */
+					clst = fp->sclust;			/* Follow from the origin */
+				} else {						/* Middle or end of the file */
+#if _USE_FASTSEEK
+					if (fp->cltbl)
+						clst = clmt_clust(fp, fp->fptr);	/* Get cluster# from the CLMT */
+					else
+#endif
+						clst = get_fat(fp->fs, fp->clust);	/* Follow cluster chain on the FAT */
+				}
+				if (clst < 2) ABORT(fp->fs, FR_INT_ERR);
+				if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+				fp->clust = clst;				/* Update current cluster */
+			}
+			sect = clust2sect(fp->fs, fp->clust);	/* Get current sector */
+			if (!sect) ABORT(fp->fs, FR_INT_ERR);
+			sect += csect;
+			cc = btr / SS(fp->fs);				/* When remaining bytes >= sector size, */
+			if (cc) {							/* Read maximum contiguous sectors directly */
+				if (csect + cc > fp->fs->csize)	/* Clip at cluster boundary */
+					cc = fp->fs->csize - csect;
+				if (disk_read(fp->fs->drv, rbuff, sect, cc) != RES_OK)
+					ABORT(fp->fs, FR_DISK_ERR);
+#if !_FS_READONLY && _FS_MINIMIZE <= 2			/* Replace one of the read sectors with cached data if it contains a dirty sector */
+#if _FS_TINY
+				if (fp->fs->wflag && fp->fs->winsect - sect < cc)
+					mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs));
+#else
+				if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc)
+					mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs));
+#endif
+#endif
+				rcnt = SS(fp->fs) * cc;			/* Number of bytes transferred */
+				continue;
+			}
+#if !_FS_TINY
+			if (fp->dsect != sect) {			/* Load data sector if not in cache */
+#if !_FS_READONLY
+				if (fp->flag & FA__DIRTY) {		/* Write-back dirty sector cache */
+					if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+						ABORT(fp->fs, FR_DISK_ERR);
+					fp->flag &= ~FA__DIRTY;
+				}
+#endif
+				if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK)	/* Fill sector cache */
+					ABORT(fp->fs, FR_DISK_ERR);
+			}
+#endif
+			fp->dsect = sect;
+		}
+		rcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));	/* Get partial sector data from sector buffer */
+		if (rcnt > btr) rcnt = btr;
+#if _FS_TINY
+		if (move_window(fp->fs, fp->dsect) != FR_OK)		/* Move sector window */
+			ABORT(fp->fs, FR_DISK_ERR);
+		mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt);	/* Pick partial sector */
+#else
+		mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt);	/* Pick partial sector */
+#endif
+	}
+
+	LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Write File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_write (
+	FIL* fp,			/* Pointer to the file object */
+	const void *buff,	/* Pointer to the data to be written */
+	UINT btw,			/* Number of bytes to write */
+	UINT* bw			/* Pointer to number of bytes written */
+)
+{
+	FRESULT res;
+	DWORD clst, sect;
+	UINT wcnt, cc;
+	const BYTE *wbuff = (const BYTE*)buff;
+	BYTE csect;
+	bool need_sync = false;
+
+	*bw = 0;	/* Clear write byte counter */
+
+	res = validate(fp);						/* Check validity */
+	if (res != FR_OK) LEAVE_FF(fp->fs, res);
+	if (fp->err)							/* Check error */
+		LEAVE_FF(fp->fs, (FRESULT)fp->err);
+	if (!(fp->flag & FA_WRITE))				/* Check access mode */
+		LEAVE_FF(fp->fs, FR_DENIED);
+	if (fp->fptr + btw < fp->fptr) btw = 0;	/* File size cannot reach 4GB */
+
+	for ( ;  btw;							/* Repeat until all data written */
+		wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {
+		if ((fp->fptr % SS(fp->fs)) == 0) {	/* On the sector boundary? */
+			csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));	/* Sector offset in the cluster */
+			if (!csect) {					/* On the cluster boundary? */
+				if (fp->fptr == 0) {		/* On the top of the file? */
+					clst = fp->sclust;		/* Follow from the origin */
+					if (clst == 0)			/* When no cluster is allocated, */
+						clst = create_chain(fp->fs, 0);	/* Create a new cluster chain */
+				} else {					/* Middle or end of the file */
+#if _USE_FASTSEEK
+					if (fp->cltbl)
+						clst = clmt_clust(fp, fp->fptr);	/* Get cluster# from the CLMT */
+					else
+#endif
+						clst = create_chain(fp->fs, fp->clust);	/* Follow or stretch cluster chain on the FAT */
+				}
+				if (clst == 0) break;		/* Could not allocate a new cluster (disk full) */
+				if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+				if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+				fp->clust = clst;			/* Update current cluster */
+				if (fp->sclust == 0) fp->sclust = clst;	/* Set start cluster if the first write */
+				
+#if FLUSH_ON_NEW_CLUSTER
+                // We do not need to flush for the first cluster
+                if (fp->fptr != 0) {
+                    need_sync = true;
+                }
+#endif
+			}
+#if _FS_TINY
+			if (fp->fs->winsect == fp->dsect && sync_window(fp->fs))	/* Write-back sector cache */
+				ABORT(fp->fs, FR_DISK_ERR);
+#else
+			if (fp->flag & FA__DIRTY) {		/* Write-back sector cache */
+				if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+					ABORT(fp->fs, FR_DISK_ERR);
+				fp->flag &= ~FA__DIRTY;
+			}
+#endif
+			sect = clust2sect(fp->fs, fp->clust);	/* Get current sector */
+			if (!sect) ABORT(fp->fs, FR_INT_ERR);
+			sect += csect;
+			cc = btw / SS(fp->fs);			/* When remaining bytes >= sector size, */
+			if (cc) {						/* Write maximum contiguous sectors directly */
+				if (csect + cc > fp->fs->csize)	/* Clip at cluster boundary */
+					cc = fp->fs->csize - csect;
+				if (disk_write(fp->fs->drv, wbuff, sect, cc) != RES_OK)
+					ABORT(fp->fs, FR_DISK_ERR);
+#if _FS_MINIMIZE <= 2
+#if _FS_TINY
+				if (fp->fs->winsect - sect < cc) {	/* Refill sector cache if it gets invalidated by the direct write */
+					mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs));
+					fp->fs->wflag = 0;
+				}
+#else
+				if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
+					mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs));
+					fp->flag &= ~FA__DIRTY;
+				}
+#endif
+#endif
+				wcnt = SS(fp->fs) * cc;		/* Number of bytes transferred */
+#if FLUSH_ON_NEW_SECTOR
+                need_sync = true;
+#endif
+				continue;
+			}
+#if _FS_TINY
+			if (fp->fptr >= fp->fsize) {	/* Avoid silly cache filling at growing edge */
+				if (sync_window(fp->fs)) ABORT(fp->fs, FR_DISK_ERR);
+				fp->fs->winsect = sect;
+			}
+#else
+			if (fp->dsect != sect) {		/* Fill sector cache with file data */
+				if (fp->fptr < fp->fsize &&
+					disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK)
+						ABORT(fp->fs, FR_DISK_ERR);
+			}
+#endif
+			fp->dsect = sect;
+		}
+		wcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */
+		if (wcnt > btw) wcnt = btw;
+#if _FS_TINY
+		if (move_window(fp->fs, fp->dsect) != FR_OK)	/* Move sector window */
+			ABORT(fp->fs, FR_DISK_ERR);
+		mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt);	/* Fit partial sector */
+		fp->fs->wflag = 1;
+#else
+		mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt);	/* Fit partial sector */
+		fp->flag |= FA__DIRTY;
+#endif
+	}
+
+	if (fp->fptr > fp->fsize) fp->fsize = fp->fptr;	/* Update file size if needed */
+	fp->flag |= FA__WRITTEN;						/* Set file change flag */
+
+	if (need_sync) {
+        f_sync (fp);
+    }
+
+	LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Synchronize the File                                                  */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_sync (
+	FIL* fp		/* Pointer to the file object */
+)
+{
+	FRESULT res;
+	DWORD tm;
+	BYTE *dir;
+
+
+	res = validate(fp);					/* Check validity of the object */
+	if (res == FR_OK) {
+		if (fp->flag & FA__WRITTEN) {	/* Is there any change to the file? */
+#if !_FS_TINY
+			if (fp->flag & FA__DIRTY) {	/* Write-back cached data if needed */
+				if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+					LEAVE_FF(fp->fs, FR_DISK_ERR);
+				fp->flag &= ~FA__DIRTY;
+			}
+#endif
+			/* Update the directory entry */
+			res = move_window(fp->fs, fp->dir_sect);
+			if (res == FR_OK) {
+				dir = fp->dir_ptr;
+				dir[DIR_Attr] |= AM_ARC;					/* Set archive bit */
+				ST_DWORD(dir + DIR_FileSize, fp->fsize);	/* Update file size */
+				st_clust(dir, fp->sclust);					/* Update start cluster */
+				tm = GET_FATTIME();							/* Update modified time */
+				ST_DWORD(dir + DIR_WrtTime, tm);
+				ST_WORD(dir + DIR_LstAccDate, 0);
+				fp->flag &= ~FA__WRITTEN;
+				fp->fs->wflag = 1;
+				res = sync_fs(fp->fs);
+			}
+		}
+	}
+
+	LEAVE_FF(fp->fs, res);
+}
+
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_close (
+	FIL *fp		/* Pointer to the file object to be closed */
+)
+{
+	FRESULT res;
+
+
+#if !_FS_READONLY
+	res = f_sync(fp);					/* Flush cached data */
+	if (res == FR_OK)
+#endif
+	{
+		res = validate(fp);				/* Lock volume */
+		if (res == FR_OK) {
+#if _FS_REENTRANT
+			FATFS *fs = fp->fs;
+#endif
+#if _FS_LOCK
+			res = dec_lock(fp->lockid);	/* Decrement file open counter */
+			if (res == FR_OK)
+#endif
+				fp->fs = 0;				/* Invalidate file object */
+#if _FS_REENTRANT
+			unlock_fs(fs, FR_OK);		/* Unlock volume */
+#endif
+		}
+	}
+	return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Current Directory or Current Drive, Get Current Directory      */
+/*-----------------------------------------------------------------------*/
+
+#if _FS_RPATH >= 1
+#if _VOLUMES >= 2
+FRESULT f_chdrive (
+	const TCHAR* path		/* Drive number */
+)
+{
+	int vol;
+
+
+	vol = get_ldnumber(&path);
+	if (vol < 0) return FR_INVALID_DRIVE;
+
+	CurrVol = (BYTE)vol;
+
+	return FR_OK;
+}
+#endif
+
+
+FRESULT f_chdir (
+	const TCHAR* path	/* Pointer to the directory path */
+)
+{
+	FRESULT res;
+	FATFS_DIR dj;
+	DEFINE_NAMEBUF;
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &path, 0);
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		res = follow_path(&dj, path);		/* Follow the path */
+		FREE_BUF();
+		if (res == FR_OK) {					/* Follow completed */
+			if (!dj.dir) {
+				dj.fs->cdir = dj.sclust;	/* Start directory itself */
+			} else {
+				if (dj.dir[DIR_Attr] & AM_DIR)	/* Reached to the directory */
+					dj.fs->cdir = ld_clust(dj.fs, dj.dir);
+				else
+					res = FR_NO_PATH;		/* Reached but a file */
+			}
+		}
+		if (res == FR_NO_FILE) res = FR_NO_PATH;
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+#if _FS_RPATH >= 2
+FRESULT f_getcwd (
+	TCHAR* buff,	/* Pointer to the directory path */
+	UINT len		/* Size of path */
+)
+{
+	FRESULT res;
+	FATFS_DIR dj;
+	UINT i, n;
+	DWORD ccl;
+	TCHAR *tp;
+	FILINFO fno;
+	DEFINE_NAMEBUF;
+
+
+	*buff = 0;
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, (const TCHAR**)&buff, 0);	/* Get current volume */
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		i = len;			/* Bottom of buffer (directory stack base) */
+		dj.sclust = dj.fs->cdir;			/* Start to follow upper directory from current directory */
+		while ((ccl = dj.sclust) != 0) {	/* Repeat while current directory is a sub-directory */
+			res = dir_sdi(&dj, 1);			/* Get parent directory */
+			if (res != FR_OK) break;
+			res = dir_read(&dj, 0);
+			if (res != FR_OK) break;
+			dj.sclust = ld_clust(dj.fs, dj.dir);	/* Goto parent directory */
+			res = dir_sdi(&dj, 0);
+			if (res != FR_OK) break;
+			do {							/* Find the entry links to the child directory */
+				res = dir_read(&dj, 0);
+				if (res != FR_OK) break;
+				if (ccl == ld_clust(dj.fs, dj.dir)) break;	/* Found the entry */
+				res = dir_next(&dj, 0);	
+			} while (res == FR_OK);
+			if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */
+			if (res != FR_OK) break;
+#if _USE_LFN
+			fno.lfname = buff;
+			fno.lfsize = i;
+#endif
+			get_fileinfo(&dj, &fno);		/* Get the directory name and push it to the buffer */
+			tp = fno.fname;
+#if _USE_LFN
+			if (*buff) tp = buff;
+#endif
+			for (n = 0; tp[n]; n++) ;
+			if (i < n + 3) {
+				res = FR_NOT_ENOUGH_CORE; break;
+			}
+			while (n) buff[--i] = tp[--n];
+			buff[--i] = '/';
+		}
+		tp = buff;
+		if (res == FR_OK) {
+#if _VOLUMES >= 2
+			*tp++ = '0' + CurrVol;			/* Put drive number */
+			*tp++ = ':';
+#endif
+			if (i == len) {					/* Root-directory */
+				*tp++ = '/';
+			} else {						/* Sub-directroy */
+				do		/* Add stacked path str */
+					*tp++ = buff[i++];
+				while (i < len);
+			}
+		}
+		*tp = 0;
+		FREE_BUF();
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+#endif /* _FS_RPATH >= 2 */
+#endif /* _FS_RPATH >= 1 */
+
+
+
+#if _FS_MINIMIZE <= 2
+/*-----------------------------------------------------------------------*/
+/* Seek File R/W Pointer                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_lseek (
+	FIL* fp,		/* Pointer to the file object */
+	DWORD ofs		/* File pointer from top of file */
+)
+{
+	FRESULT res;
+	DWORD clst, bcs, nsect, ifptr;
+#if _USE_FASTSEEK
+	DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl;
+#endif
+
+
+	res = validate(fp);					/* Check validity of the object */
+	if (res != FR_OK) LEAVE_FF(fp->fs, res);
+	if (fp->err)						/* Check error */
+		LEAVE_FF(fp->fs, (FRESULT)fp->err);
+
+#if _USE_FASTSEEK
+	if (fp->cltbl) {	/* Fast seek */
+		if (ofs == CREATE_LINKMAP) {	/* Create CLMT */
+			tbl = fp->cltbl;
+			tlen = *tbl++; ulen = 2;	/* Given table size and required table size */
+			cl = fp->sclust;			/* Top of the chain */
+			if (cl) {
+				do {
+					/* Get a fragment */
+					tcl = cl; ncl = 0; ulen += 2;	/* Top, length and used items */
+					do {
+						pcl = cl; ncl++;
+						cl = get_fat(fp->fs, cl);
+						if (cl <= 1) ABORT(fp->fs, FR_INT_ERR);
+						if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+					} while (cl == pcl + 1);
+					if (ulen <= tlen) {		/* Store the length and top of the fragment */
+						*tbl++ = ncl; *tbl++ = tcl;
+					}
+				} while (cl < fp->fs->n_fatent);	/* Repeat until end of chain */
+			}
+			*fp->cltbl = ulen;	/* Number of items used */
+			if (ulen <= tlen)
+				*tbl = 0;		/* Terminate table */
+			else
+				res = FR_NOT_ENOUGH_CORE;	/* Given table size is smaller than required */
+
+		} else {						/* Fast seek */
+			if (ofs > fp->fsize)		/* Clip offset at the file size */
+				ofs = fp->fsize;
+			fp->fptr = ofs;				/* Set file pointer */
+			if (ofs) {
+				fp->clust = clmt_clust(fp, ofs - 1);
+				dsc = clust2sect(fp->fs, fp->clust);
+				if (!dsc) ABORT(fp->fs, FR_INT_ERR);
+				dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1);
+				if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) {	/* Refill sector cache if needed */
+#if !_FS_TINY
+#if !_FS_READONLY
+					if (fp->flag & FA__DIRTY) {		/* Write-back dirty sector cache */
+						if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+							ABORT(fp->fs, FR_DISK_ERR);
+						fp->flag &= ~FA__DIRTY;
+					}
+#endif
+					if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK)	/* Load current sector */
+						ABORT(fp->fs, FR_DISK_ERR);
+#endif
+					fp->dsect = dsc;
+				}
+			}
+		}
+	} else
+#endif
+
+	/* Normal Seek */
+	{
+		if (ofs > fp->fsize					/* In read-only mode, clip offset with the file size */
+#if !_FS_READONLY
+			 && !(fp->flag & FA_WRITE)
+#endif
+			) ofs = fp->fsize;
+
+		ifptr = fp->fptr;
+		fp->fptr = nsect = 0;
+		if (ofs) {
+			bcs = (DWORD)fp->fs->csize * SS(fp->fs);	/* Cluster size (byte) */
+			if (ifptr > 0 &&
+				(ofs - 1) / bcs >= (ifptr - 1) / bcs) {	/* When seek to same or following cluster, */
+				fp->fptr = (ifptr - 1) & ~(bcs - 1);	/* start from the current cluster */
+				ofs -= fp->fptr;
+				clst = fp->clust;
+			} else {									/* When seek to back cluster, */
+				clst = fp->sclust;						/* start from the first cluster */
+#if !_FS_READONLY
+				if (clst == 0) {						/* If no cluster chain, create a new chain */
+					clst = create_chain(fp->fs, 0);
+					if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+					if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+					fp->sclust = clst;
+				}
+#endif
+				fp->clust = clst;
+			}
+			if (clst != 0) {
+				while (ofs > bcs) {						/* Cluster following loop */
+#if !_FS_READONLY
+					if (fp->flag & FA_WRITE) {			/* Check if in write mode or not */
+						clst = create_chain(fp->fs, clst);	/* Force stretch if in write mode */
+						if (clst == 0) {				/* When disk gets full, clip file size */
+							ofs = bcs; break;
+						}
+					} else
+#endif
+						clst = get_fat(fp->fs, clst);	/* Follow cluster chain if not in write mode */
+					if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+					if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR);
+					fp->clust = clst;
+					fp->fptr += bcs;
+					ofs -= bcs;
+				}
+				fp->fptr += ofs;
+				if (ofs % SS(fp->fs)) {
+					nsect = clust2sect(fp->fs, clst);	/* Current sector */
+					if (!nsect) ABORT(fp->fs, FR_INT_ERR);
+					nsect += ofs / SS(fp->fs);
+				}
+			}
+		}
+		if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) {	/* Fill sector cache if needed */
+#if !_FS_TINY
+#if !_FS_READONLY
+			if (fp->flag & FA__DIRTY) {			/* Write-back dirty sector cache */
+				if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+					ABORT(fp->fs, FR_DISK_ERR);
+				fp->flag &= ~FA__DIRTY;
+			}
+#endif
+			if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK)	/* Fill sector cache */
+				ABORT(fp->fs, FR_DISK_ERR);
+#endif
+			fp->dsect = nsect;
+		}
+#if !_FS_READONLY
+		if (fp->fptr > fp->fsize) {			/* Set file change flag if the file size is extended */
+			fp->fsize = fp->fptr;
+			fp->flag |= FA__WRITTEN;
+		}
+#endif
+	}
+
+	LEAVE_FF(fp->fs, res);
+}
+
+
+
+#if _FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Create a Directory Object                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_opendir (
+	FATFS_DIR* dp,			/* Pointer to directory object to create */
+	const TCHAR* path	/* Pointer to the directory path */
+)
+{
+	FRESULT res;
+	FATFS* fs;
+	DEFINE_NAMEBUF;
+
+
+	if (!dp) return FR_INVALID_OBJECT;
+
+	/* Get logical drive number */
+	res = find_volume(&fs, &path, 0);
+	if (res == FR_OK) {
+		dp->fs = fs;
+		INIT_BUF(*dp);
+		res = follow_path(dp, path);			/* Follow the path to the directory */
+		FREE_BUF();
+		if (res == FR_OK) {						/* Follow completed */
+			if (dp->dir) {						/* It is not the origin directory itself */
+				if (dp->dir[DIR_Attr] & AM_DIR)	/* The object is a sub directory */
+					dp->sclust = ld_clust(fs, dp->dir);
+				else							/* The object is a file */
+					res = FR_NO_PATH;
+			}
+			if (res == FR_OK) {
+				dp->id = fs->id;
+				res = dir_sdi(dp, 0);			/* Rewind directory */
+#if _FS_LOCK
+				if (res == FR_OK) {
+					if (dp->sclust) {
+						dp->lockid = inc_lock(dp, 0);	/* Lock the sub directory */
+						if (!dp->lockid)
+							res = FR_TOO_MANY_OPEN_FILES;
+					} else {
+						dp->lockid = 0;	/* Root directory need not to be locked */
+					}
+				}
+#endif
+			}
+		}
+		if (res == FR_NO_FILE) res = FR_NO_PATH;
+	}
+	if (res != FR_OK) dp->fs = 0;		/* Invalidate the directory object if function faild */
+
+	LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close Directory                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_closedir (
+	FATFS_DIR *dp		/* Pointer to the directory object to be closed */
+)
+{
+	FRESULT res;
+
+
+	res = validate(dp);
+	if (res == FR_OK) {
+#if _FS_REENTRANT
+		FATFS *fs = dp->fs;
+#endif
+#if _FS_LOCK
+		if (dp->lockid)				/* Decrement sub-directory open counter */
+			res = dec_lock(dp->lockid);
+		if (res == FR_OK)
+#endif
+			dp->fs = 0;				/* Invalidate directory object */
+#if _FS_REENTRANT
+		unlock_fs(fs, FR_OK);		/* Unlock volume */
+#endif
+	}
+	return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read Directory Entries in Sequence                                    */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_readdir (
+	FATFS_DIR* dp,			/* Pointer to the open directory object */
+	FILINFO* fno		/* Pointer to file information to return */
+)
+{
+	FRESULT res;
+	DEFINE_NAMEBUF;
+
+
+	res = validate(dp);						/* Check validity of the object */
+	if (res == FR_OK) {
+		if (!fno) {
+			res = dir_sdi(dp, 0);			/* Rewind the directory object */
+		} else {
+			INIT_BUF(*dp);
+			res = dir_read(dp, 0);			/* Read an item */
+			if (res == FR_NO_FILE) {		/* Reached end of directory */
+				dp->sect = 0;
+				res = FR_OK;
+			}
+			if (res == FR_OK) {				/* A valid entry is found */
+				get_fileinfo(dp, fno);		/* Get the object information */
+				res = dir_next(dp, 0);		/* Increment index for next */
+				if (res == FR_NO_FILE) {
+					dp->sect = 0;
+					res = FR_OK;
+				}
+			}
+			FREE_BUF();
+		}
+	}
+
+	LEAVE_FF(dp->fs, res);
+}
+
+
+
+#if _USE_FIND
+/*-----------------------------------------------------------------------*/
+/* Find next file                                                        */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_findnext (
+	FATFS_DIR* dp,		/* Pointer to the open directory object */
+	FILINFO* fno	/* Pointer to the file information structure */
+)
+{
+	FRESULT res;
+
+
+	for (;;) {
+		res = f_readdir(dp, fno);		/* Get a directory item */
+		if (res != FR_OK || !fno || !fno->fname[0]) break;	/* Terminate if any error or end of directory */
+#if _USE_LFN
+		if (fno->lfname && pattern_matching(dp->pat, fno->lfname, 0, 0)) break;	/* Test for LFN if exist */
+#endif
+		if (pattern_matching(dp->pat, fno->fname, 0, 0)) break;	/* Test for SFN */
+	}
+	return res;
+
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Find first file                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_findfirst (
+	FATFS_DIR* dp,				/* Pointer to the blank directory object */
+	FILINFO* fno,			/* Pointer to the file information structure */
+	const TCHAR* path,		/* Pointer to the directory to open */
+	const TCHAR* pattern	/* Pointer to the matching pattern */
+)
+{
+	FRESULT res;
+
+
+	dp->pat = pattern;		/* Save pointer to pattern string */
+	res = f_opendir(dp, path);		/* Open the target directory */
+	if (res == FR_OK)
+		res = f_findnext(dp, fno);	/* Find the first item */
+	return res;
+}
+
+#endif	/* _USE_FIND */
+
+
+
+#if _FS_MINIMIZE == 0
+/*-----------------------------------------------------------------------*/
+/* Get File Status                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_stat (
+	const TCHAR* path,	/* Pointer to the file path */
+	FILINFO* fno		/* Pointer to file information to return */
+)
+{
+	FRESULT res;
+	FATFS_DIR dj;
+	DEFINE_NAMEBUF;
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &path, 0);
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		res = follow_path(&dj, path);	/* Follow the file path */
+		if (res == FR_OK) {				/* Follow completed */
+			if (dj.dir) {		/* Found an object */
+				if (fno) get_fileinfo(&dj, fno);
+			} else {			/* It is root directory */
+				res = FR_INVALID_NAME;
+			}
+		}
+		FREE_BUF();
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Get Number of Free Clusters                                           */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getfree (
+	const TCHAR* path,	/* Path name of the logical drive number */
+	DWORD* nclst,		/* Pointer to a variable to return number of free clusters */
+	FATFS** fatfs		/* Pointer to return pointer to corresponding file system object */
+)
+{
+	FRESULT res;
+	FATFS *fs;
+	DWORD nfree, clst, sect, stat;
+	UINT i;
+	BYTE fat, *p;
+
+
+	/* Get logical drive number */
+	res = find_volume(fatfs, &path, 0);
+	fs = *fatfs;
+	if (res == FR_OK) {
+		/* If free_clust is valid, return it without full cluster scan */
+		if (fs->free_clust <= fs->n_fatent - 2) {
+			*nclst = fs->free_clust;
+		} else {
+			/* Get number of free clusters */
+			fat = fs->fs_type;
+			nfree = 0;
+			if (fat == FS_FAT12) {	/* Sector unalighed entries: Search FAT via regular routine. */
+				clst = 2;
+				do {
+					stat = get_fat(fs, clst);
+					if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
+					if (stat == 1) { res = FR_INT_ERR; break; }
+					if (stat == 0) nfree++;
+				} while (++clst < fs->n_fatent);
+			} else {				/* Sector alighed entries: Accelerate the FAT search. */
+				clst = fs->n_fatent; sect = fs->fatbase;
+				i = 0; p = 0;
+				do {
+					if (!i) {
+						res = move_window(fs, sect++);
+						if (res != FR_OK) break;
+						p = fs->win;
+						i = SS(fs);
+					}
+					if (fat == FS_FAT16) {
+						if (LD_WORD(p) == 0) nfree++;
+						p += 2; i -= 2;
+					} else {
+						if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) nfree++;
+						p += 4; i -= 4;
+					}
+				} while (--clst);
+			}
+			fs->free_clust = nfree;	/* free_clust is valid */
+			fs->fsi_flag |= 1;		/* FSInfo is to be updated */
+			*nclst = nfree;			/* Return the free clusters */
+		}
+	}
+	LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Truncate File                                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_truncate (
+	FIL* fp		/* Pointer to the file object */
+)
+{
+	FRESULT res;
+	DWORD ncl;
+
+
+	res = validate(fp);						/* Check validity of the object */
+	if (res == FR_OK) {
+		if (fp->err) {						/* Check error */
+			res = (FRESULT)fp->err;
+		} else {
+			if (!(fp->flag & FA_WRITE))		/* Check access mode */
+				res = FR_DENIED;
+		}
+	}
+	if (res == FR_OK) {
+		if (fp->fsize > fp->fptr) {
+			fp->fsize = fp->fptr;	/* Set file size to current R/W point */
+			fp->flag |= FA__WRITTEN;
+			if (fp->fptr == 0) {	/* When set file size to zero, remove entire cluster chain */
+				res = remove_chain(fp->fs, fp->sclust);
+				fp->sclust = 0;
+			} else {				/* When truncate a part of the file, remove remaining clusters */
+				ncl = get_fat(fp->fs, fp->clust);
+				res = FR_OK;
+				if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
+				if (ncl == 1) res = FR_INT_ERR;
+				if (res == FR_OK && ncl < fp->fs->n_fatent) {
+					res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF);
+					if (res == FR_OK) res = remove_chain(fp->fs, ncl);
+				}
+			}
+#if !_FS_TINY
+			if (res == FR_OK && (fp->flag & FA__DIRTY)) {
+				if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+					res = FR_DISK_ERR;
+				else
+					fp->flag &= ~FA__DIRTY;
+			}
+#endif
+		}
+		if (res != FR_OK) fp->err = (FRESULT)res;
+	}
+
+	LEAVE_FF(fp->fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Delete a File or Directory                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_unlink (
+	const TCHAR* path		/* Pointer to the file or directory path */
+)
+{
+	FRESULT res;
+	FATFS_DIR dj, sdj;
+	BYTE *dir;
+	DWORD dclst = 0;
+	DEFINE_NAMEBUF;
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &path, 1);
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		res = follow_path(&dj, path);		/* Follow the file path */
+		if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT))
+			res = FR_INVALID_NAME;			/* Cannot remove dot entry */
+#if _FS_LOCK
+		if (res == FR_OK) res = chk_lock(&dj, 2);	/* Cannot remove open object */
+#endif
+		if (res == FR_OK) {					/* The object is accessible */
+			dir = dj.dir;
+			if (!dir) {
+				res = FR_INVALID_NAME;		/* Cannot remove the origin directory */
+			} else {
+				if (dir[DIR_Attr] & AM_RDO)
+					res = FR_DENIED;		/* Cannot remove R/O object */
+			}
+			if (res == FR_OK) {
+				dclst = ld_clust(dj.fs, dir);
+				if (dclst && (dir[DIR_Attr] & AM_DIR)) {	/* Is it a sub-directory ? */
+#if _FS_RPATH
+					if (dclst == dj.fs->cdir) {		 		/* Is it the current directory? */
+						res = FR_DENIED;
+					} else
+#endif
+					{
+						mem_cpy(&sdj, &dj, sizeof (FATFS_DIR));	/* Open the sub-directory */
+						sdj.sclust = dclst;
+						res = dir_sdi(&sdj, 2);
+						if (res == FR_OK) {
+							res = dir_read(&sdj, 0);			/* Read an item (excluding dot entries) */
+							if (res == FR_OK) res = FR_DENIED;	/* Not empty? (cannot remove) */
+							if (res == FR_NO_FILE) res = FR_OK;	/* Empty? (can remove) */
+						}
+					}
+				}
+			}
+			if (res == FR_OK) {
+				res = dir_remove(&dj);		/* Remove the directory entry */
+				if (res == FR_OK && dclst)	/* Remove the cluster chain if exist */
+					res = remove_chain(dj.fs, dclst);
+				if (res == FR_OK) res = sync_fs(dj.fs);
+			}
+		}
+		FREE_BUF();
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create a Directory                                                    */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mkdir (
+	const TCHAR* path		/* Pointer to the directory path */
+)
+{
+	FRESULT res;
+	FATFS_DIR dj;
+	BYTE *dir, n;
+	DWORD dsc, dcl, pcl, tm = GET_FATTIME();
+	DEFINE_NAMEBUF;
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &path, 1);
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		res = follow_path(&dj, path);			/* Follow the file path */
+		if (res == FR_OK) res = FR_EXIST;		/* Any object with same name is already existing */
+		if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT))
+			res = FR_INVALID_NAME;
+		if (res == FR_NO_FILE) {				/* Can create a new directory */
+			dcl = create_chain(dj.fs, 0);		/* Allocate a cluster for the new directory table */
+			res = FR_OK;
+			if (dcl == 0) res = FR_DENIED;		/* No space to allocate a new cluster */
+			if (dcl == 1) res = FR_INT_ERR;
+			if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR;
+			if (res == FR_OK)					/* Flush FAT */
+				res = sync_window(dj.fs);
+			if (res == FR_OK) {					/* Initialize the new directory table */
+				dsc = clust2sect(dj.fs, dcl);
+				dir = dj.fs->win;
+				mem_set(dir, 0, SS(dj.fs));
+				mem_set(dir + DIR_Name, ' ', 11);	/* Create "." entry */
+				dir[DIR_Name] = '.';
+				dir[DIR_Attr] = AM_DIR;
+				ST_DWORD(dir + DIR_WrtTime, tm);
+				st_clust(dir, dcl);
+				mem_cpy(dir + SZ_DIRE, dir, SZ_DIRE); 	/* Create ".." entry */
+				dir[SZ_DIRE + 1] = '.'; pcl = dj.sclust;
+				if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase)
+					pcl = 0;
+				st_clust(dir + SZ_DIRE, pcl);
+				for (n = dj.fs->csize; n; n--) {	/* Write dot entries and clear following sectors */
+					dj.fs->winsect = dsc++;
+					dj.fs->wflag = 1;
+					res = sync_window(dj.fs);
+					if (res != FR_OK) break;
+					mem_set(dir, 0, SS(dj.fs));
+				}
+			}
+			if (res == FR_OK) res = dir_register(&dj);	/* Register the object to the directoy */
+			if (res != FR_OK) {
+				remove_chain(dj.fs, dcl);			/* Could not register, remove cluster chain */
+			} else {
+				dir = dj.dir;
+				dir[DIR_Attr] = AM_DIR;				/* Attribute */
+				ST_DWORD(dir + DIR_WrtTime, tm);	/* Created time */
+				st_clust(dir, dcl);					/* Table start cluster */
+				dj.fs->wflag = 1;
+				res = sync_fs(dj.fs);
+			}
+		}
+		FREE_BUF();
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Attribute                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_chmod (
+	const TCHAR* path,	/* Pointer to the file path */
+	BYTE attr,			/* Attribute bits */
+	BYTE mask			/* Attribute mask to change */
+)
+{
+	FRESULT res;
+	FATFS_DIR dj;
+	BYTE *dir;
+	DEFINE_NAMEBUF;
+
+
+	res = find_volume(&dj.fs, &path, 1);	/* Get logical drive number */
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		res = follow_path(&dj, path);		/* Follow the file path */
+		FREE_BUF();
+		if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT))
+			res = FR_INVALID_NAME;
+		if (res == FR_OK) {
+			dir = dj.dir;
+			if (!dir) {						/* Is it a root directory? */
+				res = FR_INVALID_NAME;
+			} else {						/* File or sub directory */
+				mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC;	/* Valid attribute mask */
+				dir[DIR_Attr] = (attr & mask) | (dir[DIR_Attr] & (BYTE)~mask);	/* Apply attribute change */
+				dj.fs->wflag = 1;
+				res = sync_fs(dj.fs);
+			}
+		}
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Rename File/Directory                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_rename (
+	const TCHAR* path_old,	/* Pointer to the object to be renamed */
+	const TCHAR* path_new	/* Pointer to the new name */
+)
+{
+	FRESULT res;
+	FATFS_DIR djo, djn;
+	BYTE buf[21], *dir;
+	DWORD dw;
+	DEFINE_NAMEBUF;
+
+
+	/* Get logical drive number of the source object */
+	res = find_volume(&djo.fs, &path_old, 1);
+	if (res == FR_OK) {
+		djn.fs = djo.fs;
+		INIT_BUF(djo);
+		res = follow_path(&djo, path_old);		/* Check old object */
+		if (_FS_RPATH && res == FR_OK && (djo.fn[NSFLAG] & NS_DOT))
+			res = FR_INVALID_NAME;
+#if _FS_LOCK
+		if (res == FR_OK) res = chk_lock(&djo, 2);
+#endif
+		if (res == FR_OK) {						/* Old object is found */
+			if (!djo.dir) {						/* Is root dir? */
+				res = FR_NO_FILE;
+			} else {
+				mem_cpy(buf, djo.dir + DIR_Attr, 21);	/* Save information about object except name */
+				mem_cpy(&djn, &djo, sizeof (FATFS_DIR));		/* Duplicate the directory object */
+				if (get_ldnumber(&path_new) >= 0)		/* Snip drive number off and ignore it */
+					res = follow_path(&djn, path_new);	/* and make sure if new object name is not conflicting */
+				else
+					res = FR_INVALID_DRIVE;
+				if (res == FR_OK) res = FR_EXIST;		/* The new object name is already existing */
+				if (res == FR_NO_FILE) { 				/* It is a valid path and no name collision */
+					res = dir_register(&djn);			/* Register the new entry */
+					if (res == FR_OK) {
+/* Start of critical section where any interruption can cause a cross-link */
+						dir = djn.dir;					/* Copy information about object except name */
+						mem_cpy(dir + 13, buf + 2, 19);
+						dir[DIR_Attr] = buf[0] | AM_ARC;
+						djo.fs->wflag = 1;
+						if ((dir[DIR_Attr] & AM_DIR) && djo.sclust != djn.sclust) {	/* Update .. entry in the sub-directory if needed */
+							dw = clust2sect(djo.fs, ld_clust(djo.fs, dir));
+							if (!dw) {
+								res = FR_INT_ERR;
+							} else {
+								res = move_window(djo.fs, dw);
+								dir = djo.fs->win + SZ_DIRE * 1;	/* Ptr to .. entry */
+								if (res == FR_OK && dir[1] == '.') {
+									st_clust(dir, djn.sclust);
+									djo.fs->wflag = 1;
+								}
+							}
+						}
+						if (res == FR_OK) {
+							res = dir_remove(&djo);		/* Remove old entry */
+							if (res == FR_OK)
+								res = sync_fs(djo.fs);
+						}
+/* End of critical section */
+					}
+				}
+			}
+		}
+		FREE_BUF();
+	}
+
+	LEAVE_FF(djo.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Timestamp                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_utime (
+	const TCHAR* path,	/* Pointer to the file/directory name */
+	const FILINFO* fno	/* Pointer to the time stamp to be set */
+)
+{
+	FRESULT res;
+	FATFS_DIR dj;
+	BYTE *dir;
+	DEFINE_NAMEBUF;
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &path, 1);
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		res = follow_path(&dj, path);	/* Follow the file path */
+		FREE_BUF();
+		if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT))
+			res = FR_INVALID_NAME;
+		if (res == FR_OK) {
+			dir = dj.dir;
+			if (!dir) {					/* Root directory */
+				res = FR_INVALID_NAME;
+			} else {					/* File or sub-directory */
+				ST_WORD(dir + DIR_WrtTime, fno->ftime);
+				ST_WORD(dir + DIR_WrtDate, fno->fdate);
+				dj.fs->wflag = 1;
+				res = sync_fs(dj.fs);
+			}
+		}
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _FS_MINIMIZE == 0 */
+#endif /* _FS_MINIMIZE <= 1 */
+#endif /* _FS_MINIMIZE <= 2 */
+
+
+
+
+#if _USE_LABEL
+/*-----------------------------------------------------------------------*/
+/* Get volume label                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getlabel (
+	const TCHAR* path,	/* Path name of the logical drive number */
+	TCHAR* label,		/* Pointer to a buffer to return the volume label */
+	DWORD* vsn			/* Pointer to a variable to return the volume serial number */
+)
+{
+	FRESULT res;
+	FATFS_DIR dj;
+	UINT i, j;
+#if _USE_LFN && _LFN_UNICODE
+	WCHAR w;
+#endif
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &path, 0);
+
+	/* Get volume label */
+	if (res == FR_OK && label) {
+		dj.sclust = 0;					/* Open root directory */
+		res = dir_sdi(&dj, 0);
+		if (res == FR_OK) {
+			res = dir_read(&dj, 1);		/* Get an entry with AM_VOL */
+			if (res == FR_OK) {			/* A volume label is exist */
+#if _USE_LFN && _LFN_UNICODE
+				i = j = 0;
+				do {
+					w = (i < 11) ? dj.dir[i++] : ' ';
+					if (IsDBCS1(w) && i < 11 && IsDBCS2(dj.dir[i]))
+						w = w << 8 | dj.dir[i++];
+					label[j++] = ff_convert(w, 1);	/* OEM -> Unicode */
+				} while (j < 11);
+#else
+				mem_cpy(label, dj.dir, 11);
+#endif
+				j = 11;
+				do {
+					label[j] = 0;
+					if (!j) break;
+				} while (label[--j] == ' ');
+			}
+			if (res == FR_NO_FILE) {	/* No label, return nul string */
+				label[0] = 0;
+				res = FR_OK;
+			}
+		}
+	}
+
+	/* Get volume serial number */
+	if (res == FR_OK && vsn) {
+		res = move_window(dj.fs, dj.fs->volbase);
+		if (res == FR_OK) {
+			i = dj.fs->fs_type == FS_FAT32 ? BS_VolID32 : BS_VolID;
+			*vsn = LD_DWORD(&dj.fs->win[i]);
+		}
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Set volume label                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_setlabel (
+	const TCHAR* label	/* Pointer to the volume label to set */
+)
+{
+	FRESULT res;
+	FATFS_DIR dj;
+	BYTE vn[11];
+	UINT i, j, sl;
+	WCHAR w;
+	DWORD tm;
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &label, 1);
+	if (res) LEAVE_FF(dj.fs, res);
+
+	/* Create a volume label in directory form */
+	vn[0] = 0;
+	for (sl = 0; label[sl]; sl++) ;				/* Get name length */
+	for ( ; sl && label[sl - 1] == ' '; sl--) ;	/* Remove trailing spaces */
+	if (sl) {	/* Create volume label in directory form */
+		i = j = 0;
+		do {
+#if _USE_LFN && _LFN_UNICODE
+			w = ff_convert(ff_wtoupper(label[i++]), 0);
+#else
+			w = (BYTE)label[i++];
+			if (IsDBCS1(w))
+				w = (j < 10 && i < sl && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0;
+#if _USE_LFN
+			w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0);
+#else
+			if (IsLower(w)) w -= 0x20;			/* To upper ASCII characters */
+#ifdef _EXCVT
+			if (w >= 0x80) w = ExCvt[w - 0x80];	/* To upper extended characters (SBCS cfg) */
+#else
+			if (!_DF1S && w >= 0x80) w = 0;		/* Reject extended characters (ASCII cfg) */
+#endif
+#endif
+#endif
+			if (!w || chk_chr("\"*+,.:;<=>\?[]|\x7F", w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) /* Reject invalid characters for volume label */
+				LEAVE_FF(dj.fs, FR_INVALID_NAME);
+			if (w >= 0x100) vn[j++] = (BYTE)(w >> 8);
+			vn[j++] = (BYTE)w;
+		} while (i < sl);
+		while (j < 11) vn[j++] = ' ';	/* Fill remaining name field */
+		if (vn[0] == DDEM) LEAVE_FF(dj.fs, FR_INVALID_NAME);	/* Reject illegal name (heading DDEM) */
+	}
+
+	/* Set volume label */
+	dj.sclust = 0;					/* Open root directory */
+	res = dir_sdi(&dj, 0);
+	if (res == FR_OK) {
+		res = dir_read(&dj, 1);		/* Get an entry with AM_VOL */
+		if (res == FR_OK) {			/* A volume label is found */
+			if (vn[0]) {
+				mem_cpy(dj.dir, vn, 11);	/* Change the volume label name */
+				tm = GET_FATTIME();
+				ST_DWORD(dj.dir + DIR_WrtTime, tm);
+			} else {
+				dj.dir[0] = DDEM;			/* Remove the volume label */
+			}
+			dj.fs->wflag = 1;
+			res = sync_fs(dj.fs);
+		} else {					/* No volume label is found or error */
+			if (res == FR_NO_FILE) {
+				res = FR_OK;
+				if (vn[0]) {				/* Create volume label as new */
+					res = dir_alloc(&dj, 1);	/* Allocate an entry for volume label */
+					if (res == FR_OK) {
+						mem_set(dj.dir, 0, SZ_DIRE);	/* Set volume label */
+						mem_cpy(dj.dir, vn, 11);
+						dj.dir[DIR_Attr] = AM_VOL;
+						tm = GET_FATTIME();
+						ST_DWORD(dj.dir + DIR_WrtTime, tm);
+						dj.fs->wflag = 1;
+						res = sync_fs(dj.fs);
+					}
+				}
+			}
+		}
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _USE_LABEL */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Forward data to the stream directly (available on only tiny cfg)      */
+/*-----------------------------------------------------------------------*/
+#if _USE_FORWARD && _FS_TINY
+
+FRESULT f_forward (
+	FIL* fp, 						/* Pointer to the file object */
+	UINT (*func)(const BYTE*,UINT),	/* Pointer to the streaming function */
+	UINT btf,						/* Number of bytes to forward */
+	UINT* bf						/* Pointer to number of bytes forwarded */
+)
+{
+	FRESULT res;
+	DWORD remain, clst, sect;
+	UINT rcnt;
+	BYTE csect;
+
+
+	*bf = 0;	/* Clear transfer byte counter */
+
+	res = validate(fp);								/* Check validity of the object */
+	if (res != FR_OK) LEAVE_FF(fp->fs, res);
+	if (fp->err)									/* Check error */
+		LEAVE_FF(fp->fs, (FRESULT)fp->err);
+	if (!(fp->flag & FA_READ))						/* Check access mode */
+		LEAVE_FF(fp->fs, FR_DENIED);
+
+	remain = fp->fsize - fp->fptr;
+	if (btf > remain) btf = (UINT)remain;			/* Truncate btf by remaining bytes */
+
+	for ( ;  btf && (*func)(0, 0);					/* Repeat until all data transferred or stream becomes busy */
+		fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) {
+		csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));	/* Sector offset in the cluster */
+		if ((fp->fptr % SS(fp->fs)) == 0) {			/* On the sector boundary? */
+			if (!csect) {							/* On the cluster boundary? */
+				clst = (fp->fptr == 0) ?			/* On the top of the file? */
+					fp->sclust : get_fat(fp->fs, fp->clust);
+				if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
+				if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+				fp->clust = clst;					/* Update current cluster */
+			}
+		}
+		sect = clust2sect(fp->fs, fp->clust);		/* Get current data sector */
+		if (!sect) ABORT(fp->fs, FR_INT_ERR);
+		sect += csect;
+		if (move_window(fp->fs, sect) != FR_OK)		/* Move sector window */
+			ABORT(fp->fs, FR_DISK_ERR);
+		fp->dsect = sect;
+		rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs));	/* Forward data from sector window */
+		if (rcnt > btf) rcnt = btf;
+		rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt);
+		if (!rcnt) ABORT(fp->fs, FR_INT_ERR);
+	}
+
+	LEAVE_FF(fp->fs, FR_OK);
+}
+#endif /* _USE_FORWARD */
+
+
+
+#if _USE_MKFS && !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Create file system on the logical drive                               */
+/*-----------------------------------------------------------------------*/
+#define N_ROOTDIR	512		/* Number of root directory entries for FAT12/16 */
+#define N_FATS		1		/* Number of FATs (1 or 2) */
+
+
+FRESULT f_mkfs (
+	const TCHAR* path,	/* Logical drive number */
+	BYTE sfd,			/* Partitioning rule 0:FDISK, 1:SFD */
+	UINT au				/* Size of allocation unit in unit of byte or sector */
+)
+{
+	static const WORD vst[] = { 1024,   512,  256,  128,   64,    32,   16,    8,    4,    2,   0};
+	static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512};
+	int vol;
+	BYTE fmt, md, sys, *tbl, pdrv, part;
+	DWORD n_clst, vs, n, wsect;
+	UINT i;
+	DWORD b_vol, b_fat, b_dir, b_data;	/* LBA */
+	DWORD n_vol, n_rsv, n_fat, n_dir;	/* Size */
+	FATFS *fs;
+	DSTATUS stat;
+#if _USE_TRIM
+	DWORD eb[2];
+#endif
+
+
+	/* Check mounted drive and clear work area */
+	if (sfd > 1) return FR_INVALID_PARAMETER;
+	vol = get_ldnumber(&path);
+	if (vol < 0) return FR_INVALID_DRIVE;
+	fs = FatFs[vol];
+	if (!fs) return FR_NOT_ENABLED;
+	fs->fs_type = 0;
+	pdrv = LD2PD(vol);	/* Physical drive */
+	part = LD2PT(vol);	/* Partition (0:auto detect, 1-4:get from partition table)*/
+
+	/* Get disk statics */
+	stat = disk_initialize(pdrv);
+	if (stat & STA_NOINIT) return FR_NOT_READY;
+	if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+#if _MAX_SS != _MIN_SS		/* Get disk sector size */
+	if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS || SS(fs) < _MIN_SS)
+		return FR_DISK_ERR;
+#endif
+	if (_MULTI_PARTITION && part) {
+		/* Get partition information from partition table in the MBR */
+		if (disk_read(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR;
+		if (LD_WORD(fs->win + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED;
+		tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];
+		if (!tbl[4]) return FR_MKFS_ABORTED;	/* No partition? */
+		b_vol = LD_DWORD(tbl + 8);	/* Volume start sector */
+		n_vol = LD_DWORD(tbl + 12);	/* Volume size */
+	} else {
+		/* Create a partition in this function */
+		if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128)
+			return FR_DISK_ERR;
+		b_vol = (sfd) ? 0 : 63;		/* Volume start sector */
+		n_vol -= b_vol;				/* Volume size */
+	}
+
+	if (au & (au - 1)) au = 0;
+	if (!au) {						/* AU auto selection */
+		vs = n_vol / (2000 / (SS(fs) / 512));
+		for (i = 0; vs < vst[i]; i++) ;
+		au = cst[i];
+	}
+	if (au >= _MIN_SS) au /= SS(fs);	/* Number of sectors per cluster */
+	if (!au) au = 1;
+	if (au > 128) au = 128;
+
+	/* Pre-compute number of clusters and FAT sub-type */
+	n_clst = n_vol / au;
+	fmt = FS_FAT12;
+	if (n_clst >= MIN_FAT16) fmt = FS_FAT16;
+	if (n_clst >= MIN_FAT32) fmt = FS_FAT32;
+
+	/* Determine offset and size of FAT structure */
+	if (fmt == FS_FAT32) {
+		n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);
+		n_rsv = 32;
+		n_dir = 0;
+	} else {
+		n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4;
+		n_fat = (n_fat + SS(fs) - 1) / SS(fs);
+		n_rsv = 1;
+		n_dir = (DWORD)N_ROOTDIR * SZ_DIRE / SS(fs);
+	}
+	b_fat = b_vol + n_rsv;				/* FAT area start sector */
+	b_dir = b_fat + n_fat * N_FATS;		/* Directory area start sector */
+	b_data = b_dir + n_dir;				/* Data area start sector */
+	if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED;	/* Too small volume */
+
+	/* Align data start sector to erase block boundary (for flash memory media) */
+	if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1;
+	n = (b_data + n - 1) & ~(n - 1);	/* Next nearest erase block from current data start */
+	n = (n - b_data) / N_FATS;
+	if (fmt == FS_FAT32) {		/* FAT32: Move FAT offset */
+		n_rsv += n;
+		b_fat += n;
+	} else {					/* FAT12/16: Expand FAT size */
+		n_fat += n;
+	}
+
+	/* Determine number of clusters and final check of validity of the FAT sub-type */
+	n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au;
+	if (   (fmt == FS_FAT16 && n_clst < MIN_FAT16)
+		|| (fmt == FS_FAT32 && n_clst < MIN_FAT32))
+		return FR_MKFS_ABORTED;
+
+	/* Determine system ID in the partition table */
+	if (fmt == FS_FAT32) {
+		sys = 0x0C;		/* FAT32X */
+	} else {
+		if (fmt == FS_FAT12 && n_vol < 0x10000) {
+			sys = 0x01;	/* FAT12(<65536) */
+		} else {
+			sys = (n_vol < 0x10000) ? 0x04 : 0x06;	/* FAT16(<65536) : FAT12/16(>=65536) */
+		}
+	}
+
+	if (_MULTI_PARTITION && part) {
+		/* Update system ID in the partition table */
+		tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];
+		tbl[4] = sys;
+		if (disk_write(pdrv, fs->win, 0, 1) != RES_OK)	/* Write it to teh MBR */
+			return FR_DISK_ERR;
+		md = 0xF8;
+	} else {
+		if (sfd) {	/* No partition table (SFD) */
+			md = 0xF0;
+		} else {	/* Create partition table (FDISK) */
+			mem_set(fs->win, 0, SS(fs));
+			tbl = fs->win + MBR_Table;	/* Create partition table for single partition in the drive */
+			tbl[1] = 1;						/* Partition start head */
+			tbl[2] = 1;						/* Partition start sector */
+			tbl[3] = 0;						/* Partition start cylinder */
+			tbl[4] = sys;					/* System type */
+			tbl[5] = 254;					/* Partition end head */
+			n = (b_vol + n_vol) / 63 / 255;
+			tbl[6] = (BYTE)(n >> 2 | 63);	/* Partition end sector */
+			tbl[7] = (BYTE)n;				/* End cylinder */
+			ST_DWORD(tbl + 8, 63);			/* Partition start in LBA */
+			ST_DWORD(tbl + 12, n_vol);		/* Partition size in LBA */
+			ST_WORD(fs->win + BS_55AA, 0xAA55);	/* MBR signature */
+			if (disk_write(pdrv, fs->win, 0, 1) != RES_OK)	/* Write it to the MBR */
+				return FR_DISK_ERR;
+			md = 0xF8;
+		}
+	}
+
+	/* Create BPB in the VBR */
+	tbl = fs->win;							/* Clear sector */
+	mem_set(tbl, 0, SS(fs));
+	mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */
+	i = SS(fs);								/* Sector size */
+	ST_WORD(tbl + BPB_BytsPerSec, i);
+	tbl[BPB_SecPerClus] = (BYTE)au;			/* Sectors per cluster */
+	ST_WORD(tbl + BPB_RsvdSecCnt, n_rsv);	/* Reserved sectors */
+	tbl[BPB_NumFATs] = N_FATS;				/* Number of FATs */
+	i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR;	/* Number of root directory entries */
+	ST_WORD(tbl + BPB_RootEntCnt, i);
+	if (n_vol < 0x10000) {					/* Number of total sectors */
+		ST_WORD(tbl + BPB_TotSec16, n_vol);
+	} else {
+		ST_DWORD(tbl + BPB_TotSec32, n_vol);
+	}
+	tbl[BPB_Media] = md;					/* Media descriptor */
+	ST_WORD(tbl + BPB_SecPerTrk, 63);		/* Number of sectors per track */
+	ST_WORD(tbl + BPB_NumHeads, 255);		/* Number of heads */
+	ST_DWORD(tbl + BPB_HiddSec, b_vol);		/* Hidden sectors */
+	n = GET_FATTIME();						/* Use current time as VSN */
+	if (fmt == FS_FAT32) {
+		ST_DWORD(tbl + BS_VolID32, n);		/* VSN */
+		ST_DWORD(tbl + BPB_FATSz32, n_fat);	/* Number of sectors per FAT */
+		ST_DWORD(tbl + BPB_RootClus, 2);	/* Root directory start cluster (2) */
+		ST_WORD(tbl + BPB_FSInfo, 1);		/* FSINFO record offset (VBR + 1) */
+		ST_WORD(tbl + BPB_BkBootSec, 6);	/* Backup boot record offset (VBR + 6) */
+		tbl[BS_DrvNum32] = 0x80;			/* Drive number */
+		tbl[BS_BootSig32] = 0x29;			/* Extended boot signature */
+		mem_cpy(tbl + BS_VolLab32, "NO NAME    " "FAT32   ", 19);	/* Volume label, FAT signature */
+	} else {
+		ST_DWORD(tbl + BS_VolID, n);		/* VSN */
+		ST_WORD(tbl + BPB_FATSz16, n_fat);	/* Number of sectors per FAT */
+		tbl[BS_DrvNum] = 0x80;				/* Drive number */
+		tbl[BS_BootSig] = 0x29;				/* Extended boot signature */
+		mem_cpy(tbl + BS_VolLab, "NO NAME    " "FAT     ", 19);	/* Volume label, FAT signature */
+	}
+	ST_WORD(tbl + BS_55AA, 0xAA55);			/* Signature (Offset is fixed here regardless of sector size) */
+	if (disk_write(pdrv, tbl, b_vol, 1) != RES_OK)	/* Write it to the VBR sector */
+		return FR_DISK_ERR;
+	if (fmt == FS_FAT32)					/* Write it to the backup VBR if needed (VBR + 6) */
+		disk_write(pdrv, tbl, b_vol + 6, 1);
+
+	/* Initialize FAT area */
+	wsect = b_fat;
+	for (i = 0; i < N_FATS; i++) {		/* Initialize each FAT copy */
+		mem_set(tbl, 0, SS(fs));			/* 1st sector of the FAT  */
+		n = md;								/* Media descriptor byte */
+		if (fmt != FS_FAT32) {
+			n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00;
+			ST_DWORD(tbl + 0, n);			/* Reserve cluster #0-1 (FAT12/16) */
+		} else {
+			n |= 0xFFFFFF00;
+			ST_DWORD(tbl + 0, n);			/* Reserve cluster #0-1 (FAT32) */
+			ST_DWORD(tbl + 4, 0xFFFFFFFF);
+			ST_DWORD(tbl + 8, 0x0FFFFFFF);	/* Reserve cluster #2 for root directory */
+		}
+		if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)
+			return FR_DISK_ERR;
+		mem_set(tbl, 0, SS(fs));			/* Fill following FAT entries with zero */
+		for (n = 1; n < n_fat; n++) {		/* This loop may take a time on FAT32 volume due to many single sector writes */
+			if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)
+				return FR_DISK_ERR;
+		}
+	}
+
+	/* Initialize root directory */
+	i = (fmt == FS_FAT32) ? au : (UINT)n_dir;
+	do {
+		if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)
+			return FR_DISK_ERR;
+	} while (--i);
+
+#if _USE_TRIM	/* Erase data area if needed */
+	{
+		eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1;
+		disk_ioctl(pdrv, CTRL_TRIM, eb);
+	}
+#endif
+
+	/* Create FSINFO if needed */
+	if (fmt == FS_FAT32) {
+		ST_DWORD(tbl + FSI_LeadSig, 0x41615252);
+		ST_DWORD(tbl + FSI_StrucSig, 0x61417272);
+		ST_DWORD(tbl + FSI_Free_Count, n_clst - 1);	/* Number of free clusters */
+		ST_DWORD(tbl + FSI_Nxt_Free, 2);			/* Last allocated cluster# */
+		ST_WORD(tbl + BS_55AA, 0xAA55);
+		disk_write(pdrv, tbl, b_vol + 1, 1);	/* Write original (VBR + 1) */
+		disk_write(pdrv, tbl, b_vol + 7, 1);	/* Write backup (VBR + 7) */
+	}
+
+	return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR;
+}
+
+
+
+#if _MULTI_PARTITION
+/*-----------------------------------------------------------------------*/
+/* Create partition table on the physical drive                          */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_fdisk (
+	BYTE pdrv,			/* Physical drive number */
+	const DWORD szt[],	/* Pointer to the size table for each partitions */
+	void* work			/* Pointer to the working buffer */
+)
+{
+	UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl;
+	BYTE s_hd, e_hd, *p, *buf = (BYTE*)work;
+	DSTATUS stat;
+	DWORD sz_disk, sz_part, s_part;
+
+
+	stat = disk_initialize(pdrv);
+	if (stat & STA_NOINIT) return FR_NOT_READY;
+	if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+	if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR;
+
+	/* Determine CHS in the table regardless of the drive geometry */
+	for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ;
+	if (n == 256) n--;
+	e_hd = n - 1;
+	sz_cyl = 63 * n;
+	tot_cyl = sz_disk / sz_cyl;
+
+	/* Create partition table */
+	mem_set(buf, 0, _MAX_SS);
+	p = buf + MBR_Table; b_cyl = 0;
+	for (i = 0; i < 4; i++, p += SZ_PTE) {
+		p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl;
+		if (!p_cyl) continue;
+		s_part = (DWORD)sz_cyl * b_cyl;
+		sz_part = (DWORD)sz_cyl * p_cyl;
+		if (i == 0) {	/* Exclude first track of cylinder 0 */
+			s_hd = 1;
+			s_part += 63; sz_part -= 63;
+		} else {
+			s_hd = 0;
+		}
+		e_cyl = b_cyl + p_cyl - 1;
+		if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER;
+
+		/* Set partition table */
+		p[1] = s_hd;						/* Start head */
+		p[2] = (BYTE)((b_cyl >> 2) + 1);	/* Start sector */
+		p[3] = (BYTE)b_cyl;					/* Start cylinder */
+		p[4] = 0x06;						/* System type (temporary setting) */
+		p[5] = e_hd;						/* End head */
+		p[6] = (BYTE)((e_cyl >> 2) + 63);	/* End sector */
+		p[7] = (BYTE)e_cyl;					/* End cylinder */
+		ST_DWORD(p + 8, s_part);			/* Start sector in LBA */
+		ST_DWORD(p + 12, sz_part);			/* Partition size */
+
+		/* Next partition */
+		b_cyl += p_cyl;
+	}
+	ST_WORD(p, 0xAA55);
+
+	/* Write it to the MBR */
+	return (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK;
+}
+
+
+#endif /* _MULTI_PARTITION */
+#endif /* _USE_MKFS && !_FS_READONLY */
+
+
+
+
+#if _USE_STRFUNC
+/*-----------------------------------------------------------------------*/
+/* Get a string from the file                                            */
+/*-----------------------------------------------------------------------*/
+
+TCHAR* f_gets (
+	TCHAR* buff,	/* Pointer to the string buffer to read */
+	int len,		/* Size of string buffer (characters) */
+	FIL* fp			/* Pointer to the file object */
+)
+{
+	int n = 0;
+	TCHAR c, *p = buff;
+	BYTE s[2];
+	UINT rc;
+
+
+	while (n < len - 1) {	/* Read characters until buffer gets filled */
+#if _USE_LFN && _LFN_UNICODE
+#if _STRF_ENCODE == 3		/* Read a character in UTF-8 */
+		f_read(fp, s, 1, &rc);
+		if (rc != 1) break;
+		c = s[0];
+		if (c >= 0x80) {
+			if (c < 0xC0) continue;	/* Skip stray trailer */
+			if (c < 0xE0) {			/* Two-byte sequence */
+				f_read(fp, s, 1, &rc);
+				if (rc != 1) break;
+				c = (c & 0x1F) << 6 | (s[0] & 0x3F);
+				if (c < 0x80) c = '?';
+			} else {
+				if (c < 0xF0) {		/* Three-byte sequence */
+					f_read(fp, s, 2, &rc);
+					if (rc != 2) break;
+					c = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F);
+					if (c < 0x800) c = '?';
+				} else {			/* Reject four-byte sequence */
+					c = '?';
+				}
+			}
+		}
+#elif _STRF_ENCODE == 2		/* Read a character in UTF-16BE */
+		f_read(fp, s, 2, &rc);
+		if (rc != 2) break;
+		c = s[1] + (s[0] << 8);
+#elif _STRF_ENCODE == 1		/* Read a character in UTF-16LE */
+		f_read(fp, s, 2, &rc);
+		if (rc != 2) break;
+		c = s[0] + (s[1] << 8);
+#else						/* Read a character in ANSI/OEM */
+		f_read(fp, s, 1, &rc);
+		if (rc != 1) break;
+		c = s[0];
+		if (IsDBCS1(c)) {
+			f_read(fp, s, 1, &rc);
+			if (rc != 1) break;
+			c = (c << 8) + s[0];
+		}
+		c = ff_convert(c, 1);	/* OEM -> Unicode */
+		if (!c) c = '?';
+#endif
+#else						/* Read a character without conversion */
+		f_read(fp, s, 1, &rc);
+		if (rc != 1) break;
+		c = s[0];
+#endif
+		if (_USE_STRFUNC == 2 && c == '\r') continue;	/* Strip '\r' */
+		*p++ = c;
+		n++;
+		if (c == '\n') break;		/* Break on EOL */
+	}
+	*p = 0;
+	return n ? buff : 0;			/* When no data read (eof or error), return with error. */
+}
+
+
+
+
+#if !_FS_READONLY
+#include <stdarg.h>
+/*-----------------------------------------------------------------------*/
+/* Put a character to the file                                           */
+/*-----------------------------------------------------------------------*/
+
+typedef struct {
+	FIL* fp;
+	int idx, nchr;
+	BYTE buf[64];
+} putbuff;
+
+
+static
+void putc_bfd (
+	putbuff* pb,
+	TCHAR c
+)
+{
+	UINT bw;
+	int i;
+
+
+	if (_USE_STRFUNC == 2 && c == '\n')	 /* LF -> CRLF conversion */
+		putc_bfd(pb, '\r');
+
+	i = pb->idx;	/* Buffer write index (-1:error) */
+	if (i < 0) return;
+
+#if _USE_LFN && _LFN_UNICODE
+#if _STRF_ENCODE == 3			/* Write a character in UTF-8 */
+	if (c < 0x80) {				/* 7-bit */
+		pb->buf[i++] = (BYTE)c;
+	} else {
+		if (c < 0x800) {		/* 11-bit */
+			pb->buf[i++] = (BYTE)(0xC0 | c >> 6);
+		} else {				/* 16-bit */
+			pb->buf[i++] = (BYTE)(0xE0 | c >> 12);
+			pb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F));
+		}
+		pb->buf[i++] = (BYTE)(0x80 | (c & 0x3F));
+	}
+#elif _STRF_ENCODE == 2			/* Write a character in UTF-16BE */
+	pb->buf[i++] = (BYTE)(c >> 8);
+	pb->buf[i++] = (BYTE)c;
+#elif _STRF_ENCODE == 1			/* Write a character in UTF-16LE */
+	pb->buf[i++] = (BYTE)c;
+	pb->buf[i++] = (BYTE)(c >> 8);
+#else							/* Write a character in ANSI/OEM */
+	c = ff_convert(c, 0);	/* Unicode -> OEM */
+	if (!c) c = '?';
+	if (c >= 0x100)
+		pb->buf[i++] = (BYTE)(c >> 8);
+	pb->buf[i++] = (BYTE)c;
+#endif
+#else							/* Write a character without conversion */
+	pb->buf[i++] = (BYTE)c;
+#endif
+
+	if (i >= (int)(sizeof pb->buf) - 3) {	/* Write buffered characters to the file */
+		f_write(pb->fp, pb->buf, (UINT)i, &bw);
+		i = (bw == (UINT)i) ? 0 : -1;
+	}
+	pb->idx = i;
+	pb->nchr++;
+}
+
+
+
+int f_putc (
+	TCHAR c,	/* A character to be output */
+	FIL* fp		/* Pointer to the file object */
+)
+{
+	putbuff pb;
+	UINT nw;
+
+
+	pb.fp = fp;			/* Initialize output buffer */
+	pb.nchr = pb.idx = 0;
+
+	putc_bfd(&pb, c);	/* Put a character */
+
+	if (   pb.idx >= 0	/* Flush buffered characters to the file */
+		&& f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK
+		&& (UINT)pb.idx == nw) return pb.nchr;
+	return EOF;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a string to the file                                              */
+/*-----------------------------------------------------------------------*/
+
+int f_puts (
+	const TCHAR* str,	/* Pointer to the string to be output */
+	FIL* fp				/* Pointer to the file object */
+)
+{
+	putbuff pb;
+	UINT nw;
+
+
+	pb.fp = fp;				/* Initialize output buffer */
+	pb.nchr = pb.idx = 0;
+
+	while (*str)			/* Put the string */
+		putc_bfd(&pb, *str++);
+
+	if (   pb.idx >= 0		/* Flush buffered characters to the file */
+		&& f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK
+		&& (UINT)pb.idx == nw) return pb.nchr;
+	return EOF;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a formatted string to the file                                    */
+/*-----------------------------------------------------------------------*/
+
+int f_printf (
+	FIL* fp,			/* Pointer to the file object */
+	const TCHAR* fmt,	/* Pointer to the format string */
+	...					/* Optional arguments... */
+)
+{
+	va_list arp;
+	BYTE f, r;
+	UINT nw, i, j, w;
+	DWORD v;
+	TCHAR c, d, s[16], *p;
+	putbuff pb;
+
+
+	pb.fp = fp;				/* Initialize output buffer */
+	pb.nchr = pb.idx = 0;
+
+	va_start(arp, fmt);
+
+	for (;;) {
+		c = *fmt++;
+		if (c == 0) break;			/* End of string */
+		if (c != '%') {				/* Non escape character */
+			putc_bfd(&pb, c);
+			continue;
+		}
+		w = f = 0;
+		c = *fmt++;
+		if (c == '0') {				/* Flag: '0' padding */
+			f = 1; c = *fmt++;
+		} else {
+			if (c == '-') {			/* Flag: left justified */
+				f = 2; c = *fmt++;
+			}
+		}
+		while (IsDigit(c)) {		/* Precision */
+			w = w * 10 + c - '0';
+			c = *fmt++;
+		}
+		if (c == 'l' || c == 'L') {	/* Prefix: Size is long int */
+			f |= 4; c = *fmt++;
+		}
+		if (!c) break;
+		d = c;
+		if (IsLower(d)) d -= 0x20;
+		switch (d) {				/* Type is... */
+		case 'S' :					/* String */
+			p = va_arg(arp, TCHAR*);
+			for (j = 0; p[j]; j++) ;
+			if (!(f & 2)) {
+				while (j++ < w) putc_bfd(&pb, ' ');
+			}
+			while (*p) putc_bfd(&pb, *p++);
+			while (j++ < w) putc_bfd(&pb, ' ');
+			continue;
+		case 'C' :					/* Character */
+			putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue;
+		case 'B' :					/* Binary */
+			r = 2; break;
+		case 'O' :					/* Octal */
+			r = 8; break;
+		case 'D' :					/* Signed decimal */
+		case 'U' :					/* Unsigned decimal */
+			r = 10; break;
+		case 'X' :					/* Hexdecimal */
+			r = 16; break;
+		default:					/* Unknown type (pass-through) */
+			putc_bfd(&pb, c); continue;
+		}
+
+		/* Get an argument and put it in numeral */
+		v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int));
+		if (d == 'D' && (v & 0x80000000)) {
+			v = 0 - v;
+			f |= 8;
+		}
+		i = 0;
+		do {
+			d = (TCHAR)(v % r); v /= r;
+			if (d > 9) d += (c == 'x') ? 0x27 : 0x07;
+			s[i++] = d + '0';
+		} while (v && i < sizeof s / sizeof s[0]);
+		if (f & 8) s[i++] = '-';
+		j = i; d = (f & 1) ? '0' : ' ';
+		while (!(f & 2) && j++ < w) putc_bfd(&pb, d);
+		do putc_bfd(&pb, s[--i]); while (i);
+		while (j++ < w) putc_bfd(&pb, d);
+	}
+
+	va_end(arp);
+
+	if (   pb.idx >= 0		/* Flush buffered characters to the file */
+		&& f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK
+		&& (UINT)pb.idx == nw) return pb.nchr;
+	return EOF;
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _USE_STRFUNC */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/FATFileSystem/ChaN/ff.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,350 @@
+/*---------------------------------------------------------------------------/
+/  FatFs - FAT file system module include R0.11a    (C)ChaN, 2015
+/----------------------------------------------------------------------------/
+/ FatFs module is a free software that opened under license policy of
+/ following conditions.
+/
+/ Copyright (C) 2015, ChaN, all right reserved.
+/
+/ 1. Redistributions of source code must retain the above copyright notice,
+/    this condition and the following disclaimer.
+/
+/ This software is provided by the copyright holder and contributors "AS IS"
+/ and any warranties related to this software are DISCLAIMED.
+/ The copyright owner or contributors be NOT LIABLE for any damages caused
+/ by use of this software.
+/---------------------------------------------------------------------------*/
+
+
+#ifndef _FATFS
+#define _FATFS	64180	/* Revision ID */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "integer.h"	/* Basic integer types */
+#include "ffconf.h"		/* FatFs configuration options */
+#if _FATFS != _FFCONF
+#error Wrong configuration file (ffconf.h).
+#endif
+
+
+
+/* Definitions of volume management */
+
+#if _MULTI_PARTITION		/* Multiple partition configuration */
+typedef struct {
+	BYTE pd;	/* Physical drive number */
+	BYTE pt;	/* Partition: 0:Auto detect, 1-4:Forced partition) */
+} PARTITION;
+extern PARTITION VolToPart[];	/* Volume - Partition resolution table */
+#define LD2PD(vol) (VolToPart[vol].pd)	/* Get physical drive number */
+#define LD2PT(vol) (VolToPart[vol].pt)	/* Get partition index */
+
+#else							/* Single partition configuration */
+#define LD2PD(vol) (BYTE)(vol)	/* Each logical drive is bound to the same physical drive number */
+#define LD2PT(vol) 0			/* Find first valid partition or in SFD */
+
+#endif
+
+
+
+/* Type of path name strings on FatFs API */
+
+#if _LFN_UNICODE			/* Unicode string */
+#if !_USE_LFN
+#error _LFN_UNICODE must be 0 at non-LFN cfg.
+#endif
+#ifndef _INC_TCHAR
+typedef WCHAR TCHAR;
+#define _T(x) L ## x
+#define _TEXT(x) L ## x
+#endif
+
+#else						/* ANSI/OEM string */
+#ifndef _INC_TCHAR
+typedef char TCHAR;
+#define _T(x) x
+#define _TEXT(x) x
+#endif
+
+#endif
+
+
+
+/* File system object structure (FATFS) */
+
+typedef struct {
+	BYTE	fs_type;		/* FAT sub-type (0:Not mounted) */
+	BYTE	drv;			/* Physical drive number */
+	BYTE	csize;			/* Sectors per cluster (1,2,4...128) */
+	BYTE	n_fats;			/* Number of FAT copies (1 or 2) */
+	BYTE	wflag;			/* win[] flag (b0:dirty) */
+	BYTE	fsi_flag;		/* FSINFO flags (b7:disabled, b0:dirty) */
+	WORD	id;				/* File system mount ID */
+	WORD	n_rootdir;		/* Number of root directory entries (FAT12/16) */
+#if _MAX_SS != _MIN_SS
+	WORD	ssize;			/* Bytes per sector (512, 1024, 2048 or 4096) */
+#endif
+#if _FS_REENTRANT
+	_SYNC_t	sobj;			/* Identifier of sync object */
+#endif
+#if !_FS_READONLY
+	DWORD	last_clust;		/* Last allocated cluster */
+	DWORD	free_clust;		/* Number of free clusters */
+#endif
+#if _FS_RPATH
+	DWORD	cdir;			/* Current directory start cluster (0:root) */
+#endif
+	DWORD	n_fatent;		/* Number of FAT entries, = number of clusters + 2 */
+	DWORD	fsize;			/* Sectors per FAT */
+	DWORD	volbase;		/* Volume start sector */
+	DWORD	fatbase;		/* FAT start sector */
+	DWORD	dirbase;		/* Root directory start sector (FAT32:Cluster#) */
+	DWORD	database;		/* Data start sector */
+	DWORD	winsect;		/* Current sector appearing in the win[] */
+	BYTE	win[_MAX_SS];	/* Disk access window for Directory, FAT (and file data at tiny cfg) */
+} FATFS;
+
+
+
+/* File object structure (FIL) */
+
+typedef struct {
+	FATFS*	fs;				/* Pointer to the related file system object (**do not change order**) */
+	WORD	id;				/* Owner file system mount ID (**do not change order**) */
+	BYTE	flag;			/* Status flags */
+	BYTE	err;			/* Abort flag (error code) */
+	DWORD	fptr;			/* File read/write pointer (Zeroed on file open) */
+	DWORD	fsize;			/* File size */
+	DWORD	sclust;			/* File start cluster (0:no cluster chain, always 0 when fsize is 0) */
+	DWORD	clust;			/* Current cluster of fpter (not valid when fprt is 0) */
+	DWORD	dsect;			/* Sector number appearing in buf[] (0:invalid) */
+#if !_FS_READONLY
+	DWORD	dir_sect;		/* Sector number containing the directory entry */
+	BYTE*	dir_ptr;		/* Pointer to the directory entry in the win[] */
+#endif
+#if _USE_FASTSEEK
+	DWORD*	cltbl;			/* Pointer to the cluster link map table (Nulled on file open) */
+#endif
+#if _FS_LOCK
+	UINT	lockid;			/* File lock ID origin from 1 (index of file semaphore table Files[]) */
+#endif
+#if !_FS_TINY
+	BYTE	buf[_MAX_SS];	/* File private data read/write window */
+#endif
+} FIL;
+
+
+
+/* Directory object structure (FATFS_DIR) */
+
+typedef struct {
+	FATFS*	fs;				/* Pointer to the owner file system object (**do not change order**) */
+	WORD	id;				/* Owner file system mount ID (**do not change order**) */
+	WORD	index;			/* Current read/write index number */
+	DWORD	sclust;			/* Table start cluster (0:Root dir) */
+	DWORD	clust;			/* Current cluster */
+	DWORD	sect;			/* Current sector */
+	BYTE*	dir;			/* Pointer to the current SFN entry in the win[] */
+	BYTE*	fn;				/* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
+#if _FS_LOCK
+	UINT	lockid;			/* File lock ID (index of file semaphore table Files[]) */
+#endif
+#if _USE_LFN
+	WCHAR*	lfn;			/* Pointer to the LFN working buffer */
+	WORD	lfn_idx;		/* Last matched LFN index number (0xFFFF:No LFN) */
+#endif
+#if _USE_FIND
+	const TCHAR*	pat;	/* Pointer to the name matching pattern */
+#endif
+} FATFS_DIR;
+
+
+
+/* File information structure (FILINFO) */
+
+typedef struct {
+	DWORD	fsize;			/* File size */
+	WORD	fdate;			/* Last modified date */
+	WORD	ftime;			/* Last modified time */
+	BYTE	fattrib;		/* Attribute */
+	TCHAR	fname[13];		/* Short file name (8.3 format) */
+#if _USE_LFN
+	TCHAR*	lfname;			/* Pointer to the LFN buffer */
+	UINT 	lfsize;			/* Size of LFN buffer in TCHAR */
+#endif
+} FILINFO;
+
+
+
+/* File function return code (FRESULT) */
+
+typedef enum {
+	FR_OK = 0,				/* (0) Succeeded */
+	FR_DISK_ERR,			/* (1) A hard error occurred in the low level disk I/O layer */
+	FR_INT_ERR,				/* (2) Assertion failed */
+	FR_NOT_READY,			/* (3) The physical drive cannot work */
+	FR_NO_FILE,				/* (4) Could not find the file */
+	FR_NO_PATH,				/* (5) Could not find the path */
+	FR_INVALID_NAME,		/* (6) The path name format is invalid */
+	FR_DENIED,				/* (7) Access denied due to prohibited access or directory full */
+	FR_EXIST,				/* (8) Access denied due to prohibited access */
+	FR_INVALID_OBJECT,		/* (9) The file/directory object is invalid */
+	FR_WRITE_PROTECTED,		/* (10) The physical drive is write protected */
+	FR_INVALID_DRIVE,		/* (11) The logical drive number is invalid */
+	FR_NOT_ENABLED,			/* (12) The volume has no work area */
+	FR_NO_FILESYSTEM,		/* (13) There is no valid FAT volume */
+	FR_MKFS_ABORTED,		/* (14) The f_mkfs() aborted due to any parameter error */
+	FR_TIMEOUT,				/* (15) Could not get a grant to access the volume within defined period */
+	FR_LOCKED,				/* (16) The operation is rejected according to the file sharing policy */
+	FR_NOT_ENOUGH_CORE,		/* (17) LFN working buffer could not be allocated */
+	FR_TOO_MANY_OPEN_FILES,	/* (18) Number of open files > _FS_LOCK */
+	FR_INVALID_PARAMETER	/* (19) Given parameter is invalid */
+} FRESULT;
+
+
+
+/*--------------------------------------------------------------*/
+/* FatFs module application interface                           */
+
+FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode);				/* Open or create a file */
+FRESULT f_close (FIL* fp);											/* Close an open file object */
+FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br);			/* Read data from a file */
+FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw);	/* Write data to a file */
+FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf);	/* Forward data to the stream */
+FRESULT f_lseek (FIL* fp, DWORD ofs);								/* Move file pointer of a file object */
+FRESULT f_truncate (FIL* fp);										/* Truncate file */
+FRESULT f_sync (FIL* fp);											/* Flush cached data of a writing file */
+FRESULT f_opendir (FATFS_DIR* dp, const TCHAR* path);						/* Open a directory */
+FRESULT f_closedir (FATFS_DIR* dp);										/* Close an open directory */
+FRESULT f_readdir (FATFS_DIR* dp, FILINFO* fno);							/* Read a directory item */
+FRESULT f_findfirst (FATFS_DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern);	/* Find first file */
+FRESULT f_findnext (FATFS_DIR* dp, FILINFO* fno);							/* Find next file */
+FRESULT f_mkdir (const TCHAR* path);								/* Create a sub directory */
+FRESULT f_unlink (const TCHAR* path);								/* Delete an existing file or directory */
+FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new);	/* Rename/Move a file or directory */
+FRESULT f_stat (const TCHAR* path, FILINFO* fno);					/* Get file status */
+FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask);			/* Change attribute of the file/dir */
+FRESULT f_utime (const TCHAR* path, const FILINFO* fno);			/* Change times-tamp of the file/dir */
+FRESULT f_chdir (const TCHAR* path);								/* Change current directory */
+FRESULT f_chdrive (const TCHAR* path);								/* Change current drive */
+FRESULT f_getcwd (TCHAR* buff, UINT len);							/* Get current directory */
+FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs);	/* Get number of free clusters on the drive */
+FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn);	/* Get volume label */
+FRESULT f_setlabel (const TCHAR* label);							/* Set volume label */
+FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt);			/* Mount/Unmount a logical drive */
+FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au);				/* Create a file system on the volume */
+FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work);			/* Divide a physical drive into some partitions */
+int f_putc (TCHAR c, FIL* fp);										/* Put a character to the file */
+int f_puts (const TCHAR* str, FIL* cp);								/* Put a string to the file */
+int f_printf (FIL* fp, const TCHAR* str, ...);						/* Put a formatted string to the file */
+TCHAR* f_gets (TCHAR* buff, int len, FIL* fp);						/* Get a string from the file */
+
+#define f_eof(fp) ((int)((fp)->fptr == (fp)->fsize))
+#define f_error(fp) ((fp)->err)
+#define f_tell(fp) ((fp)->fptr)
+#define f_size(fp) ((fp)->fsize)
+#define f_rewind(fp) f_lseek((fp), 0)
+#define f_rewinddir(dp) f_readdir((dp), 0)
+
+#ifndef EOF
+#define EOF (-1)
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Additional user defined functions                            */
+
+/* RTC function */
+#if !_FS_READONLY && !_FS_NORTC
+DWORD get_fattime (void);
+#endif
+
+/* Unicode support functions */
+#if _USE_LFN							/* Unicode - OEM code conversion */
+WCHAR ff_convert (WCHAR chr, UINT dir);	/* OEM-Unicode bidirectional conversion */
+WCHAR ff_wtoupper (WCHAR chr);			/* Unicode upper-case conversion */
+#if _USE_LFN == 3						/* Memory functions */
+void* ff_memalloc (UINT msize);			/* Allocate memory block */
+void ff_memfree (void* mblock);			/* Free memory block */
+#endif
+#endif
+
+/* Sync functions */
+#if _FS_REENTRANT
+int ff_cre_syncobj (BYTE vol, _SYNC_t* sobj);	/* Create a sync object */
+int ff_req_grant (_SYNC_t sobj);				/* Lock sync object */
+void ff_rel_grant (_SYNC_t sobj);				/* Unlock sync object */
+int ff_del_syncobj (_SYNC_t sobj);				/* Delete a sync object */
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Flags and offset address                                     */
+
+
+/* File access control and file status flags (FIL.flag) */
+
+#define	FA_READ				0x01
+#define	FA_OPEN_EXISTING	0x00
+
+#if !_FS_READONLY
+#define	FA_WRITE			0x02
+#define	FA_CREATE_NEW		0x04
+#define	FA_CREATE_ALWAYS	0x08
+#define	FA_OPEN_ALWAYS		0x10
+#define FA__WRITTEN			0x20
+#define FA__DIRTY			0x40
+#endif
+
+
+/* FAT sub type (FATFS.fs_type) */
+
+#define FS_FAT12	1
+#define FS_FAT16	2
+#define FS_FAT32	3
+
+
+/* File attribute bits for directory entry */
+
+#define	AM_RDO	0x01	/* Read only */
+#define	AM_HID	0x02	/* Hidden */
+#define	AM_SYS	0x04	/* System */
+#define	AM_VOL	0x08	/* Volume label */
+#define AM_LFN	0x0F	/* LFN entry */
+#define AM_DIR	0x10	/* Directory */
+#define AM_ARC	0x20	/* Archive */
+#define AM_MASK	0x3F	/* Mask of defined bits */
+
+
+/* Fast seek feature */
+#define CREATE_LINKMAP	0xFFFFFFFF
+
+
+
+/*--------------------------------*/
+/* Multi-byte word access macros  */
+
+#if _WORD_ACCESS == 1	/* Enable word access to the FAT structure */
+#define	LD_WORD(ptr)		(WORD)(*(WORD*)(BYTE*)(ptr))
+#define	LD_DWORD(ptr)		(DWORD)(*(DWORD*)(BYTE*)(ptr))
+#define	ST_WORD(ptr,val)	*(WORD*)(BYTE*)(ptr)=(WORD)(val)
+#define	ST_DWORD(ptr,val)	*(DWORD*)(BYTE*)(ptr)=(DWORD)(val)
+#else					/* Use byte-by-byte access to the FAT structure */
+#define	LD_WORD(ptr)		(WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
+#define	LD_DWORD(ptr)		(DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr))
+#define	ST_WORD(ptr,val)	*(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8)
+#define	ST_DWORD(ptr,val)	*(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FATFS */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/FATFileSystem/ChaN/ffconf.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,285 @@
+/*---------------------------------------------------------------------------/
+/  FatFs - FAT file system module configuration file  R0.11a (C)ChaN, 2015
+/---------------------------------------------------------------------------*/
+
+#define _FFCONF 64180	/* Revision ID */
+
+#define FFS_DBG			0
+
+/*---------------------------------------------------------------------------/
+/ Function Configurations
+/---------------------------------------------------------------------------*/
+
+#define _FS_READONLY	0
+/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
+/  Read-only configuration removes writing API functions, f_write(), f_sync(),
+/  f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
+/  and optional writing functions as well. */
+
+
+#define _FS_MINIMIZE	0
+/* This option defines minimization level to remove some basic API functions.
+/
+/   0: All basic functions are enabled.
+/   1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(),
+/      f_truncate() and f_rename() function are removed.
+/   2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
+/   3: f_lseek() function is removed in addition to 2. */
+
+
+#define	_USE_STRFUNC	0
+/* This option switches string functions, f_gets(), f_putc(), f_puts() and
+/  f_printf().
+/
+/  0: Disable string functions.
+/  1: Enable without LF-CRLF conversion.
+/  2: Enable with LF-CRLF conversion. */
+
+
+#define _USE_FIND		0
+/* This option switches filtered directory read feature and related functions,
+/  f_findfirst() and f_findnext(). (0:Disable or 1:Enable) */
+
+
+#define	_USE_MKFS		1
+/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
+
+
+#define	_USE_FASTSEEK	0
+/* This option switches fast seek feature. (0:Disable or 1:Enable) */
+
+
+#define _USE_LABEL		0
+/* This option switches volume label functions, f_getlabel() and f_setlabel().
+/  (0:Disable or 1:Enable) */
+
+
+#define	_USE_FORWARD	0
+/* This option switches f_forward() function. (0:Disable or 1:Enable)
+/  To enable it, also _FS_TINY need to be set to 1. */
+
+
+/*---------------------------------------------------------------------------/
+/ Locale and Namespace Configurations
+/---------------------------------------------------------------------------*/
+
+#define _CODE_PAGE	850
+/* This option specifies the OEM code page to be used on the target system.
+/  Incorrect setting of the code page can cause a file open failure.
+/
+/   1   - ASCII (No extended character. Non-LFN cfg. only)
+/   437 - U.S.
+/   720 - Arabic
+/   737 - Greek
+/   771 - KBL
+/   775 - Baltic
+/   850 - Latin 1
+/   852 - Latin 2
+/   855 - Cyrillic
+/   857 - Turkish
+/   860 - Portuguese
+/   861 - Icelandic
+/   862 - Hebrew
+/   863 - Canadian French
+/   864 - Arabic
+/   865 - Nordic
+/   866 - Russian
+/   869 - Greek 2
+/   932 - Japanese (DBCS)
+/   936 - Simplified Chinese (DBCS)
+/   949 - Korean (DBCS)
+/   950 - Traditional Chinese (DBCS)
+*/
+
+
+#define	_USE_LFN	1
+#define	_MAX_LFN	255
+/* The _USE_LFN option switches the LFN feature.
+/
+/   0: Disable LFN feature. _MAX_LFN has no effect.
+/   1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
+/   2: Enable LFN with dynamic working buffer on the STACK.
+/   3: Enable LFN with dynamic working buffer on the HEAP.
+/
+/  When enable the LFN feature, Unicode handling functions (option/unicode.c) must
+/  be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes.
+/  When use stack for the working buffer, take care on stack overflow. When use heap
+/  memory for the working buffer, memory management functions, ff_memalloc() and
+/  ff_memfree(), must be added to the project. */
+
+
+#define	_LFN_UNICODE	0
+/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode)
+/  To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE
+/  to 1. This option also affects behavior of string I/O functions. */
+
+
+#define _STRF_ENCODE	3
+/* When _LFN_UNICODE is 1, this option selects the character encoding on the file to
+/  be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().
+/
+/  0: ANSI/OEM
+/  1: UTF-16LE
+/  2: UTF-16BE
+/  3: UTF-8
+/
+/  When _LFN_UNICODE is 0, this option has no effect. */
+
+
+#define _FS_RPATH	0
+/* This option configures relative path feature.
+/
+/   0: Disable relative path feature and remove related functions.
+/   1: Enable relative path feature. f_chdir() and f_chdrive() are available.
+/   2: f_getcwd() function is available in addition to 1.
+/
+/  Note that directory items read via f_readdir() are affected by this option. */
+
+
+/*---------------------------------------------------------------------------/
+/ Drive/Volume Configurations
+/---------------------------------------------------------------------------*/
+
+#define _VOLUMES	1
+/* Number of volumes (logical drives) to be used. */
+
+
+#define _STR_VOLUME_ID	0
+#define _VOLUME_STRS	"RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
+/* _STR_VOLUME_ID option switches string volume ID feature.
+/  When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
+/  number in the path name. _VOLUME_STRS defines the drive ID strings for each
+/  logical drives. Number of items must be equal to _VOLUMES. Valid characters for
+/  the drive ID strings are: A-Z and 0-9. */
+
+
+#define	_MULTI_PARTITION	0
+/* This option switches multi-partition feature. By default (0), each logical drive
+/  number is bound to the same physical drive number and only an FAT volume found on
+/  the physical drive will be mounted. When multi-partition feature is enabled (1),
+/  each logical drive number is bound to arbitrary physical drive and partition
+/  listed in the VolToPart[]. Also f_fdisk() funciton will be available. */
+
+
+#define	_MIN_SS		512
+#define	_MAX_SS		512
+/* These options configure the range of sector size to be supported. (512, 1024,
+/  2048 or 4096) Always set both 512 for most systems, all type of memory cards and
+/  harddisk. But a larger value may be required for on-board flash memory and some
+/  type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
+/  to variable sector size and GET_SECTOR_SIZE command must be implemented to the
+/  disk_ioctl() function. */
+
+
+#define	_USE_TRIM	0
+/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable)
+/  To enable Trim feature, also CTRL_TRIM command should be implemented to the
+/  disk_ioctl() function. */
+
+
+#define _FS_NOFSINFO	0
+/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
+/  option, and f_getfree() function at first time after volume mount will force
+/  a full FAT scan. Bit 1 controls the use of last allocated cluster number.
+/
+/  bit0=0: Use free cluster count in the FSINFO if available.
+/  bit0=1: Do not trust free cluster count in the FSINFO.
+/  bit1=0: Use last allocated cluster number in the FSINFO if available.
+/  bit1=1: Do not trust last allocated cluster number in the FSINFO.
+*/
+
+
+
+/*---------------------------------------------------------------------------/
+/ System Configurations
+/---------------------------------------------------------------------------*/
+
+#define	_FS_TINY	0
+/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
+/  At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS
+/  bytes. Instead of private sector buffer eliminated from the file object,
+/  common sector buffer in the file system object (FATFS) is used for the file
+/  data transfer. */
+
+
+#define _FS_NORTC	0
+#define _NORTC_MON	1
+#define _NORTC_MDAY	1
+#define _NORTC_YEAR	2015
+/* The _FS_NORTC option switches timestamp feature. If the system does not have
+/  an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable
+/  the timestamp feature. All objects modified by FatFs will have a fixed timestamp
+/  defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR.
+/  When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need
+/  to be added to the project to read current time form RTC. _NORTC_MON,
+/  _NORTC_MDAY and _NORTC_YEAR have no effect. 
+/  These options have no effect at read-only configuration (_FS_READONLY == 1). */
+
+
+#define	_FS_LOCK	0
+/* The _FS_LOCK option switches file lock feature to control duplicated file open
+/  and illegal operation to open objects. This option must be 0 when _FS_READONLY
+/  is 1.
+/
+/  0:  Disable file lock feature. To avoid volume corruption, application program
+/      should avoid illegal open, remove and rename to the open objects.
+/  >0: Enable file lock feature. The value defines how many files/sub-directories
+/      can be opened simultaneously under file lock control. Note that the file
+/      lock feature is independent of re-entrancy. */
+
+
+#define _FS_REENTRANT	0
+#define _FS_TIMEOUT		1000
+#define	_SYNC_t			HANDLE
+/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs
+/  module itself. Note that regardless of this option, file access to different
+/  volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
+/  and f_fdisk() function, are always not re-entrant. Only file/directory access
+/  to the same volume is under control of this feature.
+/
+/   0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.
+/   1: Enable re-entrancy. Also user provided synchronization handlers,
+/      ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
+/      function, must be added to the project. Samples are available in
+/      option/syscall.c.
+/
+/  The _FS_TIMEOUT defines timeout period in unit of time tick.
+/  The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
+/  SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be
+/  included somewhere in the scope of ff.c. */
+
+
+#define _WORD_ACCESS	0
+/* The _WORD_ACCESS option is an only platform dependent option. It defines
+/  which access method is used to the word data on the FAT volume.
+/
+/   0: Byte-by-byte access. Always compatible with all platforms.
+/   1: Word access. Do not choose this unless under both the following conditions.
+/
+/  * Address misaligned memory access is always allowed to ALL instructions.
+/  * Byte order on the memory is little-endian.
+/
+/  If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size.
+/  Following table shows allowable settings of some type of processors.
+/
+/  ARM7TDMI   0   *2          ColdFire   0    *1         V850E      0    *2
+/  Cortex-M3  0   *3          Z80        0/1             V850ES     0/1
+/  Cortex-M0  0   *2          x86        0/1             TLCS-870   0/1
+/  AVR        0/1             RX600(LE)  0/1             TLCS-900   0/1
+/  AVR32      0   *1          RL78       0    *2         R32C       0    *2
+/  PIC18      0/1             SH-2       0    *1         M16C       0/1
+/  PIC24      0   *2          H8S        0    *1         MSP430     0    *2
+/  PIC32      0   *1          H8/300H    0    *1         8051       0/1
+/
+/  *1:Big-endian.
+/  *2:Unaligned memory access is not supported.
+/  *3:Some compilers generate LDM/STM for mem_cpy function.
+*/
+
+#define FLUSH_ON_NEW_CLUSTER    0   /* Sync the file on every new cluster */
+#define FLUSH_ON_NEW_SECTOR     1   /* Sync the file on every new sector */
+/* Only one of these two defines needs to be set to 1. If both are set to 0
+   the file is only sync when closed.
+   Clusters are group of sectors (eg: 8 sectors). Flushing on new cluster means
+   it would be less often than flushing on new sector. Sectors are generally
+   512 Bytes long. */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/FATFileSystem/ChaN/integer.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,33 @@
+/*-------------------------------------------*/
+/* Integer type definitions for FatFs module */
+/*-------------------------------------------*/
+
+#ifndef _FF_INTEGER
+#define _FF_INTEGER
+
+#ifdef _WIN32	/* Development platform */
+
+#include <windows.h>
+#include <tchar.h>
+
+#else			/* Embedded platform */
+
+/* This type MUST be 8-bit */
+typedef unsigned char	BYTE;
+
+/* These types MUST be 16-bit */
+typedef short			SHORT;
+typedef unsigned short	WORD;
+typedef unsigned short	WCHAR;
+
+/* These types MUST be 16-bit or 32-bit */
+typedef int				INT;
+typedef unsigned int	UINT;
+
+/* These types MUST be 32-bit */
+typedef long			LONG;
+typedef unsigned long	DWORD;
+
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/FATFileSystem/FATDirHandle.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,79 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2012 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <string.h>
+#include "ff.h"
+#include "FATDirHandle.h"
+
+using namespace mbed;
+
+FATDirHandle::FATDirHandle(const FATFS_DIR &the_dir) {
+    dir = the_dir;
+}
+
+int FATDirHandle::closedir() {
+    int retval = f_closedir(&dir);
+    delete this;
+    return retval;
+}
+
+struct dirent *FATDirHandle::readdir() {
+    FILINFO finfo;
+
+#if _USE_LFN
+    finfo.lfname = cur_entry.d_name;
+    finfo.lfsize = sizeof(cur_entry.d_name);
+#endif // _USE_LFN
+
+    FRESULT res = f_readdir(&dir, &finfo);
+
+#if _USE_LFN
+    if(res != 0 || finfo.fname[0]==0) {
+        return NULL;
+    } else {
+        if(cur_entry.d_name[0]==0) {
+            // No long filename so use short filename.
+            memcpy(cur_entry.d_name, finfo.fname, sizeof(finfo.fname));
+        }
+        return &cur_entry;
+    }
+#else
+    if(res != 0 || finfo.fname[0]==0) {
+        return NULL;
+    } else {
+        memcpy(cur_entry.d_name, finfo.fname, sizeof(finfo.fname));
+        return &cur_entry;
+    }
+#endif /* _USE_LFN */
+}
+
+void FATDirHandle::rewinddir() {
+    dir.index = 0;
+}
+
+off_t FATDirHandle::telldir() {
+    return dir.index;
+}
+
+void FATDirHandle::seekdir(off_t location) {
+    dir.index = location;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/FATFileSystem/FATDirHandle.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,45 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2012 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef MBED_FATDIRHANDLE_H
+#define MBED_FATDIRHANDLE_H
+
+#include "DirHandle.h"
+
+using namespace mbed;
+
+class FATDirHandle : public DirHandle {
+
+ public:
+    FATDirHandle(const FATFS_DIR &the_dir);
+    virtual int closedir();
+    virtual struct dirent *readdir();
+    virtual void rewinddir();
+    virtual off_t telldir();
+    virtual void seekdir(off_t location);
+
+ private:
+    FATFS_DIR dir;
+    struct dirent cur_entry;
+
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/FATFileSystem/FATFileHandle.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,90 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2012 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "ff.h"
+#include "ffconf.h"
+#include "mbed_debug.h"
+
+#include "FATFileHandle.h"
+
+FATFileHandle::FATFileHandle(FIL fh) {
+    _fh = fh;
+}
+
+int FATFileHandle::close() {
+    int retval = f_close(&_fh);
+    delete this;
+    return retval;
+}
+
+ssize_t FATFileHandle::write(const void* buffer, size_t length) {
+    UINT n;
+    FRESULT res = f_write(&_fh, buffer, length, &n);
+    if (res) {
+        debug_if(FFS_DBG, "f_write() failed: %d", res);
+        return -1;
+    }
+    return n;
+}
+
+ssize_t FATFileHandle::read(void* buffer, size_t length) {
+    debug_if(FFS_DBG, "read(%d)\n", length);
+    UINT n;
+    FRESULT res = f_read(&_fh, buffer, length, &n);
+    if (res) {
+        debug_if(FFS_DBG, "f_read() failed: %d\n", res);
+        return -1;
+    }
+    return n;
+}
+
+int FATFileHandle::isatty() {
+    return 0;
+}
+
+off_t FATFileHandle::lseek(off_t position, int whence) {
+    if (whence == SEEK_END) {
+        position += _fh.fsize;
+    } else if(whence==SEEK_CUR) {
+        position += _fh.fptr;
+    }
+    FRESULT res = f_lseek(&_fh, position);
+    if (res) {
+        debug_if(FFS_DBG, "lseek failed: %d\n", res);
+        return -1;
+    } else {
+        debug_if(FFS_DBG, "lseek OK, returning %i\n", _fh.fptr);
+        return _fh.fptr;
+    }
+}
+
+int FATFileHandle::fsync() {
+    FRESULT res = f_sync(&_fh);
+    if (res) {
+        debug_if(FFS_DBG, "f_sync() failed: %d\n", res);
+        return -1;
+    }
+    return 0;
+}
+
+off_t FATFileHandle::flen() {
+    return _fh.fsize;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/FATFileSystem/FATFileHandle.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,47 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2012 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef MBED_FATFILEHANDLE_H
+#define MBED_FATFILEHANDLE_H
+
+#include "FileHandle.h"
+
+using namespace mbed;
+
+class FATFileHandle : public FileHandle {
+public:
+
+    FATFileHandle(FIL fh);
+    virtual int close();
+    virtual ssize_t write(const void* buffer, size_t length);
+    virtual ssize_t read(void* buffer, size_t length);
+    virtual int isatty();
+    virtual off_t lseek(off_t position, int whence);
+    virtual int fsync();
+    virtual off_t flen();
+
+protected:
+
+    FIL _fh;
+
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/FATFileSystem/FATFileSystem.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,154 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2012 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "mbed.h"
+
+#include "ffconf.h"
+#include "mbed_debug.h"
+
+#include "FATFileSystem.h"
+#include "FATFileHandle.h"
+#include "FATDirHandle.h"
+
+DWORD get_fattime(void) {
+    time_t rawtime;
+    time(&rawtime);
+    struct tm *ptm = localtime(&rawtime);
+    return (DWORD)(ptm->tm_year - 80) << 25
+         | (DWORD)(ptm->tm_mon + 1  ) << 21
+         | (DWORD)(ptm->tm_mday     ) << 16
+         | (DWORD)(ptm->tm_hour     ) << 11
+         | (DWORD)(ptm->tm_min      ) << 5
+         | (DWORD)(ptm->tm_sec/2    );
+}
+
+FATFileSystem *FATFileSystem::_ffs[_VOLUMES] = {0};
+
+FATFileSystem::FATFileSystem(const char* n) : FileSystemLike(n) {
+    debug_if(FFS_DBG, "FATFileSystem(%s)\n", n);
+    for(int i=0; i<_VOLUMES; i++) {
+        if(_ffs[i] == 0) {
+            _ffs[i] = this;
+            _fsid[0] = '0' + i;
+            _fsid[1] = '\0';
+            debug_if(FFS_DBG, "Mounting [%s] on ffs drive [%s]\n", getName(), _fsid);
+            f_mount(&_fs, _fsid, 0);
+            return;
+        }
+    }
+    error("Couldn't create %s in FATFileSystem::FATFileSystem\n", n);
+}
+
+FATFileSystem::~FATFileSystem() {
+    for (int i=0; i<_VOLUMES; i++) {
+        if (_ffs[i] == this) {
+            _ffs[i] = 0;
+            f_mount(NULL, _fsid, 0);
+        }
+    }
+}
+
+FileHandle *FATFileSystem::open(const char* name, int flags) {
+    debug_if(FFS_DBG, "open(%s) on filesystem [%s], drv [%s]\n", name, getName(), _fsid);
+    char n[64];
+    sprintf(n, "%s:/%s", _fsid, name);
+
+    /* POSIX flags -> FatFS open mode */
+    BYTE openmode;
+    if (flags & O_RDWR) {
+        openmode = FA_READ|FA_WRITE;
+    } else if(flags & O_WRONLY) {
+        openmode = FA_WRITE;
+    } else {
+        openmode = FA_READ;
+    }
+    if(flags & O_CREAT) {
+        if(flags & O_TRUNC) {
+            openmode |= FA_CREATE_ALWAYS;
+        } else {
+            openmode |= FA_OPEN_ALWAYS;
+        }
+    }
+
+    FIL fh;
+    FRESULT res = f_open(&fh, n, openmode);
+    if (res) {
+        debug_if(FFS_DBG, "f_open('w') failed: %d\n", res);
+        return NULL;
+    }
+    if (flags & O_APPEND) {
+        f_lseek(&fh, fh.fsize);
+    }
+    return new FATFileHandle(fh);
+}
+
+int FATFileSystem::remove(const char *filename) {
+    FRESULT res = f_unlink(filename);
+    if (res) {
+        debug_if(FFS_DBG, "f_unlink() failed: %d\n", res);
+        return -1;
+    }
+    return 0;
+}
+
+int FATFileSystem::rename(const char *oldname, const char *newname) {
+    FRESULT res = f_rename(oldname, newname);
+    if (res) {
+        debug_if(FFS_DBG, "f_rename() failed: %d\n", res);
+        return -1;
+    }
+    return 0;
+}
+
+int FATFileSystem::format() {
+    FRESULT res = f_mkfs(_fsid, 0, 512); // Logical drive number, Partitioning rule, Allocation unit size (bytes per cluster)
+    if (res) {
+        debug_if(FFS_DBG, "f_mkfs() failed: %d\n", res);
+        return -1;
+    }
+    return 0;
+}
+
+DirHandle *FATFileSystem::opendir(const char *name) {
+    FATFS_DIR dir;
+    FRESULT res = f_opendir(&dir, name);
+    if (res != 0) {
+        return NULL;
+    }
+    return new FATDirHandle(dir);
+}
+
+int FATFileSystem::mkdir(const char *name, mode_t mode) {
+    FRESULT res = f_mkdir(name);
+    return res == 0 ? 0 : -1;
+}
+
+int FATFileSystem::mount() {
+    FRESULT res = f_mount(&_fs, _fsid, 1);
+    return res == 0 ? 0 : -1;
+}
+
+int FATFileSystem::unmount() {
+    if (disk_sync())
+        return -1;
+    FRESULT res = f_mount(NULL, _fsid, 0);
+    return res == 0 ? 0 : -1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/FATFileSystem/FATFileSystem.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,94 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2012 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef MBED_FATFILESYSTEM_H
+#define MBED_FATFILESYSTEM_H
+
+#include "FileSystemLike.h"
+#include "FileHandle.h"
+#include "ff.h"
+#include <stdint.h>
+
+using namespace mbed;
+
+/**
+ * FATFileSystem based on ChaN's Fat Filesystem library v0.8 
+ */
+class FATFileSystem : public FileSystemLike {
+public:
+
+    FATFileSystem(const char* n);
+    virtual ~FATFileSystem();
+
+    static FATFileSystem * _ffs[_VOLUMES];   // FATFileSystem objects, as parallel to FatFs drives array
+    FATFS _fs;                               // Work area (file system object) for logical drive
+    char _fsid[2];
+
+    /**
+     * Opens a file on the filesystem
+     */
+    virtual FileHandle *open(const char* name, int flags);
+    
+    /**
+     * Removes a file path
+     */
+    virtual int remove(const char *filename);
+    
+    /**
+     * Renames a file
+     */
+    virtual int rename(const char *oldname, const char *newname);
+    
+    /**
+     * Formats a logical drive, FDISK artitioning rule, 512 bytes per cluster
+     */
+    virtual int format();
+    
+    /**
+     * Opens a directory on the filesystem
+     */
+    virtual DirHandle *opendir(const char *name);
+    
+    /**
+     * Creates a directory path
+     */
+    virtual int mkdir(const char *name, mode_t mode);
+    
+    /**
+     * Mounts the filesystem
+     */
+    virtual int mount();
+    
+    /**
+     * Unmounts the filesystem
+     */
+    virtual int unmount();
+
+    virtual int disk_initialize() { return 0; }
+    virtual int disk_status() { return 0; }
+    virtual int disk_read(uint8_t *buffer, uint32_t sector, uint32_t count) = 0;
+    virtual int disk_write(const uint8_t *buffer, uint32_t sector, uint32_t count) = 0;
+    virtual int disk_sync() { return 0; }
+    virtual uint32_t disk_sectors() = 0;
+
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/FATFileSystem/MemFileSystem.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,77 @@
+/* mbed Microcontroller Library - MemFileSystem
+ * Copyright (c) 2008, sford
+ */
+
+
+#ifndef MBED_MEMFILESYSTEM_H
+#define MBED_MEMFILESYSTEM_H
+
+#include "FATFileSystem.h"
+
+namespace mbed
+{
+
+    class MemFileSystem : public FATFileSystem
+    {
+    public:
+    
+        // 2000 sectors, each 512 bytes (malloced as required)
+        char *sectors[2000];
+    
+        MemFileSystem(const char* name) : FATFileSystem(name) {
+            memset(sectors, 0, sizeof(sectors));
+        }
+    
+        virtual ~MemFileSystem() {
+            for(int i = 0; i < 2000; i++) {
+                if(sectors[i]) {
+                    free(sectors[i]);
+                }
+            }
+        }
+    
+        // read a sector in to the buffer, return 0 if ok
+        virtual int disk_read(char *buffer, int sector) {
+            if(sectors[sector] == 0) {
+                // nothing allocated means sector is empty
+                memset(buffer, 0, 512);
+            } else {
+                memcpy(buffer, sectors[sector], 512);
+            }
+            return 0;
+        }
+    
+        // write a sector from the buffer, return 0 if ok
+        virtual int disk_write(const char *buffer, int sector) {
+            // if buffer is zero deallocate sector
+            char zero[512];
+            memset(zero, 0, 512);
+            if(memcmp(zero, buffer, 512)==0) {
+                if(sectors[sector] != 0) {
+                    free(sectors[sector]);
+                    sectors[sector] = 0;
+                }
+                return 0;
+            }
+            // else allocate a sector if needed, and write
+            if(sectors[sector] == 0) {
+                char *sec = (char*)malloc(512);
+                if(sec==0) {
+                    return 1; // out of memory
+                }
+                sectors[sector] = sec;
+            }
+            memcpy(sectors[sector], buffer, 512);
+            return 0;
+        }
+    
+        // return the number of sectors
+        virtual int disk_sectors() {
+            return sizeof(sectors)/sizeof(sectors[0]);
+        }
+    
+    };
+
+}
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/SDFileSystem.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,498 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2012 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+/* Introduction
+ * ------------
+ * SD and MMC cards support a number of interfaces, but common to them all
+ * is one based on SPI. This is the one I'm implmenting because it means
+ * it is much more portable even though not so performant, and we already
+ * have the mbed SPI Interface!
+ *
+ * The main reference I'm using is Chapter 7, "SPI Mode" of:
+ *  http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf
+ *
+ * SPI Startup
+ * -----------
+ * The SD card powers up in SD mode. The SPI interface mode is selected by
+ * asserting CS low and sending the reset command (CMD0). The card will
+ * respond with a (R1) response.
+ *
+ * CMD8 is optionally sent to determine the voltage range supported, and
+ * indirectly determine whether it is a version 1.x SD/non-SD card or
+ * version 2.x. I'll just ignore this for now.
+ *
+ * ACMD41 is repeatedly issued to initialise the card, until "in idle"
+ * (bit 0) of the R1 response goes to '0', indicating it is initialised.
+ *
+ * You should also indicate whether the host supports High Capicity cards,
+ * and check whether the card is high capacity - i'll also ignore this
+ *
+ * SPI Protocol
+ * ------------
+ * The SD SPI protocol is based on transactions made up of 8-bit words, with
+ * the host starting every bus transaction by asserting the CS signal low. The
+ * card always responds to commands, data blocks and errors.
+ *
+ * The protocol supports a CRC, but by default it is off (except for the
+ * first reset CMD0, where the CRC can just be pre-calculated, and CMD8)
+ * I'll leave the CRC off I think!
+ *
+ * Standard capacity cards have variable data block sizes, whereas High
+ * Capacity cards fix the size of data block to 512 bytes. I'll therefore
+ * just always use the Standard Capacity cards with a block size of 512 bytes.
+ * This is set with CMD16.
+ *
+ * You can read and write single blocks (CMD17, CMD25) or multiple blocks
+ * (CMD18, CMD25). For simplicity, I'll just use single block accesses. When
+ * the card gets a read command, it responds with a response token, and then
+ * a data token or an error.
+ *
+ * SPI Command Format
+ * ------------------
+ * Commands are 6-bytes long, containing the command, 32-bit argument, and CRC.
+ *
+ * +---------------+------------+------------+-----------+----------+--------------+
+ * | 01 | cmd[5:0] | arg[31:24] | arg[23:16] | arg[15:8] | arg[7:0] | crc[6:0] | 1 |
+ * +---------------+------------+------------+-----------+----------+--------------+
+ *
+ * As I'm not using CRC, I can fix that byte to what is needed for CMD0 (0x95)
+ *
+ * All Application Specific commands shall be preceded with APP_CMD (CMD55).
+ *
+ * SPI Response Format
+ * -------------------
+ * The main response format (R1) is a status byte (normally zero). Key flags:
+ *  idle - 1 if the card is in an idle state/initialising
+ *  cmd  - 1 if an illegal command code was detected
+ *
+ *    +-------------------------------------------------+
+ * R1 | 0 | arg | addr | seq | crc | cmd | erase | idle |
+ *    +-------------------------------------------------+
+ *
+ * R1b is the same, except it is followed by a busy signal (zeros) until
+ * the first non-zero byte when it is ready again.
+ *
+ * Data Response Token
+ * -------------------
+ * Every data block written to the card is acknowledged by a byte
+ * response token
+ *
+ * +----------------------+
+ * | xxx | 0 | status | 1 |
+ * +----------------------+
+ *              010 - OK!
+ *              101 - CRC Error
+ *              110 - Write Error
+ *
+ * Single Block Read and Write
+ * ---------------------------
+ *
+ * Block transfers have a byte header, followed by the data, followed
+ * by a 16-bit CRC. In our case, the data will always be 512 bytes.
+ *
+ * +------+---------+---------+- -  - -+---------+-----------+----------+
+ * | 0xFE | data[0] | data[1] |        | data[n] | crc[15:8] | crc[7:0] |
+ * +------+---------+---------+- -  - -+---------+-----------+----------+
+ */
+#include "SDFileSystem.h"
+#include "mbed_debug.h"
+
+#define SD_COMMAND_TIMEOUT 5000
+
+#define SD_DBG             0
+
+SDFileSystem::SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, const char* name) :
+    FATFileSystem(name), _spi(mosi, miso, sclk), _cs(cs), _is_initialized(0) {
+    _cs = 1;
+
+    // Set default to 100kHz for initialisation and 1MHz for data transfer
+    _init_sck = 100000;
+    _transfer_sck = 1000000;
+}
+
+#define R1_IDLE_STATE           (1 << 0)
+#define R1_ERASE_RESET          (1 << 1)
+#define R1_ILLEGAL_COMMAND      (1 << 2)
+#define R1_COM_CRC_ERROR        (1 << 3)
+#define R1_ERASE_SEQUENCE_ERROR (1 << 4)
+#define R1_ADDRESS_ERROR        (1 << 5)
+#define R1_PARAMETER_ERROR      (1 << 6)
+
+// Types
+//  - v1.x Standard Capacity
+//  - v2.x Standard Capacity
+//  - v2.x High Capacity
+//  - Not recognised as an SD Card
+#define SDCARD_FAIL 0
+#define SDCARD_V1   1
+#define SDCARD_V2   2
+#define SDCARD_V2HC 3
+
+int SDFileSystem::initialise_card() {
+    // Set to SCK for initialisation, and clock card with cs = 1
+    _spi.frequency(_init_sck);
+    _cs = 1;
+    for (int i = 0; i < 16; i++) {
+        _spi.write(0xFF);
+    }
+
+    // send CMD0, should return with all zeros except IDLE STATE set (bit 0)
+    if (_cmd(0, 0) != R1_IDLE_STATE) {
+        debug("No disk, or could not put SD card in to SPI idle state\n");
+        return SDCARD_FAIL;
+    }
+
+    // send CMD8 to determine whther it is ver 2.x
+    int r = _cmd8();
+    if (r == R1_IDLE_STATE) {
+        return initialise_card_v2();
+    } else if (r == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND)) {
+        return initialise_card_v1();
+    } else {
+        debug("Not in idle state after sending CMD8 (not an SD card?)\n");
+        return SDCARD_FAIL;
+    }
+}
+
+int SDFileSystem::initialise_card_v1() {
+    for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) {
+        _cmd(55, 0);
+        if (_cmd(41, 0) == 0) {
+            cdv = 512;
+            debug_if(SD_DBG, "\n\rInit: SEDCARD_V1\n\r");
+            return SDCARD_V1;
+        }
+    }
+
+    debug("Timeout waiting for v1.x card\n");
+    return SDCARD_FAIL;
+}
+
+int SDFileSystem::initialise_card_v2() {
+    for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) {
+        wait_ms(50);
+        _cmd58();
+        _cmd(55, 0);
+        if (_cmd(41, 0x40000000) == 0) {
+            _cmd58();
+            debug_if(SD_DBG, "\n\rInit: SDCARD_V2\n\r");
+            cdv = 1;
+            return SDCARD_V2;
+        }
+    }
+
+    debug("Timeout waiting for v2.x card\n");
+    return SDCARD_FAIL;
+}
+
+int SDFileSystem::disk_initialize() {
+    _is_initialized = initialise_card();
+    if (_is_initialized == 0) {
+        debug("Fail to initialize card\n");
+        return 1;
+    }
+    debug_if(SD_DBG, "init card = %d\n", _is_initialized);
+    _sectors = _sd_sectors();
+
+    // Set block length to 512 (CMD16)
+    if (_cmd(16, 512) != 0) {
+        debug("Set 512-byte block timed out\n");
+        return 1;
+    }
+
+    // Set SCK for data transfer
+    _spi.frequency(_transfer_sck);
+    return 0;
+}
+
+int SDFileSystem::disk_write(const uint8_t* buffer, uint32_t block_number, uint32_t count) {
+    if (!_is_initialized) {
+        return -1;
+    }
+    
+    for (uint32_t b = block_number; b < block_number + count; b++) {
+        // set write address for single block (CMD24)
+        if (_cmd(24, b * cdv) != 0) {
+            return 1;
+        }
+        
+        // send the data block
+        _write(buffer, 512);
+        buffer += 512;
+    }
+    
+    return 0;
+}
+
+int SDFileSystem::disk_read(uint8_t* buffer, uint32_t block_number, uint32_t count) {
+    if (!_is_initialized) {
+        return -1;
+    }
+    
+    for (uint32_t b = block_number; b < block_number + count; b++) {
+        // set read address for single block (CMD17)
+        if (_cmd(17, b * cdv) != 0) {
+            return 1;
+        }
+        
+        // receive the data
+        _read(buffer, 512);
+        buffer += 512;
+    }
+
+    return 0;
+}
+
+int SDFileSystem::disk_status() {
+    // FATFileSystem::disk_status() returns 0 when initialized
+    if (_is_initialized) {
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+int SDFileSystem::disk_sync() { return 0; }
+uint32_t SDFileSystem::disk_sectors() { return _sectors; }
+
+
+// PRIVATE FUNCTIONS
+int SDFileSystem::_cmd(int cmd, int arg) {
+    _cs = 0;
+
+    // send a command
+    _spi.write(0x40 | cmd);
+    _spi.write(arg >> 24);
+    _spi.write(arg >> 16);
+    _spi.write(arg >> 8);
+    _spi.write(arg >> 0);
+    _spi.write(0x95);
+
+    // wait for the repsonse (response[7] == 0)
+    for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) {
+        int response = _spi.write(0xFF);
+        if (!(response & 0x80)) {
+            _cs = 1;
+            _spi.write(0xFF);
+            return response;
+        }
+    }
+    _cs = 1;
+    _spi.write(0xFF);
+    return -1; // timeout
+}
+int SDFileSystem::_cmdx(int cmd, int arg) {
+    _cs = 0;
+
+    // send a command
+    _spi.write(0x40 | cmd);
+    _spi.write(arg >> 24);
+    _spi.write(arg >> 16);
+    _spi.write(arg >> 8);
+    _spi.write(arg >> 0);
+    _spi.write(0x95);
+
+    // wait for the repsonse (response[7] == 0)
+    for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) {
+        int response = _spi.write(0xFF);
+        if (!(response & 0x80)) {
+            return response;
+        }
+    }
+    _cs = 1;
+    _spi.write(0xFF);
+    return -1; // timeout
+}
+
+
+int SDFileSystem::_cmd58() {
+    _cs = 0;
+    int arg = 0;
+
+    // send a command
+    _spi.write(0x40 | 58);
+    _spi.write(arg >> 24);
+    _spi.write(arg >> 16);
+    _spi.write(arg >> 8);
+    _spi.write(arg >> 0);
+    _spi.write(0x95);
+
+    // wait for the repsonse (response[7] == 0)
+    for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) {
+        int response = _spi.write(0xFF);
+        if (!(response & 0x80)) {
+            int ocr = _spi.write(0xFF) << 24;
+            ocr |= _spi.write(0xFF) << 16;
+            ocr |= _spi.write(0xFF) << 8;
+            ocr |= _spi.write(0xFF) << 0;
+            _cs = 1;
+            _spi.write(0xFF);
+            return response;
+        }
+    }
+    _cs = 1;
+    _spi.write(0xFF);
+    return -1; // timeout
+}
+
+int SDFileSystem::_cmd8() {
+    _cs = 0;
+
+    // send a command
+    _spi.write(0x40 | 8); // CMD8
+    _spi.write(0x00);     // reserved
+    _spi.write(0x00);     // reserved
+    _spi.write(0x01);     // 3.3v
+    _spi.write(0xAA);     // check pattern
+    _spi.write(0x87);     // crc
+
+    // wait for the repsonse (response[7] == 0)
+    for (int i = 0; i < SD_COMMAND_TIMEOUT * 1000; i++) {
+        char response[5];
+        response[0] = _spi.write(0xFF);
+        if (!(response[0] & 0x80)) {
+            for (int j = 1; j < 5; j++) {
+                response[i] = _spi.write(0xFF);
+            }
+            _cs = 1;
+            _spi.write(0xFF);
+            return response[0];
+        }
+    }
+    _cs = 1;
+    _spi.write(0xFF);
+    return -1; // timeout
+}
+
+int SDFileSystem::_read(uint8_t *buffer, uint32_t length) {
+    _cs = 0;
+
+    // read until start byte (0xFF)
+    while (_spi.write(0xFF) != 0xFE);
+
+    // read data
+    for (uint32_t i = 0; i < length; i++) {
+        buffer[i] = _spi.write(0xFF);
+    }
+    _spi.write(0xFF); // checksum
+    _spi.write(0xFF);
+
+    _cs = 1;
+    _spi.write(0xFF);
+    return 0;
+}
+
+int SDFileSystem::_write(const uint8_t*buffer, uint32_t length) {
+    _cs = 0;
+
+    // indicate start of block
+    _spi.write(0xFE);
+
+    // write the data
+    for (uint32_t i = 0; i < length; i++) {
+        _spi.write(buffer[i]);
+    }
+
+    // write the checksum
+    _spi.write(0xFF);
+    _spi.write(0xFF);
+
+    // check the response token
+    if ((_spi.write(0xFF) & 0x1F) != 0x05) {
+        _cs = 1;
+        _spi.write(0xFF);
+        return 1;
+    }
+
+    // wait for write to finish
+    while (_spi.write(0xFF) == 0);
+
+    _cs = 1;
+    _spi.write(0xFF);
+    return 0;
+}
+
+static uint32_t ext_bits(unsigned char *data, int msb, int lsb) {
+    uint32_t bits = 0;
+    uint32_t size = 1 + msb - lsb;
+    for (uint32_t i = 0; i < size; i++) {
+        uint32_t position = lsb + i;
+        uint32_t byte = 15 - (position >> 3);
+        uint32_t bit = position & 0x7;
+        uint32_t value = (data[byte] >> bit) & 1;
+        bits |= value << i;
+    }
+    return bits;
+}
+
+uint32_t SDFileSystem::_sd_sectors() {
+    uint32_t c_size, c_size_mult, read_bl_len;
+    uint32_t block_len, mult, blocknr, capacity;
+    uint32_t hc_c_size;
+    uint32_t blocks;
+
+    // CMD9, Response R2 (R1 byte + 16-byte block read)
+    if (_cmdx(9, 0) != 0) {
+        debug("Didn't get a response from the disk\n");
+        return 0;
+    }
+
+    uint8_t csd[16];
+    if (_read(csd, 16) != 0) {
+        debug("Couldn't read csd response from disk\n");
+        return 0;
+    }
+
+    // csd_structure : csd[127:126]
+    // c_size        : csd[73:62]
+    // c_size_mult   : csd[49:47]
+    // read_bl_len   : csd[83:80] - the *maximum* read block length
+
+    int csd_structure = ext_bits(csd, 127, 126);
+
+    switch (csd_structure) {
+        case 0:
+            cdv = 512;
+            c_size = ext_bits(csd, 73, 62);
+            c_size_mult = ext_bits(csd, 49, 47);
+            read_bl_len = ext_bits(csd, 83, 80);
+
+            block_len = 1 << read_bl_len;
+            mult = 1 << (c_size_mult + 2);
+            blocknr = (c_size + 1) * mult;
+            capacity = blocknr * block_len;
+            blocks = capacity / 512;
+            debug_if(SD_DBG, "\n\rSDCard\n\rc_size: %d \n\rcapacity: %ld \n\rsectors: %lld\n\r", c_size, capacity, blocks);
+            break;
+
+        case 1:
+            cdv = 1;
+            hc_c_size = ext_bits(csd, 63, 48);
+            blocks = (hc_c_size+1)*1024;
+            debug_if(SD_DBG, "\n\rSDHC Card \n\rhc_c_size: %d\n\rcapacity: %lld \n\rsectors: %lld\n\r", hc_c_size, blocks*512, blocks);
+            break;
+
+        default:
+            debug("CSD struct unsupported\r\n");
+            return 0;
+    };
+    return blocks;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem/SDFileSystem.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,89 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2012 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef MBED_SDFILESYSTEM_H
+#define MBED_SDFILESYSTEM_H
+
+#include "mbed.h"
+#include "FATFileSystem.h"
+#include <stdint.h>
+
+/** Access the filesystem on an SD Card using SPI
+ *
+ * @code
+ * #include "mbed.h"
+ * #include "SDFileSystem.h"
+ *
+ * SDFileSystem sd(p5, p6, p7, p12, "sd"); // mosi, miso, sclk, cs
+ *
+ * int main() {
+ *     FILE *fp = fopen("/sd/myfile.txt", "w");
+ *     fprintf(fp, "Hello World!\n");
+ *     fclose(fp);
+ * }
+ */
+class SDFileSystem : public FATFileSystem {
+public:
+
+    /** Create the File System for accessing an SD Card using SPI
+     *
+     * @param mosi SPI mosi pin connected to SD Card
+     * @param miso SPI miso pin conencted to SD Card
+     * @param sclk SPI sclk pin connected to SD Card
+     * @param cs   DigitalOut pin used as SD Card chip select
+     * @param name The name used to access the virtual filesystem
+     */
+    SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, const char* name);
+    virtual int disk_initialize();
+    virtual int disk_status();
+    virtual int disk_read(uint8_t* buffer, uint32_t block_number, uint32_t count);
+    virtual int disk_write(const uint8_t* buffer, uint32_t block_number, uint32_t count);
+    virtual int disk_sync();
+    virtual uint32_t disk_sectors();
+
+protected:
+
+    int _cmd(int cmd, int arg);
+    int _cmdx(int cmd, int arg);
+    int _cmd8();
+    int _cmd58();
+    int initialise_card();
+    int initialise_card_v1();
+    int initialise_card_v2();
+
+    int _read(uint8_t * buffer, uint32_t length);
+    int _write(const uint8_t *buffer, uint32_t length);
+    uint32_t _sd_sectors();
+    uint32_t _sectors;
+
+    void set_init_sck(uint32_t sck) { _init_sck = sck; }
+    // Note: The highest SPI clock rate is 20 MHz for MMC and 25 MHz for SD
+    void set_transfer_sck(uint32_t sck) { _transfer_sck = sck; }
+    uint32_t _init_sck;
+    uint32_t _transfer_sck;
+
+    SPI _spi;
+    DigitalOut _cs;
+    int cdv;
+    int _is_initialized;
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sprites/Sprites.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,282 @@
+
+
+#include "mbed.h"
+#include "N5110.h"
+#include "Gamepad.h"
+
+
+static int three_data[] = {
+
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static int two_data[] = {
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+  
+static int one_data[] = {
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static int breakwhite_data[] = {
+      
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+
+static int breakblack_data[] = {    
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static int arrowup_data[] = {
+    0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 1, 0, 0, 0, 
+    0, 0, 1, 1, 1, 0, 0, 
+    0, 1, 1, 1, 1, 1, 0, 
+    1, 1, 1, 1, 1, 1, 1
+};
+
+
+static int arrowdown_data[] = {
+    1, 1, 1, 1, 1, 1, 1, 
+    0, 1, 1, 1, 1, 1, 0, 
+    0, 0, 1, 1, 1, 0, 0, 
+    0, 0, 0, 1, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,641 @@
+///////// pre-processor directives ////////
+
+#include "mbed.h"
+#include "Gamepad.h"
+#include "N5110.h"
+#include "BreakoutEngine.h"
+#include "Bitmap.h"
+#include "Sprites.h"
+#include "SDFileSystem.h"
+
+#ifdef WITH_TESTING
+# include "tests.h"
+#endif
+
+/////////////// structs /////////////////
+
+struct UserInput {
+    Direction d;
+    float mag;
+};
+
+/////////////// objects ///////////////
+
+N5110 lcd(PTC9,PTC0,PTC7,PTD2,PTD1,PTC11);
+Gamepad pad;
+BreakoutEngine breakout;
+SDFileSystem sd(PTE3, PTE1, PTE2, PTE4, "sd"); // MOSI, MISO, SCK, CS
+Serial serial(USBTX, USBRX);  // for PC debug
+
+///////////// prototypes ///////////////
+
+void init();
+void update_game(UserInput input);
+void render();
+void main_menu();
+void settings();
+void how_to_play();
+void loss_screen();
+void victory_screen();
+void title_screen();
+void main_game();
+void flash_screen(N5110 &lcd);
+void countdown();
+void save_hi_score(int hi_score);
+int get_hi_score();
+bool compare_to_hi_score(int score);
+void print_hi_score(int col,int row);
+void flash_hi_score_screen();
+void reset_victory(int score, int bonus);
+void reset_loss();
+
+Bitmap breakwhite(breakwhite_data, 48, 84);  // assign the title screen sprites
+Bitmap breakblack(breakblack_data, 48, 84);
+Bitmap arrowup(arrowup_data, 5, 7);  // assign the arrow up sprite data
+Bitmap arrowdown(arrowdown_data, 5, 7);  // assign the arrow down sprite data
+Bitmap three(three_data, 48, 84);  // assign the 3 sprite data
+Bitmap two(two_data, 48, 84);  // assign the 2 sprite data
+Bitmap one(one_data, 48, 84);  // assign the 1 sprite data
+
+bool tilt = false;
+float sens = 0.5; // default sens
+int number_of_frames = 0; // tracks the number of frames passed to use for bonus score
+
+///////////// functions ////////////////
+
+int main()
+{
+    
+#ifdef WITH_TESTING
+    int number_of_failures = run_all_tests();
+
+    if(number_of_failures > 0) return number_of_failures;
+#endif
+
+    init();     // initialise the gamepad
+    title_screen();  // run the title screen
+}
+
+
+
+
+void init()  // initialies all classes and libraries
+{
+    // need to initialise LCD and Gamepad
+    lcd.init();
+    pad.init();
+    breakout.init(PADDLE_WIDTH,PADDLE_HEIGHT,BALL_SIZE,BALL_SPEED); // init the objects
+}
+
+void render()  // draws each frame on the LCD
+{
+    // clear screen, re-draw and refresh
+    lcd.clear();
+    breakout.draw(lcd);
+    lcd.refresh();
+}
+
+
+//////////// MENUING & GAME LOOP FUNCTIONS //////////////////
+
+
+void title_screen()
+{
+    tilt = false;   // reset the tilt variable so that on start, joystick is default
+    breakout.reset_mult();
+    lcd.clear();
+
+    lcd.setBrightness(1); // stops the game from dimming (hardware bug?)
+
+    breakblack.render(lcd, 0, 0);  // render the first frame
+    lcd.refresh();
+    pad.leds_off();
+    wait(0.5);
+    
+    pad.reset_flags();
+
+    while (pad.check_event(Gamepad::START_PRESSED) == false) {  // while start is not pressed alternate sprites
+
+        lcd.clear();
+
+        breakwhite.render(lcd, 0, 0);
+        lcd.refresh();
+        pad.leds_on();
+        wait(0.5);
+
+        lcd.clear();
+
+        breakblack.render(lcd, 0, 0);
+        lcd.refresh();
+        pad.leds_off();
+        wait(0.5);
+    }
+
+    pad.tone(750.0,0.3);
+    wait(0.2);
+    main_menu();  // load main menu
+}
+
+
+void main_menu()
+{
+    lcd.setBrightness(1);  // fixes random screen dimming (hardware bug?)
+    
+    lcd.clear();
+    lcd.printString("   START ",0,1);    // start game with default as joystick
+    lcd.printString("   SETTINGS ",0,2);   // choose between joystick and tilt
+    lcd.printString("   HOW TO PLAY ",0,3);  // brief text on how to interact with the game
+    lcd.printString("HI-SCORE: ",0,5);
+    print_hi_score(55,5);
+    lcd.refresh();
+    wait(0.3);  // load initial frame 
+    
+    pad.reset_flags();  // manually resets all button flags which fixes button bounce and prevents inputs carrying through to different menus
+    pad.leds_off(); // prevents hardware bug where right most led flickers
+    
+    int pointer = 1;
+
+    while (pad.check_event(Gamepad::A_PRESSED) == false) {
+        lcd.clear();
+        lcd.printString("   START ",0,1);    // start game with default as joystick
+        lcd.printString("   SETTINGS ",0,2);   // choose between joystick and tilt
+        lcd.printString("   HOW TO PLAY ",0,3);  // brief text on how to interact with the game
+        lcd.printString("HI-SCORE: ",0,5);
+        print_hi_score(55,5);
+        lcd.printString(" >",0,pointer);
+        lcd.refresh();
+        wait(0.1);
+        if (pad.get_direction() == N && pointer > 1) {  // if L is pressed and pointer isnt already on START, move it up one line
+            pointer -= 1;
+            pad.tone(750.0,0.3);
+            wait(0.1);   
+        } else if (pad.get_direction() == S && pointer < 3) {  // if R is pressed and pointer isnt already on HOW TO PLAY, move it down one line
+            pointer += 1;
+            pad.tone(750.0,0.3);
+            wait(0.1);  
+        }
+        if (pad.check_event(Gamepad::X_PRESSED) & pad.check_event(Gamepad::Y_PRESSED)) {
+            save_hi_score(0);  // resets hi score
+        }
+        //printf("Pointer 1 = %d",pointer);
+
+    }
+    if (pointer == 1) {    // if START was selected on exit of the while loop, run main game with the appropriate tilt/joystick setting
+        pad.tone(750.0,0.3);
+        number_of_frames = 0;
+        main_game();
+    } else if (pointer == 2) { // if SETTINGS was selected, enter the settings menu
+        pad.tone(750.0,0.3);
+        settings();
+    } else if (pointer == 3) { // if HOW TO PLAY was selected, display instructions on how to play
+        pad.tone(750.0,0.3);
+        how_to_play();
+    }
+}
+
+void settings()
+{
+    lcd.clear();    // load initial frame
+    lcd.printString("   JOYSTICK ",0,1);    // choose joystick
+    lcd.printString("   TILT ",0,2);   // choose tilt
+    lcd.printString("SENS :",0,4);
+    lcd.drawRect(42,30, 40,10,FILL_TRANSPARENT);
+    lcd.drawRect(42,30,40 * pad.read_pot() + 1,10,FILL_BLACK); // represents the sensitivity which is changed by turning the pot
+    lcd.refresh();
+    wait(0.1);   
+    
+    pad.reset_flags();  // fixes button bounce
+
+    int pointer = 1;  // init the pointer
+
+    while (pad.check_event(Gamepad::B_PRESSED) == false) { // while B is not pressed to return to main menu
+        lcd.clear();
+        lcd.printString("   JOYSTICK ",0,1);    // choose joystick
+        lcd.printString("   TILT ",0,2);   // choose tilt
+        lcd.printString("SENS :",0,4);
+        lcd.printString(" >",0,pointer);
+        lcd.drawRect(42,30, 40,10,FILL_TRANSPARENT);
+        lcd.drawRect(42,30,40 * pad.read_pot() + 1,10,FILL_BLACK); // have it so it fills half the transparent one (default position)
+        lcd.refresh();
+        wait(0.1);
+        if (pad.get_direction() == N && pointer > 1) {  // if L is pressed and pointer isnt already on JOYSTICK, move it up one line
+            pointer -= 1;
+            pad.tone(750.0,0.3);
+            wait(0.1);  
+        } else if (pad.get_direction() == S && pointer < 2) {  // if R is pressed and pointer isnt already on TILT, move it down one line
+            pointer += 1;
+            pad.tone(750.0,0.3);
+            wait(0.1);  
+        }
+
+        if (pad.check_event(Gamepad::A_PRESSED)) { // if A is pressed, switch the tilt option accordingly
+            pad.tone(750.0,0.3);
+            wait(0.1);
+            if (pointer == 1) {  // if A is pressed on JOYSTICK, tilt = false
+                tilt = false;
+            } else if (pointer == 2) { // if A is pressed on TILT, tilt == true
+                tilt = true;
+            }
+        }
+
+        if (pad.check_event(Gamepad::B_PRESSED)) {  // when B is pressed return to main menu
+            pad.tone(750.0,0.3);
+            sens = pad.read_pot() + 0.5f; // sens is set as the pot value at the instant the settings menu is exited +the minimum value of 0.5
+            breakout.set_paddle_motion(tilt,sens); // sets the paddles motion options
+            //printf("Sensitivity = %d",sens);
+            wait(0.3);
+            main_menu();
+        }
+        //printf("Pointer 2 = %d",pointer);
+    }
+}
+
+void how_to_play()  // explains how to interact with the game
+{
+    lcd.clear();
+    lcd.printString("   B = LASER  ",0,0);
+    lcd.printString(" START = PAUSE  ",0,1);
+    lcd.printString("  DESTROY ALL   ",0,2);
+    lcd.printString("    BRICKS. ",0,3);
+    arrowdown.render(lcd, 38, 43);
+    lcd.refresh();
+    wait(0.1);           // load initial frame 
+    
+    pad.reset_flags();  // fixes button bounce
+
+    while (pad.check_event(Gamepad::B_PRESSED) == false) { // while B is not pressed to return to main menu, display instruction on how to interact with the game
+
+        if (pad.get_direction() == S) {
+            lcd.clear();
+            lcd.printString(" CONTINUE TO  ",0,2);
+            lcd.printString("INCREASE SCORE.  ",0,3);
+            lcd.printString(" BONUS SCORE  ",0,4);
+            lcd.printString("BASED ON TIME.  ",0,5);
+            arrowup.render(lcd, 38, 0);
+            lcd.refresh();
+            wait(0.1);
+        }
+        if (pad.get_direction() == N) {
+            lcd.clear();
+            lcd.printString("   B = LASER  ",0,0);
+            lcd.printString(" START = PAUSE  ",0,1);
+            lcd.printString("  DESTROY ALL   ",0,2);
+            lcd.printString("    BRICKS ",0,3);
+            arrowdown.render(lcd, 38, 43);
+            lcd.refresh();
+            wait(0.1);
+        }
+    }
+    wait(0.3);
+    main_menu();  // when B is pressed, the loop is exited and main menu is entered once again
+}
+
+
+void main_game()    // the Game loop
+{   
+    pad.reset_flags();  // fixes button bounce
+    
+    int fps = 8;  // frames per second
+    bool pause = false; // set pause screen to false
+    //printf("Pause = %d",pointer);
+
+    countdown(); // run the countdown
+
+    render();  // first draw the initial frame
+    wait(1.0f/fps);  // and wait for one frame period
+
+
+    // game loop - read input, update the game state and render the display
+    while (1) {
+        breakout.read_input(pad); // read input from pad
+        breakout.update(pad);  // update game
+        lcd.setBrightness(1); // stops the game from dimming (bug)
+        render();  // draw new frame
+
+        if (breakout.check_loss(pad) == true) { // if life lost flash screen
+            flash_screen(lcd);
+        }
+        if (pad.check_event(Gamepad::START_PRESSED)) { // if BACK pressed, toggle pause
+            pause = !pause;
+            pad.reset_flags();  // fixes button bounce?
+        }
+
+        while (pause == true) { // if pause is true, display pause screen
+            lcd.clear();
+            lcd.printString("    PAUSED ",0,1);
+            lcd.printString(" START = PLAY",0,3);
+            lcd.printString("  BACK = MENU",0,4);
+            lcd.refresh();
+            wait(0.1);
+            if (pad.check_event(Gamepad::START_PRESSED)) { // if START pressed, toggle pause, leaving pause screen
+                pause = !pause;
+            }
+            if (pad.check_event(Gamepad::BACK_PRESSED)) { // if BACK pressed, return to the title screen
+                reset_loss();
+                title_screen();
+            }
+        }
+
+        if (breakout.get_paddle_lives() == 0) { // when all lives lost, enter loss screen
+            pad.leds_off(); //turns last led off (doesnt run through and update board so last led doent turn off via lives leds
+            loss_screen();
+        }
+
+        if (breakout.get_num_left() == 0) { // when all bricks destroyed, display victory screen
+            victory_screen();
+        }
+        number_of_frames ++; // track the number of frames passed to add to the score (inversely proportional)
+        wait(1.0f/fps); // wait for 1 frame
+    }
+}
+
+
+void loss_screen()   // loss screen when lives of paddle == 0
+{
+    if (compare_to_hi_score(breakout.get_score()) == true) {  // little hi-score sequence if current score is higher than prev
+        flash_hi_score_screen();
+    }
+
+    lcd.clear();
+    char buffer[14]; // init the buffer
+    sprintf(buffer,"%2d",breakout.get_score());  // print the score to the buffer
+    lcd.printString(buffer,WIDTH/2 - 12,2);  // print buffer on lcd
+    lcd.printString("   RESTART? ",2,1);
+    lcd.printString("  PRESS START ",1,4);
+    lcd.refresh();
+    pad.tone(750.0,0.3);
+    wait(0.4);
+
+    pad.tone(300.0,0.3);
+    wait(0.4);
+    
+    pad.reset_flags();
+
+    while (pad.check_event(Gamepad::START_PRESSED) == false) {  // flashes the score, waits for START to return to title screen
+        lcd.clear();
+
+        lcd.printString("   RESTART? ",2,1);
+        lcd.printString("  PRESS START ",1,4);
+        lcd.refresh();
+
+        wait(0.4);
+
+        lcd.clear();
+
+        lcd.printString(buffer,WIDTH/2 - 12,2);
+        lcd.printString("   RESTART? ",2,1);
+        lcd.printString("  PRESS START ",1,4);
+        lcd.refresh();
+
+        wait(0.4);
+    }
+    reset_loss();
+    title_screen();
+}
+
+
+void victory_screen() // victory screen when all bricks are destroyed
+{
+    int bonus = NULL;
+
+    if (breakout.get_score()/2 - ((breakout.get_score()/2) * number_of_frames)/720 > 0) {   // beat within 90 seconds (8 fps) and you get a bonus
+        bonus = breakout.get_score()/2 - ((breakout.get_score()/2) * number_of_frames)/720;
+    } else {
+        bonus = 0; // else you get no bonus
+    }
+
+    if (compare_to_hi_score(bonus+breakout.get_score()) == true) {   // little hi-score sequence if current score is higher than prev
+        flash_hi_score_screen();
+    }
+
+    lcd.clear();
+
+    char buffer1[14];
+    sprintf(buffer1,"%2d",breakout.get_score());
+    lcd.printString(buffer1,WIDTH/2 - 12,2);
+
+    char buffer2[14];
+    sprintf(buffer2,"%2d",bonus);
+    lcd.printString(buffer2,WIDTH/2 + 12,3);
+    lcd.printString(" BONUS: ",2,3);
+
+    lcd.printString("   VICTORY! ",2,0);
+    lcd.printString(" CONT = START ",0,4);
+    lcd.printString("  MENU = BACK ",0,5);
+    lcd.refresh();
+    pad.tone(2500.0,0.1);
+    wait(0.4);
+
+    pad.tone(2500.0,0.1);
+    wait(0.2);
+
+    pad.tone(4000.0,0.6);
+    wait(0.6);
+    
+    pad.reset_flags();
+
+    while (pad.check_event(Gamepad::START_PRESSED) || pad.check_event(Gamepad::BACK_PRESSED) == false) {  // while neither START or BACK is pressed, flash the score and display options
+        lcd.clear();
+
+        lcd.printString("   VICTORY! ",2,0);     
+        lcd.printString(" CONT = START ",0,4);
+        lcd.printString("  MENU = BACK ",0,5);
+
+        lcd.refresh();
+
+        wait(0.4);
+
+        lcd.clear();
+
+        lcd.printString(buffer1,WIDTH/2 - 12,2);
+        lcd.printString(buffer2,WIDTH/2 + 12,3);
+        lcd.printString(" BONUS:",2,3);
+
+        lcd.printString("   VICTORY! ",2,0);
+        lcd.printString(" CONT = START ",0,4);
+        lcd.printString("  MENU = BACK ",0,5);
+        lcd.refresh();
+
+        pad.tone(2500.0,0.1);
+
+        lcd.refresh();
+
+        wait(0.4);
+
+        if (pad.check_event(Gamepad::START_PRESSED)) {
+            reset_victory(breakout.get_score(),bonus);  // reset game
+            main_game();  // reload game
+        } else if (pad.check_event(Gamepad::BACK_PRESSED)) {
+            reset_loss();    // reset game
+            title_screen();  // load title screen
+        } 
+    }
+}
+
+
+void flash_screen(N5110 &lcd)   // flash the screen when a life is lost
+{
+    lcd.setBrightness(0);
+    wait(0.1);
+    lcd.setBrightness(1);
+    wait(0.1);
+    lcd.setBrightness(0);
+    wait(0.1);
+    lcd.setBrightness(1);
+    wait(0.1);
+    lcd.setBrightness(0);
+    wait(0.1);
+    lcd.setBrightness(1);
+    wait(0.1);
+    lcd.setBrightness(0);
+    wait(0.1);
+    lcd.setBrightness(1);
+}
+
+void countdown()       // draw the countdown
+{
+    lcd.setBrightness(1); // stops the game from dimming (bug)
+
+    lcd.clear();
+    three.render(lcd, 0, 0);   // render the 3
+    lcd.refresh();
+    pad.tone(500.0,0.5);
+    wait(1);    // wait 1 second
+
+    lcd.setBrightness(1);
+
+    lcd.clear();
+    two.render(lcd, 0, 0);   // render 2
+    lcd.refresh();
+    pad.tone(500.0,0.5);
+    wait(1);    // wait 1 second
+
+    lcd.setBrightness(1);
+
+    lcd.clear();
+    one.render(lcd, 0, 0);  // render 1
+    lcd.refresh();
+    pad.tone(1000.0,1);
+    wait(1);     // wait 1 second
+}
+
+
+//////////////// SD CARD FUNCTIONS ////////////////////////
+
+
+void save_hi_score(int hi_score)  // save score to SD card
+{
+    serial.baud(115200);  // max speed
+    FILE *fp; // file pointer
+    fp = fopen("/sd/hi_score.txt", "w");
+    if (fp == NULL) {  // if it can't open the file then print error message
+        serial.printf("Error\n");
+    } else {  // opened file so can write
+        fprintf(fp, "%d",hi_score);
+        serial.printf("Written to file.\n");
+        fclose(fp);  // close the file after writing
+    }
+}
+
+
+int get_hi_score()  // retrieve score from SD card
+{
+    serial.baud(115200); // max speed
+    FILE *fp;
+    fp = fopen("/sd/hi_score.txt", "r");
+    int stored_hi_score = NULL;
+    if (fp == NULL) {  // if it can't open the file then print error message
+        serial.printf("Error\n");
+    } else {  // opened file so can write
+        fscanf(fp, "%d",&stored_hi_score);
+        serial.printf("Read %d from file.\n",stored_hi_score);
+        fclose(fp);  // close the file after reading
+    }
+    return stored_hi_score;  // returns the stored hi score
+}
+
+
+bool compare_to_hi_score(int score)  // returns true if new score higher than current stored hi score
+{
+    if (score >= get_hi_score()) {
+        //printf("hi score! \n");
+        save_hi_score(score);  // writes the new score to the SD card
+        return true;
+    } else {
+        return false;
+    }
+}
+
+
+void print_hi_score(int col,int row)  // print hi score to lcd at specified location
+{
+    char buffer[14];  // creates buffer
+    sprintf(buffer,"%2d",get_hi_score()); // puts hi-score in buffer
+    lcd.printString(buffer,col,row);   // prints buffer to the screen
+
+}
+
+
+void reset_loss()  // reset the game when returning to title screen from any point
+{
+    breakout.reset_paddle_lives(); // resets lives back to 6
+    breakout.set_prev_score(0);   // resets prev score to 0
+    number_of_frames = 0;  // reset the number of frames
+    breakout.reset_mult(); // reset multiplier
+    breakout.reset_game(); // return game to initial positions
+}
+
+
+void reset_victory(int score, int bonus)  // reset the game after a victory
+{
+    breakout.set_prev_score(score + bonus);  // saves score
+    number_of_frames = 0;  // reset the number of frames
+    breakout.inc_mult();  // increment multiplier
+    breakout.reset_game(); // return game to initial positions
+}
+
+
+void flash_hi_score_screen()  // flash the hi score, called when hi score acheieved
+{
+    lcd.clear();
+    lcd.printString("      NEW ",0,2);
+    lcd.printString("   HI-SCORE! ",0,3);
+    lcd.refresh();
+
+    pad.tone(2500.0,0.2);
+    wait(0.2);
+
+    pad.tone(4000.0,0.4);
+    wait(1);
+
+    lcd.clear();
+    print_hi_score(30,3);
+    lcd.refresh();
+
+    pad.tone(2500.0,0.2);
+    wait(0.2);
+
+    pad.tone(4000.0,0.4);
+    wait(1);
+
+    lcd.clear();
+    lcd.printString("      NEW ",0,2);
+    lcd.printString("   HI-SCORE! ",0,3);
+    lcd.refresh();
+
+    pad.tone(2500.0,0.2);
+    wait(0.2);
+
+    pad.tone(4000.0,0.4);
+    wait(1);
+
+    lcd.clear();
+    print_hi_score(30,3);
+    lcd.refresh();
+
+    pad.tone(2500.0,0.2);
+    wait(0.2);
+
+    pad.tone(4000.0,0.4);
+    wait(1.3);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/176b8275d35d
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests.h	Tue Jan 05 01:14:11 2021 +0000
@@ -0,0 +1,67 @@
+#ifndef TESTS_H
+#define TESTS_H
+
+#include "Ball-test.h"
+#include "Brick-test.h"
+#include "Laser-test.h"
+
+/**
+ * @brief Run unit tests for relevant objects in the Breakout game
+ *
+ * @returns The number of failed tests
+ */
+int run_all_tests()
+{
+    int n_tests_failed = 0; // initialised at 0
+
+    // Run the Ball_test_movement test
+    printf("Testing Ball_test_movement...\n");
+    bool test_passed1 = Ball_test_movement();
+
+    // Print out the result of this test
+    if (test_passed1) {
+        printf("...Passed!\n");
+    }
+    else {
+        printf("...FAILED!\n");
+        ++n_tests_failed; // Increment number of failures
+    }
+    
+    // Run the Ball_test_movement test
+    printf("Testing Brick_test_movement...\n");
+    bool test_passed2 = Brick_test_movement();
+
+    // Print out the result of this test
+    if (test_passed2) {
+        printf("...Passed!\n");
+    }
+    else {
+        printf("...FAILED!\n");
+        ++n_tests_failed; // Increment number of failures
+    }
+    
+    // Run the Ball_test_movement test
+    printf("Testing Laser_test_movement...\n");
+    bool test_passed3 = Laser_test_movement();
+
+    // Print out the result of this test
+    if (test_passed3) {
+        printf("...Passed!\n");
+    }
+    else {
+        printf("...FAILED!\n");
+        ++n_tests_failed; // Increment number of failures
+    }
+
+    // Finish by printing a summary of the tests
+    if (n_tests_failed > 0) {
+        printf("%d tests FAILED!\n", n_tests_failed);
+    }
+    else {
+        printf("All tests passed!\n");
+    }
+
+    return n_tests_failed;
+}
+
+#endif
\ No newline at end of file