image
ECEN-433
Microprocessor Systems Design
Final Report
image
Carter Brehm
Spring 2024

University of Nebraska-Lincoln
Department of Electrical and Computer Engineering
Peter Kiewit Institute

Summary

This lab explored the process of planning, designing, building, and programming a computer system with a variety of memory and I/O devices. The goal is to serve as a manifestation of concepts taught in lecture such as memory organization and types of electronic communication. At the end of this lab, a full computer system was demonstrated with all of the composite functionality of each of the previous labs given in the course.

This report is divided into logical sections. The Objective provides a holistic overview of the lab, including several key deliverables. The Introduction provides background information, theories, or specialized hardware and tools required to understand the lab procedures. The Hardware Discussion reviews both the schematic and generated circuit board, as well as describing relationships within. The Software Discussion discusses each piece of software functionality provided by the system, as well as explaining program flow. The Problem Discussion provides a look into several problems encountered during lab, and how they were fixed or worked around. The Conclusion has some final thoughts regarding the entire project. Finally, the Appendix contains all code and other artifacts required to reproduce the lab.

Objective

The desired outcome of this lab was a microprocessor computer system using the 8051 platform to run RAM functions defined in Labs 1-4. The system would take user input via two methods: directly via the attached keypad, and serially from the attached ESP8266’s web server. The output includes a high-quality LCD display as well as a 7-segment display. Each of the success criterion are enumerated specifically below (drawn from lab handouts):

  1. The microprocessor platform used in the design shall be the AT89C51RC.

  2. The microprocessor shall use a memory decoding scheme to interface with 64K of ROM as well as 64K of RAM.

  3. The microprocessor shall communicate with several I/O devices:

    1. 16 button matrix keypad

    2. 7-segment common cathode display

    3. 2.8" TFT LCD

    4. Real-time clock

    5. Analog to digital converter

    6. ESP-8266

  4. The code must implement and support the following RAM functionality:

    1. Check

    2. Dump

    3. Move

    4. Find

    5. Edit

    6. Count

Once these functionalities were completed, they would be demonstrated to the lab TA for project completion.

Introduction

During research for this project, some concepts became vital to complete the lab.

Address Latching

The 8051 has the ability to address 64K of memory using 16 address lines, but half of the lines (AD0-AD7) are multiplexed with the incoming data bus, and must be demultiplexed before being able to use either bus.

This is done with the addition of a small address latch. The 8051 provides an ALE signal to indicate whether it’s intending to send a 16-bit address using all 16 lines, or receive 8 bits of data through the 8 data lines. This signal can be fed to the latch to hold address lines high when data is being received, and to pass them through to the controller when it’s calling an address.

Serial vs. Parallel Communication

In this lab, both serial and parallel communication were used in different areas for different reasons.

Parallel

As discussed above, address and data fetching used parallel communication across the board. Having a wide bus made communication quick and simple, as the minimum unit of data was a byte. The disadvantages were the amount of lines required (which became slightly unruly during routing) as well as the requirement of multiple components sharing a data bus. This issue was solved using a GAL decoder, which served as a meta-addressor and only activate the specific chip being addressed. This would keep the data bus clear for communication.

Serial

Serial communication was added on to the board as an example of communicating with hardware that used a more standardized communication protocol (in this case, RS232). In most cases, parallel communication is cost-prohibitive or impossible, so serial communication is possible with only two lines.

SDCC

SDCC (small device C compiler) was used extensively during the lab to assemble and compile C code into 8051 assembly. SDCC differs from other C compilers (like those on the desktop) by allowing the developer to choose where in memory (or on external hardware, such as the ROM chips) each variable or constant was stored. This allowed for optimization for specific routines. For example, a section of internal RAM was designated specifically to hold incoming serial, to avoid having to make a trip out to the external memory during a serial interrupt. This was also used to fit image data onto the second ROM specifically, to keep it separate from the rest of code memory.

Hardware Discussion

Connection began at the DC barrel jack connector, which took in DC 5V and spread ground and VCC throughout the board via planes to simplify wiring.
The 8051 sat at the top of the circuit hierarchy as it controlled all operation. Through port 0 and port 2, it sent and received addresses and data, using PSEN, ALE, and IO/M to control decoding and addressing individual chips.
The 8051 has no internal clock, so an external crystal was provided and connect to the controller’s XTAL1 clock input to tick the circuit.
For decoding, the signals shown in the figure were used to decode a single chip selection signal that would use the data and address bus.
In addition to the PAL, the address latch was also essential for decoding. With the help of the ALE signal, it would latch the bottom half of the address lines at their current value, allowing data reception from a specific address.
Once the address and data lines have been demultiplexed, they can be sent to the ROM. Since the ROM is only half of the memory space, the A15 signal is only used to decode chip select, and isn’t provided to the ROM. The emitted data is sent back through the data bus while the reading address is held. WR is held high to avoid accidentally overwriting code memory.
Similarly to the ROM, the RAM is governed by the PAL decoder, receives all but one address line from the controller, emitting its data back to the data bus.
The LCD has a built-in controller, which simplified drawing to the screen. Less lines had to be connected back to the main controller. The LCD could be selected via decoding/addressing, and controlled by sending data in parallel through D0-D7. The alternative SPI mode was not used.
The 4x4 matrix keypad had no controller, and thus must be decoded manually by the 8051. It was connected directly to port 1, and rows/columns of the matrix would be flashed to decode user input.

.

The 7-segment display must have the ability to hold it’s value, which means another latch had to be used. The latch could be selected via decoding scheme and pass through a value to hold on the display. This was usually the number pressed on the keypad.
The ADC was connected as another generic I/O device, in which the decoding was used to select the chip so the data bus could be used to receive incoming data. In this case, measurements were made by selecting the chip, and reading the 8bit voltage value as a ratio of VCC.
The RTC was connected in the same manner as other I/O devices. It could be selected via decoding at a specific set of addresses, in which there were several registers holding year, month, etc. These values could be sent back over the data bus. A small coin-cell battery was attached to keep accurate time while the circuit is powered down.
Finally, the ESP was connected serially, using a voltage transformer to step down to 3V. RX and TX were swapped on their way to the board because both elements of the communication were terminal equipment.
The final prototype made on the breadboard. This did not include the ESP, which was held until the final board design to avoid complication.

.

Software Discussion

Function Blocks or Modules

For this lab, the code was divided into logical groupings based off functionality. Each of these logical groupings are explained in detail below.

Main

The main module is where main program flow resides in the program. First, an interrupt is scheduled (explained further in Serial). Once the LCD, clock, and serial communication have been initialized, all functionality is delegated to other modules. The main function calls the menu whenever no other functionality is happening (on boot, and after exiting another menu) and calls the function depending on the user input.

The menu module is used to present options to the user. The main menu prints out a list of predefined menu options, waits for the user to press a key, validates that it is within the bounds of available options, then finally returns the chosen option as an exit code. This allows for the main module to call the correct functionality.

The other menu defined in the menu module is to choose block size. Multiple different functionalities featured elsewhere in the program take a block size for their datatype, and this functionality was collected into the block size menu function. Just as the main menu, the program lists out the possible block sizes and returns the user’s choice to the calling function.

Finally, the menu module is also responsible for printing the intro screen and updating the clock when the system boots.

LCD

The LCD driver was provided as a part of the lab materials as a means to control the LCD display. These functions include printing text, shapes like circles and squares, as well as housekeeping functions such as clearing the display or manipulating the cursor.

One interesting implementation detail of the LCD library is a maintained state within the library that can be accessed from other libraries. For example, the cursor_x variable, containing the cursor’s current x position, has a static position in internal RAM. Then, instead of querying for the cursor position from the LCD, external libraries can call the getCursorX() function to determine where they are drawing on the screen.

The LCD driver also contains the pixel data for the picture, as well as the code to display it. The pixel data is encoded as an array of 16bit RGB565 values, and printed row-by-row as a set of 2x2 squares (resulting in a 2x upscaling of a 160x120 image).

Keypad

The keypad library collects functions that revolve around the keypad (or user input in general). It provides both a blocking and non-blocking function for getting the button the user pressed on the keypad, whether the calling function needs to determine the user isn’t pressing a key, or wait for them to do so.

The keypad library also includes prompting functions, which use the LCD to prompt the user for a byte or word in hex, taking care of sanitization and conversion before the calling function receives the hex input. The library also contains stubs for inserting serial input (from the ESP virtual keypad) into the regular keypad input routines.

Utils

The utilities library contains helper functions that are utilized by almost all libraries. The first is a pair of functions to print hex characters to the display given a byte or a word. This function is used everywhere when needing to translate an address or piece of data to the user.

The second is a set of generic I/O write routines. These helper functions make sure that the IO/M pin is set correctly when addressing I/O devices, since the 8051 does not have hardware support for this pin.

Constants

Predictably, the constants library contains no executable code, instead holding lookup tables for ASCII and 7-segment decoding, as well as menu options for the menu library.

Segment

The segment library contains functions for interacting with the 7-segment display on the board. The library can draw a character (this is used in the keypad library to echo the user’s input) or display a small moving animation (this is used to show the board is powering on).

Memory

The memory module is used to provide helper functions to the other memory manipulation functions like Count and Find. The module provides generic ‘readFromExternalAddress()‘ and ‘writeToExternalAddress()‘ so that the calling function doesn’t have to perform the assembly operations required to move the data pointer. The library also provides functions to page through (forward or backward) memory, finding or counting

ADC

The ADC module is responsible for interfacing with the ADC. Since ADC functionality is limited in the current form of the system, this module is only responsible for printing the ADC menu as well as the current read voltage value.

Count

The count module is responsible for controlling the count menu and functionality. It prompts the user for the necessary inputs and then performs the count operation. The user is presented with the report depending on the returned results. At the end, the user can choose to jump into the find routine with the current arguments to show the location of the counted values.

Find

The find module is responsible for the find menu and the actual finding operations. The find module is split into three layers, the find menu, the find operation, and the find page in which the results are displayed.

The find menu is how the user normally accesses find, from the main menu. It prompts the user for the neccessary inputs and then jumps into the find operation.

The find operation uses the memory library to scan forward and backward for the user’s selected value. To avoid holding too many found values in memory, this operation stops once it has reached the specified value, and displays it in the find page. The find operation is abstracted from the menu in cases where another function (like count) needs to jump right into finding the value it just displayed to the user. This avoids prompting the user again for the same information.

The find page takes the found value at a specific address and displays it to the user. Then, it searches forward and backward for another address to jump to, giving the user a previous and next page option if available.

Dump

The dump module works in a similar manner to the find module. It asks for the required information through the keypad, then reads that section of memory and passes it to the dump page.

The dump page prints the current address and value to the screen, then does a bounds check for the chosen bounds as well as the start & end of memory to give the user previous and next page indicators so they can scroll to see a new page of memory.

Edit

The edit module is responsible for guiding the user to editing a section of memory with the keypad. The function will first prompt the user for the required address to edit, then pass that to an edit page.

An edit page will display the current address and the values to be edited, then let the user type over the displayed values to overwrite the actual memory location. Once this value is entered, the edit page will return this new edited value.

Stepping out of the page, the edit function will write the user value to memory and prompt the user to either go to the next byte to edit or return to the menu.

Move

The move module is responsible for the move menu as well as the memory move operation. It will prompt the user for required input such as source and destination, perform error checks to ensure the move is logical, and finally run through a simple loop to move values from source to destination.

RamCheck

The RamCheck module is responsible for performing the RAM check across all of the memory space. First, it prompts the user for a byte to write, which it passes to a set of inline assembly functions which write and check the entire memory chip with the value. If that succeeds, the module will invert the value and run again, printing the status out to the user.

RTC

The RTC module is responsible for controlling the RTC and providing its value to the rest of the system (eg. the title screen). The module contains functions for encoding and decoding date/time values back and forth from the RTC. It also contains the RTC menu, which allows the user to view the current time as well as edit it, using helper functions to convert user input into the correct values to be placed in the RTC registers.

Serial

The Serial module contains only one function, the initialization of the serial port on the 8051 to the correct baud rate. SDCC dictated that the serial interrupt must be included in the main module to be compiled and placed in the interrupt vector table.

Sequential Diagram for Program

Top-level flowchart of the entire program. The menu functions as a computing layer, allowing the user to call up functionality that takes complete control until the user chooses to exit.
Showing program flow of the RAM check.
Showing program flow of the dump function.
Showing program flow of the edit function.
Showing program flow of the count function.
Showing program flow of the find function.
Showing program flow of the move function.

Problem Discussion

Throughout this project, most problems were transient, and fixed with a quick continuity test or recompile. However, there were some problems that plagued the project for multiple lab sessions.

Decoding Issues

Early on in development, in the breadboarding stage, an issue arose in which the circuit would appear to turn on correctly after reset, but would quickly stop functioning soon after without doing anything. Each component was tested, but with each piece of evidence the conclusion was that the processor simply stopped issuing commands soon after boot-up. The chip selection for ROM would stop selecting, and the system would simply freeze.

The solution ended up being to change the decoding. In error, the decoding for the ROM chip select had included IO/M. Due to this error, each time the chip would switch that pin to attempt to access an I/O device, it would lose access to its ROM and cease function.

Debouncing

The keypad used in this project had a lot of issues, most of which were related to noise and debouncing. Essentially, input from the keypad could rarely be trusted, as noise through the lines would press several "phantom" keys immediately after.

The solution to this was to add a delay to the non-blocking keypad read before taking any more input. This would allow the noise to subside and

Conclusion

Throughout the course, all of the design lectures and labs built towards one final system. This system was planned, designed, prototypes on the breadboard, and finally manufactured on a printed circuit board. It began with conceptual labs, writing assembly and later C code targeted against an 8051 simulator. As the lectures turned to systems design, the board began to take shape, finalizing a decoding and operation schema. Finally, the course pivoted to PCB design, and the final output of the lab depended heavily on one’s design and routing.

I could be considered lucky in that my board was routed well enough that I had to make no corrections to my final printed board. I learned a lot about designing a system from the ground up, using my preliminary theoretical knowledge of gates and logic to slowly construct a device that computes. I learned about how to troubleshoot efficiently, learning about the expected output of each part and dialing-in debugging on pieces that didn’t, making each issue an exercise in precision. I learned that while continuing to add pieces on and onto a system works, having a plan and executing it top-to-bottom extensively makes for an easier process. Finally, I learned about all of the ways that machine code can be converted and executed by a system, as well as the advantages and disadvantages of each.

Overall, I had much success in the course and greatly enjoyed the process. There were definitely difficulties, in which troubleshooting seemed daunting. How could I possibly figure out what’s wrong, without seeing the internals of the entire system at the same time? I learned skills to help me work through this paralysis and deduce how to move towards "better," not necessarily jumping to perfect.

Appendix

PCB Design

Finalized PCB design with only the bottom layer showing.

.

Finalized PCB design with only the top layer showing.
Finalized PCB design with both layers and vias showing.

Bill of Materials

Demo Images

Title screen that is displayed when the circuit is first reset, that displays author details and live date & time.
Menu screen that displays after the user dismisses the title screen. Allows for jumping to any of the other functionality.
Performing a RAM check on the device and reading back successful debug info.
Counting the instances of a specific hex value through all RAM.
Dumping a section of RAM to the display in hex.
Editing the value of a specific byte in RAM.
Editing the time stored in the real-time clock from the keypad.
Moving the values from one section of RAM into another section.
Reading the voltage of a connected analog signal.
Displaying an image on the screen from code memory.

Implementation Code for Microprocessor

// including standard headers and portable datatypes
#include "8051.h"
#include <stdint.h>
#include "LCD.h"
#include "Constants.h"
#include "Bool.h"
#include "RamCheck.h"
#include "Utils.h"
#include "Menu.h"
#include "Dump.h"
#include "Move.h"
#include "Find.h"
#include "Edit.h"
#include "Count.h"
#include "Memory.h"
#include "Keypad.h"
#include "Segment.h"
#include "include/LCD.h"
#include "main.h"
#include "RTC.h"
#include "ADC.h"
#include "Serial.h"

void serial_receive_interrupt(void) __interrupt (4)
{
	volatile uint8_t data = SBUF;
	setSerialData(data);
	SCON &= ~(1<<0);
	return;
}

void main(void) {
	AUXR = 0x02;
	serialInit();
	loadingLoop(2);
	initLCD();
	// rtcInit();
	setRotation(3);
	setTextSize(2);
	setTextColor(WHITE, GREEN);
	clearLCD();
	titleScreen();
	while (1) {
		uint8_t choice = menu();
		switch (choice) {
			case 0x0A: // RAM Check
				ramCheck();
				break;
			case 0x0B: // Move
				move();
				break;
			case 0x0C: // Count
				count();
				break;
			case 0x0D: // Dump
				dump();
				break;
			case 0x0E: // Edit
				edit();
				break;
			case 0x0F: // Find
				find();
				break;
			case 0x01: // Clock
				rtcMenu();
				break;
			case 0x02: // ADC
				adcMenu();
				break;
			case 0x03: // Fry
				fryScreen();
				break;
		}
	}
}
#ifndef MAIN_H
#define MAIN_H

#include "8051.h"
__sfr __at(0x8E) AUXR;


#endif
#include "ADC.h"
#include "Constants.h"
#include <stdint.h>
#include "LCD.h"
#include "Keypad.h"
#include "Utils.h"

void adcMenu(void) {
	readKeypad();
	setCursor(10, 10);
	printString("Reading voltage...");
	setCursor(7, 216);
	printString("<E Exit (hold)");
	uint8_t keypadIndex = 0;
	while (keypadIndex == 0) {
		setCursor(130, 120);
		adcPrint();
		keypadIndex = readKeypad();
	}
}

void adcPrint(void) {
	uint8_t adcVal;
	uint8_t adcVoltageVal;
	adcVal = ioread8((uint8_t __xdata*) 0xC000);
	adcVoltageVal = (adcVal * 500) / 255;
	printChar(asciiLookup[adcVoltageVal / 100]);
	printString(".");
	printChar(asciiLookup[adcVoltageVal % 10]);
	printString("V");
}
#include "Constants.h"

__code char asciiLookup[17] = "0123456789ABCDEF";
__code char menuCount = 8;
__code char* menuOptions[8] = {"A | RAM Check",
 			       "B | Move",
           		   "C | Count",
			       "D | Dump",
			       "E | Edit",
			       "F | Find",
			       "1 | Clock",
			       "2 | ADC"};

__code char blockSizeMenuCount = 4;
__code char* blockSizeMenuOptions[4] = {"A     Byte      ",
					"B     Word      ",
					"C Double  Word  ",
					"D  Quad  Word   "};
__code char segconvert[16] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,  0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71};
// associated header file
#include "Count.h"

// including standard headers and portable datatypes
#include "8051.h"
#include <stdint.h>
#include "LCD.h"
#include "Keypad.h"
#include "Utils.h"
#include "Bool.h"
#include "Memory.h"
#include "Find.h"

void count(void) {
	uint16_t startAddress = promptForWord("Count Address?");
	uint16_t blockQuantity = promptForWord("Byte Quantity?");
	uint8_t searchValue = promptForByte("Count Value?");

	// quick check to make sure they didn't want zero blocks
	while (!blockQuantity) {
		blockQuantity = promptForWord("Quantity > 0!");
	}

	// quick check to see if we will overflow address
	if (startAddress + blockQuantity < startAddress) {
		blockQuantity = promptForWord("Overflow! Try less blocks.");
		return;
	}

	clearLCD();
	setCursor(10, 10);
	printString("Counting 0x");
	printHexByteToASCIIArray(searchValue);
	printString("...");

	// iterate through memory using searchForwardForExternalValue
	// every time we find an address, increment a counter and then search again from the next address
	uint16_t currentAddress = startAddress;
	uint16_t endAddress = startAddress + blockQuantity;
	uint8_t count = 0;
	count = countValueInRange(startAddress, endAddress, searchValue);

	setCursor(10, 35);
	printString("Matches: ");
	printHexByteToASCIIArray(count);

	setCursor(10, 216);

	if (count) {
		printString("<E Exit           View 1>");
		uint8_t choice = waitForKeypad();
		if (choice == 1) {
			findArgs(0, blockQuantity, searchValue);
		}
	} else {
		printString("<E Exit                 |");
		waitForKeypad();
	}
	return;

}
// associated header file
#include "Dump.h"

// including standard headers and portable datatypes
#include "8051.h"
#include <stdint.h>
#include "LCD.h"
#include "Keypad.h"
#include "Utils.h"
#include "Memory.h"
#include "Menu.h"

uint8_t dumpPage(uint16_t address, uint8_t byteCount, bool pagesBefore, bool pagesAfter) {
	setCursor(10, 10);
	printString("Dumping... E to exit");
	// back indicator
	setCursor(7, 216);
	if (pagesBefore) {
		printString("<1");
	} else {
		printString("| ");
	}

	// current memory address
	setCursor(120, 216);
	printString("0x");
	printHexWordToASCIIArray(address);

	// next indicator
	setCursor(295, 216);
	if (pagesAfter) {
		printString("0>");
	} else {
		printString(" |");
	}

	// data at memory location
	setCursor(150 - byteCount*12, 120);
	while (byteCount > 0) {
		printHexByteToASCIIArray(readFromExternalAddress(address));
		byteCount--;
		address++;
	}

	while (1) {
		uint8_t pageChoice = waitForKeypad();
		if ((pageChoice == 0 && pagesAfter) || (pageChoice == 1 && pagesBefore) || pageChoice == 0x0E) {
			return pageChoice;
		}
	}

}

void dump(void) {
	uint16_t startAddress = promptForWord("Dump Address?");
	uint8_t blockSize = blockSizeMenu();
	uint16_t blockQuantity = promptForWord("Block Quantity?");

	// quick check to make sure they didn't want zero blocks
	while (!blockQuantity) {
		blockQuantity = promptForWord("Quantity > 0!");
	}

	// turns the user's block size from a letter choice to a number
	switch (blockSize) {
		case 0x0B: // word
			blockSize = 2;
			break;
		case 0x0C: // double word
			blockSize = 4;
			break;
		case 0x0D: // quad word!
			blockSize = 8;
			break;
		default: // byte
			blockSize = 1;
			break;
	}

	clearLCD();

	// begin our pagination
	uint8_t pageChoice = 0;
	uint16_t currentPage = 1;
	while (pageChoice != 0x0E) {
		bool pagesBefore = currentPage > 1;
		// check both for if the user requested this many pages, and also make sure we don't overflow
		bool pagesAfter = (currentPage < blockQuantity) && ((startAddress + blockSize) > startAddress);
		pageChoice = dumpPage(startAddress, blockSize, pagesBefore, pagesAfter);
		if (pageChoice == 0 && currentPage < blockQuantity) { // next page
			currentPage++;
			startAddress += blockSize;
		}
		if (pageChoice == 1 && currentPage > 1) { // prev page
			currentPage--;
			startAddress -= blockSize;
		}
	}
}
// associated header file
#include "Edit.h"

// including standard headers and portable datatypes
#include "8051.h"
#include <stdint.h>
#include "LCD.h"
#include "Keypad.h"
#include "Utils.h"
#include "Memory.h"
#include "Constants.h"

uint8_t editPage(uint16_t address) {
	clearLCD();

	// current memory address
	setCursor(10, 10);
	printString("Editing 0x");
	printHexWordToASCIIArray(address);
	printString("...");
	setCursor(10, 35);
	printString("Type to replace value");

	// data at memory location
	setCursor(140, 120);
	printHexByteToASCIIArray(readFromExternalAddress(address));

	// let the user write a value
	setCursor(140, 120);

	// first nibble
	uint8_t firstCharacter = waitForKeypad();
	printChar(asciiLookup[firstCharacter]);

	// second nibble
	uint8_t secondCharacter = waitForKeypad();
	printChar(asciiLookup[secondCharacter]);

	// combine and return
	return (firstCharacter << 4) | secondCharacter;
}

void edit(void) {
	uint16_t startAddress = promptForWord("Edit Address?");

	// lets show the memory at that address
	uint8_t userChoice = 2;
	while (userChoice != 0xE) {
		userChoice = 2;
		uint8_t userByte = editPage(startAddress);
		writeToExternalAddress(startAddress, userByte);
		setCursor(10, 216);
		printString("<E Exit           Next 0>");
		while (userChoice != 0 && userChoice != 0xE) {
			userChoice = waitForKeypad();
		}
		// overflow check
		if (startAddress + 1 < startAddress) {
			errorBackToMenu("End of memory!");
			return;
		} else {
			startAddress++;
		}
	}
	return;
}
// associated header file
#include "Find.h"

// including standard headers and portable datatypes
#include "8051.h"
#include <stdint.h>
#include "LCD.h"
#include "Keypad.h"
#include "Utils.h"
#include "Memory.h"
#include "Bool.h"

uint8_t findPage(uint16_t address, bool pagesBefore, bool pagesAfter) {
	// back indicator
	setCursor(7, 216);
	if (pagesBefore) {
		printString("<1");
	} else {
		printString("| ");
	}

	// current memory address
	setCursor(120, 216);
	printString("0x");
	printHexWordToASCIIArray(address);

	// next indicator
	setCursor(295, 216);
	if (pagesAfter) {
		printString("0>");
	} else {
		printString(" |");
	}

	while (1) {
		uint8_t pageChoice = waitForKeypad();
		if ((pageChoice == 0 && pagesAfter) || (pageChoice == 1 && pagesBefore) || pageChoice == 0x0E) {
			return pageChoice;
		}
	}
}

void findArgs(uint16_t startAddress, uint16_t blockQuantity, uint8_t searchValue) {
	clearLCD();

	setCursor(10, 10);
	printString("Finding 0x");
	printHexByteToASCIIArray(searchValue);
	printString("... E to exit");

	// start searching
	bool found = false;
	uint16_t foundAddress = searchForwardForExternalValue(startAddress, searchValue, &found);
	if (foundAddress < startAddress || foundAddress > startAddress + blockQuantity) {
		// we couldn't find anything
		errorBackToMenu("Byte not found!");
		return;
	} else {
		uint8_t userChoice = 0xF;
		while (userChoice != 0xE) {
			// see if we have before
			bool pagesBefore = false;
			uint16_t beforeAddress = searchBackwardForExternalValue(foundAddress - 1, searchValue, &pagesBefore);
			// make sure the address we found is actually within our range
			if (pagesBefore) {
				pagesBefore = beforeAddress >= startAddress;
			}
			// see if we have after
			bool pagesAfter = false;
			uint16_t afterAddress = searchForwardForExternalValue(foundAddress + 1, searchValue, &pagesAfter);
			// make sure the address we found is actually within our range
			if (pagesAfter) {
				pagesAfter = afterAddress <= startAddress + blockQuantity;
			}
			userChoice = findPage(foundAddress, pagesBefore, pagesAfter);
			if (userChoice == 0) {
				foundAddress = afterAddress;
			} else if (userChoice == 1) {
				foundAddress = beforeAddress;
			}
		}
		return;
	}
}

void find(void) {
	uint16_t startAddress = promptForWord("Find Address?");
	uint16_t blockQuantity = promptForWord("Byte Quantity?");
	uint8_t searchValue = promptForByte("Search For?");

	// quick check to make sure they didn't want zero blocks
	while (!blockQuantity) {
		blockQuantity = promptForWord("Quantity > 0!");
	}

	findArgs(startAddress, blockQuantity, searchValue);
}
// associated header file
#include "Keypad.h"

// including standard headers and portable datatypes
#include "8051.h"
#include <stdint.h>
#include "LCD.h"
#include "Constants.h"
#include "Segment.h"

#define KEYPAD_PORT P1

uint8_t __idata serialData;

// flashes the rows of the matrix keypad and returns a byte representing the current row pressed (if any)
uint8_t checkRows(void) {
	KEYPAD_PORT = 0xF0;
	return KEYPAD_PORT;
}

// flashes the columns of the matrix keypad and returns a byte representing the current column pressed (if any)
uint8_t checkColumns(void) {
	KEYPAD_PORT = 0x0F;
	return KEYPAD_PORT;
}
// non-blocking function to read the current button pressed
// if none is pressed, return 0
uint8_t readKeypad(void) {
	uint8_t rowValue = checkRows();
	if (rowValue != 0xF0) {
		uint8_t columnValue = checkColumns();
		if (columnValue != 0x0F) {
			return rowValue | columnValue;
		}
	}
	// check our serial
	if (serialData != 0) {
		uint8_t incomingSerial = serialData;
		serialData = 0;
		return incomingSerial;
	}
	return 0;
}

// converts the incoming keypad index into the numerical value of the button pressed
uint8_t decodeKeypadToHex(uint8_t keypadIndex) {
	switch(keypadIndex) {
		case 0xD7: // 3
			return 3;
			break;
		case 0xEE: // D
			return 13;
			break;
		case 0xDE: // E
			return 14;
			break;
		case 0xBE: // 0
			return 0;
			break;
		case 0xED: // C
			return 12;
			break;
		case 0xDD: // 9
			return 9;
			break;
		case 0xBD: // 8
			return 8;
			break;
		case 0xEB: // B
			return 11;
			break;
		case 0xDB: // 6
			return 6;
			break;
		case 0xBB: // 5
			return 5;
			break;
		case 0x7E: // F
			return 15;
			break;
		case 0x7D: // 7
			return 7;
			break;
		case 0x7B: // 4
			return 4;
			break;
		case 0x77: // 1
			return 1;
			break;
		case 0xB7: // 2
			return 2;
			break;
		case 0xE7: // A
			return 10;
			break;
		default:
			return 16;
			break;
	}
}

// blocking function, returns the numerical value of the key the user pressed
uint8_t waitForKeypad(void) {
	uint8_t keypadIndex = 0;
	while (keypadIndex == 0) {
		keypadIndex = readKeypad();
	}
	uint8_t keypadValue = decodeKeypadToHex(keypadIndex);
	hexToSegment(keypadValue);
	delay(500);
	while (keypadIndex != 0) {
		keypadIndex = readKeypad();
	}
	return keypadValue;
}

// blocking function to wait for a specific key to be pressed
void waitForKey(uint8_t key) {
	uint8_t keypadValue = 16;
	while (keypadValue != key) {
		keypadValue = waitForKeypad();
	}
}

// prompts for a byte using the passed string and returning a single byte
uint8_t promptForByte(char* prompt) {
	// print instructions
	clearLCD();
	setCursor(20, 10);
	printString(prompt);
	setCursor(20, 35);
	printString("Byte: 0x");
	int oldCursorX = getCursorX();
	printString("__");
	setCursor(oldCursorX, 35);

	// first character
	uint8_t firstCharacter = waitForKeypad();
	printChar(asciiLookup[firstCharacter]);

	// second character
	uint8_t secondCharacter = waitForKeypad();
	printChar(asciiLookup[secondCharacter]);

	// combine and return
	return (firstCharacter << 4) | secondCharacter;
}

uint16_t promptForWord(char* prompt) {
	// print instructions
	clearLCD();
	setCursor(20, 10);
	printString(prompt);
	setCursor(20, 35);
	printString("Word: 0x");
	int oldCursorX = getCursorX();
	printString("____");
	setCursor(oldCursorX, 35);

	// first character
	uint8_t firstCharacter = waitForKeypad();
	printChar(asciiLookup[firstCharacter]);

	// second character
	uint8_t secondCharacter = waitForKeypad();
	printChar(asciiLookup[secondCharacter]);

	// third character
	uint8_t thirdCharacter = waitForKeypad();
	printChar(asciiLookup[thirdCharacter]);

	// fourth character
	uint8_t fourthCharacter = waitForKeypad();
	printChar(asciiLookup[fourthCharacter]);

	// combine and return
	return (firstCharacter << 12) | (secondCharacter << 8) | (thirdCharacter << 4) | fourthCharacter;
}

void setSerialData(uint8_t data) {
	serialData = data;
}

uint8_t getSerialData(void) {
	return serialData;
}
/*
	author: Matthew Boeding
	version: v1.0

	README

/// TFT-LCD (ILI9341) Driver code for the 8051. Can be repurposed for other drivers controllers :)

/// I optimized the code quite a bit for speed, utilizing internal pointers

/// Please do not post any of the code from this course to GITHUB.

*/
#include "LCD.h"
#include "bmp_image.h"
#include "font.h"
#include "Keypad.h"

__xdata uint8_t* lcd_address = (uint8_t __xdata*) LCD_ADDRESS;
/* cursor_y and cursor_x globals*/
uint16_t __idata* cursor_x = (uint16_t __idata*)(0xC2);
uint16_t __idata* cursor_y = (uint16_t __idata*)(0xC4);
/* textsize and rotation*/
uint8_t __idata* textsize = (uint8_t __idata*)(0xC6);
uint8_t __idata* rotation = (uint8_t __idata*)(0xC7);
/* text color and background */
uint16_t __idata* textcolor = (uint16_t __idata*)(0xC8);
uint16_t __idata* textbgcolor = (uint16_t __idata*)(0xCA);
/* lcd width and height */
uint16_t __idata* _width = (uint16_t __idata*)(0xCC);
uint16_t __idata* _height = (uint16_t __idata*)(0xCE);

__code uint8_t intro1[9] = {"Welcome\n",0};

void delay (int16_t d)
{
	int16_t i, j = 0; /* declare for backwards compatibility with older C standards */
	for (i=0;i<d;i++) /* this is For(); loop delay used to define delay value in Embedded C*/
	{
		for (j=0;j<80;j++)
		{
		}
	}
}


inline void lcdWrite8Reg(uint8_t d) {
	CD = 0;
	IOM = 1;
	*lcd_address = d;
	IOM = 0;
}
inline void lcdWrite8Data(uint8_t d) {
	CD = 1;
	IOM = 1;
	*lcd_address = d;
	IOM = 0;
}

inline void lcdWrite8DataBlocking(uint8_t d)
{
	*lcd_address = d;
}
inline void lcdWriteRegData8(uint8_t a, uint8_t d) {
	CD = 0;
	IOM = 1;
	*lcd_address = a;
	IOM = 0;
	CD = 1;
	IOM = 1;
	*lcd_address = d;
	IOM = 0;
}

uint16_t getCursorX(void)
{
	return *cursor_x;
}

uint16_t getCursorY(void)
{
	return *cursor_y;
}

uint8_t getTextSize(void)
{
	return *textsize;
}

void setCursorx(uint16_t x)
{
	*cursor_x = x;
}

void setCursory(uint16_t y)
{
	*cursor_y = y;
}

void lcdWriteRegData16(uint16_t a, uint16_t d){
	uint8_t hi, lo;
 	hi = (a) >> 8;
 	lo = (a);
 	lcdWrite8Reg(hi);
 	lcdWrite8Reg(lo);
  	hi = (d) >> 8;
  	lo = (d);
  	lcdWrite8Data(hi);
  	lcdWrite8Data(lo);
}


void setCursor(uint16_t x, uint16_t y){
	*cursor_x = x;
	*cursor_y = y;
}

void setTextColor(uint16_t x, uint16_t y){
	*textcolor =  x;
	*textbgcolor = y;
}



void setTextSize(uint8_t s){
	if (s > 8) return;
	*textsize = (s>0) ? s : 1 ;
}

void setRotation(uint8_t flag){
	*rotation = flag;
	switch(flag) {
		case 0:
			flag = (ILI9341_MADCTL_MX | ILI9341_MADCTL_BGR);
			*_width = TFTWIDTH;
			*_height = TFTHEIGHT;
			break;
		case 1:
			flag = (ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR);
			*_width = TFTHEIGHT;
			*_height = TFTWIDTH;
			break;
		case 2:
			flag = (ILI9341_MADCTL_MY | ILI9341_MADCTL_BGR);
			*_width = TFTWIDTH;
			*_height = TFTHEIGHT;
			break;
	  	case 3:
			flag = (ILI9341_MADCTL_MX | ILI9341_MADCTL_MY | ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR);
			*_width = TFTHEIGHT;
			*_height = TFTWIDTH;
			break;
		default:
			flag = (ILI9341_MADCTL_MX | ILI9341_MADCTL_BGR);
			*_width = TFTWIDTH;
			*_height = TFTHEIGHT;
			break;
	}
	lcdWriteRegData8(ILI9341_MEMCONTROL, flag);
}


void setAddress(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2){
	lcdWrite8Reg(ILI9341_COLADDRSET);
	lcdWrite8Data(x1 >> 8);
	lcdWrite8Data(x1);
	lcdWrite8Data(x2 >> 8);
	lcdWrite8Data(x2);

	lcdWrite8Reg(ILI9341_PAGEADDRSET);
	lcdWrite8Data(y1 >> 8);
	lcdWrite8Data(y1);
	lcdWrite8Data(y2 >> 8);
	lcdWrite8Data(y2);
}

void initLCD(void){
	*_width = TFTWIDTH;
	*_height = TFTHEIGHT;

	lcdWrite8Reg(ILI9341_MADCTL_RGB);
	lcdWrite8Data(0x00);
	lcdWrite8Data(0x00);
	lcdWrite8Data(0x00);

	delay(200);

	lcdWrite8Reg(ILI9341_SOFTRESET);
    delay(50);
    lcdWrite8Reg(ILI9341_DISPLAYOFF);
    delay(10);

    lcdWriteRegData8(ILI9341_POWERCONTROL1, 0x23);
    lcdWriteRegData8(ILI9341_POWERCONTROL2, 0x11);
    lcdWrite8Reg(ILI9341_VCOMCONTROL1);
	lcdWrite8Data(0x3d);
	lcdWrite8Data(0x30);
	lcdWriteRegData8(ILI9341_VCOMCONTROL2, 0xaa);
    lcdWriteRegData8(ILI9341_MEMCONTROL, ILI9341_MADCTL_MY | ILI9341_MADCTL_BGR);
    lcdWrite8Reg(ILI9341_PIXELFORMAT);
	lcdWrite8Data(0x55);lcdWrite8Data(0x00);
    lcdWriteRegData16(ILI9341_FRAMECONTROL, 0x001B);

    lcdWriteRegData8(ILI9341_ENTRYMODE, 0x07);

    lcdWrite8Reg(ILI9341_SLEEPOUT);
    delay(150);
    lcdWrite8Reg(ILI9341_DISPLAYON);
    delay(500);
	setAddress(0,0,*_width-1,*_height-1);
     /************* Start Initial Sequence ILI9341 controller **********/
}

void drawPixel(uint16_t x3,uint16_t y3,uint16_t color1)
{
	setAddress(x3,y3,x3+1,y3+1);
    lcdWrite8Reg(ILI9341_MEMORYWRITE);
	lcdWrite8Data(color1>>8);
	lcdWrite8Data(color1);
}



void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color){
	int f = 1 - r;
    	int ddF_x = 1;
    	int ddF_y = -2 * r;
    	int x = 0;
    	int y = r;


	drawPixel(x0  , y0+r, color);
    	drawPixel(x0  , y0-r, color);
    	drawPixel(x0+r, y0  , color);
    	drawPixel(x0-r, y0  , color);

    	while (x<y) {
        	if (f >= 0) {
            	y--;
            	ddF_y += 2;
            	f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;

        drawPixel(x0 + x, y0 + y, color);
        drawPixel(x0 - x, y0 + y, color);
        drawPixel(x0 + x, y0 - y, color);
        drawPixel(x0 - x, y0 - y, color);
        drawPixel(x0 + y, y0 + x, color);
        drawPixel(x0 - y, y0 + x, color);
        drawPixel(x0 + y, y0 - x, color);
        drawPixel(x0 - y, y0 - x, color);
    }
}

void fillRect(uint16_t x,uint16_t y,uint16_t w,uint16_t h,uint16_t color){
	if ((x >= *_width) || (y >= *_height))
	{
		return;
	}

	if ((x+w-1) >= *_width)
	{
		w = *_width-x;
	}

	if ((y+h-1) >= *_height)
	{
		h = *_height-y;
	}

	setAddress(x, y, x+w-1, y+h-1);


	lcdWrite8Reg(ILI9341_MEMORYWRITE);
	CD = 1;
	for(y=h; y>0; y--)
	{
		for(x=w; x>0; x--)
		{
			lcdWrite8Data(color>>8);
			lcdWrite8Data(color);
		}
	}
}

void fillScreen(void){
	long len = 76800;
	int blocks = 1200;
   	uint8_t  i, hi =*textbgcolor >> 8;
        uint8_t lo = *textbgcolor;
	setAddress(0,0,*_width-1,*_height-1);

	lcdWrite8Reg(ILI9341_MEMORYWRITE);
	lcdWrite8Data(hi);
	lcdWrite8Data(lo);
	len--;
	IOM = 1;
	while(blocks--) {
      i = 16;
      do {
      		*lcd_address = hi;*lcd_address = lo;
      		*lcd_address = hi;*lcd_address = lo;
      		*lcd_address = hi;*lcd_address = lo;
      		*lcd_address = hi;*lcd_address = lo;
      } while(--i);
    }
    for(i = (char)len & 63; i--; ) {
    	*lcd_address = hi;*lcd_address = lo;
    }
    IOM = 0;

}

void clearLCD(void){
	fillScreen();
	setCursor(0, 0);
}

void drawChar(uint8_t c){
	int16_t x = *cursor_x;
	int16_t y = *cursor_y;
	uint16_t color = *textcolor;
	uint16_t bg = *textbgcolor;
	int8_t size = *textsize;

	int8_t i, j;
	uint8_t line;
	for (i=0; i<6; i++)
	{

		if (i == 5)
		{
			line = 0x0;
		}
		else
		{
			line = pgm_read_byte(font+(c*5)+i);
		}
		for (j = 0; j<8; j++)
		{
			if (line & 0x1)
			{
				if (size == 1)
				{
					drawPixel(x+i, y+j, color);
				}
				else {
					fillRect(x+(i*size), y+(j*size), size, size, color);
				}
			} else if (bg != color)
			{
				if (size == 1)
				{
					drawPixel(x+i, y+j, bg);
				}
				else
				{
					fillRect(x+i*size, y+j*size, size, size, bg);
				}
			}

			line >>= 1;
		}
	}
}

void printChar(uint8_t c)
{
	if (c == '\n')
	{
		*cursor_y += *textsize*8;
		*cursor_x  = 0;
	}
	else if (c == '\r')
	{

	}
	else
	{
		drawChar(c);
		*cursor_x += *textsize*6;
	}
}
void printString(char *str)
{
	int16_t i;
	for(i=0;str[i]!=0;i++)	/* Send each char of string till the NULL */
	{
		printChar(str[i]);	/* Call transmit data function */
	}
}

uint16_t reverse(uint8_t d) {
	uint16_t rev = 0;
	uint16_t val = 0;
	while(d >= 1){

		val = d%10;
		d = d/10;
		rev = rev * 10 + val;
	}
	return rev;
}

void asciiToDec(uint8_t d) {
	uint8_t val;
	uint16_t id;
	id = reverse(d);
	while (id >= 1){

		val = id % 10;
		id = id/10;
		printChar(val + '0');
	}
}

void asciiToHex(uint8_t d) {
	uint8_t val;
	uint8_t store[2];
	store[0] = 0;
	store[1] = 0;
	val = d & 0x0F;
	d = d >> 4;
	if (val <= 9) {
		store[0] = val + '0';
	}
	else {
		store[1] = (val%10) + 'A';
	}
	if (d <= 9) {
		store[0] = d + '0';
	}
	else {
		store[1] = (d%10) + 'A';
	}
	printChar(store[1]);
	printChar(store[0]);
}

void errorBackToMenu(char* message) {
	setCursor(10, 191);
	printString(message);
	setCursor(10, 216);
	printString("<1 Back to Menu");
	waitForKey(1);
}
// associated header file
#include "Memory.h"

// including standard headers and portable datatypes
#include "8051.h"
#include <stdint.h>
#include "Bool.h"
#include "LCD.h"

uint8_t readFromExternalAddress(uint16_t address) {
	// load our data pointer with the incoming address before we jump to assembly
	DPH = (address >> 8);
	DPL = address;
	__asm
		MOVX A, @DPTR
	__endasm;
	uint8_t value = ACC;
	return value;
}

void writeToExternalAddress(uint16_t address, uint8_t value) {
	// load our data pointer with the incoming address and A with the value before we jump to assembly
	DPH = (address >> 8);
	DPL = address;
	ACC = value;
	__asm
		MOVX @DPTR, A
	__endasm;
	return;
}

uint16_t searchForwardForExternalValue(uint16_t address, uint8_t value, bool* success) {
	// starting at our current address, we'll compare each byte to our value
	// if we find it, we'll set success true and return with the current address
	// otherwise, we'll increment the address and continue
	// if we reach the end of the memory block, we'll set success false and return
	for (address; address + 1 > 0; address++) {
		uint8_t currentValue = readFromExternalAddress(address);
		if (currentValue == value) {
			*success = true;
			return address;
		}
	}
	success = false;
	return 0;
}

uint16_t searchBackwardForExternalValue(uint16_t address, uint8_t value, bool* success) {
	for (address; address - 1 < 0xFFFF; address--) {
		uint8_t currentValue = readFromExternalAddress(address);
		if (currentValue == value) {
			*success = true;
			return address;
		}
	}
	success = false;
	return 0;
}

uint16_t searchRangeForValue(uint16_t startAddress, uint16_t endAddress, uint8_t value, bool* success) {
	for (startAddress; startAddress <= endAddress; startAddress++) {
		uint8_t readValue = readFromExternalAddress(startAddress);
		if (readValue == value) {
			*success = true;
			return startAddress;
		}
	}
	*success = false;
	return 0;
}


uint16_t countValueInRange(uint16_t startAddress, uint16_t endAddress, uint8_t value) {
	uint16_t count = 0;
	for (startAddress; startAddress < endAddress; startAddress++) {
		uint8_t readValue = readFromExternalAddress(startAddress);
		if (readValue == value) {
			count++;
		}
	}
	// quick final check
	if (startAddress == 0xFFFF) {
		uint8_t readValue = readFromExternalAddress(startAddress);
		if (readValue == value) {
			count++;
		}
	}
	return count;
}
// associated header file
#include "Menu.h"

// including standard headers and portable datatypes
#include "8051.h"
#include <stdint.h>
#include "LCD.h"
#include "Keypad.h"
#include "Utils.h"
#include "Constants.h"
#include "RTC.h"

__code uint16_t futurama[] = {0x10C3,0x2146,0x2146,0x2146,0x1945,0x2146,0x2146,0x2146,0x2146,0x2146,0x1945,0x1945,0x2146,0x2146,0x2146,0x2146,0x1945}; // full array omitted for brevity

void titleScreen(void) {
	setCursor(60, 70);
	printString("  Carter Brehm  ");
	setCursor(60, 150);
	printString(" ECEN 433    *> ");
	// we don't care what button they press here
	while (readKeypad() == 0) {
		setCursor(50, 216);
		rtcPrint();
	}
	clearLCD();
}

void fryScreen(void) {
	uint16_t i = 0;
	uint16_t j = 0;
	uint16_t k = 0;
	while (i < 19200) {
		//drawPixel(2*j, 2*k, futurama[i]);
		fillRect(2*j, 2*k, 2, 2, futurama[i]);
		j++;
		i++;
		if (j == 160) {
			j = 0;
			k++;
		}
	}
	setCursor(30, 216);
	setTextColor(WHITE, BLACK);
	printString("I.C. Wiener? Oh, crud.");
	while (readKeypad() == 0);
	setTextColor(WHITE, GREEN);
}

uint8_t menu(void) {
	clearLCD();
	setCursor(85, 5);
	printString("Menu Options");

	for(int i = 0; i < menuCount; i++) {
		setCursor(80, (i+2)*24);
		printString(menuOptions[i]);
	}
	while (1) {
		uint8_t menuChoice = waitForKeypad();
		if ((menuChoice >= 0x0A && menuChoice <= 0x0F) || (menuChoice >= 0x01 && menuChoice <= 0x03)) { // return the user's choice
			clearLCD();
			return menuChoice;
		} else {
			menuChoice = waitForKeypad();
			break;
		}
	}
	return 0;
}

uint8_t blockSizeMenu(void) {
	clearLCD();
	setCursor(85, 10);
	printString("Block Size");

	for(int i = 0; i < blockSizeMenuCount; i++) {
		setCursor(70, (i+3)*25);
		printString(blockSizeMenuOptions[i]);
	}
	while (1) {
		uint8_t menuChoice = waitForKeypad();
		if (menuChoice >= 0x0A && menuChoice <= 0x0F) { // return the user's choice
			clearLCD();
			return menuChoice;
		} else {
			menuChoice = waitForKeypad();
			break;
		}
	}
	return 0;
}
// associated header file
#include "Move.h"

// including standard headers and portable datatypes
#include "8051.h"
#include <stdint.h>
#include "LCD.h"
#include "Keypad.h"
#include "Utils.h"
#include "Menu.h"
#include "Memory.h"

void move(void) {
	uint16_t srcAddress = promptForWord("Src Address?");
	uint8_t blockSize = blockSizeMenu();
	uint16_t blockQuantity = promptForWord("Block Quantity?");

	// quick check to make sure they didn't want zero blocks
	while (!blockQuantity) {
		blockQuantity = promptForWord("Quantity > 0!");
	}

	// turns the user's block size from a letter choice to a number
	switch (blockSize) {
		case 0x0B: // word
			blockSize = 2;
			break;
		case 0x0C: // double word
			blockSize = 4;
			break;
		case 0x0D: // quad word!
			blockSize = 8;
			break;
		default: // byte
			blockSize = 1;
			break;
	}

	clearLCD();

	uint16_t totalBytes = 0;
	// check for if our block of memory will be too big
	if (blockSize * blockQuantity < blockSize) {
		errorBackToMenu("Block Overflow!");
		return;
	} else {
		totalBytes = blockSize * blockQuantity;
	}

	// sanity checks to make sure we won't overflow
	if (srcAddress + (totalBytes) < srcAddress) {
		errorBackToMenu("Src Overflow!");
		return;
	}

	uint16_t destAddress = promptForWord("Dest Address?");

	if (destAddress + (totalBytes) < destAddress) {
		errorBackToMenu("Dest Overflow!");
		return;
	}

	clearLCD();

	// XXXX * Y bytes
	setCursor(20, 10);
	printString("Moving ");
	printHexWordToASCIIArray(blockQuantity);
	printString(" * ");
	printHexByteToASCIIArray(blockSize);
	printString(" bytes");

	// 0xAAAA -> 0xBBBB
	setCursor(20, 35);
	printString("from 0x");
	printHexWordToASCIIArray(srcAddress);
	printString(" -> 0x");
	printHexWordToASCIIArray(destAddress);

	// for each byte in totalBytes, copy from src to dest, then increase pointers
	while (totalBytes > 0) {
		writeToExternalAddress(destAddress, readFromExternalAddress(srcAddress));
		totalBytes--;
		srcAddress++;
		destAddress++;
	}

	errorBackToMenu("Move Complete!");

}
#include "RTC.h"
#include "LCD.h"
#include <stdint.h>
#include "Utils.h"
#include "Keypad.h"
#include "Constants.h"

// void rtcInit(void)
// {
//     uint8_t mon_ctrl = __MON_E32K_SET__ | 0x05;   // Set month to May (05)
//     uint8_t ctrl_a =  __CTRLA_PAB_SET__;          // Control A settings (no changes needed here)
//     uint8_t ctrl_b = (__CTRLB_TE_SET__ | __CTRLB_BME_SET__); // Control B settings (no changes needed here)

//     iowrite8((uint8_t __xdata *)(__RTC_CTRL_A_REG__), ctrl_a); // Set Control A register
//     iowrite8((uint8_t __xdata *)(__RTC_CTRL_B_REG__), ctrl_b); // Set Control B register
//     iowrite8((uint8_t __xdata *)(__RTC_MONTH_REG__), mon_ctrl); // Set month to May
//     iowrite8((uint8_t __xdata *)(__RTC_YEAR_REG__), 0x24); // Set year to 24 (2024 in BCD)
//     iowrite8((uint8_t __xdata *)(__RTC_DATE_REG__), 0x09); // Set date to 08 (for May 8th)
//     iowrite8((uint8_t __xdata *)(__RTC_HOURS_REG__), 0x01); // Hours in 24-hour format, 14 for 2 PM
//     iowrite8((uint8_t __xdata *)(__RTC_MINUTES_REG__), 0x00); // Set minutes to 30
//     iowrite8((uint8_t __xdata *)(__RTC_SECONDS_REG__), 0x00); // Set seconds to 00

//     // Additional setup can be done below if needed (e.g., alarms)
// }




uint8_t rtcBcdToAscii(uint8_t bcd)
{
	bcd = bcd & 0x0F;
	if (bcd < 10)
	{
		bcd = 0x30 + bcd;
	}
	return bcd;
}

uint8_t rtcAsciiToBcd(uint8_t ascii)
{
	if(ascii >= 0x30 && ascii < 0x3A)
	{
		ascii = ascii & 0x0F;
	}
	return ascii;
}

void rtcRegToLCD(uint8_t __xdata * reg, uint8_t mask)
{
	uint8_t rtcTemp,rtcCurrent;
	rtcCurrent = ioread8(reg);
	rtcCurrent = rtcCurrent & mask;
	rtcTemp = rtcCurrent >> 4;
	printChar(rtcBcdToAscii(rtcTemp));
	printChar(rtcBcdToAscii(rtcCurrent));
}

void rtcPrint(void)
{
	uint8_t __xdata * ptr = (uint8_t __xdata *)(__RTC_MONTH_REG__);
	rtcRegToLCD(ptr, 0x1F);
	printChar('/');
	ptr = (uint8_t __xdata *)(__RTC_DATE_REG__);
	rtcRegToLCD(ptr, 0x3F);
	printChar('/');
	ptr = (uint8_t __xdata *)(__RTC_YEAR_REG__);
	rtcRegToLCD(ptr, 0xFF);
	printChar(' ');
	ptr = (uint8_t __xdata *)(__RTC_HOURS_REG__);
	rtcRegToLCD(ptr, 0x3F);
	printChar(':');
	ptr = (uint8_t __xdata *)(__RTC_MINUTES_REG__);
	rtcRegToLCD(ptr, 0x7F);
	printChar(':');
	ptr = (uint8_t __xdata *)(__RTC_SECONDS_REG__);
	rtcRegToLCD(ptr,0x7F);
}

void rtcMenu(void) {
	readKeypad();
	setCursor(10, 10);
	printString("Current date and time:");
	setCursor(7, 216);
	printString("<0 Exit (hold)    E Edit>");
	uint8_t keypadIndex = 0;
	while (keypadIndex == 0) {
		setCursor(40, 120);
		rtcPrint();
		keypadIndex = readKeypad();
	}
	// they pressed edit
	if (keypadIndex == 0xDE) {
		setCursor(10, 10);
		printString("Editing date and time...");
		setCursor(40, 120);
		printString("__/__/__ __:__:__");
		setCursor(40, 120);

		// month
		uint8_t firstCharacter = waitForKeypad();
		printChar(asciiLookup[firstCharacter]);
		uint8_t secondCharacter = waitForKeypad();
		printChar(asciiLookup[secondCharacter]);

		uint8_t month = (firstCharacter << 4) | secondCharacter;

		printString("/");

		// day
		firstCharacter = waitForKeypad();
		printChar(asciiLookup[firstCharacter]);
		secondCharacter = waitForKeypad();
		printChar(asciiLookup[secondCharacter]);

		uint8_t day = (firstCharacter << 4) | secondCharacter;

		printString("/");

		// year
		firstCharacter = waitForKeypad();
		printChar(asciiLookup[firstCharacter]);
		secondCharacter = waitForKeypad();
		printChar(asciiLookup[secondCharacter]);

		uint8_t year = (firstCharacter << 4) | secondCharacter;

		printString(" ");

		// hours
		firstCharacter = waitForKeypad();
		printChar(asciiLookup[firstCharacter]);
		secondCharacter = waitForKeypad();
		printChar(asciiLookup[secondCharacter]);

		uint8_t hours = (firstCharacter << 4) | secondCharacter;

		printString(":");

		// minutes
		firstCharacter = waitForKeypad();
		printChar(asciiLookup[firstCharacter]);
		secondCharacter = waitForKeypad();
		printChar(asciiLookup[secondCharacter]);

		uint8_t minutes = (firstCharacter << 4) | secondCharacter;

		printString(":");

		// seconds
		firstCharacter = waitForKeypad();
		printChar(asciiLookup[firstCharacter]);
		secondCharacter = waitForKeypad();
		printChar(asciiLookup[secondCharacter]);

		uint8_t seconds = (firstCharacter << 4) | secondCharacter;

		uint8_t mon_ctrl = __MON_E32K_SET__ | month;   // Set month to May (05)
	    uint8_t ctrl_a =  __CTRLA_PAB_SET__;          // Control A settings (no changes needed here)
	    uint8_t ctrl_b = (__CTRLB_TE_SET__ | __CTRLB_BME_SET__); // Control B settings (no changes needed here)

	    iowrite8((uint8_t __xdata *)(__RTC_CTRL_A_REG__), ctrl_a); // Set Control A register
	    iowrite8((uint8_t __xdata *)(__RTC_CTRL_B_REG__), ctrl_b); // Set Control B register
	    iowrite8((uint8_t __xdata *)(__RTC_MONTH_REG__), mon_ctrl); // Set month to May
	    iowrite8((uint8_t __xdata *)(__RTC_YEAR_REG__), year); // Set year to 24 (2024 in BCD)
	    iowrite8((uint8_t __xdata *)(__RTC_DATE_REG__), day); // Set date to 08 (for May 8th)
	    iowrite8((uint8_t __xdata *)(__RTC_HOURS_REG__), hours); // Hours in 24-hour format, 14 for 2 PM
	    iowrite8((uint8_t __xdata *)(__RTC_MINUTES_REG__), minutes); // Set minutes to 30
	    iowrite8((uint8_t __xdata *)(__RTC_SECONDS_REG__), seconds); // Set seconds to 00

	}
}
// associated header file
#include "RamCheck.h"

// including standard headers and portable datatypes
#include "8051.h"
#include <stdint.h>
#include "Bool.h"
#include "LCD.h"
#include "Utils.h"
#include "Keypad.h"

void writeAllRam(uint8_t value) {
	// the assembly code will fetch the value to be written from the bccumulator
	B = value;
	__asm
		RAMCHECK_WRITE_LOOP .EQU 1
		MOV DPH, #0
		MOV DPL, #0
		RAMCHECK_WRITE_LOOP$:
			MOV A, B
			MOVX @DPTR, A
			INC DPTR
			MOV A, DPL
			ORL A, DPH
			JNZ RAMCHECK_WRITE_LOOP$
	__endasm;
}

bool readAllRam(uint8_t value) {
	// the assembly code will fetch the value to be compared against from the bccumulator
	B = value;
	__asm
		RAMCHECK_READ_LOOP .EQU 1
		RAMCHECK_READ_FAIL .EQU 2
		RAMCHECK_READ_DONE .EQU 3
		MOV DPH, #0
		MOV DPL, #0
		RAMCHECK_READ_LOOP$:
			MOVX A, @DPTR
			SUBB A, B
			JNZ RAMCHECK_READ_FAIL$
			INC DPTR
			MOV A, DPL
			ORL A, DPH
			JNZ RAMCHECK_READ_LOOP$
			MOV B, #1
			SJMP RAMCHECK_READ_DONE$
		RAMCHECK_READ_FAIL$:
			MOV B, #0
		RAMCHECK_READ_DONE$:
	__endasm;
	// returns the value our assembly put in regB as a true/false
	return B;
}

// takes a byte value, writes it to the entire XRAM, then inverts and repeats
// returns FALSE on success, TRUE on error
bool ramCheckCycle(uint8_t value) {
	// Writing RAM w/ value: 0x__
	printString("Writing 0x");
	printHexByteToASCIIArray(value);
	printString("... ");

	// perform the write
	writeAllRam(value);

	printString("done");

	// Checking RAM w/
	// leaving "value: 0x__" on screen
	setCursor(10, getCursorY() + 25);
	printString("Checking for 0x");
	printHexByteToASCIIArray(value);
	printString("... ");

	// perform the read
	bool readSuccess = readAllRam(value);

	// if we failed the read, tell the user and duck out immediately
	if (!readSuccess) {
		errorBackToMenu("RAM Fail!");
		// TODO: return address of failure?
		return true;
	}

	// we succeeded!
	printString("done");
	setCursor(10, getCursorY() + 25);
	return false;
}

void ramCheck(void) {
	uint8_t value = promptForByte("RAM Check Value?");

	clearLCD();
	setCursor(10, 10);

	if (ramCheckCycle(value) != 0 || ramCheckCycle(~value) != 0)
		// error, return immediately to menu
		return;

	errorBackToMenu("RAM Success!");
	return;
}
#include "Segment.h"
#include "8051.h"
#include "LCD.h"
#include "Constants.h"
#include <stdint.h>
#include "Utils.h"


void hexToSegment(uint8_t byte) {
	uint8_t segment = 0;
	segment = segconvert[byte];
	iowrite8((char __xdata*)(0x8000), segment);
}

void loadingLoop(uint8_t count) {
	for (uint8_t j = 0; j < count; j++) {
		iowrite8((char __xdata*)(0x8000), 0x01);
		delay(100);
		iowrite8((char __xdata*)(0x8000), 0x02);
		delay(100);
		iowrite8((char __xdata*)(0x8000), 0x04);
		delay(100);
		iowrite8((char __xdata*)(0x8000), 0x08);
		delay(100);
		iowrite8((char __xdata*)(0x8000), 0x10);
		delay(100);
		iowrite8((char __xdata*)(0x8000), 0x20);
		delay(100);
	}
	iowrite8((char __xdata*)(0x8000), 0x00);
}
#include "Serial.h"
#include "8051.h"

void serialInit(void) {
	P3 |= (1<<0) | (1<<1);
	TL1 = 0xF3;
	TH1 = 0xF3;
	TMOD = 0x20;
	PCON |= (1<<7);
	SCON |= (1<<6) | (1<<4);
	IE |= (1<<7) | (1<<4);

	TCON = 0x40;
}
// associated header file
#include "Utils.h"

// including standard headers and portable datatypes
#include "8051.h"
#include <stdint.h>
#include "Constants.h"
#include "LCD.h"

void printHexByteToASCIIArray(uint8_t byte) {
	printChar(asciiLookup[((byte >> 4) & 0x0F)]);
	printChar(asciiLookup[(byte & 0x0F)]);
}

void printHexWordToASCIIArray(uint16_t word) {
	printChar(asciiLookup[((word >> 12) & 0x0F)]);
	printChar(asciiLookup[((word >> 8) & 0x0F)]);
	printChar(asciiLookup[((word >> 4) & 0x0F)]);
	printChar(asciiLookup[(word & 0x0F)]);
}

inline void iowrite8(uint8_t __xdata* map_address, uint8_t d) {
	IOM = 1;
	*map_address = d;
	IOM = 0;
}

inline uint8_t ioread8(uint8_t __xdata* map_address)
{
	uint8_t d;
	IOM = 1;
	d = *map_address;
	IOM = 0;
	return d;
}
#ifndef ADC_H
#define ADC_H

void adcMenu(void);
void adcPrint(void);

#endif
#ifndef CONSTANTS_H
#define CONSTANTS_H
#include <stdint.h>
extern __code char asciiLookup[17];

extern __code char menuCount;
extern __code char* menuOptions[8];

extern __code char blockSizeMenuCount;
extern __code char* blockSizeMenuOptions[4];
extern __code char blockSizeMenuButtons[4];
extern __code char segconvert[16];
#endif
#ifndef COUNT_H
#define COUNT_H
void count(void);
#endif
#ifndef DUMP_H
#define DUMP_H

#include "8051.h"
#include <stdint.h>
#include "Bool.h"

uint8_t dumpPage(uint16_t address, uint8_t byteCount, bool pagesBefore, bool pagesAfter);

void dump(void);

#endif
#ifndef EDIT_H
#define EDIT_H
#include <stdint.h>
uint8_t editPage(uint16_t address);
void edit(void);
#endif
#ifndef FIND_H
#define FIND_H
#include <stdint.h>
#include "Bool.h"
uint8_t findPage(uint16_t address, bool pagesBefore, bool pagesAfter);
void findArgs(uint16_t startAddress, uint16_t blockQuantity, uint8_t searchValue);
void find(void);
#endif
#ifndef KEYPAD_H
#define KEYPAD_H

#include "8051.h"
#include <stdint.h>

uint8_t checkRows(void);
uint8_t checkColumns(void);
uint8_t readKeypad(void);
uint8_t decodeKeypadToHex(uint8_t keypadIndex);
uint8_t waitForKeypad(void);
void waitForKey(uint8_t key);
uint8_t promptForByte(char* prompt);
uint16_t promptForWord(char* prompt);
void setSerialData(uint8_t data);
uint8_t getSerialData(void);

#endif
/* all the required header for project here*/

#ifndef LCD_H
#define LCD_H

#include <8051.h>
#include <stdint.h>

/* define any address for lcd for address decoding */
#define LCD_ADDRESS 0x4000


/* LCD specific variables */
#define TFTWIDTH 240
#define TFTHEIGHT 320



/* A good way to stay consistent with writing signals */

#define ACTIVE	0
#define IDLE 	1
#define CMD 	0
#define DATA 	1
#define IO 	1
#define MEM 	0

/* LCD control pin interfacing. */
#define IOM P3_4
#define CD P3_5


/* definition of colors in 2-bytes*/
#define BLACK 0x0000
#define GRAY 0xD6BA
#define BLUE 0x001F
#define RED 0xF800
#define LIME 0x07E0
#define GREEN 0x0401
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF

/*ILI9341 Registers*/
#define ILI9341_SOFTRESET          0x01
#define ILI9341_SLEEPIN            0x10
#define ILI9341_SLEEPOUT           0x11
#define ILI9341_NORMALDISP         0x13
#define ILI9341_INVERTOFF          0x20
#define ILI9341_INVERTON           0x21
#define ILI9341_GAMMASET           0x26
#define ILI9341_DISPLAYOFF         0x28
#define ILI9341_DISPLAYON          0x29
#define ILI9341_COLADDRSET         0x2A
#define ILI9341_PAGEADDRSET        0x2B
#define ILI9341_MEMORYWRITE        0x2C
#define ILI9341_PIXELFORMAT        0x3A
#define ILI9341_FRAMECONTROL       0xB1
#define ILI9341_DISPLAYFUNC        0xB6
#define ILI9341_ENTRYMODE          0xB7
#define ILI9341_POWERCONTROL1      0xC0
#define ILI9341_POWERCONTROL2      0xC1
#define ILI9341_VCOMCONTROL1       0xC5
#define ILI9341_VCOMCONTROL2       0xC7
#define ILI9341_MEMCONTROL         0x36
#define ILI9341_MADCTL  	   0x36

#define ILI9341_MADCTL_MY  	   0x80
#define ILI9341_MADCTL_MX  	   0x40
#define ILI9341_MADCTL_MV  	   0x20
#define ILI9341_MADCTL_ML  	   0x10
#define ILI9341_MADCTL_RGB 	   0x00
#define ILI9341_MADCTL_BGR 	   0x08
#define ILI9341_MADCTL_MH  	   0x04


//Prototypes!

void delay (int16_t d);

inline void lcdWrite8Reg(uint8_t d);

inline void lcdWrite8Data(uint8_t d);

inline void lcdWrite8DataBlocking(uint8_t d);

inline void lcdWriteRegData8(uint8_t a, uint8_t d);

void setCursorx(uint16_t x);

void setCursory(uint16_t y);

void lcdWriteRegData16(uint16_t a, uint16_t d);

void setCursor(uint16_t x, uint16_t y);

void setTextColor(uint16_t x, uint16_t y);

void setTextSize(uint8_t s);

void setRotation(uint8_t flag);


void setAddress(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2);

void initLCD(void);

void drawPixel(uint16_t x3,uint16_t y3,uint16_t color1);

void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);

void fillRect(uint16_t x,uint16_t y,uint16_t w,uint16_t h,uint16_t color);

void fillScreen(void);

void drawChar(uint8_t c);

void printChar(uint8_t c);

void printString(char *str);

uint16_t reverse(uint8_t d);

void asciiToDec(uint8_t d);

void asciiToHex(uint8_t d);

uint16_t getCursorX(void);

uint16_t getCursorY(void);

uint8_t getTextSize(void);

void clearLCD(void);

void errorBackToMenu(char* message);

#endif
#ifndef MEMORY_H
#define MEMORY_H
#include "8051.h"
#include <stdint.h>
#include "Bool.h"
uint8_t readFromExternalAddress(uint16_t address);
void writeToExternalAddress(uint16_t address, uint8_t value);
uint16_t searchForwardForExternalValue(uint16_t address, uint8_t value, bool* success);
uint16_t searchBackwardForExternalValue(uint16_t address, uint8_t value, bool* success);
uint16_t countValueInRange(uint16_t startAddress, uint16_t endAddress, uint8_t value);

#endif
#ifndef MENU_H
#define MENU_H

#include <stdint.h>

void titleScreen(void);

void fryScreen(void);

uint8_t menu(void);

uint8_t blockSizeMenu(void);

#endif
#ifndef MOVE_H
#define MOVE_H
void move(void);
#endif
#ifndef RTC_H
#define RTC_H

#include <stdint.h>

#define RTC_ADDRESS 0x0000

#define __RTC_SECONDS_REG__                 RTC_ADDRESS + (0x00 << 9)
#define __RTC_MINUTES_REG__                 RTC_ADDRESS + (0x01 << 9)
#define __RTC_HOURS_REG__                	RTC_ADDRESS + (0x02 << 9)
#define __RTC_DAYS_REG__ 		            RTC_ADDRESS + (0x03 << 9)
#define __RTC_DATE_REG__        	        RTC_ADDRESS + (0x04 << 9)
#define __RTC_MONTH_REG__           	    RTC_ADDRESS + (0x05 << 9)
#define __RTC_YEAR_REG__                	RTC_ADDRESS + (0x06 << 9)
#define __RTC_CENTURY_REG__                	RTC_ADDRESS + (0x07 << 9)
#define __RTC_ALARM_SEC_REG__               RTC_ADDRESS + (0x08 << 9)
#define __RTC_ALARM_MIN_REG__               RTC_ADDRESS + (0x09 << 9)
#define __RTC_ALARM_HOUR_REG__              RTC_ADDRESS + (0x0A << 9)
#define __RTC_ALARM_DAY_REG__               RTC_ADDRESS + (0x0B << 9)
#define __RTC_WATCHDOG1_REG__               RTC_ADDRESS + (0x0C << 9)
#define __RTC_WATCHDOG2_REG__               RTC_ADDRESS + (0x0D << 9)
#define __RTC_CTRL_A_REG__                  RTC_ADDRESS + (0x0E << 9)
#define __RTC_CTRL_B_REG__                  RTC_ADDRESS + (0x0F << 9)
#define __RTC_RAM_ADDR_REG__				RTC_ADDRESS + (0x10 << 9)
#define __RTC_EXT_RAM_ADDR_REG__			RTC_ADDRESS + (0x13 << 9)

// Control A Register Values
#define __CTRLA_WDF_SET__		0x02
#define __CTRLA_KSF_SET__ 		0x04
#define __CTRLA_TDF_SET__ 		0x08
#define __CTRLA_PAB_SET__		0x10
#define __CTRLA_PRS_SET__		0x20

// Control B Register Values
#define __CTRLB_WDS_SET__ 		0x01
#define __CTRLB_WDE_SET__ 		0x02
#define __CTRLB_KIE_SET__ 		0x04
#define __CTRLB_TIE_SET__ 		0x08
#define __CTRLB_TPE_SET__ 		0x10
#define __CTRLB_BME_SET__ 		0x20
#define __CTRLB_CS_SET__ 		0x40
#define __CTRLB_TE_SET__ 		0x80

//Month Control Bits
#define __MON_EOSC_SET__ 		0x80
#define __MON_E32K_SET__ 		0x40
#define __MON_BB32_SET__ 		0x20

//Masks
#define __RTC_MONTH_MASK__ 		0x1F
#define __RTC_DATE_MASK__ 		0x3F
#define __RTC_SECONDS_MASK__  	0x7F
#define __RTC_MINUTES_MASK__    0x7F
#define __RTC_HOURS_MASK__		0x3F
#define __RTC_DAYS_MASK__		0x07
#define __RTC_YEAR_MASK__		0xFF
#define __RTC_CENTURY_MASK__	0xFF

// void rtcInit(void);

uint8_t rtcBcdToAscii(uint8_t bcd);

uint8_t rtcAsciiToBcd(uint8_t ascii);

void rtcRegToLCD(uint8_t __xdata * reg, uint8_t mask);

void rtcPrint(void);

void rtcMenu(void);

#endif
#ifndef RAMCHECK_H
#define RAMCHECK_H

#include <stdint.h>
#include "Bool.h"

void writeAllRam(uint8_t value);
bool readAllRam(uint8_t value);
bool ramCheckCycle(uint8_t value);
void ramCheck(void);


#endif
#ifndef SEG_H
#define SEG_H
#define SEG_7_ADDRESS 0x8000
#include <stdint.h>
#include <8051.h>

void hexToSegment(uint8_t byte);

void loadingLoop(uint8_t count);
#endif
#ifndef SERIAL_H
#define SERIAL_H

#include <stdint.h>
#include "8051.h"

void serialInit(void);

#endif
#ifndef UTILS_H
#define UTILS_H
#include <stdint.h>
void printHexByteToASCIIArray(uint8_t byte);

void printHexWordToASCIIArray(uint16_t word);

char * hexByteToASCIIArray(uint8_t byte);

char* hexWordToASCIIArray(uint16_t word);

inline void iowrite8(uint8_t __xdata* map_address, uint8_t d);

inline uint8_t ioread8(uint8_t __xdata* map_address);

#endif

Implementation Code for PLD

/****** INPUT PINS ******/
PIN 2 = iom;
PIN 3 = psen;
PIN 4 = wr;
PIN 5 = a15;
PIN 6 = a14;

/****** OUTPUT PINS ******/
PIN 19 = cs_rom1;
PIN 18 = cs_rom2;
PIN 17 = cs_ram1;
PIN 16 = cs_ram2;
PIN 15 = cs_adc;
PIN 14 = cs_lcd;
PIN 13 = cd_rtc;
PIN 12 = cs_seg;

cs_rom1 = !(!psen & !iom & !a15);
cs_rom2 = !(!psen & !iom & a15);
cs_ram1 = !(psen & !iom & !a15);
cs_ram2 = !(psen & !iom & a15);
cs_adc = !(psen & iom & a15 & a14);
cs_lcd = !(psen & iom & !a15 & a14);
cd_rtc = !(psen & iom & !a15 & !a14);
cs_seg = (psen & iom & a15 & !a14 & !wr);