// This code is provided as an example only and is not guaranteed by FTDI. FTDI accept no responsibility for any 
// issues resulting from its use. The developer of the final application incorporating any parts of this sample 
// project is responsible for ensuring its safe and correct operation and for any conequences resulting from its use.


#include "derivative.h" /* include peripheral declarations */
#include "FT_Gpu.h" 




// =======================================================================
// Function Declarations
// =======================================================================


// ******* FT800 SPI Functions *******

// Functions to specify the address before a read or write
void FT800_SPI_SendAddressWR(dword);
void FT800_SPI_SendAddressRD(dword);

// Functions to Read or Write data
byte FT800_SPI_Read8(void);
dword FT800_SPI_Read32(void);
void FT800_SPI_Write32(dword);
void FT800_SPI_Write16(unsigned int);
void FT800_SPI_Write8(byte);

// Functions specifically for host commands to the FT800
void FT800_SPI_HostCommand(byte);
void FT800_SPI_HostCommandDummyRead(void);

// Functions for creating Command Lists for the Co-Processor
unsigned int FT800_IncCMDOffset(unsigned int, byte);

// ******* Hardware Specific Functions *******

// MCU-specific functions
void HAL_Configure_MCU(void);
byte HAL_SPI_ReadWrite(byte);
void HAL_SPI_CSLow(void);
void HAL_SPI_CSHigh(void);
void HAL_SPI_PDlow(void);
void HAL_SPI_PDhigh(void);

// General functions
void Delay(void);


// =======================================================================
// Variable Declarations
// =======================================================================

byte chipid = 0;                        // Holds value of Chip ID read from the FT800

dword cmdBufferRd = 0x00000000;         // Store the value read from the REG_CMD_READ register
dword cmdBufferWr = 0x00000000;         // Store the value read from the REG_CMD_WRITE register

   
   
   
   
   
   
   
   
// ############################################################################################################
// User Application - Initialization of MCU / FT800 / Display
// ############################################################################################################    
    
void main(void) 
{
     
    unsigned int CMD_Offset = 0;
   
    // =======================================================================
    // MCU Configuration (specific to the MCU used)
    // =======================================================================
   
    HAL_Configure_MCU();

    // =======================================================================
    // Initial FT800 Configuration
    // =======================================================================
    
    // Note: Keep SPI below 11MHz here 
        
    HAL_SPI_PDhigh();                               // Put the Power Down # pin high to wake FT800

    Delay();                                        // Delay for power up of regulator
               
    FT800_SPI_HostCommandDummyRead();               // Read location 0 to wake up FT800
        
    FT800_SPI_HostCommand(FT_GPU_EXTERNAL_OSC);     // Change the PLL to external clock - optional
         
    FT800_SPI_HostCommand(FT_GPU_PLL_48M);          //  Ensure configured to 48 MHz
 
    // Note: Could now increase SPI clock rate up to 30MHz SPI if preferred
 
    Delay();                                        // Delay 
        
    FT800_SPI_HostCommand(FT_GPU_CORE_RESET);       // Reset the core
 
    Delay();                                        // Delay 
         
    FT800_SPI_HostCommand(FT_GPU_ACTIVE_M);         // Read address 0 to ensure FT800 is active
  
    chipid = 0x00;                                  // Read the Chip ID to check comms with the FT800 - should be 0x7C
      while(chipid != 0x7C)                       
   {
        HAL_SPI_CSLow();                            // CS low
        FT800_SPI_SendAddressRD(REG_ID);            // Send the address
        chipid = FT800_SPI_Read8();                 // Read the actual value
        HAL_SPI_CSHigh();                           // CS high
   }
    
   
    // =======================================================================
    // Write the display registers on the FT800 for your particular display
    // =======================================================================
    
    HAL_SPI_CSLow();                                // CS low                   Write REG_HCYCLE to 548
    FT800_SPI_SendAddressWR(REG_HCYCLE);            // Send the address
    FT800_SPI_Write16(548);                         // Send the 16-bit value
    HAL_SPI_CSHigh();                               // CS high

    HAL_SPI_CSLow();                                // CS low                   Write REG_HOFFSET to 43
    FT800_SPI_SendAddressWR(REG_HOFFSET);           // Send the address
    FT800_SPI_Write16(43);                          // Send the 16-bit value
    HAL_SPI_CSHigh();                               // CS high

    HAL_SPI_CSLow();                                // CS low                   Write REG_HSYNC0 to 0
    FT800_SPI_SendAddressWR(REG_HSYNC0);            // Send the address
    FT800_SPI_Write16(0);                           // Send the 16-bit value
    HAL_SPI_CSHigh();                               // CS high
     
    HAL_SPI_CSLow();                                // CS low                   Write REG_HSYNC1 to 41
    FT800_SPI_SendAddressWR(REG_HSYNC1);            // Send the address
    FT800_SPI_Write16(41);                          // Send the 16-bit value
    HAL_SPI_CSHigh();                               // CS high
   
    HAL_SPI_CSLow();                                // CS low                   Write REG_VCYCLE to 292
    FT800_SPI_SendAddressWR(REG_VCYCLE);            // Send the address
    FT800_SPI_Write16(292);                         // Send the 16-bit value
    HAL_SPI_CSHigh();                               // CS high

    HAL_SPI_CSLow();                                // CS low                   Write REG_VOFFSET to 12
    FT800_SPI_SendAddressWR(REG_VOFFSET);           // Send the address
    FT800_SPI_Write16(12);                          // Send the 16-bit value
    HAL_SPI_CSHigh();                               // CS high

    HAL_SPI_CSLow();                                // CS low                   Write REG_VSYNC0 to 0
    FT800_SPI_SendAddressWR(REG_VSYNC0);            // Send the address
    FT800_SPI_Write16(0);                           // Send the 16-bit value
    HAL_SPI_CSHigh();                               // CS high
        
    HAL_SPI_CSLow();                                // CS low                   Write REG_VSYNC1 to 10
    FT800_SPI_SendAddressWR(REG_VSYNC1);            // Send the address
    FT800_SPI_Write16(10);                          // Send the 16-bit value
    HAL_SPI_CSHigh();                               // CS high

    HAL_SPI_CSLow();                                // CS low                   Write REG_SWIZZLE to 2
    FT800_SPI_SendAddressWR(REG_SWIZZLE);           // Send the address
    FT800_SPI_Write16(2);                           // Send the 16-bit value
    HAL_SPI_CSHigh();                               // CS high

    HAL_SPI_CSLow();                                // CS low                   Write REG_PCLK_POL to 1
    FT800_SPI_SendAddressWR(REG_PCLK_POL);          // Send the address
    FT800_SPI_Write16(1);                           // Send the 16-bit value
    HAL_SPI_CSHigh();                               // CS high
        
    HAL_SPI_CSLow();                                // CS low                   Write REG_HSIZE to 480
    FT800_SPI_SendAddressWR(REG_HSIZE);             // Send the address
    FT800_SPI_Write16(480);                         // Send the 16-bit value
    HAL_SPI_CSHigh();                               // CS high
        
    HAL_SPI_CSLow();                                // CS low                   Write REG_VSIZE to 272
    FT800_SPI_SendAddressWR(REG_VSIZE);             // Send the address
    FT800_SPI_Write16(272);                         // Send the 16-bit value
    HAL_SPI_CSHigh();                               // CS high
    
    HAL_SPI_CSLow();                                // CS low                   Write REG_PCLK to 5
    FT800_SPI_SendAddressWR(REG_PCLK);              // Send the address
    FT800_SPI_Write16(5);                           // Send the 16-bit value
    HAL_SPI_CSHigh();                               // CS high   
    
    
    // =======================================================================
    // Configure the touch screen
    // =======================================================================
    
    HAL_SPI_CSLow();                                // CS low                   Write the touch threshold
    FT800_SPI_SendAddressWR(REG_TOUCH_RZTHRESH);    // Send the address
    FT800_SPI_Write16(1200);                        // Send the 16-bit value 
    HAL_SPI_CSHigh();                               // CS high
    
    
    // =======================================================================
    // Send an initial display list which will clear the screen to a black colour
    // ======================================================================= 
    
    // Set the colour which is used when the colour buffer is cleared        
    HAL_SPI_CSLow();                                //
    FT800_SPI_SendAddressWR(RAM_DL + 0);            // Write first entry in Display List (first location in DL Ram)
    FT800_SPI_Write32(0x02000000);                  // Clear Color RGB   00000010 BBBBBBBB GGGGGGGG RRRRRRRR  (B/G/R = Colour values)
    HAL_SPI_CSHigh();                               //

    // Clear the Colour, Stencil and Tag buffers. This will set the screen to the 'clear' colour set above.
    HAL_SPI_CSLow();                                //
    FT800_SPI_SendAddressWR(RAM_DL + 4);            // Write next entry in display list (each command is 4 bytes)
    FT800_SPI_Write32(0x26000007);                  // Clear 00100110 -------- -------- -----CST  (C/S/T define which parameters to clear)
    HAL_SPI_CSHigh();                               //
                   
    // Display command ends the display list
    HAL_SPI_CSLow();                                //
    FT800_SPI_SendAddressWR(RAM_DL + 8);            // Write next entry in display list 
    FT800_SPI_Write32(0x00000000);                  // DISPLAY command 00000000 00000000 00000000 00000000
    HAL_SPI_CSHigh();                               //
                        
    // Writing to the DL_SWAP register tells the Display Engine to render the new screen designed above.
    HAL_SPI_CSLow();                                //         
    FT800_SPI_SendAddressWR(REG_DLSWAP);            // Writing to the DL_SWAP register...value 10 means render after last frame complete        
    FT800_SPI_Write32(0x00000002);                  // 00000000 00000000 00000000 000000SS  (SS bits define when render occurs)
    HAL_SPI_CSHigh();                               // CS high
       
    
    
    // =======================================================================
    // Set the GPIO pins of the FT800 to enable the display now
    // =======================================================================
    
    // note: Refer to the GPIO section in the FT800 datasheet and also the connections to the GPIO
    // used on the particular development module being used
    
    // Bit 7 enables the LCD Display. It is set to output driving high
    // Bit 1 enables the Audio Amplifier. It is set to output driving low to shut down the amplifier since audio not used here
    // Bit 0 is unused but set to output driving high.
    
    HAL_SPI_CSLow();                                // CS low                   Write REG_GPIO_DIR
    FT800_SPI_SendAddressWR(REG_GPIO_DIR);          // Send the address
    FT800_SPI_Write8(0x83);                         // Send the 8-bit value
    HAL_SPI_CSHigh();                               // CS high
        
    HAL_SPI_CSLow();                                // CS low                   Write REG_GPIO
    FT800_SPI_SendAddressWR(REG_GPIO);              // Send the address
    FT800_SPI_Write8(0x81);                         // Send the 8-bit value
    HAL_SPI_CSHigh();                               // CS high

    
    // =======================================================================
    // Delay before we begin to display anything
    // =======================================================================
        
    Delay();                                        // Delay 
    Delay();                                        // Delay 
    Delay();                                        // Delay 
    Delay();                                        // Delay 
    Delay();                                        // Delay 
    Delay();                                        // Delay   
    
// ############################################################################################################
// ############################################################################################################  
    
    
    
    
    
    
    
    
    
    
    
    
      
// ############################################################################################################
// Main Program - User Application
// ############################################################################################################ 
    
    while(1)
    {
                 
       
        // ================================================================================================================
        // Generate the Co-Processor commands for the FIRST screen
        // ================================================================================================================ 
    
        // Wait for the Graphics Engine to become idle by reading the REG_CMD_WRITE and REG_CMD_READ buffers 
        // REG_CMD_WRITE will show the address in the FIFO of the last command entered
        // REG_CMD_READ shows the address of the command at which the Graphics Engine has reached
        // When equal, this means that the Graphics Engine has finished executing the last command list
        
        // Since the FIFO is circular and the READ pointer equals the WRITE pointer, we can assume we have the full 
        // 4096 bytes of the FIFO to write this next command list.

        do
        {
            HAL_SPI_CSLow();                            //
            FT800_SPI_SendAddressRD(REG_CMD_WRITE);     // 
            cmdBufferWr = FT800_SPI_Read32();           // Read the value of the REG_CMD_WRITE register
            HAL_SPI_CSHigh();                           //
       
            HAL_SPI_CSLow();                            //
            FT800_SPI_SendAddressRD(REG_CMD_READ);      // 
            cmdBufferRd = FT800_SPI_Read32();           // Read the value of the REG_CMD_READ register  
            HAL_SPI_CSHigh();                           //
        } while(cmdBufferWr != cmdBufferRd); 
        
               
        // Check the actual value of the current WRITE register (pointer)
        
        CMD_Offset = cmdBufferWr;                       // Get the current value of the CMD_WRITE pointer in the FIFO
                     
                       
        // ************************** This is the actual list of commands to draw the screen ***********************        
        
        // First of all, add the DL_Start command to indicate that we are creating a new display list
        // We write this first entry to the location pointed to by CMD_WRITE within the FIFO
        
        HAL_SPI_CSLow();                                //
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)
        FT800_SPI_Write32(0xFFFFFF00);                  // Write the DL_START command 
        HAL_SPI_CSHigh();                               //
        
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 
        
        // Next, define the background colour as black and clear the display parameters. This will produce a blank black screen. 
        
        HAL_SPI_CSLow();                                //
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)      
        FT800_SPI_Write32(0x02000000);                  // Clear Color RGB   00000010 BBBBBBBB GGGGGGGG RRRRRRRR  (B/G/R = Colour values)
        HAL_SPI_CSHigh();                               //

        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 
        
        HAL_SPI_CSLow();                                //
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)    
        FT800_SPI_Write32(0x26000007);                  // Clear 00100110 -------- -------- -----CST  (C/S/T define which parameters to clear)
        HAL_SPI_CSHigh();                               //
        
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 
      
        // Next, set the colour (white) and point radius (0xFF / 16 pixels) for any graphics objects which will follow
        
        HAL_SPI_CSLow();                                //
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)  
        FT800_SPI_Write32(0x04FFFFFF);                  // Color RGB   00000100 BBBBBBBB GGGGGGGG RRRRRRRR  (B/G/R = Colour values)
        HAL_SPI_CSHigh();                               //
    
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 
    
        HAL_SPI_CSLow();                                //
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)  
        FT800_SPI_Write32(0x0D0000FF);                  // Point Size  00001101 00000000 SSSSSSSS SSSSSSSS  (S = Size value)
        HAL_SPI_CSHigh();                               //
    
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 
    
        // Next, we use the Begin command to tell the Grahics Engine that we are going to draw point(s) 
        
        HAL_SPI_CSLow();                                //
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)         
        FT800_SPI_Write32(0x1F000002);                  // Begin  00011111 00000000 00000000 0000PPPP  (P = Primitive code)     
        HAL_SPI_CSHigh();                               //
    
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 
        
        // The Vertex command places the graphics primitive (this time a point, as selected in 'Begin' above) at the desired coordinate

        HAL_SPI_CSLow();                                //
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)  
        FT800_SPI_Write32(0x43000880);                  // Vertex 2F    01XXXXXX XXXXXXXX XYYYYYYY YYYYYYYY        
        HAL_SPI_CSHigh();                               //
    
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 

        // We now add the End command to tell the Graphics Engine that we are finished drawing the primitive specified in 'Begin' above             

        HAL_SPI_CSLow();                                //
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)        
        FT800_SPI_Write32(0x21000000);                  // End        
        HAL_SPI_CSHigh();                               //
        
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 

        // The Display command marks the end of this set of commands which will draw the next screen 


        HAL_SPI_CSLow();                                //
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)  
        FT800_SPI_Write32(0x00000000);                  // Display
        HAL_SPI_CSHigh();                               //
    
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 

        // The Swap command tells the Graphics Engine to draw this new screen as soon as the last display has finished being scanned out       


        HAL_SPI_CSLow();                                //
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)  
        FT800_SPI_Write32(0xFFFFFF01);                  // Swap
        HAL_SPI_CSHigh();                               // 
    
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 

        
        // ************************** This is the end of the list of commands to draw the screen ***********************        

        // Currently, CMD_WRITE still equals CMD_READ.  The FT800 has still not executed any of our new commands.
        // We now write the CMD_WRITE register to tell the FT800 where the end of our new command list is
        // The FT800 will now work it's way through our command list until CMD_READ equals our new CMD_WRITE
                
        HAL_SPI_CSLow();                                //
        FT800_SPI_SendAddressWR(REG_CMD_WRITE);         // 
        FT800_SPI_Write16(CMD_Offset);                  //    
        HAL_SPI_CSHigh();                               //
        
        // ================================================================================================================
        // End of FIRST screen
        // ================================================================================================================ 
        
               

        
        Delay();                                        // Delay to let the user view the screen above
        Delay();                                        // Delay 
        Delay();                                        // Delay 
        Delay();                                        // Delay 
        Delay();                                        // Delay 
        Delay();                                        // Delay 
        
        
               
        
        
        // ================================================================================================================
        // Generate the Co-Processor commands for the SECOND screen
        // ================================================================================================================ 
    
        // Wait for the Graphics Engine to become idle by reading the REG_CMD_WRITE and REG_CMD_READ buffers 
        // REG_CMD_WRITE will show the address in the FIFO of the last command entered
        // REG_CMD_READ shows the address of the command at which the Graphics Engine has reached
        // When equal, this means that the Graphics Engine has finished executing the last command list
        
        // Since the FIFO is circular and the READ pointer equals the WRITE pointer, we can assume we have the full 
        // 4096 bytes of the FIFO to write this next command list.

        do
        {
            HAL_SPI_CSLow();                            //
            FT800_SPI_SendAddressRD(REG_CMD_WRITE);     // 
            cmdBufferWr = FT800_SPI_Read32();           // Read the vaulue of the REG_CMD_WRITE register
            HAL_SPI_CSHigh();                           //
       
            HAL_SPI_CSLow();                            //
            FT800_SPI_SendAddressRD(REG_CMD_READ);      // 
            cmdBufferRd = FT800_SPI_Read32();           // Read the vaulue of the REG_CMD_READ register  
            HAL_SPI_CSHigh();                           //
        } while(cmdBufferWr != cmdBufferRd); 
        
               
        // Check the actual value of the current WRITE register (pointer)
        
        CMD_Offset = cmdBufferWr;                       // Get the current value of the CMD_WRITE pointer in the FIFO
                     
                       
        // ************************** This is the actual list of commands to draw the screen ***********************        
        
        // First of all, add the DL_Start command to indicate that we are creating a new display list
        // We write this first entry to the location pointed to by CMD_WRITE within the FIFO
        
        HAL_SPI_CSLow();
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)
        FT800_SPI_Write32(0xFFFFFF00);                  // Write the DL_START command 
        HAL_SPI_CSHigh();
        
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 
        
        // Next, define the background colour as black and clear the display parameters. This will produce a blank black screen. 
        
        HAL_SPI_CSLow();
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)      
        FT800_SPI_Write32(0x02000000);                  // Clear Color RGB   00000010 BBBBBBBB GGGGGGGG RRRRRRRR  (B/G/R = Colour values)
        HAL_SPI_CSHigh();   

        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 
        
        HAL_SPI_CSLow();
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)    
        FT800_SPI_Write32(0x26000007);                  // Clear 00100110 -------- -------- -----CST  (C/S/T define which parameters to clear)
        HAL_SPI_CSHigh();      
        
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 
      
        // Next, set the colour (white) and point radius (0xFF / 16 pixels) for any graphics objects which will follow
        
        HAL_SPI_CSLow();
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)  
        FT800_SPI_Write32(0x040000FF);                  // Color RGB   00000100 BBBBBBBB GGGGGGGG RRRRRRRR  (B/G/R = Colour values)
        HAL_SPI_CSHigh(); 
    
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 
    
        HAL_SPI_CSLow();
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)  
        FT800_SPI_Write32(0x0D0000FF);                  // Point Size  00001101 00000000 SSSSSSSS SSSSSSSS  (S = Size value)
        HAL_SPI_CSHigh(); 
    
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 
    
        // Next, we use the Begin command to tell the Graphics Engine that we are going to draw point(s) 
        
        HAL_SPI_CSLow();
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)         
        FT800_SPI_Write32(0x1F000002);                  // Begin  00011111 00000000 00000000 0000PPPP  (P = Primitive code)     
        HAL_SPI_CSHigh();
    
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 
        
        // The Vertex command places the graphics primitive (this time a point, as selected in 'Begin' above) at the desired coordinate

        HAL_SPI_CSLow();
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)  
        FT800_SPI_Write32(0x43000880);                  // Vertex 2F    01XXXXXX XXXXXXXX XYYYYYYY YYYYYYYY        
        HAL_SPI_CSHigh();
    
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 

        // We now add the End command to tell the Graphics Engine that we are finished drawing the primitive specified in 'Begin' above             

        HAL_SPI_CSLow();
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)        
        FT800_SPI_Write32(0x21000000);                  // End        
        HAL_SPI_CSHigh();
        
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 

        // The Display command marks the end of this set of commands which will draw the next screen 


        HAL_SPI_CSLow();
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)  
        FT800_SPI_Write32(0x00000000);                  // Display
        HAL_SPI_CSHigh();  
    
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 

        // The Swap command tells the Graphics Engine to draw this new screen as soon as the last display has finished being scanned out       


        HAL_SPI_CSLow();
        FT800_SPI_SendAddressWR(RAM_CMD + CMD_Offset);  // Writing to next available location in FIFO (FIFO base address + offset)  
        FT800_SPI_Write32(0xFFFFFF01);                  // Swap
        HAL_SPI_CSHigh();          
    
        CMD_Offset = FT800_IncCMDOffset(CMD_Offset, 4); // Move the CMD Offset since we have just added 4 bytes to the FIFO 

        
        // ************************** This is the end of the list of commands to draw the screen ***********************        

        // Currently, CMD_WRITE still equals CMD_READ.  The FT800 has still not executed any of our new commands.
        // We now write the CMD_WRITE register to tell the FT800 where the end of our new command list is
        // The FT800 will now work it's way through our command list until CMD_READ equals our new CMD_WRITE
                
        HAL_SPI_CSLow();
        FT800_SPI_SendAddressWR(REG_CMD_WRITE);         // 
        FT800_SPI_Write16(CMD_Offset);                  //    
        HAL_SPI_CSHigh();
        
        // ================================================================================================================
        // End of SECOND screen
        // ================================================================================================================        
        
        
        
        
        Delay();                                        // Delay to let the user view the screen above
        Delay();                                        // Delay 
        Delay();                                        // Delay 
        Delay();                                        // Delay 
        Delay();                                        // Delay 
        Delay();                                        // Delay 
    }
}

// ############################################################################################################
// ############################################################################################################ 











        
        
        
       
    

// ############################################################################################################
// FT800 SPI Functions 
// ############################################################################################################  



// ============================================================================================================
// Send address of a register which is to be Written
// ============================================================================================================

// This function sends an address of a register which is to be written. It sets the MSBs to 10 to indicate a write  
//
// Pass in:         00000000 xxxxxxxx yyyyyyyy zzzzzzzz     address as double word 32-bit value, only lower 3 bytes used
//
// Sends:           10xxxxxx                                bits 23 - 16 of the address, with bit 23/22 forced to 10
//                  yyyyyyyy                                bits 15 - 8  of the address
//                  zzzzzzzz                                bits 7 -  0  of the address
//
// Returns:         N/A

void FT800_SPI_SendAddressWR(dword Memory_Address)
{
    byte SPI_Writebyte = 0x00;
    
    // Write out the address. Only the lower 3 bytes are sent, with the most significant byte sent first
    SPI_Writebyte = ((Memory_Address & 0x00FF0000) >> 16);  // Mask off the first byte to send
    SPI_Writebyte = (SPI_Writebyte & 0xBF | 0x80);          // Since this is a write, the MSBs are forced to 10
    HAL_SPI_ReadWrite(SPI_Writebyte);                       // Call the low-level SPI routine for this MCU to send this byte                                                   //
    
    SPI_Writebyte = ((Memory_Address & 0x0000FF00) >> 8);   // Mask off the next byte to be sent
    HAL_SPI_ReadWrite(SPI_Writebyte);                       // Call the low-level SPI routine for this MCU to send this byte 

    SPI_Writebyte = (Memory_Address & 0x000000FF);          // Mask off the next byte to be sent (least significant byte)
    HAL_SPI_ReadWrite(SPI_Writebyte);                       // Call the low-level SPI routine for this MCU to send this byte 
    
} 


// ============================================================================================================
// Send address of a register which is to be Read
// ============================================================================================================

// This function sends an address of a register which is to be read. It clears the two MS bits to indicate a read
//
// Pass in:         00000000 xxxxxxxx yyyyyyyy zzzzzzzz     address as double word 32-bit value, only lower 3 bytes used
//
// Sends:           00xxxxxx                                bits 23 - 16 of the address, with bits 23/22 forced to 0
//                  yyyyyyyy                                bits 15 - 8  of the address
//                  zzzzzzzz                                bits 7 -  0  of the address
//                  00000000                                dummy byte 0x00 written as described in FT800 datasheet
//
// Returns:         No return value

void FT800_SPI_SendAddressRD(dword Memory_Address)
 {

    byte SPI_Writebyte = 0x00;

    // Write out the address. Only the lower 3 bytes are sent, with the most significant byte sent first
    SPI_Writebyte = ((Memory_Address & 0x00FF0000) >> 16);  // Mask off the first byte to send
    SPI_Writebyte = (SPI_Writebyte & 0x3F);                 // Since this is a read, the upper two bits are forced to 00
    HAL_SPI_ReadWrite(SPI_Writebyte);                       // Call the low-level SPI routine for this MCU to send this byte
    
    SPI_Writebyte = ((Memory_Address & 0x0000FF00) >> 8);   // Mask off the next byte to be sent
    HAL_SPI_ReadWrite(SPI_Writebyte);                       // Call the low-level SPI routine for this MCU to send this byte 

    SPI_Writebyte = (Memory_Address & 0x000000FF);          // Mask off the next byte to be sent (least significant byte)
    HAL_SPI_ReadWrite(SPI_Writebyte);                       // Call the low-level SPI routine for this MCU to send this byte 
                
    // Send dummy 00 as required in the FT800 datasheet when doing a read
    SPI_Writebyte = 0x00;                                   // Write dummy byte of 0
    HAL_SPI_ReadWrite(SPI_Writebyte);                       //

 }


// ============================================================================================================
// Write a 32-bit value
// ============================================================================================================

// This function writes a 32-bit value. Call FT800_SPI_SendAddressWR first to specify the register address
//
// Pass in:         pppppppp qqqqqqqq rrrrrrrr ssssssss             32-bit value to be written to the register
//
// Sends:           ssssssss         least signicant byte first
//                  rrrrrrrr        
//                  qqqqqqqq        
//                  pppppppp         most significant byte last
//  
// Returns:         N/A

void FT800_SPI_Write32(dword SPIValue32)
{
    byte SPI_Writebyte = 0x00; 
        
    SPI_Writebyte = (SPIValue32 & 0x000000FF);              //
    HAL_SPI_ReadWrite(SPI_Writebyte);                       // Write the first (least significant) byte
    
    SPI_Writebyte = ((SPIValue32 & 0x0000FF00) >> 8);       //
    HAL_SPI_ReadWrite(SPI_Writebyte);                       //
    
    SPI_Writebyte = (SPIValue32 >> 16);                     //
    HAL_SPI_ReadWrite(SPI_Writebyte);                       //
        
    SPI_Writebyte = ((SPIValue32 & 0xFF000000) >> 24);      //
    HAL_SPI_ReadWrite(SPI_Writebyte);                       // Write the last (most significant) byte
   
}  


// ============================================================================================================
// Write a 16-bit value
// ============================================================================================================

// This function writes a 16-bit value. Call FT800_SPI_SendAddressWR first to specify the register address
//
// Pass in:         rrrrrrrr ssssssss               16-bit value to be written to the register
//
// Sends:           ssssssss                        least significant byte first
//                  rrrrrrrr                        most significant byte last
//  
// Returns:         N/A

void FT800_SPI_Write16(unsigned int SPIValue16)
{
    byte SPI_Writebyte = 0x00;
    
    SPI_Writebyte = (SPIValue16 & 0x00FF);                  //
    HAL_SPI_ReadWrite(SPI_Writebyte);                       // Write the first (least significant) byte
    
    SPI_Writebyte = ((SPIValue16 & 0xFF00) >> 8);           //
    HAL_SPI_ReadWrite(SPI_Writebyte);                       // Write the last (most significant) byte
}  


// ============================================================================================================
// Write an 8-bit value
// ============================================================================================================

// This function writes an 8-bit value. Call FT800_SPI_SendAddressWR first to specify the register address
//
// Pass in:         ssssssss          16-bit value to be written to the register
//
// Sends:           ssssssss          sends the specified byte
//  
// Returns:         N/A
    
void FT800_SPI_Write8(byte SPIValue8)
{
    byte SPI_Writebyte = 0x00;

    // Write out the data
    SPI_Writebyte = (SPIValue8);                            //
    HAL_SPI_ReadWrite(SPI_Writebyte);                       // Write the data byte
}   


// ============================================================================================================
// Read a 32-bit register
// ============================================================================================================

// This function reads the actual 8-bit data from a register. Call FT800_SPI_SendAddressRD first to specify the address
//
// Pass In:         N/A
//
// Sends:           00000000        dummy byte whilst clocking in the Least Significant byte (e.g. aaaaaaaa) from register
//                  00000000        dummy byte whilst clocking in the next byte (e.g. bbbbbbbb) from register
//                  00000000        dummy byte whilst clocking in the next byte (e.g. cccccccc) from register
//                  00000000        dummy byte whilst clocking in the Most Significant byte (e.g. dddddddd) from register
//
// Returns:         dddddddd cccccccc bbbbbbbb aaaaaaaa

dword FT800_SPI_Read32()
 {
    byte SPI_Writebyte = 0x00;
    byte SPI_Readbyte = 0x00;
    dword SPI_DWordRead = 0x00000000;
    dword DwordTemp = 0x00000000;
    
    // Read the first data byte (this is the least significant byte of the register). SPI writes out dummy byte of 0x00
    SPI_Writebyte = 0x00;                                   // Write a dummy 0x00
    SPI_Readbyte = HAL_SPI_ReadWrite(SPI_Writebyte);        // Get the byte which was read by the low-level MCU routine
    DwordTemp = SPI_Readbyte;                               // Put received byte into a temporary 32-bit value
    SPI_DWordRead = DwordTemp;                              // Put the byte into a 32-bit variable in the lower 8 bits

    // Read the actual data byte. We pass a 0x00 into the SPI routine since it always writes when it reads
    SPI_Writebyte = 0x00;                                   // Write a dummy 0x00
    SPI_Readbyte = HAL_SPI_ReadWrite(SPI_Writebyte);        // Get the byte which was read by the low-level MCU routine
    DwordTemp = SPI_Readbyte;                               // Put received byte into a temporary 32-bit value
    SPI_DWordRead |= (DwordTemp << 8);                      // Put the byte into a 32-bit variable (shifted 8 bits up)

    // Read the actual data byte. We pass a 0x00 into the SPI routine since it always writes when it reads
    SPI_Writebyte = 0x00;                                   // Write a dummy 0x00
    SPI_Readbyte = HAL_SPI_ReadWrite(SPI_Writebyte);        // Get the byte which was read by the low-level MCU routine
        DwordTemp = SPI_Readbyte;                           // Put received byte into a temporary 32-bit value
    SPI_DWordRead |= (DwordTemp << 16);                     // Put the byte into a 32-bit variable (shifted 16 bits up)

    // Read the actual data byte. We pass a 0x00 into the SPI routine since it always writes when it reads
    SPI_Writebyte = 0x00;                                   // Write a dummy 0x00
    SPI_Readbyte = HAL_SPI_ReadWrite(SPI_Writebyte);        // Get the byte which was read by the low-level MCU routine
        DwordTemp = SPI_Readbyte;                           // Put received byte into a temporary 32-bit value
    SPI_DWordRead |= (DwordTemp << 24);                     // Put the byte into a 32-bit variable (shifted 24 bits up)
    
    // Return the byte which we read
    return(SPI_DWordRead);                                  //
}  


// ============================================================================================================
// Read an 8-bit register
// ============================================================================================================

// This function reads the actual 8-bit data from a register. Call FT800_SPI_SendAddressRD first to specify the address
// 
// Pass in:         N/A
//
// Sends:           00000000                                Dummy write 0x00 whilst clocking in the byte 
//
// Returns:         aaaaaaaa                                Byte which was clocked in

byte FT800_SPI_Read8()
 {
    byte SPI_Writebyte = 0x00;
    byte SPI_Readbyte = 0x00;
    
    // Read the data byte. We pass a 0x00 into the SPI routine since it always writes when it reads
    SPI_Writebyte = 0x00;                                   // Write a dummy 0x00
    SPI_Readbyte = HAL_SPI_ReadWrite(SPI_Writebyte);        // Get the byte which was read by the low-level MCU routine
    
    // Return the byte which we read
    return(SPI_Readbyte);                                   //
}  


// ============================================================================================================
// Write a Host Command
// ============================================================================================================

// This function is specifically designed to send a Host Command
//
// Pass in:         xxhhhhhh             8-bit host command where upper 2 bits are dont-care
//
// Sends:           [Chip Select Low]
//                  01hhhhhh         Host command with bit 7 forced to 0 and bit 6 forced to 1
//                  00000000         Dummy 0x00        
//                  00000000         Dummy 0x00 
//                  [Chip Select High] 
//  
// Returns:         N/A

void FT800_SPI_HostCommand(byte Host_Command)
{
    byte PortRead = 0x00;
    byte SPI_Readbyte = 0x00;
    byte SPI_Writebyte = 0x00;
    
    // Chip Select Low
    HAL_SPI_CSLow();
     
    SPI_Writebyte = (Host_Command & 0x3F | 0x40);           // This is the command being sent
    HAL_SPI_ReadWrite(SPI_Writebyte);                       //

    SPI_Writebyte = 0x00;                                   // Sending dummy byte
    HAL_SPI_ReadWrite(SPI_Writebyte);                       // 


    SPI_Writebyte = 0x00;                                   // Sending dummy byte
    HAL_SPI_ReadWrite(SPI_Writebyte);                       //


    // Chip Select High
    HAL_SPI_CSHigh();
}  


// ============================================================================================================
// Host Command Dummy Read
// ============================================================================================================

// This function is specifically designed to send three 0x00 bytes to wake up the FT800
//
// Pass in:         N/A             
//
// Sends:           [Chip Select Low]
//                  00000000         Dummy 0x00 
//                  00000000         Dummy 0x00        
//                  00000000         Dummy 0x00 
//                  [Chip Select High] 
//  
// Returns:         N/A

void FT800_SPI_HostCommandDummyRead(void)
 {
    byte PortRead = 0x00;
    byte SPI_byte_Read = 0x00;
    byte SPI_Writebyte = 0x00;
    
    // Chip Select Low
    HAL_SPI_CSLow();
     
    // Read/Write
    SPI_Writebyte = 0x00;                                   // Sending dummy 00 byte
    HAL_SPI_ReadWrite(SPI_Writebyte);


    SPI_Writebyte = 0x00;                                   // Sending dummy 00 byte
    HAL_SPI_ReadWrite(SPI_Writebyte);


    SPI_Writebyte = 0x00;                                   // Sending dummy 00 byte
    HAL_SPI_ReadWrite(SPI_Writebyte);


    // Chip Select High
    HAL_SPI_CSHigh();
}   

// ============================================================================================================
// Increment Command Offset
// ============================================================================================================

// This function is used when adding commands to the Command FIFO.
// After adding a command to the FIFO, this function will calculate the FIFO address offset where the next command
// should be written to.
// In general, the next command would be written to [previous offset plus the length of the last command] but due 
// to the circular FIFO used, a command list may need to wrap round from an offset of 4095 to 0. 
//
// Pass in:         N/A             
//
// Sends:           [Chip Select Low]
//                  00000000         Dummy 0x00 
//                  00000000         Dummy 0x00        
//                  00000000         Dummy 0x00 
//                  [Chip Select High] 
//  
// Returns:         N/A

  
unsigned int FT800_IncCMDOffset(unsigned int Current_Offset, byte Command_Size)
{
    unsigned int New_Offset;
    
    New_Offset = Current_Offset + Command_Size;
    
    if(New_Offset > 4095)
    {
        New_Offset = (New_Offset - 4096);
    }
    
    return New_Offset;
}

// ############################################################################################################
// ############################################################################################################














// ############################################################################################################
// Hardware-Specific Functions
// ############################################################################################################ 

void HAL_Configure_MCU(void)
{
    SOPT1 = 0x42;                       // Disable the watchdog timer in the Options register
        
    // Set up the GPIO pins
    
    // Port A
    PTADD = 0x00;                       // Port A directions are all inputs
    PTAD = 0xFF;                        // Preset data value register to '1's
    
    // Port B
    PTBDD = 0x2D;                       // Port B directions    SPI SS, SPI MOSI, SPI Clock and Power_Down are all outputs
    PTBD = 0xFB;                        // Initial
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    '1' except the SPI Clock which should idle low

    // Set up the SPI
    SPIC1 = 0x50;                       // 0101 0000    Enable SPI, Configure as Master, CPOL and CPHA configured for mode 0
    SPIC2 = 0x00;                       // 0000 0000    None of the other features such as bi-directional mode used here.
    SPIBR = 0x00;                       // 0000 0000    Optional SPI clock divider. 
                                        // No divider used, SPI runs from the MCU's default 10MHz bus clock  
}

// ============================================================================================================
// Writes a byte to the MCU's SPI hardware and reads the byte received at the same time
// ============================================================================================================

byte HAL_SPI_ReadWrite(byte MCU_Writebyte)
{
    byte TempSPIstatus = 0;
    byte MCU_Readbyte = 0;

    // Wait until the MCU SPI peripheral is ready for the next write, and then write the byte to the SPI Data register
    // Note that writes to SPID will write the byte to the SPI module Tx Register (to be sent out of MOSI) 
    // and reads from SPID will read a byte from the SPI module Rx Register (which has come in through MISO)
    
    TempSPIstatus = 0x00;   
               
    // Wait for bit 5 of the SPI Status register to be set           
    while((TempSPIstatus & 0x20) != 0x20)
    {
        TempSPIstatus = SPIS;           // Read the SPI Status register
    }
    
    SPID = MCU_Writebyte;               // Now write the byte to the SPI Data Transmit register 
            
    // Now wait until the MCU SPI peripheral makes the byte clocked in available   
       
    TempSPIstatus = 0x00;
    
    // Wait for bit 7 of the  SPI Status register to be set
    while((TempSPIstatus & 0x80) != 0x80)
    {
        TempSPIstatus = SPIS;           // Read the SPI Status register
    }
    
    MCU_Readbyte = SPID;                // Now read the byte from the SPI Data Recieve register 
        
    return MCU_Readbyte;
}

// ============================================================================================================
// Puts Chip Select pin Low
// ============================================================================================================

void HAL_SPI_CSLow(void)
{
    byte PortRead = 0x00;               // Temporary byte to hold the value read from the port
    
    PortRead = PTBD;                    // Read value of port D data register
    PortRead = PortRead & 0xDF;         // Clear bit 5 of the value      
    PTBD = PortRead;                    // Write the value back to the port
}

// ============================================================================================================
// Puts Chip Select pin High
// ============================================================================================================

void HAL_SPI_CSHigh(void)
{
    byte PortRead = 0x00;               // Temporary byte to hold the value read from the port
    
    PortRead = PTBD;                    // Read value of port D data register
    PortRead = PortRead | 0x20;         // Set bit 5 of the value
    PTBD = PortRead;                    // Write the value back to the port
}

// ============================================================================================================
// Puts FT800 Power_Down_# pin Low
// ============================================================================================================

void HAL_SPI_PDlow(void)
{
    byte PortRead = 0x00;               // Temporary byte to hold the value read from the port
    
    PortRead = PTBD;                    // Read port D data register
    PortRead = PortRead & 0xFE;         // Clear bit 0 of the value
    PTBD = PortRead;                    // Write the value back to the port
}

// ============================================================================================================
// Puts FT800 Power_Down_# pin high
// ============================================================================================================

void HAL_SPI_PDhigh(void)
{
    byte PortRead = 0x00;               // Temporary byte to hold the value read from the port
    
    PortRead = PTBD;                    // Read port D data register
    PortRead = PortRead | 0x01;         // Set bit 0 of the value
    PTBD = PortRead;                    // Write the value back to the port
}

// ============================================================================================================
// General Functions
// ============================================================================================================

void Delay(void)
{
    unsigned int outer_count = 0x0000;
    unsigned int inner_count = 0x0000;
    
    outer_count = 0x0004;
    inner_count = 0xFFFF;
    
    while(outer_count > 0x0000)
    {
        while (inner_count > 0x0000)
        {
            inner_count --;
        }
    outer_count --;
    }
}

// ############################################################################################################
// ############################################################################################################

















