This commit is contained in:
shadlyd15 2022-05-07 22:02:00 +02:00
parent 7913ea3735
commit 2ba07d064c
54 changed files with 4794 additions and 0 deletions

111
NesUEFI/Makefile Normal file
View File

@ -0,0 +1,111 @@
#
# Copyright (c) 2011, Intel Corporation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
IMAGE = NesUEFI
TARGET = main.efi
INCDIR = /usr/local/include
LIBDIR = /usr/local/lib
EFILIB = /usr/local/lib
OVMF_DIR = ../OVMF
CC = gcc
OBJCOPY = objcopy
%.efi: %.so
$(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel \
-j .rela -j .reloc -S --target=$(FORMAT) $*.so $@
HOST = $(shell $(CC) -dumpmachine | sed "s/\(-\).*$$//")
ARCH := $(shell $(CC) -dumpmachine | sed "s/\(-\).*$$//")
ifeq ($(ARCH),x86_64)
FORMAT=efi-app-x86-64
else
ARCH=ia32
FORMAT=efi-app-$(ARCH)
endif
EFI_CRT_OBJS := $(EFILIB)/crt0-efi-$(ARCH).o
EFI_LDS := $(EFILIB)/elf_$(ARCH)_efi.lds
CFLAGS=-I. -I$(INCDIR)/efi -I$(INCDIR)/efi/$(ARCH) -mno-red-zone -Wno-unused-variable\
-DEFI_FUNCTION_WRAPPER -fPIC -fshort-wchar -ffreestanding \
-Wall -D$(ARCH) -Werror
ifeq ($(ARCH),ia32)
ifeq ($(HOST),x86_64)
CFLAGS += -m32
endif
endif
ifeq ($(ARCH),x86_64)
CFLAGS += -mno-red-zone
endif
LDFLAGS=-T $(EFI_LDS) -Bsymbolic -shared -nostdlib -znocombreloc \
-L$(LIBDIR) $(EFI_CRT_OBJS)
SOURCES := $(wildcard ./**/*.c) $(wildcard ./*.c)
OBJS := $(patsubst ./%.c, ./%.o, $(SOURCES))
all: $(TARGET)
$(TARGET): main.so
main.so: $(OBJS)
$(LD) $(LDFLAGS) -o $@ $^ -lgnuefi -lefi $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
clean:
rm -f $(TARGET) main.so $(OBJS) $(IMAGE).img
img:
dd if=/dev/zero of=$(IMAGE).img bs=512 count=93750
mformat -i $(IMAGE).img -h 32 -t 32 -n 64 -c 1 ::
mmd -i $(IMAGE).img ::/EFI
mmd -i $(IMAGE).img ::/EFI/BOOT
mcopy -i $(IMAGE).img $(TARGET) ::/EFI/BOOT
mcopy -i $(IMAGE).img startup.nsh ::
mcopy -i $(IMAGE).img Test.nes ::
mcopy -i $(IMAGE).img 1942.nes ::
mcopy -i $(IMAGE).img SM.nes ::
mcopy -i $(IMAGE).img splash.bmp ::
# Add your roms here
run:
qemu-system-x86_64 -drive format=raw,file=$(IMAGE).img -m 256M -cpu qemu64 \
-drive if=pflash,format=raw,unit=0,file="$(OVMF_DIR)/OVMF_CODE-pure-efi.fd",readonly=on \
-drive if=pflash,format=raw,unit=1,file="$(OVMF_DIR)/OVMF_VARS-pure-efi.fd" \
-net none

BIN
NesUEFI/NesUEFI.img Normal file

Binary file not shown.

3
NesUEFI/build.sh Executable file
View File

@ -0,0 +1,3 @@
make clean
make
make img

77
NesUEFI/compile.md Normal file
View File

@ -0,0 +1,77 @@
# Compile and run GNU-EFI applications under linux
## Requirements :
Download gnu-efi from here : [https://sourceforge.net/projects/gnu-efi/](https://sourceforge.net/projects/gnu-efi/)
- **gcc-3.0 or newer**
```bash
gcc --version
```
- A version of **objcopy** that supports EFI applications :
```bash
objcopy --help
```
Check if supported target contains elf64-x86-64 elf32-i386 elf32-x86-64
- **mtools** : Utilities to access MS-DOS disks from Linux
## Compile and install GNU-EFI Library:
Go to gnu-efi source folder
```bash
make
make install
```
## Compile GNU-EFI application :
Use this makefile to easily compile and run gnu-efi application. It will recursively compile all c files in the sub-directories.
Change the following macros to locate the libraries and headers installed on the previous step.
```makefile
IMAGE = uefi_app
TARGET = main.efi
INCDIR := /usr/local/include
LIBDIR =/usr/local/lib
EFILIB = /usr/local/lib
```
## Run on qemu :
### Locate OVMF in Makefile:
To run the compiled application in qemu we need OVMF for UEFI emulation. OVMF is a port of Intel's tianocore firmware to the qemu virtual machine. Download it from here.
```makefile
OVMF_DIR := ../OVMFbin
```
Change the OVMF directory in the makefile.
### Create Image :
```bash
make img
```
It will do the following tasks :
```bash
# Create a new image file that will contain the GNU-EFI application.
dd if=/dev/zero of=$(IMAGE).img bs=512 count=93750
# mformat to format it with FAT16.
mformat -i $(IMAGE).img -h 32 -t 32 -n 64 -c 1 ::
# Create directory
mmd -i $(IMAGE).img ::/EFI
mmd -i $(IMAGE).img ::/EFI/BOOT
# Copy image
mcopy -i $(IMAGE).img $(TARGET) ::/EFI/BOOT
# Copy the nsh script to load efi application automatically from fs0
mcopy -i $(IMAGE).img startup.nsh ::
```
### Run :
```bash
make run
```
## Running on real hardware :
- Format a pendrive with FAT32 file system.
- Create /efi/boot/ folders in the pendrive
- Rename main.efi to bootx64.efi for 64 bit and bootia32.efi for 32 bit application.
- For 64 bit application the pendrive should look like this : **/efi/boot/bootx64.efi**
- For 32 bit application the pendrive should look like this : **/efi/boot/bootia32.efi**
# Further Reading :
- https://wiki.osdev.org/UEFI
- https://wiki.osdev.org/GNU-EFI
- https://wiki.osdev.org/UEFI_App_Bare_Bones

View File

@ -0,0 +1,25 @@
#ifndef BMP_HEADER
#define BMP_HEADER
typedef struct {
CHAR8 CharB;
CHAR8 CharM;
UINT32 Size;
UINT16 Reserved[2];
UINT32 ImageOffset;
UINT32 HeaderSize;
UINT32 PixelWidth;
UINT32 PixelHeight;
UINT16 Planes;
UINT16 BitPerPixel;
UINT32 CompressionType;
UINT32 ImageSize;
UINT32 XPixelsPerMeter;
UINT32 YPixelsPerMeter;
UINT32 NumberOfColors;
UINT32 ImportantColors;
} __attribute__((__packed__)) BMP_IMAGE_HEADER;
// Read more: https://blog.fpmurphy.com/2015/08/display-bmp-details-and-image-in-uefi-shell.html#ixzz7SHLiufWS
#endif

200
NesUEFI/driver/graphics.c Normal file
View File

@ -0,0 +1,200 @@
#include <efi.h>
#include <efilib.h>
#include <string.h>
#include "graphics.h"
#include "bmp_header.h"
extern EFI_GUID GraphicsOutputProtocol;
#define be32_to_cpu(x) __builtin_bswap32(x)
#if defined(__x86_64__) || defined(__aarch64__)
UINT64 * _graphics_buffer;
#elif defined(__i386__) || defined(__arm__)
UINT32 * _graphics_buffer;
#else
#error Arch Not Supported
#endif
EFI_STATUS graphics_init(EFI_GRAPHICS_OUTPUT_PROTOCOL ** gop){
EFI_STATUS rc;
EFI_GRAPHICS_OUTPUT_PROTOCOL * protocol;
Print(L"Initializing GOP ...\n");
// rc = uefi_call_wrapper(LibLocateProtocol, 2, &GraphicsOutputProtocol, (void **)&protocol);
rc = LibLocateProtocol(&GraphicsOutputProtocol, (void **)&protocol);
if (EFI_ERROR(rc)) {
Print(L"Could not locate GOP: %r\n", rc);
return rc;
}
if (!protocol) {
Print(L"LocateProtocol(GOP, &gop) returned %r but GOP is NULL\n", rc);
return EFI_UNSUPPORTED;
}
* gop = protocol;
return EFI_SUCCESS;
}
void graphics_print_modes(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop){
int i, imax;
EFI_STATUS rc;
if (gop->Mode) {
imax = gop->Mode->MaxMode;
Print(L"GOP reports MaxMode %d\n", imax);
} else {
Print(L"gop->Mode is NULL\n");
imax = 1;
}
for (i = 0; i < imax; i++) {
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
UINTN SizeOfInfo;
rc = uefi_call_wrapper(gop->QueryMode, 4, gop, i, &SizeOfInfo,
&info);
if (EFI_ERROR(rc) && rc == EFI_NOT_STARTED) {
Print(L"gop->QueryMode() returned %r\n", rc);
Print(L"Trying to start GOP with SetMode().\n");
rc = uefi_call_wrapper(gop->SetMode, 2, gop,
gop->Mode ? gop->Mode->Mode : 0);
rc = uefi_call_wrapper(gop->QueryMode, 4, gop, i,
&SizeOfInfo, &info);
}
if (EFI_ERROR(rc)) {
Print(L"%d: Bad response from QueryMode: %r (%d)\n",
i, rc, rc);
continue;
}
Print(L"%c%d: %dx%d ",
(gop->Mode &&
CompareMem(info,gop->Mode->Info,sizeof(*info)) == 0
) ? '*' : ' ',
i, info->HorizontalResolution, info->VerticalResolution);
switch(info->PixelFormat) {
case PixelRedGreenBlueReserved8BitPerColor:
Print(L"RGBR");
break;
case PixelBlueGreenRedReserved8BitPerColor:
Print(L"BGRR");
break;
case PixelBitMask:
Print(L"R:%08x G:%08x B:%08x X:%08x",
info->PixelInformation.RedMask,
info->PixelInformation.GreenMask,
info->PixelInformation.BlueMask,
info->PixelInformation.ReservedMask);
break;
case PixelBltOnly:
Print(L"(blt only)");
break;
default:
Print(L"(Invalid pixel format)");
break;
}
Print(L" pitch %d\n", info->PixelsPerScanLine);
}
}
EFI_STATUS graphics_set_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop){
int i, imax;
EFI_STATUS rc;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
Print(L"Setting Graphics Mode ...\n");
if (gop->Mode) {
imax = gop->Mode->MaxMode;
} else {
Print(L"gop->Mode is NULL\n");
return EFI_UNSUPPORTED;
}
for (i = 0; i < imax; i++) {
UINTN SizeOfInfo;
rc = uefi_call_wrapper(gop->QueryMode, 4, gop, i, &SizeOfInfo,
&info);
if (EFI_ERROR(rc) && rc == EFI_NOT_STARTED) {
Print(L"gop->QueryMode() returned %r\n", rc);
Print(L"Trying to start GOP with SetMode().\n");
rc = uefi_call_wrapper(gop->SetMode, 2, gop,
gop->Mode ? gop->Mode->Mode : 0);
rc = uefi_call_wrapper(gop->QueryMode, 4, gop, i,
&SizeOfInfo, &info);
}
if (EFI_ERROR(rc)) {
Print(L"%d: Bad response from QueryMode: %r (%d)\n",
i, rc, rc);
continue;
}
if (CompareMem(info, gop->Mode->Info, sizeof (*info)))
continue;
UINT32 pixels = info->VerticalResolution * info->PixelsPerScanLine;
#if defined(__x86_64__) || defined(__aarch64__)
UINT32 graphics_buffer_size = pixels * sizeof(UINT64);
#elif defined(__i386__) || defined(__arm__)
UINT32 graphics_buffer_size = pixels * sizeof(UINT32);
#else
#error Arch Not Supported
#endif
Print(L"Vertical Resolution : %ld\n", info->VerticalResolution);
Print(L"Horizontal Resolution : %ld\n", info->HorizontalResolution);
Print(L"Pixel Format : %ld\n", info->PixelFormat);
Print(L"Pixels PerScanLine : %ld\n", info->PixelsPerScanLine);
return EFI_SUCCESS;
}
Print(L"Never found the active video mode?\n");
return EFI_UNSUPPORTED;
}
EFI_STATUS graphics_set_pixel(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, UINT32 x, UINT32 y, UINT32 colour){
EFI_STATUS rc;
UINT32 PixelsPerScanLine = gop->Mode->Info->PixelsPerScanLine;
#if defined(__x86_64__) || defined(__aarch64__)
*(UINT64*)((UINT64)gop->Mode->FrameBufferBase + (x*4) + (y * PixelsPerScanLine * 4)) = colour;
#elif defined(__i386__) || defined(__arm__)
*(UINT32*)((UINT32)gop->Mode->FrameBufferBase + (x*4) + (y * PixelsPerScanLine * 4)) = colour;
#else
#error Arch Not Supported
#endif
rc = EFI_SUCCESS;
return rc;
}
void graphics_clear_framebuffer(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop){
if(gop){
memset((VOID *)gop->Mode->FrameBufferBase, 0x00, gop->Mode->FrameBufferSize);
}
}
EFI_STATUS
graphics_draw_bitmap( EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,
int x_offset,
int y_offset,
EFI_HANDLE *bmp_buffer){
BMP_IMAGE_HEADER *BmpHeader = (BMP_IMAGE_HEADER *) bmp_buffer;
EFI_STATUS rc = EFI_SUCCESS;
UINT8 *bitmap_data;
bitmap_data = (UINT8*)bmp_buffer + BmpHeader->ImageOffset;
for (unsigned int y = 0; y < BmpHeader->PixelHeight; ++y){
for (unsigned int x = 0; x < BmpHeader->PixelWidth; ++x){
unsigned int* pixPtr = (unsigned int *)bitmap_data + (BmpHeader->PixelHeight - 1 - y) * BmpHeader->PixelWidth + x;
graphics_set_pixel(gop, x_offset + x, y_offset + y, (UINT32)*pixPtr);
}
}
return rc;
}

16
NesUEFI/driver/graphics.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef GRAPHICS_H
#define GRAPHICS_H
#include <efi.h>
#include <efilib.h>
EFI_STATUS graphics_init(EFI_GRAPHICS_OUTPUT_PROTOCOL **gop);
void graphics_print_modes(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop);
EFI_STATUS graphics_set_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop);
EFI_STATUS graphics_set_pixel(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, UINT32 x, UINT32 y, UINT32 colour);
EFI_STATUS graphics_flush_buffer(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop);
EFI_STATUS graphics_draw_bitmap( EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, int x_offset, int y_offset,EFI_HANDLE *bmp_buffer);
void graphics_clear_framebuffer(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop);
#endif

19
NesUEFI/driver/kernel.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef BOOT_H
#define BOOT_H
#include <efi.h>
#include <efilib.h>
typedef struct {
EFI_HANDLE image_handle;
EFI_SYSTEM_TABLE *systab;
EFI_GRAPHICS_OUTPUT_PROTOCOL * graphics;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL * fs;
void (*keyboard_event_handler)(UINT32 key);
unsigned long long ticks;
} Kernel;
extern Kernel kernel;
#endif

34
NesUEFI/driver/keyboard.c Normal file
View File

@ -0,0 +1,34 @@
#include <efi.h>
#include <efilib.h>
#include <string.h>
#include <stdbool.h>
#include "kernel.h"
#include "keyboard.h"
EFI_STATUS keyboard_poll(){
if(!kernel.keyboard_event_handler) return EFI_INVALID_PARAMETER;
EFI_INPUT_KEY efi_input_key;
EFI_STATUS rc;
while(1){
rc = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &efi_input_key);
if(rc == EFI_SUCCESS){
if(efi_input_key.UnicodeChar){
kernel.keyboard_event_handler(efi_input_key.UnicodeChar);
} else{
kernel.keyboard_event_handler(efi_input_key.ScanCode);
}
}
else {
return rc;
}
}
return rc;
}
EFI_STATUS keyboard_init(void (*handler)(UINT32 key)){
if(!handler) return EFI_INVALID_PARAMETER;
kernel.keyboard_event_handler = handler;
return EFI_SUCCESS;
}

10
NesUEFI/driver/keyboard.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef KEYBOARD_H
#define KEYBOARD_H
#include <efi.h>
#include <stdbool.h>
EFI_STATUS keyboard_poll();
EFI_STATUS keyboard_init(void (*handler)(UINT32 key));
#endif

109
NesUEFI/driver/sfs.c Normal file
View File

@ -0,0 +1,109 @@
#include <efi.h>
#include <efilib.h>
#include "sfs.h"
extern EFI_GUID LoadedImageProtocol;
UINT64 file_size(EFI_FILE_HANDLE file_handle){
UINT64 ret;
EFI_FILE_INFO * file_info;
file_info = LibFileInfo(file_handle);
ret = file_info->FileSize;
FreePool(file_info);
return ret;
}
EFI_STATUS EFIAPI list_directory(
IN EFI_FILE_HANDLE dir,
IN EFI_DEVICE_PATH * dir_device_path,
void (*cb)(EFI_FILE_INFO * info)
) {
EFI_STATUS rc;
EFI_FILE_INFO * file_info;
CHAR16 * file_name;
UINTN file_infoSize;
EFI_DEVICE_PATH * dp;
file_info = AllocatePool(MAX_FILE_INFO_SIZE);
if (file_info == NULL) {
return EFI_OUT_OF_RESOURCES;
}
for (;;) {
file_infoSize = MAX_FILE_INFO_SIZE;
rc = uefi_call_wrapper(dir -> Read, 3, dir, & file_infoSize, (VOID * ) file_info);
if (EFI_ERROR(rc) || file_infoSize == 0) {
if (rc == EFI_BUFFER_TOO_SMALL) {
// Print(L"EFI_FILE_INFO > MAX_FILE_INFO_SIZE. Increase the size\n");
}
FreePool(file_info);
return rc;
}
file_name = file_info -> FileName;
if (StrCmp(file_name, L".") == 0 || StrCmp(file_name, L"..") == 0) {
continue;
}
dp = FileDevicePath(dir_device_path, file_name);
if (dp == NULL) {
FreePool(file_info);
return EFI_OUT_OF_RESOURCES;
}
cb(file_info);
if (file_info -> Attribute & EFI_FILE_DIRECTORY) {
EFI_FILE_HANDLE new_dir;
rc = uefi_call_wrapper(dir -> Open, 5, dir, & new_dir, file_name, EFI_FILE_MODE_READ, 0);
if (rc != EFI_SUCCESS) {
FreePool(file_info);
FreePool(dp);
return rc;
}
uefi_call_wrapper(new_dir -> SetPosition, 2, new_dir, 0);
rc = list_directory(new_dir, dp, cb);
uefi_call_wrapper(dir -> Close, 1, new_dir);
if (rc != EFI_SUCCESS) {
FreePool(file_info);
FreePool(dp);
return rc;
}
}
FreePool(dp);
}
}
EFI_FILE_HANDLE get_volume(EFI_HANDLE image, EFI_DEVICE_PATH * dp)
{
EFI_STATUS rc;
EFI_LOADED_IMAGE *loaded_image = NULL;
EFI_GUID lipGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
EFI_FILE_IO_INTERFACE *fs;
EFI_GUID fsGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
EFI_FILE_HANDLE root;
uefi_call_wrapper(BS->HandleProtocol, 3, image, &lipGuid, (void **) &loaded_image);
uefi_call_wrapper(BS->HandleProtocol, 3, loaded_image->DeviceHandle, &fsGuid, (VOID*)&fs);
rc = uefi_call_wrapper(BS -> OpenProtocol,
6,
loaded_image->DeviceHandle, &
gEfiDevicePathProtocolGuid, &
dp,
NULL,
image,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
// Print(L"LocateHandleBuffer : %r\n\r", rc);
if(rc == EFI_SUCCESS){
uefi_call_wrapper(fs->OpenVolume, 2, fs, &root);
}
return root;
}

21
NesUEFI/driver/sfs.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef SFS_H
#define SFS_H
#include <efi.h>
#include <efilib.h>
#define MAX_FILE_INFO_SIZE 1024
#define MAX_FILE_NUMBER 1024
#define MAX_FILE_NAME_SIZE 64
EFI_FILE_HANDLE get_volume(EFI_HANDLE image, EFI_DEVICE_PATH * Dp);
EFI_STATUS EFIAPI list_directory(
IN EFI_FILE_HANDLE dir,
IN EFI_DEVICE_PATH * dir_device_path,
void (*cb)(EFI_FILE_INFO * info)
);
UINT64 file_size(EFI_FILE_HANDLE file_handle);
#endif

27
NesUEFI/driver/timer.c Normal file
View File

@ -0,0 +1,27 @@
#include <efi.h>
#include <efilib.h>
#include "kernel.h"
#include "timer.h"
EFI_EVENT TimerEvent;
EFIAPI void TimerHandler (
IN EFI_EVENT Event,
IN VOID *Context
)
{ kernel.ticks++; }
unsigned long long timer_ticks(){
return kernel.ticks;
}
EFI_STATUS timer_init(uint32_t tps){
EFI_STATUS rc;
rc = uefi_call_wrapper(BS->CreateEvent, 5, EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, TimerHandler, NULL, &TimerEvent);
Print(L"Event Status : %r\n", rc);
rc = uefi_call_wrapper(BS->SetTimer, 3, TimerEvent, TimerPeriodic, 10000000 / tps);
Print(L"Timer Status : %r\n", rc);
return rc;
}

11
NesUEFI/driver/timer.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef TIMER_H
#define TIMER_H
#include <efi.h>
#define TICK_PER_SECOND 120
EFI_STATUS timer_init(uint32_t tps);
unsigned long long timer_ticks();
#endif

17
NesUEFI/emulator/common.c Normal file
View File

@ -0,0 +1,17 @@
#include "common.h"
bool common_bit_set(long long value, byte position) { return value & (1L << position); }
// I could do this through non-void methods with returns in one copy,
// but this variant is slightly faster, and needs less typing in client code
#define M_common(SUFFIX, TYPE) \
void common_set_bit##SUFFIX(TYPE *variable, byte position) { *variable |= 1L << position; } \
void common_unset_bit##SUFFIX(TYPE *variable, byte position) { *variable &= ~(1L << position); } \
void common_toggle_bit##SUFFIX(TYPE *variable, byte position) { *variable ^= 1L << position; } \
void common_modify_bit##SUFFIX(TYPE *variable, byte position, bool set) \
{ set ? common_set_bit##SUFFIX(variable, position) : common_unset_bit##SUFFIX(variable, position); }
M_common(b, byte)
M_common(w, word)
M_common(d, dword)
M_common(q, qword)

44
NesUEFI/emulator/common.h Normal file
View File

@ -0,0 +1,44 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
// #include "util.h"
#ifndef COMMON_H
#define COMMON_H
typedef uint8_t byte;
typedef uint16_t word;
typedef uint32_t dword;
typedef uint64_t qword;
// Binary Operations
bool common_bit_set(long long value, byte position);
// Byte Bit Operations
void common_set_bitb(byte *variable, byte position);
void common_unset_bitb(byte *variable, byte position);
void common_toggle_bitb(byte *variable, byte position);
void common_modify_bitb(byte *variable, byte position, bool set);
// Word Bit Operations
void common_set_bitw(word *variable, byte position);
void common_unset_bitw(word *variable, byte position);
void common_toggle_bitw(word *variable, byte position);
void common_modify_bitw(word *variable, byte position, bool set);
// Double Word Bit Operations
void common_set_bitd(dword *variable, byte position);
void common_unset_bitd(dword *variable, byte position);
void common_toggle_bitd(dword *variable, byte position);
void common_modify_bitd(dword *variable, byte position, bool set);
// Quad Word Bit Operations
void common_set_bitq(qword *variable, byte position);
void common_unset_bitq(qword *variable, byte position);
void common_toggle_bitq(qword *variable, byte position);
void common_modify_bitq(qword *variable, byte position, bool set);
#endif

View File

@ -0,0 +1,115 @@
#include "cpu.h"
#include "cpu-internal.h"
#include "memory.h"
// CPU Addressing Modes
void cpu_address_implied()
{
}
void cpu_address_immediate()
{
op_value = memory_readb(cpu.PC);
cpu.PC++;
}
void cpu_address_zero_page()
{
op_address = memory_readb(cpu.PC);
op_value = CPU_RAM[op_address];
cpu.PC++;
}
void cpu_address_zero_page_x()
{
op_address = (memory_readb(cpu.PC) + cpu.X) & 0xFF;
op_value = CPU_RAM[op_address];
cpu.PC++;
}
void cpu_address_zero_page_y()
{
op_address = (memory_readb(cpu.PC) + cpu.Y) & 0xFF;
op_value = CPU_RAM[op_address];
cpu.PC++;
}
void cpu_address_absolute()
{
op_address = memory_readw(cpu.PC);
op_value = memory_readb(op_address);
cpu.PC += 2;
}
void cpu_address_absolute_x()
{
op_address = memory_readw(cpu.PC) + cpu.X;
op_value = memory_readb(op_address);
cpu.PC += 2;
if ((op_address >> 8) != (cpu.PC >> 8)) {
op_cycles++;
}
}
void cpu_address_absolute_y()
{
op_address = (memory_readw(cpu.PC) + cpu.Y) & 0xFFFF;
op_value = memory_readb(op_address);
cpu.PC += 2;
if ((op_address >> 8) != (cpu.PC >> 8)) {
op_cycles++;
}
}
void cpu_address_relative()
{
op_address = memory_readb(cpu.PC);
cpu.PC++;
if (op_address & 0x80)
op_address -= 0x100;
op_address += cpu.PC;
if ((op_address >> 8) != (cpu.PC >> 8)) {
op_cycles++;
}
}
void cpu_address_indirect()
{
word arg_addr = memory_readw(cpu.PC);
// The famous 6502 bug when instead of reading from $C0FF/$C100 it reads from $C0FF/$C000
if ((arg_addr & 0xFF) == 0xFF) {
// Buggy code
op_address = (memory_readb(arg_addr & 0xFF00) << 8) + memory_readb(arg_addr);
}
else {
// Normal code
op_address = memory_readw(arg_addr);
}
cpu.PC += 2;
}
void cpu_address_indirect_x()
{
byte arg_addr = memory_readb(cpu.PC);
op_address = (memory_readb((arg_addr + cpu.X + 1) & 0xFF) << 8) | memory_readb((arg_addr + cpu.X) & 0xFF);
op_value = memory_readb(op_address);
cpu.PC++;
}
void cpu_address_indirect_y()
{
byte arg_addr = memory_readb(cpu.PC);
op_address = (((memory_readb((arg_addr + 1) & 0xFF) << 8) | memory_readb(arg_addr)) + cpu.Y) & 0xFFFF;
op_value = memory_readb(op_address);
cpu.PC++;
if ((op_address >> 8) != (cpu.PC >> 8)) {
op_cycles++;
}
}

View File

@ -0,0 +1,107 @@
#ifndef CPU_INTERNAL_H
#define CPU_INTERNAL_H
typedef enum {
carry_flag = 0x01,
zero_flag = 0x02,
interrupt_flag = 0x04,
decimal_flag = 0x08,
break_flag = 0x10,
unused_flag = 0x20,
overflow_flag = 0x40,
negative_flag = 0x80
} cpu_p_flag;
typedef enum {
carry_bp = 0,
zero_bp = 1,
interrupt_bp = 2,
decimal_bp = 3,
break_bp = 4,
unused_bp = 5,
overflow_bp = 6,
negative_bp = 7
} cpu_p_bp;
typedef struct {
word PC; // Program Counter,
byte SP; // Stack Pointer,
byte A, X, Y; // Registers
byte P; // Flag Register
} CPU_STATE;
CPU_STATE cpu;
byte CPU_RAM[0x8000];
byte op_code; // Current instruction code
int op_value, op_address; // Arguments for current instruction
int op_cycles; // Additional instruction cycles used (e.g. when paging occurs)
unsigned long long cpu_cycles; // Total CPU Cycles Since Power Up (wraps)
void (*cpu_op_address_mode[256])(); // Array of address modes
void (*cpu_op_handler[256])(); // Array of instruction function pointers
bool cpu_op_in_base_instruction_set[256]; // true if instruction is in base 6502 instruction set
char *cpu_op_name[256]; // Instruction names
int cpu_op_cycles[256]; // CPU cycles used by instructions
byte cpu_ram_read(word address);
void cpu_ram_write(word address, byte data);
// Interrupt Addresses
word cpu_nmi_interrupt_address();
word cpu_reset_interrupt_address();
word cpu_irq_interrupt_address();
// Updates Zero and Negative flags in P
void cpu_update_zn_flags(byte value);
// If OP_TRACE, print current instruction with all registers into the console
void cpu_trace_instruction();
// CPU Adressing Modes
void cpu_address_implied();
void cpu_address_immediate();
void cpu_address_zero_page();
void cpu_address_zero_page_x();
void cpu_address_zero_page_y();
void cpu_address_absolute();
void cpu_address_absolute_x();
void cpu_address_absolute_y();
void cpu_address_relative();
void cpu_address_indirect();
void cpu_address_indirect_x();
void cpu_address_indirect_y();
static const byte cpu_zn_flag_table[256] =
{
zero_flag,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
};
#endif

576
NesUEFI/emulator/cpu.c Normal file
View File

@ -0,0 +1,576 @@
#include "cpu.h"
#include "cpu-internal.h"
#include "memory.h"
#include "ppu.h"
// CPU Memory
inline byte cpu_ram_read(word address)
{
return CPU_RAM[address & 0x7FF];
}
void cpu_ram_write(word address, byte data)
{
CPU_RAM[address & 0x7FF] = data;
}
// Interrupt Addresses
inline word cpu_nmi_interrupt_address() { return memory_readw(0xFFFA); }
inline word cpu_reset_interrupt_address() { return memory_readw(0xFFFC); }
inline word cpu_irq_interrupt_address() { return memory_readw(0xFFFE); }
// Stack Routines
void cpu_stack_pushb(byte data) { memory_writeb(0x100 + cpu.SP--, data); }
void cpu_stack_pushw(word data) { memory_writew(0xFF + cpu.SP, data); cpu.SP -= 2; }
byte cpu_stack_popb() { return memory_readb(0x100 + ++cpu.SP); }
word cpu_stack_popw() { cpu.SP += 2; return memory_readw(0xFF + cpu.SP); }
// CPU Instructions
void ____FE____() { /* Instruction for future Extension */ }
#define cpu_flag_set(flag) common_bit_set(cpu.P, flag)
#define cpu_modify_flag(flag, value) common_modify_bitb(&cpu.P, flag, value)
#define cpu_set_flag(flag) common_set_bitb(&cpu.P, flag)
#define cpu_unset_flag(flag) common_unset_bitb(&cpu.P, flag)
#define cpu_update_zn_flags(value) cpu.P = (cpu.P & ~(zero_flag | negative_flag)) | cpu_zn_flag_table[value]
#define cpu_branch(flag) if (flag) cpu.PC = op_address;
#define cpu_compare(reg) int result = reg - op_value; \
cpu_modify_flag(carry_bp, result >= 0); \
cpu_modify_flag(zero_bp, result == 0); \
cpu_modify_flag(negative_bp, (result >> 7) & 1);
// CPU Instructions
// NOP
void cpu_op_nop() {}
// Addition
void cpu_op_adc()
{
int result = cpu.A + op_value + (cpu_flag_set(carry_bp) ? 1 : 0);
cpu_modify_flag(carry_bp, !!(result & 0x100));
cpu_modify_flag(overflow_bp, !!(~(cpu.A ^ op_value) & (cpu.A ^ result) & 0x80));
cpu.A = result & 0xFF;
cpu_update_zn_flags(cpu.A);
}
// Subtraction
void cpu_op_sbc()
{
int result = cpu.A - op_value - (cpu_flag_set(carry_bp) ? 0 : 1);
cpu_modify_flag(carry_bp, !(result & 0x100));
cpu_modify_flag(overflow_bp, !!((cpu.A ^ op_value) & (cpu.A ^ result) & 0x80));
cpu.A = result & 0xFF;
cpu_update_zn_flags(cpu.A);
}
// Bit Manipulation Operations
void cpu_op_and() { cpu_update_zn_flags(cpu.A &= op_value); }
void cpu_op_bit() { cpu_modify_flag(zero_bp, !(cpu.A & op_value)); cpu.P = (cpu.P & 0x3F) | (0xC0 & op_value); }
void cpu_op_eor() { cpu_update_zn_flags(cpu.A ^= op_value); }
void cpu_op_ora() { cpu_update_zn_flags(cpu.A |= op_value); }
void cpu_op_asla()
{
cpu_modify_flag(carry_bp, cpu.A & 0x80);
cpu.A <<= 1;
cpu_update_zn_flags(cpu.A);
}
void cpu_op_asl()
{
cpu_modify_flag(carry_bp, op_value & 0x80);
op_value <<= 1;
op_value &= 0xFF;
cpu_update_zn_flags(op_value);
memory_writeb(op_address, op_value);
}
void cpu_op_lsra()
{
int value = cpu.A >> 1;
cpu_modify_flag(carry_bp, cpu.A & 0x01);
cpu.A = value & 0xFF;
cpu_update_zn_flags(value);
}
void cpu_op_lsr()
{
cpu_modify_flag(carry_bp, op_value & 0x01);
op_value >>= 1;
op_value &= 0xFF;
memory_writeb(op_address, op_value);
cpu_update_zn_flags(op_value);
}
void cpu_op_rola()
{
int value = cpu.A << 1;
value |= cpu_flag_set(carry_bp) ? 1 : 0;
cpu_modify_flag(carry_bp, value > 0xFF);
cpu.A = value & 0xFF;
cpu_update_zn_flags(cpu.A);
}
void cpu_op_rol()
{
op_value <<= 1;
op_value |= cpu_flag_set(carry_bp) ? 1 : 0;
cpu_modify_flag(carry_bp, op_value > 0xFF);
op_value &= 0xFF;
memory_writeb(op_address, op_value);
cpu_update_zn_flags(op_value);
}
void cpu_op_rora()
{
unsigned char carry = cpu_flag_set(carry_bp);
cpu_modify_flag(carry_bp, cpu.A & 0x01);
cpu.A = (cpu.A >> 1) | (carry << 7);
cpu_modify_flag(zero_bp, cpu.A == 0);
cpu_modify_flag(negative_bp, !!carry);
}
void cpu_op_ror()
{
unsigned char carry = cpu_flag_set(carry_bp);
cpu_modify_flag(carry_bp, op_value & 0x01);
op_value = ((op_value >> 1) | (carry << 7)) & 0xFF;
cpu_modify_flag(zero_bp, op_value == 0);
cpu_modify_flag(negative_bp, !!carry);
memory_writeb(op_address, op_value);
}
// Loading
void cpu_op_lda() { cpu_update_zn_flags(cpu.A = op_value); }
void cpu_op_ldx() { cpu_update_zn_flags(cpu.X = op_value); }
void cpu_op_ldy() { cpu_update_zn_flags(cpu.Y = op_value); }
// Storing
void cpu_op_sta() { memory_writeb(op_address, cpu.A); }
void cpu_op_stx() { memory_writeb(op_address, cpu.X); }
void cpu_op_sty() { memory_writeb(op_address, cpu.Y); }
// Transfering
void cpu_op_tax() { cpu_update_zn_flags(cpu.X = cpu.A); }
void cpu_op_txa() { cpu_update_zn_flags(cpu.A = cpu.X); }
void cpu_op_tay() { cpu_update_zn_flags(cpu.Y = cpu.A); }
void cpu_op_tya() { cpu_update_zn_flags(cpu.A = cpu.Y); }
void cpu_op_tsx() { cpu_update_zn_flags(cpu.X = cpu.SP); }
void cpu_op_txs() { cpu.SP = cpu.X; }
// Branching Positive
void cpu_op_bcs() { cpu_branch(cpu_flag_set(carry_bp)); }
void cpu_op_beq() { cpu_branch(cpu_flag_set(zero_bp)); }
void cpu_op_bmi() { cpu_branch(cpu_flag_set(negative_bp)); }
void cpu_op_bvs() { cpu_branch(cpu_flag_set(overflow_bp)); }
// Branching Negative
void cpu_op_bne() { cpu_branch(!cpu_flag_set(zero_bp)); }
void cpu_op_bcc() { cpu_branch(!cpu_flag_set(carry_bp)); }
void cpu_op_bpl() { cpu_branch(!cpu_flag_set(negative_bp)); }
void cpu_op_bvc() { cpu_branch(!cpu_flag_set(overflow_bp)); }
// Jumping
void cpu_op_jmp() { cpu.PC = op_address; }
// Subroutines
void cpu_op_jsr() { cpu_stack_pushw(cpu.PC - 1); cpu.PC = op_address; }
void cpu_op_rts() { cpu.PC = cpu_stack_popw() + 1; }
// Interruptions
void cpu_op_brk() { cpu_stack_pushw(cpu.PC - 1); cpu_stack_pushb(cpu.P); cpu.P |= unused_flag | break_flag; cpu.PC = cpu_nmi_interrupt_address(); }
void cpu_op_rti() { cpu.P = cpu_stack_popb() | unused_flag; cpu.PC = cpu_stack_popw(); }
// Flags
void cpu_op_clc() { cpu_unset_flag(carry_bp); }
void cpu_op_cld() { cpu_unset_flag(decimal_bp); }
void cpu_op_cli() { cpu_unset_flag(interrupt_bp); }
void cpu_op_clv() { cpu_unset_flag(overflow_bp); }
void cpu_op_sec() { cpu_set_flag(carry_bp); }
void cpu_op_sed() { cpu_set_flag(decimal_bp); }
void cpu_op_sei() { cpu_set_flag(interrupt_bp); }
// Comparison
void cpu_op_cmp() { cpu_compare(cpu.A); }
void cpu_op_cpx() { cpu_compare(cpu.X); }
void cpu_op_cpy() { cpu_compare(cpu.Y); }
// Increment
void cpu_op_inc() { byte result = op_value + 1; memory_writeb(op_address, result); cpu_update_zn_flags(result); }
void cpu_op_inx() { cpu_update_zn_flags(++cpu.X); }
void cpu_op_iny() { cpu_update_zn_flags(++cpu.Y); }
// Decrement
void cpu_op_dec() { byte result = op_value - 1; memory_writeb(op_address, result); cpu_update_zn_flags(result); }
void cpu_op_dex() { cpu_update_zn_flags(--cpu.X); }
void cpu_op_dey() { cpu_update_zn_flags(--cpu.Y); }
// Stack
void cpu_op_php() { cpu_stack_pushb(cpu.P | 0x30); }
void cpu_op_pha() { cpu_stack_pushb(cpu.A); }
void cpu_op_pla() { cpu.A = cpu_stack_popb(); cpu_update_zn_flags(cpu.A); }
void cpu_op_plp() { cpu.P = (cpu_stack_popb() & 0xEF) | 0x20; }
// Extended Instruction Set
void cpu_op_aso() { cpu_op_asl(); cpu_op_ora(); }
void cpu_op_axa() { memory_writeb(op_address, cpu.A & cpu.X & (op_address >> 8)); }
void cpu_op_axs() { memory_writeb(op_address, cpu.A & cpu.X); }
void cpu_op_dcm()
{
op_value--;
op_value &= 0xFF;
memory_writeb(op_address, op_value);
cpu_op_cmp();
}
void cpu_op_ins()
{
op_value = (op_value + 1) & 0xFF;
memory_writeb(op_address, op_value);
cpu_op_sbc();
}
void cpu_op_lax() { cpu_update_zn_flags(cpu.A = cpu.X = op_value); }
void cpu_op_lse() { cpu_op_lsr(); cpu_op_eor(); }
void cpu_op_rla() { cpu_op_rol(); cpu_op_and(); }
void cpu_op_rra() { cpu_op_ror(); cpu_op_adc(); }
// Base 6502 instruction set
#define CPU_OP_BIS(o, c, f, n, a) cpu_op_cycles[0x##o] = c; \
cpu_op_handler[0x##o] = cpu_op_##f; \
cpu_op_name[0x##o] = n; \
cpu_op_address_mode[0x##o] = cpu_address_##a; \
cpu_op_in_base_instruction_set[0x##o] = true;
// Not implemented instructions
#define CPU_OP_NII(o, a) cpu_op_cycles[0x##o] = 1; \
cpu_op_handler[0x##o] = ____FE____; \
cpu_op_name[0x##o] = "NOP"; \
cpu_op_address_mode[0x##o] = cpu_address_##a; \
cpu_op_in_base_instruction_set[0x##o] = false;
// Extended instruction set found in other CPUs and implemented for compatibility
#define CPU_OP_EIS(o, c, f, n, a) cpu_op_cycles[0x##o] = c; \
cpu_op_handler[0x##o] = cpu_op_##f; \
cpu_op_name[0x##o] = n; \
cpu_op_address_mode[0x##o] = cpu_address_##a; \
cpu_op_in_base_instruction_set[0x##o] = false;
// CPU Lifecycle
void cpu_init()
{
CPU_OP_BIS(00, 7, brk, "BRK", implied)
CPU_OP_BIS(01, 6, ora, "ORA", indirect_x)
CPU_OP_BIS(05, 3, ora, "ORA", zero_page)
CPU_OP_BIS(06, 5, asl, "ASL", zero_page)
CPU_OP_BIS(08, 3, php, "PHP", implied)
CPU_OP_BIS(09, 2, ora, "ORA", immediate)
CPU_OP_BIS(0A, 2, asla,"ASL", implied)
CPU_OP_BIS(0D, 4, ora, "ORA", absolute)
CPU_OP_BIS(0E, 6, asl, "ASL", absolute)
CPU_OP_BIS(10, 2, bpl, "BPL", relative)
CPU_OP_BIS(11, 5, ora, "ORA", indirect_y)
CPU_OP_BIS(15, 4, ora, "ORA", zero_page_x)
CPU_OP_BIS(16, 6, asl, "ASL", zero_page_x)
CPU_OP_BIS(18, 2, clc, "CLC", implied)
CPU_OP_BIS(19, 4, ora, "ORA", absolute_y)
CPU_OP_BIS(1D, 4, ora, "ORA", absolute_x)
CPU_OP_BIS(1E, 7, asl, "ASL", absolute_x)
CPU_OP_BIS(20, 6, jsr, "JSR", absolute)
CPU_OP_BIS(21, 6, and, "AND", indirect_x)
CPU_OP_BIS(24, 3, bit, "BIT", zero_page)
CPU_OP_BIS(25, 3, and, "AND", zero_page)
CPU_OP_BIS(26, 5, rol, "ROL", zero_page)
CPU_OP_BIS(28, 4, plp, "PLP", implied)
CPU_OP_BIS(29, 2, and, "AND", immediate)
CPU_OP_BIS(2A, 2, rola,"ROL", implied)
CPU_OP_BIS(2C, 4, bit, "BIT", absolute)
CPU_OP_BIS(2D, 2, and, "AND", absolute)
CPU_OP_BIS(2E, 6, rol, "ROL", absolute)
CPU_OP_BIS(30, 2, bmi, "BMI", relative)
CPU_OP_BIS(31, 5, and, "AND", indirect_y)
CPU_OP_BIS(35, 4, and, "AND", zero_page_x)
CPU_OP_BIS(36, 6, rol, "ROL", zero_page_x)
CPU_OP_BIS(38, 2, sec, "SEC", implied)
CPU_OP_BIS(39, 4, and, "AND", absolute_y)
CPU_OP_BIS(3D, 4, and, "AND", absolute_x)
CPU_OP_BIS(3E, 7, rol, "ROL", absolute_x)
CPU_OP_BIS(40, 6, rti, "RTI", implied)
CPU_OP_BIS(41, 6, eor, "EOR", indirect_x)
CPU_OP_BIS(45, 3, eor, "EOR", zero_page)
CPU_OP_BIS(46, 5, lsr, "LSR", zero_page)
CPU_OP_BIS(48, 3, pha, "PHA", implied)
CPU_OP_BIS(49, 2, eor, "EOR", immediate)
CPU_OP_BIS(4A, 2, lsra,"LSR", implied)
CPU_OP_BIS(4C, 3, jmp, "JMP", absolute)
CPU_OP_BIS(4D, 4, eor, "EOR", absolute)
CPU_OP_BIS(4E, 6, lsr, "LSR", absolute)
CPU_OP_BIS(50, 2, bvc, "BVC", relative)
CPU_OP_BIS(51, 5, eor, "EOR", indirect_y)
CPU_OP_BIS(55, 4, eor, "EOR", zero_page_x)
CPU_OP_BIS(56, 6, lsr, "LSR", zero_page_x)
CPU_OP_BIS(58, 2, cli, "CLI", implied)
CPU_OP_BIS(59, 4, eor, "EOR", absolute_y)
CPU_OP_BIS(5D, 4, eor, "EOR", absolute_x)
CPU_OP_BIS(5E, 7, lsr, "LSR", absolute_x)
CPU_OP_BIS(60, 6, rts, "RTS", implied)
CPU_OP_BIS(61, 6, adc, "ADC", indirect_x)
CPU_OP_BIS(65, 3, adc, "ADC", zero_page)
CPU_OP_BIS(66, 5, ror, "ROR", zero_page)
CPU_OP_BIS(68, 4, pla, "PLA", implied)
CPU_OP_BIS(69, 2, adc, "ADC", immediate)
CPU_OP_BIS(6A, 2, rora,"ROR", implied)
CPU_OP_BIS(6C, 5, jmp, "JMP", indirect)
CPU_OP_BIS(6D, 4, adc, "ADC", absolute)
CPU_OP_BIS(6E, 6, ror, "ROR", absolute)
CPU_OP_BIS(70, 2, bvs, "BVS", relative)
CPU_OP_BIS(71, 5, adc, "ADC", indirect_y)
CPU_OP_BIS(75, 4, adc, "ADC", zero_page_x)
CPU_OP_BIS(76, 6, ror, "ROR", zero_page_x)
CPU_OP_BIS(78, 2, sei, "SEI", implied)
CPU_OP_BIS(79, 4, adc, "ADC", absolute_y)
CPU_OP_BIS(7D, 4, adc, "ADC", absolute_x)
CPU_OP_BIS(7E, 7, ror, "ROR", absolute_x)
CPU_OP_BIS(81, 6, sta, "STA", indirect_x)
CPU_OP_BIS(84, 3, sty, "STY", zero_page)
CPU_OP_BIS(85, 3, sta, "STA", zero_page)
CPU_OP_BIS(86, 3, stx, "STX", zero_page)
CPU_OP_BIS(88, 2, dey, "DEY", implied)
CPU_OP_BIS(8A, 2, txa, "TXA", implied)
CPU_OP_BIS(8C, 4, sty, "STY", absolute)
CPU_OP_BIS(8D, 4, sta, "STA", absolute)
CPU_OP_BIS(8E, 4, stx, "STX", absolute)
CPU_OP_BIS(90, 2, bcc, "BCC", relative)
CPU_OP_BIS(91, 6, sta, "STA", indirect_y)
CPU_OP_BIS(94, 4, sty, "STY", zero_page_x)
CPU_OP_BIS(95, 4, sta, "STA", zero_page_x)
CPU_OP_BIS(96, 4, stx, "STX", zero_page_y)
CPU_OP_BIS(98, 2, tya, "TYA", implied)
CPU_OP_BIS(99, 5, sta, "STA", absolute_y)
CPU_OP_BIS(9A, 2, txs, "TXS", implied)
CPU_OP_BIS(9D, 5, sta, "STA", absolute_x)
CPU_OP_BIS(A0, 2, ldy, "LDY", immediate)
CPU_OP_BIS(A1, 6, lda, "LDA", indirect_x)
CPU_OP_BIS(A2, 2, ldx, "LDX", immediate)
CPU_OP_BIS(A4, 3, ldy, "LDY", zero_page)
CPU_OP_BIS(A5, 3, lda, "LDA", zero_page)
CPU_OP_BIS(A6, 3, ldx, "LDX", zero_page)
CPU_OP_BIS(A8, 2, tay, "TAY", implied)
CPU_OP_BIS(A9, 2, lda, "LDA", immediate)
CPU_OP_BIS(AA, 2, tax, "TAX", implied)
CPU_OP_BIS(AC, 4, ldy, "LDY", absolute)
CPU_OP_BIS(AD, 4, lda, "LDA", absolute)
CPU_OP_BIS(AE, 4, ldx, "LDX", absolute)
CPU_OP_BIS(B0, 2, bcs, "BCS", relative)
CPU_OP_BIS(B1, 5, lda, "LDA", indirect_y)
CPU_OP_BIS(B4, 4, ldy, "LDY", zero_page_x)
CPU_OP_BIS(B5, 4, lda, "LDA", zero_page_x)
CPU_OP_BIS(B6, 4, ldx, "LDX", zero_page_y)
CPU_OP_BIS(B8, 2, clv, "CLV", implied)
CPU_OP_BIS(B9, 4, lda, "LDA", absolute_y)
CPU_OP_BIS(BA, 2, tsx, "TSX", implied)
CPU_OP_BIS(BC, 4, ldy, "LDY", absolute_x)
CPU_OP_BIS(BD, 4, lda, "LDA", absolute_x)
CPU_OP_BIS(BE, 4, ldx, "LDX", absolute_y)
CPU_OP_BIS(C0, 2, cpy, "CPY", immediate)
CPU_OP_BIS(C1, 6, cmp, "CMP", indirect_x)
CPU_OP_BIS(C4, 3, cpy, "CPY", zero_page)
CPU_OP_BIS(C5, 3, cmp, "CMP", zero_page)
CPU_OP_BIS(C6, 5, dec, "DEC", zero_page)
CPU_OP_BIS(C8, 2, iny, "INY", implied)
CPU_OP_BIS(C9, 2, cmp, "CMP", immediate)
CPU_OP_BIS(CA, 2, dex, "DEX", implied)
CPU_OP_BIS(CC, 4, cpy, "CPY", absolute)
CPU_OP_BIS(CD, 4, cmp, "CMP", absolute)
CPU_OP_BIS(CE, 6, dec, "DEC", absolute)
CPU_OP_BIS(D0, 2, bne, "BNE", relative)
CPU_OP_BIS(D1, 5, cmp, "CMP", indirect_y)
CPU_OP_BIS(D5, 4, cmp, "CMP", zero_page_x)
CPU_OP_BIS(D6, 6, dec, "DEC", zero_page_x)
CPU_OP_BIS(D8, 2, cld, "CLD", implied)
CPU_OP_BIS(D9, 4, cmp, "CMP", absolute_y)
CPU_OP_BIS(DD, 4, cmp, "CMP", absolute_x)
CPU_OP_BIS(DE, 7, dec, "DEC", absolute_x)
CPU_OP_BIS(E0, 2, cpx, "CPX", immediate)
CPU_OP_BIS(E1, 6, sbc, "SBC", indirect_x)
CPU_OP_BIS(E4, 3, cpx, "CPX", zero_page)
CPU_OP_BIS(E5, 3, sbc, "SBC", zero_page)
CPU_OP_BIS(E6, 5, inc, "INC", zero_page)
CPU_OP_BIS(E8, 2, inx, "INX", implied)
CPU_OP_BIS(E9, 2, sbc, "SBC", immediate)
CPU_OP_BIS(EA, 2, nop, "NOP", implied)
CPU_OP_BIS(EC, 4, cpx, "CPX", absolute)
CPU_OP_BIS(ED, 4, sbc, "SBC", absolute)
CPU_OP_BIS(EE, 6, inc, "INC", absolute)
CPU_OP_BIS(F0, 2, beq, "BEQ", relative)
CPU_OP_BIS(F1, 5, sbc, "SBC", indirect_y)
CPU_OP_BIS(F5, 4, sbc, "SBC", zero_page_x)
CPU_OP_BIS(F6, 6, inc, "INC", zero_page_x)
CPU_OP_BIS(F8, 2, sed, "SED", implied)
CPU_OP_BIS(F9, 4, sbc, "SBC", absolute_y)
CPU_OP_BIS(FD, 4, sbc, "SBC", absolute_x)
CPU_OP_BIS(FE, 7, inc, "INC", absolute_x)
CPU_OP_EIS(03, 8, aso, "SLO", indirect_x)
CPU_OP_EIS(07, 5, aso, "SLO", zero_page)
CPU_OP_EIS(0F, 6, aso, "SLO", absolute)
CPU_OP_EIS(13, 8, aso, "SLO", indirect_y)
CPU_OP_EIS(17, 6, aso, "SLO", zero_page_x)
CPU_OP_EIS(1B, 7, aso, "SLO", absolute_y)
CPU_OP_EIS(1F, 7, aso, "SLO", absolute_x)
CPU_OP_EIS(23, 8, rla, "RLA", indirect_x)
CPU_OP_EIS(27, 5, rla, "RLA", zero_page)
CPU_OP_EIS(2F, 6, rla, "RLA", absolute)
CPU_OP_EIS(33, 8, rla, "RLA", indirect_y)
CPU_OP_EIS(37, 6, rla, "RLA", zero_page_x)
CPU_OP_EIS(3B, 7, rla, "RLA", absolute_y)
CPU_OP_EIS(3F, 7, rla, "RLA", absolute_x)
CPU_OP_EIS(43, 8, lse, "SRE", indirect_x)
CPU_OP_EIS(47, 5, lse, "SRE", zero_page)
CPU_OP_EIS(4F, 6, lse, "SRE", absolute)
CPU_OP_EIS(53, 8, lse, "SRE", indirect_y)
CPU_OP_EIS(57, 6, lse, "SRE", zero_page_x)
CPU_OP_EIS(5B, 7, lse, "SRE", absolute_y)
CPU_OP_EIS(5F, 7, lse, "SRE", absolute_x)
CPU_OP_EIS(63, 8, rra, "RRA", indirect_x)
CPU_OP_EIS(67, 5, rra, "RRA", zero_page)
CPU_OP_EIS(6F, 6, rra, "RRA", absolute)
CPU_OP_EIS(73, 8, rra, "RRA", indirect_y)
CPU_OP_EIS(77, 6, rra, "RRA", zero_page_x)
CPU_OP_EIS(7B, 7, rra, "RRA", absolute_y)
CPU_OP_EIS(7F, 7, rra, "RRA", absolute_x)
CPU_OP_EIS(83, 6, axs, "SAX", indirect_x)
CPU_OP_EIS(87, 3, axs, "SAX", zero_page)
CPU_OP_EIS(8F, 4, axs, "SAX", absolute)
CPU_OP_EIS(93, 6, axa, "SAX", indirect_y)
CPU_OP_EIS(97, 4, axs, "SAX", zero_page_y)
CPU_OP_EIS(9F, 5, axa, "SAX", absolute_y)
CPU_OP_EIS(A3, 6, lax, "LAX", indirect_x)
CPU_OP_EIS(A7, 3, lax, "LAX", zero_page)
CPU_OP_EIS(AF, 4, lax, "LAX", absolute)
CPU_OP_EIS(B3, 5, lax, "LAX", indirect_y)
CPU_OP_EIS(B7, 4, lax, "LAX", zero_page_y)
CPU_OP_EIS(BF, 4, lax, "LAX", absolute_y)
CPU_OP_EIS(C3, 8, dcm, "DCP", indirect_x)
CPU_OP_EIS(C7, 5, dcm, "DCP", zero_page)
CPU_OP_EIS(CF, 6, dcm, "DCP", absolute)
CPU_OP_EIS(D3, 8, dcm, "DCP", indirect_y)
CPU_OP_EIS(D7, 6, dcm, "DCP", zero_page_x)
CPU_OP_EIS(DB, 7, dcm, "DCP", absolute_y)
CPU_OP_EIS(DF, 7, dcm, "DCP", absolute_x)
CPU_OP_EIS(E3, 8, ins, "ISB", indirect_x)
CPU_OP_EIS(E7, 5, ins, "ISB", zero_page)
CPU_OP_EIS(EB, 2, sbc, "SBC", immediate)
CPU_OP_EIS(EF, 6, ins, "ISB", absolute)
CPU_OP_EIS(F3, 8, ins, "ISB", indirect_y)
CPU_OP_EIS(F7, 6, ins, "ISB", zero_page_x)
CPU_OP_EIS(FB, 7, ins, "ISB", absolute_y)
CPU_OP_EIS(FF, 7, ins, "ISB", absolute_x)
CPU_OP_NII(04, zero_page)
CPU_OP_NII(0C, absolute)
CPU_OP_NII(14, zero_page_x)
CPU_OP_NII(1A, implied)
CPU_OP_NII(1C, absolute_x)
CPU_OP_NII(34, zero_page_x)
CPU_OP_NII(3A, implied)
CPU_OP_NII(3C, absolute_x)
CPU_OP_NII(44, zero_page)
CPU_OP_NII(54, zero_page_x)
CPU_OP_NII(5A, implied)
CPU_OP_NII(5C, absolute_x)
CPU_OP_NII(64, zero_page)
CPU_OP_NII(74, zero_page_x)
CPU_OP_NII(7A, implied)
CPU_OP_NII(7C, absolute_x)
CPU_OP_NII(80, immediate)
CPU_OP_NII(D4, zero_page_x)
CPU_OP_NII(DA, implied)
CPU_OP_NII(DC, absolute_x)
CPU_OP_NII(F4, zero_page_x)
CPU_OP_NII(FA, implied)
CPU_OP_NII(FC, absolute_x)
cpu.P = 0x24;
cpu.SP = 0x00;
cpu.A = cpu.X = cpu.Y = 0;
}
void cpu_reset()
{
cpu.PC = cpu_reset_interrupt_address();
cpu.SP -= 3;
cpu.P |= interrupt_flag;
}
void cpu_interrupt()
{
// if (ppu_in_vblank()) {
if (ppu_generates_nmi()) {
cpu.P |= interrupt_flag;
cpu_unset_flag(unused_bp);
cpu_stack_pushw(cpu.PC);
cpu_stack_pushb(cpu.P);
cpu.PC = cpu_nmi_interrupt_address();
}
// }
}
inline unsigned long long cpu_clock()
{
return cpu_cycles;
}
void cpu_run(long cycles)
{
while (cycles > 0) {
op_code = memory_readb(cpu.PC++);
if (cpu_op_address_mode[op_code] == NULL) {
}
else {
cpu_op_address_mode[op_code]();
cpu_op_handler[op_code]();
}
cycles -= cpu_op_cycles[op_code] + op_cycles;
cpu_cycles -= cpu_op_cycles[op_code] + op_cycles;
op_cycles = 0;
}
}

17
NesUEFI/emulator/cpu.h Normal file
View File

@ -0,0 +1,17 @@
#include "common.h"
#ifndef CPU_H
#define CPU_H
byte cpu_ram_read(word address);
void cpu_ram_write(word address, byte data);
void cpu_init();
void cpu_reset();
void cpu_interrupt();
void cpu_run(long cycles);
// CPU cycles that passed since power up
unsigned long long cpu_clock();
#endif

137
NesUEFI/emulator/fce.c Normal file
View File

@ -0,0 +1,137 @@
#include "fce.h"
#include "cpu.h"
#include "memory.h"
#include "ppu.h"
#include "../hal/nes_hal.h"
#include "../hal/nes.h"
#include <string.h>
#include <efi.h>
#include <efilib.h>
PixelBuf bg, bbg, fg;
static byte buf[1048576];
typedef struct {
char signature[4];
byte prg_block_count;
byte chr_block_count;
word rom_type;
byte reserved[8];
} ines_header;
static ines_header fce_rom_header;
// FCE Lifecycle
void romread(char *rom, void *buf, int size, int offset){
memcpy(buf, rom + offset, size);
}
static inline int _memcmp (const void *s1, const void *s2, size_t n){
const char *start = s1;
const char *start2 = s2;
int i = 0;
while(i < n){
if(*start != *start2)
break;
i ++;
start ++;
start2 ++;
}
return (i == n)?0:1;
}
int fce_load_rom(char *rom){
int offset = 0;
romread(rom, &fce_rom_header, sizeof(fce_rom_header), offset);
offset += sizeof(fce_rom_header);
if (_memcmp(fce_rom_header.signature, "NES\x1A", 4)) {
// Print(L"ROM header signature mismatched\n\r");
return -1;
}
mmc_id = ((fce_rom_header.rom_type & 0xF0) >> 4);
int prg_size = fce_rom_header.prg_block_count * 0x4000;
romread(rom, buf, prg_size, offset);
offset += prg_size;
if (mmc_id == 0 || mmc_id == 3) {
// if there is only one PRG block, we must repeat it twice
if (fce_rom_header.prg_block_count == 1) {
mmc_copy(0x8000, buf, 0x4000);
mmc_copy(0xC000, buf, 0x4000);
}
else {
mmc_copy(0x8000, buf, 0x8000);
}
}
else {
return -1;
}
// Copying CHR pages into MMC and PPU
int i;
for (i = 0; i < fce_rom_header.chr_block_count; i++) {
romread(rom, buf, 0x2000, offset);
offset += 0x2000;
mmc_append_chr_rom_page(buf);
if (i == 0) {
ppu_copy(0x0000, buf, 0x2000);
}
}
return 0;
}
void fce_init(){
nes_hal_init();
cpu_init();
ppu_init();
ppu_set_mirroring(fce_rom_header.rom_type & 1);
cpu_reset();
}
void fce_run(){
int scanlines = 262;
while (scanlines-- > 0){
ppu_run(1);
cpu_run(1364 / 12); // 1 scanline
}
}
void fce_update_screen(){
int idx = ppu_ram_read(0x3F00);
nes_set_bg_color(idx);
int i;
if (ppu_shows_sprites()) {
for (i = 0; i < bbg.size; i ++) {
nes_draw_pixel(bbg.buf + i);
}
}
if (ppu_shows_background()) {
for (i = 0; i < bg.size; i ++) {
nes_draw_pixel(bg.buf + i);
}
}
if (ppu_shows_sprites()) {
for (i = 0; i < fg.size; i ++) {
nes_draw_pixel(fg.buf + i);
}
}
// nes_flip_display();
pixbuf_clean(bbg);
pixbuf_clean(bg);
pixbuf_clean(fg);
}

9
NesUEFI/emulator/fce.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef FCE_H
#define FCE_H
int fce_load_rom(char *rom);
void fce_init();
void fce_run();
void fce_update_screen();
#endif

17
NesUEFI/emulator/key.c Normal file
View File

@ -0,0 +1,17 @@
#include "key.h"
#include "../hal/nes_key_hal.h"
#include <stdbool.h>
static bool nes_keys[9];
// nes_keys[0] = true; // Power Button
void nes_set_key(int key){
nes_keys[key] = true;
}
int nes_get_key(int key){
return hal_nes_get_key(key);
}

7
NesUEFI/emulator/key.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef NES_KEY_H
#define NES_KEY_H
void nes_set_key(int key);
int nes_get_key(int key);
#endif

45
NesUEFI/emulator/memory.c Normal file
View File

@ -0,0 +1,45 @@
#include "memory.h"
#include "cpu.h"
#include "ppu.h"
#include "psg.h"
byte memory_readb(word address)
{
switch (address >> 13) {
case 0: return cpu_ram_read(address & 0x07FF);
case 1: return ppu_io_read(address);
case 2: return psg_io_read(address);
case 3: return cpu_ram_read(address & 0x1FFF);
default: return mmc_read(address);
}
}
void memory_writeb(word address, byte data)
{
// DMA transfer
int i;
if (address == 0x4014) {
for (i = 0; i < 256; i++) {
ppu_sprram_write(cpu_ram_read((0x100 * data) + i));
}
return;
}
switch (address >> 13) {
case 0: return cpu_ram_write(address & 0x07FF, data);
case 1: return ppu_io_write(address, data);
case 2: return psg_io_write(address, data);
case 3: return cpu_ram_write(address & 0x1FFF, data);
default: return mmc_write(address, data);
}
}
word memory_readw(word address)
{
return memory_readb(address) + (memory_readb(address + 1) << 8);
}
void memory_writew(word address, word data)
{
memory_writeb(address, data & 0xFF);
memory_writeb(address + 1, data >> 8);
}

15
NesUEFI/emulator/memory.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef MEM_H
#define MEM_H
#include "common.h"
#include "mmc.h"
// Single byte
byte memory_readb(word address);
void memory_writeb(word address, byte data);
// Two bytes (word), LSB first
word memory_readw(word address);
void memory_writew(word address, word data);
#endif

37
NesUEFI/emulator/mmc.c Normal file
View File

@ -0,0 +1,37 @@
#include "mmc.h"
#include "ppu.h"
#include <string.h>
#define MMC_MAX_PAGE_COUNT 256
byte mmc_prg_pages[MMC_MAX_PAGE_COUNT][0x4000];
byte mmc_chr_pages[MMC_MAX_PAGE_COUNT][0x2000];
int mmc_prg_pages_number, mmc_chr_pages_number;
byte memory[0x10000];
inline byte mmc_read(word address)
{
return memory[address];
}
inline void mmc_write(word address, byte data)
{
switch (mmc_id) {
case 0x3: {
ppu_copy(0x0000, &mmc_chr_pages[data & 3][0], 0x2000);
}
break;
}
memory[address] = data;
}
inline void mmc_copy(word address, byte *source, int length)
{
memcpy(&memory[address], source, length);
}
inline void mmc_append_chr_rom_page(byte *source)
{
memcpy(&mmc_chr_pages[mmc_chr_pages_number++][0], source, 0x2000);
}

8
NesUEFI/emulator/mmc.h Normal file
View File

@ -0,0 +1,8 @@
#include "common.h"
byte mmc_id;
byte mmc_read(word address);
void mmc_write(word address, byte data);
void mmc_copy(word address, byte *source, int length);
void mmc_append_chr_rom_page(byte *source);

View File

@ -0,0 +1,103 @@
#include "common.h"
#ifndef PPU_INTERNAL_H
#define PPU_INTERNAL_H
// PPU Memory and State
typedef struct {
byte PPUCTRL; // $2000 write only
byte PPUMASK; // $2001 write only
byte PPUSTATUS; // $2002 read only
byte OAMADDR; // $2003 write only
byte OAMDATA; // $2004
word PPUSCROLL;
byte PPUSCROLL_X, PPUSCROLL_Y; // $2005 write only x2
word PPUADDR; // $2006 write only x2
word PPUDATA; // $2007
bool scroll_received_x;
bool addr_received_high_byte;
bool ready;
int mirroring, mirroring_xor;
int x, scanline;
} PPU_STATE;
PPU_STATE ppu;
byte ppu_latch;
bool ppu_sprite_hit_occured = false;
word ppu_get_real_ram_address(word address);
// PPU Constants
static const word ppu_base_nametable_addresses[4] = { 0x2000, 0x2400, 0x2800, 0x2C00 };
// Screen State and Rendering
// For sprite-0-hit checks
byte ppu_screen_background[264][248];
// Precalculated tile high and low bytes addition for pattern tables
byte ppu_l_h_addition_table[256][256][8];
byte ppu_l_h_addition_flip_table[256][256][8];
// Draws current screen pixels in ppu_background_pixels & ppu_sprite_pixels and clears them
void ppu_render_screen();
void ppu_set_background_color(byte color);
// PPUCTRL Functions
word ppu_base_nametable_address();
byte ppu_vram_address_increment();
word ppu_sprite_pattern_table_address();
word ppu_background_pattern_table_address();
byte ppu_sprite_width();
byte ppu_sprite_height();
bool ppu_generates_nmi();
// PPUMASK Functions
bool ppu_renders_grayscale();
bool ppu_shows_background_in_leftmost_8px();
bool ppu_shows_sprites_in_leftmost_8px();
bool ppu_intensifies_reds();
bool ppu_intensifies_greens();
bool ppu_intensifies_blues();
void ppu_set_renders_grayscale(bool yesno);
void ppu_set_shows_background_in_leftmost_8px(bool yesno);
void ppu_set_shows_sprites_in_leftmost_8px(bool yesno);
void ppu_set_shows_background(bool yesno);
void ppu_set_shows_sprites(bool yesno);
void ppu_set_intensifies_reds(bool yesno);
void ppu_set_intensifies_greens(bool yesno);
void ppu_set_intensifies_blues(bool yesno);
// PPUSTATUS Functions
bool ppu_sprite_overflow();
bool ppu_sprite_0_hit();
bool ppu_in_vblank();
void ppu_set_sprite_overflow(bool yesno);
void ppu_set_sprite_0_hit(bool yesno);
void ppu_set_in_vblank(bool yesno);
#endif

401
NesUEFI/emulator/ppu.c Normal file
View File

@ -0,0 +1,401 @@
#include "ppu.h"
#include "ppu-internal.h"
#include "cpu.h"
#include "fce.h"
#include "memory.h"
#include "../hal/nes_hal.h"
#include <string.h>
byte ppu_sprite_palette[4][4];
bool ppu_2007_first_read;
byte ppu_addr_latch;
// PPUCTRL Functions
inline word ppu_base_nametable_address() { return ppu_base_nametable_addresses[ppu.PPUCTRL & 0x3]; }
inline byte ppu_vram_address_increment() { return common_bit_set(ppu.PPUCTRL, 2) ? 32 : 1; }
inline word ppu_sprite_pattern_table_address() { return common_bit_set(ppu.PPUCTRL, 3) ? 0x1000 : 0x0000; }
inline word ppu_background_pattern_table_address() { return common_bit_set(ppu.PPUCTRL, 4) ? 0x1000 : 0x0000; }
inline byte ppu_sprite_height() { return common_bit_set(ppu.PPUCTRL, 5) ? 16 : 8; }
inline bool ppu_generates_nmi() { return common_bit_set(ppu.PPUCTRL, 7); }
// PPUMASK Functions
inline bool ppu_renders_grayscale() { return common_bit_set(ppu.PPUMASK, 0); }
inline bool ppu_shows_background_in_leftmost_8px() { return common_bit_set(ppu.PPUMASK, 1); }
inline bool ppu_shows_sprites_in_leftmost_8px() { return common_bit_set(ppu.PPUMASK, 2); }
inline bool ppu_shows_background() { return common_bit_set(ppu.PPUMASK, 3); }
inline bool ppu_shows_sprites() { return common_bit_set(ppu.PPUMASK, 4); }
inline bool ppu_intensifies_reds() { return common_bit_set(ppu.PPUMASK, 5); }
inline bool ppu_intensifies_greens() { return common_bit_set(ppu.PPUMASK, 6); }
inline bool ppu_intensifies_blues() { return common_bit_set(ppu.PPUMASK, 7); }
inline void ppu_set_renders_grayscale(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 0, yesno); }
inline void ppu_set_shows_background_in_leftmost_8px(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 1, yesno); }
inline void ppu_set_shows_sprites_in_leftmost_8px(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 2, yesno); }
inline void ppu_set_shows_background(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 3, yesno); }
inline void ppu_set_shows_sprites(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 4, yesno); }
inline void ppu_set_intensifies_reds(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 5, yesno); }
inline void ppu_set_intensifies_greens(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 6, yesno); }
inline void ppu_set_intensifies_blues(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 7, yesno); }
// PPUSTATUS Functions
inline bool ppu_sprite_overflow() { return common_bit_set(ppu.PPUSTATUS, 5); }
inline bool ppu_sprite_0_hit() { return common_bit_set(ppu.PPUSTATUS, 6); }
inline bool ppu_in_vblank() { return common_bit_set(ppu.PPUSTATUS, 7); }
inline void ppu_set_sprite_overflow(bool yesno) { common_modify_bitb(&ppu.PPUSTATUS, 5, yesno); }
inline void ppu_set_sprite_0_hit(bool yesno) { common_modify_bitb(&ppu.PPUSTATUS, 6, yesno); }
inline void ppu_set_in_vblank(bool yesno) { common_modify_bitb(&ppu.PPUSTATUS, 7, yesno); }
// RAM
inline word ppu_get_real_ram_address(word address)
{
if (address < 0x2000) {
return address;
}
else if (address < 0x3F00) {
if (address < 0x3000) {
return address;
}
else {
return address;// - 0x1000;
}
}
else if (address < 0x4000) {
address = 0x3F00 | (address & 0x1F);
if (address == 0x3F10 || address == 0x3F14 || address == 0x3F18 || address == 0x3F1C)
return address - 0x10;
else
return address;
}
return 0xFFFF;
}
inline byte ppu_ram_read(word address)
{
return PPU_RAM[ppu_get_real_ram_address(address)];
}
inline void ppu_ram_write(word address, byte data)
{
PPU_RAM[ppu_get_real_ram_address(address)] = data;
}
// 3F01 = 0F (00001111)
// 3F02 = 2A (00101010)
// 3F03 = 09 (00001001)
// 3F04 = 07 (00000111)
// 3F05 = 0F (00001111)
// 3F06 = 30 (00110000)
// 3F07 = 27 (00100111)
// 3F08 = 15 (00010101)
// 3F09 = 0F (00001111)
// 3F0A = 30 (00110000)
// 3F0B = 02 (00000010)
// 3F0C = 21 (00100001)
// 3F0D = 0F (00001111)
// 3F0E = 30 (00110000)
// 3F0F = 00 (00000000)
// 3F11 = 0F (00001111)
// 3F12 = 16 (00010110)
// 3F13 = 12 (00010010)
// 3F14 = 37 (00110111)
// 3F15 = 0F (00001111)
// 3F16 = 12 (00010010)
// 3F17 = 16 (00010110)
// 3F18 = 37 (00110111)
// 3F19 = 0F (00001111)
// 3F1A = 17 (00010111)
// 3F1B = 11 (00010001)
// 3F1C = 35 (00110101)
// 3F1D = 0F (00001111)
// 3F1E = 17 (00010111)
// 3F1F = 11 (00010001)
// 3F20 = 2B (00101011)
// Rendering
void ppu_draw_background_scanline(bool mirror)
{
int tile_x;
for (tile_x = ppu_shows_background_in_leftmost_8px() ? 0 : 1; tile_x < 32; tile_x++) {
// Skipping off-screen pixels
if (((tile_x << 3) - ppu.PPUSCROLL_X + (mirror ? 256 : 0)) > 256)
continue;
int tile_y = ppu.scanline >> 3;
int tile_index = ppu_ram_read(ppu_base_nametable_address() + tile_x + (tile_y << 5) + (mirror ? 0x400 : 0));
word tile_address = ppu_background_pattern_table_address() + 16 * tile_index;
int y_in_tile = ppu.scanline & 0x7;
byte l = ppu_ram_read(tile_address + y_in_tile);
byte h = ppu_ram_read(tile_address + y_in_tile + 8);
int x;
for (x = 0; x < 8; x++) {
byte color = ppu_l_h_addition_table[l][h][x];
// Color 0 is transparent
if (color != 0) {
word attribute_address = (ppu_base_nametable_address() + (mirror ? 0x400 : 0) + 0x3C0 + (tile_x >> 2) + (ppu.scanline >> 5) * 8);
bool top = (ppu.scanline % 32) < 16;
bool left = (tile_x % 4 < 2);
byte palette_attribute = ppu_ram_read(attribute_address);
if (!top) {
palette_attribute >>= 4;
}
if (!left) {
palette_attribute >>= 2;
}
palette_attribute &= 3;
word palette_address = 0x3F00 + (palette_attribute << 2);
int idx = ppu_ram_read(palette_address + color);
ppu_screen_background[(tile_x << 3) + x][ppu.scanline] = color;
pixbuf_add(bg, (tile_x << 3) + x - ppu.PPUSCROLL_X + (mirror ? 256 : 0), ppu.scanline + 1, idx);
}
}
}
}
void ppu_draw_sprite_scanline()
{
int scanline_sprite_count = 0;
int n;
for (n = 0; n < 0x100; n += 4) {
byte sprite_x = PPU_SPRRAM[n + 3];
byte sprite_y = PPU_SPRRAM[n];
// Skip if sprite not on scanline
if (sprite_y > ppu.scanline || sprite_y + ppu_sprite_height() < ppu.scanline)
continue;
scanline_sprite_count++;
// PPU can't render > 8 sprites
if (scanline_sprite_count > 8) {
ppu_set_sprite_overflow(true);
// break;
}
bool vflip = PPU_SPRRAM[n + 2] & 0x80;
bool hflip = PPU_SPRRAM[n + 2] & 0x40;
word tile_address = ppu_sprite_pattern_table_address() + 16 * PPU_SPRRAM[n + 1];
int y_in_tile = ppu.scanline & 0x7;
byte l = ppu_ram_read(tile_address + (vflip ? (7 - y_in_tile) : y_in_tile));
byte h = ppu_ram_read(tile_address + (vflip ? (7 - y_in_tile) : y_in_tile) + 8);
byte palette_attribute = PPU_SPRRAM[n + 2] & 0x3;
word palette_address = 0x3F10 + (palette_attribute << 2);
int x;
for (x = 0; x < 8; x++) {
int color = hflip ? ppu_l_h_addition_flip_table[l][h][x] : ppu_l_h_addition_table[l][h][x];
// Color 0 is transparent
if (color != 0) {
int screen_x = sprite_x + x;
int idx = ppu_ram_read(palette_address + color);
if (PPU_SPRRAM[n + 2] & 0x20) {
pixbuf_add(bbg, screen_x, sprite_y + y_in_tile + 1, idx);
}
else {
pixbuf_add(fg, screen_x, sprite_y + y_in_tile + 1, idx);
}
// Checking sprite 0 hit
if (ppu_shows_background() && !ppu_sprite_hit_occured && n == 0 && ppu_screen_background[screen_x][sprite_y + y_in_tile] == color) {
ppu_set_sprite_0_hit(true);
ppu_sprite_hit_occured = true;
}
}
}
}
}
// PPU Lifecycle
void ppu_run(int cycles)
{
while (cycles-- > 0) {
ppu_cycle();
}
}
void ppu_cycle()
{
if (!ppu.ready && cpu_clock() > 29658)
ppu.ready = true;
ppu.scanline++;
if (ppu_shows_background()) {
ppu_draw_background_scanline(false);
ppu_draw_background_scanline(true);
}
if (ppu_shows_sprites()) ppu_draw_sprite_scanline();
if (ppu.scanline == 241) {
ppu_set_in_vblank(true);
ppu_set_sprite_0_hit(false);
cpu_interrupt();
}
else if (ppu.scanline == 262) {
ppu.scanline = -1;
ppu_sprite_hit_occured = false;
ppu_set_in_vblank(false);
fce_update_screen();
}
}
inline void ppu_copy(word address, byte *source, int length)
{
memcpy(&PPU_RAM[address], source, length);
}
inline byte ppu_io_read(word address)
{
ppu.PPUADDR &= 0x3FFF;
switch (address & 7) {
case 2:
{
byte value = ppu.PPUSTATUS;
ppu_set_in_vblank(false);
ppu_set_sprite_0_hit(false);
ppu.scroll_received_x = 0;
ppu.PPUSCROLL = 0;
ppu.addr_received_high_byte = 0;
ppu_latch = value;
ppu_addr_latch = 0;
ppu_2007_first_read = true;
return value;
}
case 4: return ppu_latch = PPU_SPRRAM[ppu.OAMADDR];
case 7:
{
byte data;
if (ppu.PPUADDR < 0x3F00) {
data = ppu_latch = ppu_ram_read(ppu.PPUADDR);
}
else {
data = ppu_ram_read(ppu.PPUADDR);
ppu_latch = 0;
}
if (ppu_2007_first_read) {
ppu_2007_first_read = false;
}
else {
ppu.PPUADDR += ppu_vram_address_increment();
}
return data;
}
default:
return 0xFF;
}
}
inline void ppu_io_write(word address, byte data)
{
address &= 7;
ppu_latch = data;
ppu.PPUADDR &= 0x3FFF;
switch(address) {
case 0: if (ppu.ready) ppu.PPUCTRL = data; break;
case 1: if (ppu.ready) ppu.PPUMASK = data; break;
case 3: ppu.OAMADDR = data; break;
case 4: PPU_SPRRAM[ppu.OAMADDR++] = data; break;
case 5:
{
if (ppu.scroll_received_x)
ppu.PPUSCROLL_Y = data;
else
ppu.PPUSCROLL_X = data;
ppu.scroll_received_x ^= 1;
break;
}
case 6:
{
if (!ppu.ready)
return;
if (ppu.addr_received_high_byte)
ppu.PPUADDR = (ppu_addr_latch << 8) + data;
else
ppu_addr_latch = data;
ppu.addr_received_high_byte ^= 1;
ppu_2007_first_read = true;
break;
}
case 7:
{
if (ppu.PPUADDR > 0x1FFF || ppu.PPUADDR < 0x4000) {
ppu_ram_write(ppu.PPUADDR ^ ppu.mirroring_xor, data);
ppu_ram_write(ppu.PPUADDR, data);
}
else {
ppu_ram_write(ppu.PPUADDR, data);
}
}
}
ppu_latch = data;
}
void ppu_init()
{
ppu.PPUCTRL = ppu.PPUMASK = ppu.PPUSTATUS = ppu.OAMADDR = ppu.PPUSCROLL_X = ppu.PPUSCROLL_Y = ppu.PPUADDR = 0;
ppu.PPUSTATUS |= 0xA0;
ppu.PPUDATA = 0;
ppu_2007_first_read = true;
// Initializing low-high byte-pairs for pattern tables
int h, l, x;
for (h = 0; h < 0x100; h++) {
for (l = 0; l < 0x100; l++) {
for (x = 0; x < 8; x++) {
ppu_l_h_addition_table[l][h][x] = (((h >> (7 - x)) & 1) << 1) | ((l >> (7 - x)) & 1);
ppu_l_h_addition_flip_table[l][h][x] = (((h >> x) & 1) << 1) | ((l >> x) & 1);
}
}
}
}
void ppu_sprram_write(byte data)
{
PPU_SPRRAM[ppu.OAMADDR++] = data;
}
void ppu_set_background_color(byte color)
{
nes_set_bg_color(color);
}
void ppu_set_mirroring(byte mirroring)
{
ppu.mirroring = mirroring;
ppu.mirroring_xor = 0x400 << mirroring;
}

36
NesUEFI/emulator/ppu.h Normal file
View File

@ -0,0 +1,36 @@
#include "common.h"
#ifndef PPU_H
#define PPU_H
byte PPU_SPRRAM[0x100];
byte PPU_RAM[0x4000];
void ppu_init();
void ppu_finish();
byte ppu_ram_read(word address);
void ppu_ram_write(word address, byte data);
byte ppu_io_read(word address);
void ppu_io_write(word address, byte data);
bool ppu_generates_nmi();
void ppu_set_generates_nmi(bool yesno);
void ppu_set_mirroring(byte mirroring);
void ppu_run(int cycles);
void ppu_cycle();
int ppu_scanline();
void ppu_set_scanline(int s);
void ppu_copy(word address, byte *source, int length);
void ppu_sprram_write(byte data);
// PPUCTRL
bool ppu_shows_background();
bool ppu_shows_sprites();
bool ppu_in_vblank();
void ppu_set_in_vblank(bool yesno);
#endif

28
NesUEFI/emulator/psg.c Normal file
View File

@ -0,0 +1,28 @@
#include "psg.h"
#include "../hal/nes_hal.h"
#include "key.h"
static byte prev_write;
static int p = 10;
inline byte psg_io_read(word address)
{
// Joystick 1
if (address == 0x4016) {
if (p++ < 9) {
return nes_get_key(p);
}
}
return 0;
}
inline void psg_io_write(word address, byte data)
{
if (address == 0x4016) {
if ((data & 1) == 0 && prev_write == 1) {
// strobe
p = 0;
}
}
prev_write = data & 1;
}

14
NesUEFI/emulator/psg.h Normal file
View File

@ -0,0 +1,14 @@
// #define psg_io_read(...) 0xFF
// #define psg_io_write(...) /**/
#include "common.h"
#ifndef PSG_H
#define PSG_H
extern unsigned char psg_joy1[8];
byte psg_io_read(word address);
void psg_io_write(word address, byte data);
#endif

14
NesUEFI/hal/nes.h Normal file
View File

@ -0,0 +1,14 @@
#define NES_FPS 60
#define NES_SCREEN_WIDTH 256
#define NES_SCREEN_HEIGHT 240
#define NES_SCREEN_ZOOM 3
#define KEY_A 0x6b // k
#define KEY_B 0x6a // j
#define KEY_SELECT 0x75 // u
#define KEY_START 0x69 // i
#define KEY_UP 0x77 // w
#define KEY_DOWN 0x73 // s
#define KEY_LEFT 0x61 // a
#define KEY_RIGHT 0x64 // d
#define KEY_REBOOT 0x72 // r

33
NesUEFI/hal/nes_gfx_hal.c Normal file
View File

@ -0,0 +1,33 @@
#include "nes_gfx_hal.h"
#include "../driver/graphics.h"
#include "../ui/adafruit_gfx.h"
#include <string.h>
UINT32 _nes_screen_buffer_prev[(NES_SCREEN_WIDTH) * (NES_SCREEN_HEIGHT) + 1];
UINT32 _nes_screen_buffer_current[(NES_SCREEN_WIDTH) * (NES_SCREEN_HEIGHT) + 1];
void nes_set_pixel(int x, int y, UINT32 nes_colour){
if(x>-1){
_nes_screen_buffer_current[x + y * NES_SCREEN_WIDTH] = nes_colour;
}
}
void nes_set_bg(int colour){
for (int i = 0; i < NES_SCREEN_WIDTH*NES_SCREEN_HEIGHT; ++i){
nes_set_pixel(i%NES_SCREEN_WIDTH, i / NES_SCREEN_WIDTH , colour);
}
}
void nes_gfx_swap(){
for (int i = 0; i < NES_SCREEN_WIDTH*NES_SCREEN_HEIGHT; ++i){
if((_nes_screen_buffer_current[i] != _nes_screen_buffer_prev[i]) ){
int x_offset = (kernel.graphics->Mode->Info->HorizontalResolution - NES_SCREEN_WIDTH*NES_SCREEN_ZOOM)/2 +1;
int y_offset = (kernel.graphics->Mode->Info->VerticalResolution - NES_SCREEN_HEIGHT*NES_SCREEN_ZOOM)/2;
_nes_screen_buffer_prev[i] = _nes_screen_buffer_current[i];
fillRect( x_offset + (i%NES_SCREEN_WIDTH)*NES_SCREEN_ZOOM,
y_offset + (i/NES_SCREEN_WIDTH)*NES_SCREEN_ZOOM,
NES_SCREEN_ZOOM, NES_SCREEN_ZOOM, _nes_screen_buffer_current[i]);
}
}
}

11
NesUEFI/hal/nes_gfx_hal.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef NES_GFX_HAL_H
#define NES_GFX_HAL_H
#include "nes.h"
#include "../driver/kernel.h"
void nes_set_pixel(int x, int y, UINT32 nes_colour);
void nes_set_bg(int colour);
void nes_gfx_swap();
#endif

80
NesUEFI/hal/nes_hal.c Normal file
View File

@ -0,0 +1,80 @@
/*
This file presents all abstractions needed to port LiteNES.
(The current working implementation uses allegro library.)
To port this project, replace the following functions by your own:
1) nes_hal_init()
Do essential initialization work, including starting a FPS HZ timer.
2) nes_set_bg_color(c)
Set the back ground color to be the NES internal color code c.
3) nes_flush_buf(*buf)
Flush the entire pixel buf's data to frame buffer.
4) nes_flip_display()
Fill the screen with previously set background color, and
display all contents in the frame buffer.
5) wait_for_frame()
Implement it to make the following code is executed FPS times a second:
while (1) {
wait_for_frame();
do_something();
}
6) int nes__key_state(int b)
Query button b's state (1 to be pressed, otherwise 0).
The correspondence of b and the buttons:
0 - Power
1 - A
2 - B
3 - SELECT
4 - START
5 - UP
6 - DOWN
7 - LEFT
8 - RIGHT
*/
#include <stdbool.h>
#include <stdint.h>
#include "nes_hal.h"
#include "nes_palette.h"
#include "nes_key_hal.h"
#include "nes_gfx_hal.h"
// fill the screen with background color
void nes_set_bg_color(int c){
nes_set_bg(nes_color_palette(c));
// nes_flip_display();
}
// flush pixel buffer to frame buffer
void nes_flush_buf(PixelBuf *buf){
}
void nes_draw_pixel(Pixel *p){
uint8_t color = p->c;
nes_set_pixel(p->x, p->y, nes_color_palette(p->c));
}
// initialization
void nes_hal_init(){
}
// display and empty the current frame buffer
void nes_flip_display(){
nes_gfx_swap();
}
// query key-press status
int nes_key_state(int key){
return hal_nes_get_key(key);
}

54
NesUEFI/hal/nes_hal.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef NES_HAL_H
#define NES_HAL_H
#include "nes.h"
struct Pixel {
int x, y; // (x, y) coordinate
int c; // RGB value of colors can be found in fce.h
};
typedef struct Pixel Pixel;
/* A buffer of pixels */
struct PixelBuf {
Pixel buf[264 * 264];
int size;
};
typedef struct PixelBuf PixelBuf;
extern PixelBuf bg, bbg, fg;
// clear a pixel buffer
#define pixbuf_clean(bf) \
do { \
(bf).size = 0; \
} while (0)
// add a pending pixel into a buffer
#define pixbuf_add(bf, xa, ya, ca) \
do { \
if ((xa) < NES_SCREEN_WIDTH && (ya) < NES_SCREEN_HEIGHT) { \
(bf).buf[(bf).size].x = (xa); \
(bf).buf[(bf).size].y = (ya); \
(bf).buf[(bf).size].c = (ca); \
(bf).size++; \
} \
} while (0)
// fill the screen with background color
void nes_set_bg_color(int c);
// flush pixel buffer to frame buffer
void nes_flush_buf(PixelBuf *buf);
// display and empty the current frame buffer
void nes_flip_display();
// initialization
void nes_hal_init();
// query key-press status
int nes_key_state(int b);
void nes_draw_pixel(Pixel *p);
#endif

73
NesUEFI/hal/nes_key_hal.c Normal file
View File

@ -0,0 +1,73 @@
#include <string.h>
#include "nes.h"
#include "nes_key_hal.h"
#include "../emulator/key.h"
#include "../driver/keyboard.h"
bool _key_state[10];
void hal_nes_key_clear(){
memset(_key_state, 0x00, sizeof(_key_state));
}
void hal_nes_ctrl_key_clear(){
_key_state[NES_KEY_UP] = false;
_key_state[NES_KEY_LEFT] = false;
_key_state[NES_KEY_DOWN] = false;
_key_state[NES_KEY_RIGHT] = false;
_key_state[NES_KEY_SELECT] = false;
_key_state[NES_KEY_START] = false;
// _key_state[NES_KEY_B] = false; // Otherwise Mario will not jump long
_key_state[NES_KEY_A] = false;
_key_state[NES_KEY_REBOOT] = false;
}
void hal_nes_set_key(uint32_t key){
hal_nes_key_clear();
switch(key){
case KEY_UP:
_key_state[NES_KEY_UP] = true;
break;
case KEY_LEFT:
_key_state[NES_KEY_LEFT] = true;
break;
case KEY_DOWN:
_key_state[NES_KEY_DOWN] = true;
break;
case KEY_RIGHT:
_key_state[NES_KEY_RIGHT] = true;
break;
case KEY_SELECT:
_key_state[NES_KEY_SELECT] = true;
break;
case KEY_START:
_key_state[NES_KEY_START] = true;
break;
case KEY_B:
_key_state[NES_KEY_B] = true;
break;
case KEY_A:
_key_state[NES_KEY_A] = true;
break;
case KEY_REBOOT:
_key_state[NES_KEY_REBOOT] = true;
break;
default:
break;
}
}
bool hal_nes_get_key(uint16_t key){
if(!key) return true; // Power default on
return _key_state[key];
}

27
NesUEFI/hal/nes_key_hal.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef NES_KEY_HAL_H
#define NES_KEY_HAL_H
#include <efi.h>
#include <stdbool.h>
#include "nes.h"
typedef enum nes_key_t {
NES_KEY_POWER,
NES_KEY_A,
NES_KEY_B,
NES_KEY_SELECT,
NES_KEY_START,
NES_KEY_UP,
NES_KEY_DOWN,
NES_KEY_LEFT,
NES_KEY_RIGHT,
NES_KEY_REBOOT
} nes_key_t;
void hal_nes_key_clear();
void hal_nes_set_key(uint32_t key);
bool hal_nes_get_key(uint16_t key);
void hal_nes_ctrl_key_clear();
#endif

76
NesUEFI/hal/nes_palette.h Normal file
View File

@ -0,0 +1,76 @@
#ifndef NES_PALATTE
#define NES_PALATTE
static const uint32_t palette[64] = {
0x808080,
0x0000BB,
0x3700BF,
0x8400A6,
0xBB006A,
0xB7001E,
0xB30000,
0x912600,
0x7B2B00,
0x003E00,
0x00480D,
0x003C22,
0x002F66,
0x000000,
0x050505,
0x050505,
0xC8C8C8,
0x0059FF,
0x443CFF,
0xB733CC,
0xFF33AA,
0xFF375E,
0xFF371A,
0xD54B00,
0xC46200,
0x3C7B00,
0x1E8415,
0x009566,
0x0084C4,
0x111111,
0x090909,
0x090909,
0xFFFFFF,
0x0095FF,
0x6F84FF,
0xD56FFF,
0xFF77CC,
0xFF6F99,
0xFF7B59,
0xFF915F,
0xFFA233,
0xA6BF00,
0x51D96A,
0x4DD5AE,
0x00D9FF,
0x666666,
0x0D0D0D,
0x0D0D0D,
0xFFFFFF,
0x84BFFF,
0xBBBBFF,
0xD0BBFF,
0xFFBFEA,
0xFFBFCC,
0xFFC4B7,
0xFFCCAE,
0xFFD9A2,
0xCCE199,
0xAEEEB7,
0xAAF7EE,
0xB3EEFF,
0xDDDDDD,
0x111111,
0x111111
};
uint32_t static nes_color_palette(int i){
if(i > 63) return 0;
return palette[i];
}
#endif

79
NesUEFI/main.c Normal file
View File

@ -0,0 +1,79 @@
#include <efi.h>
#include <efilib.h>
#include <efidevp.h>
#include <string.h>
#include "./driver/graphics.h"
#include "./driver/kernel.h"
#include "./driver/timer.h"
#include "./driver/keyboard.h"
#include "./driver/sfs.h"
#include "./hal/nes.h"
#include "./hal/nes_key_hal.h"
#include "./hal/nes_gfx_hal.h"
#include "./emulator/fce.h"
#include "./emulator/ppu.h"
#include "./emulator/cpu.h"
#include "./ui/ui_gfx.h"
#include "./ui/adafruit_gfx.h"
Kernel kernel;
extern EFI_GUID GraphicsOutputProtocol;
EFI_GRAPHICS_OUTPUT_PROTOCOL * gop;
EFI_HANDLE * handles;
UINTN number_of_handles;
EFI_STATUS
efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab){
EFI_GUID sfspGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
EFI_STATUS rc;
kernel.image_handle = image_handle;
InitializeLib(image_handle, systab);
Print(L"Initializing NesUEFI ...\n");
rc = timer_init(TICK_PER_SECOND);
Print(L"Timer Intialized : %r\n", rc);
ST->BootServices->SetWatchdogTimer(0, 0, 0, NULL);
rc = graphics_init(&gop);
if (rc == EFI_SUCCESS){
Print(L"Graphics Protocol Intialized : %r\n", rc);
rc = graphics_set_mode(gop);
if (rc == EFI_SUCCESS){
Print(L"Graphics Mode Set : %r\n", rc);
Print(L"\a");
kernel.graphics = gop;
}
}
adafruit_gfx_init(kernel.graphics->Mode->Info->HorizontalResolution, kernel.graphics->Mode->Info->VerticalResolution);
populate_menu_items();
keyboard_init(ui_key_handler);
#if 1
unsigned long last_frame = 0;
while(1){
rc = keyboard_poll();
const unsigned long now = (unsigned long)timer_ticks();
if ((now - last_frame) > TICK_PER_SECOND/NES_FPS){
last_frame = now;
ui_state_t state = ui_manage_states();
if(state == UI_STATE_PLAY){
nes_gfx_swap();
}
}
}
#endif // #if 1
Print(L"EFI EXIT : %r\n", rc);
return EFI_SUCCESS;
}

BIN
NesUEFI/main.efi Executable file

Binary file not shown.

1
NesUEFI/pendrive.sh Executable file
View File

@ -0,0 +1 @@
cp main.efi /media/shadlyd15/C0AE-8AB9/efi/boot/bootx64.efi

129
NesUEFI/readme.md Normal file
View File

@ -0,0 +1,129 @@
# NesUEFI - A bootable NES Emulator running on UEFI
NesUEFI is a NES Emulator running without an operating system directly on Unified Extensible Firmware Interface (UEFI).
The core drivers (Graphics, File System, Keyboard, Timer) are written using GNU-EFI library and the emulator part is a port of [LiteNES](https://github.com/NJU-ProjectN/LiteNES). Adafruit GFX Graphics Library is ported for GNU-EFI ecosystem in C for high level graphics operations and text rendering.
# Compile NesUEFI
## Requirements
- **GNU-EFI** Library
Download gnu-efi from here : [https://sourceforge.net/projects/gnu-efi](https://sourceforge.net/projects/gnu-efi/)
- **gcc-3.0 or newer**
- A version of **objcopy** that supports EFI applications
- **qemu** (To run without real hardware)
- **mtools** : Utilities to access MS-DOS disks from Linux (To run on qemu)
- **OVMF** (To run on qemu)
## Compile and install GNU-EFI Library
Go to gnu-efi source folder
```bash
make
make install
```
Here is a guide you can follow for more details on how to compile and install gnu-efi.
## Compile NesUEFI
Change the following macros in makefile to locate GNU-EFI libraries and headers installed on the previous step.
```makefile
INCDIR = /usr/local/include
LIBDIR = /usr/local/lib
EFILIB = /usr/local/lib
```
# NesUEFI on qemu :
### Locate OVMF in Makefile:
To run the compiled application in qemu we need OVMF for UEFI emulation. OVMF is a port of Intel's tianocore firmware to the qemu virtual machine. Download it from here.
```makefile
OVMF_DIR = ../OVMF
```
Change the OVMF directory in the makefile.
### Create Image
Add your roms here in the makefile to write it to your image. For example : test_1.nes, test_2.nes
```makefile
mcopy -i $(IMAGE).img splash.bmp ::
# Add your roms here
mcopy -i $(IMAGE).img test_1.nes ::
mcopy -i $(IMAGE).img test_2.nes ::
```
```bash
make img
```
### Run
```bash
make run
```
# NesUEFI on real hardware
**Do at your own risk. Under no circumstances shall the author be liable for any damage.**
Technically NesUEFI should not mess with your system. But you should at least know what you are doing.
- Format a pendrive with **FAT32** file system.
- Create **/efi/boot/** folders in the pendrive
- Rename **main.efi** to **bootx64.efi** for **64 bit** and **bootia32.efi** for **32 bit** application.
- Copy **bootx64.efi** or **bootia32.efi** to boot folder.
- For **64 bit** application the pendrive should look like this : **/efi/boot/bootx64.efi**
- For **32 bit** application the pendrive should look like this : **/efi/boot/bootia32.efi**
- Simply place your roms in the pendrive. **NesUEFI file browser** will show the available roms.
# Demo on Real Hardware
[![Test](http://img.youtube.com/vi/apbS205t53w/0.jpg)](http://www.youtube.com/watch?v=apbS205t53w)
## Controls
| NesUEFI Key | Keyboard Key |
| ----------- | ----------- |
| Up | W |
| Down | S |
| Left | A |
| Right | D |
| Select | U |
| Start | I |
| A | K |
| B | J |
| Reload | R |
## Known Issues
- Mappers are not implemented yet.
- It does not support Audio emulation yet.
- EFI only have a simple text input protocol. So it only recognizes input character but not keystroke. So key press and release events are emulated by holding down a key until a new key is pressed. Otherwise we can not emulate multi key press events.
- Only single player is supported.
## Tested Games
Below games are currently tested on real hardware. But NesUEFI is not only limited to these games. It should technically run all the classic roms that use mapper 0. [Here](https://nesdir.github.io/mapper0.html) is a list of mapper 0 games.
- [x] Super Mario Bros
- [x] Popeye
- [x] Yie Ar Kung-Fu
- [x] Battle City
- [x] Super Arabian
- [x] Road Fighters
- [x] 1942
- [x] F1 Race
## Screenshots
## TODO
- [x] Graphics
- [x] Timer
- [x] Keyboard
- [x] File System
- [x] File Browser
- [x] Graphics Library
- [ ] APU
- [ ] Mappers
## Acknowledgements
- https://wiki.osdev.org
- https://wiki.osdev.org/Tutorials
- https://wiki.osdev.org/UEFI
- https://wiki.osdev.org/GNU-EFI
- https://wiki.osdev.org/UEFI_App_Bare_Bones
- https://wiki.osdev.org/Loading_files_under_UEFI
- https://github.com/NJU-ProjectN/LiteNES
- https://learn.adafruit.com/adafruit-gfx-graphics-library
- https://edk2-devel.narkive.com/WhxSiG6I/edk2-directory-file-system-traversal-example
- https://www.youtube.com/watch?v=mpPbKEeWIHU&list=PLxN4E629pPnJxCQCLy7E0SQY_zuumOVyZ
- https://blog.fpmurphy.com/2015/08/display-bmp-details-and-image-in-uefi-shell.html#ixzz7SHLiufWS
- https://unsplash.com/photos/UqRnUzV5pjQ
# License

1
NesUEFI/run.sh Executable file
View File

@ -0,0 +1 @@
make run

BIN
NesUEFI/splash.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

5
NesUEFI/startup.nsh Normal file
View File

@ -0,0 +1,5 @@
if exist fs0:\efi\boot\main.efi then
fs0:
echo Booting NesUEFI:
efi\boot\main.efi
endif

1302
NesUEFI/ui/adafruit_gfx.c Normal file

File diff suppressed because it is too large Load Diff

125
NesUEFI/ui/adafruit_gfx.h Normal file
View File

@ -0,0 +1,125 @@
#ifndef _ADAFRUIT_GFX_H
#define _ADAFRUIT_GFX_H
// #include "types.h"
#include <efi.h>
#include <efilib.h>
#include <math.h>
#include <stddef.h>
#include <stdbool.h>
#include "gfxfont.h"
#ifndef pgm_read_byte
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#endif
#ifndef pgm_read_word
#define pgm_read_word(addr) (*(const unsigned short *)(addr))
#endif
#ifndef pgm_read_dword
#define pgm_read_dword(addr) (*(const unsigned long *)(addr))
#endif
/// A generic graphics superclass that can handle all sorts of drawing. At a
/// minimum you can subclass and provide drawPixel(). At a maximum you can do a
/// ton of overriding to optimize. Used for any/all Adafruit displays!
void adafruit_gfx_init(INT16 w, INT16 h); // Constructor
/**********************************************************************/
/*!
@brief Draw to the screen/framebuffer/etc.
Must be overridden in subclass.
@param x X coordinate in pixels
@param y Y coordinate in pixels
@param color 16-bit pixel color.
*/
/**********************************************************************/
void drawPixel(INT16 x, INT16 y, UINT32 color);
// TRANSACTION API / CORE DRAW API
// These MAY be overridden by the subclass to provide device-specific
// optimized code. Otherwise 'generic' versions are used.
void startWrite(void);
void writePixel(INT16 x, INT16 y, UINT32 color);
void writeFillRect(INT16 x, INT16 y, INT16 w, INT16 h,
UINT32 color);
void writeFastVLine(INT16 x, INT16 y, INT16 h, UINT32 color);
void writeFastHLine(INT16 x, INT16 y, INT16 w, UINT32 color);
void writeLine(INT16 x0, INT16 y0, INT16 x1, INT16 y1,
UINT32 color);
void endWrite(void);
// CONTROL API
// These MAY be overridden by the subclass to provide device-specific
// optimized code. Otherwise 'generic' versions are used.
void setRotation(UINT8 r);
void invertDisplay(bool i);
// BASIC DRAW API
void drawFastVLine(INT16 x, INT16 y, INT16 h, UINT32 color);
void drawFastHLine(INT16 x, INT16 y, INT16 w, UINT32 color);
void fillRect(INT16 x, INT16 y, INT16 w, INT16 h,
UINT32 color);
void fillScreen(UINT32 color);
// Optional and probably not necessary to change
void drawLine(INT16 x0, INT16 y0, INT16 x1, INT16 y1,
UINT32 color);
void drawRect(INT16 x, INT16 y, INT16 w, INT16 h,
UINT32 color);
// These exist only with Adafruit_GFX (no subclass overrides)
void drawCircle(INT16 x0, INT16 y0, INT16 r, UINT32 color);
void drawCircleHelper(INT16 x0, INT16 y0, INT16 r, UINT8 cornername,
UINT32 color);
void fillCircle(INT16 x0, INT16 y0, INT16 r, UINT32 color);
void fillCircleHelper(INT16 x0, INT16 y0, INT16 r, UINT8 cornername,
INT16 delta, UINT32 color);
void drawTriangle(INT16 x0, INT16 y0, INT16 x1, INT16 y1, INT16 x2,
INT16 y2, UINT32 color);
void fillTriangle(INT16 x0, INT16 y0, INT16 x1, INT16 y1, INT16 x2,
INT16 y2, UINT32 color);
void drawRoundRect(INT16 x0, INT16 y0, INT16 w, INT16 h,
INT16 radius, UINT32 color);
void fillRoundRect(INT16 x0, INT16 y0, INT16 w, INT16 h,
INT16 radius, UINT32 color);
void drawBitmap(INT16 x, INT16 y, UINT8 *bitmap, INT16 w, INT16 h, UINT32 color, UINT32 bg);
void drawXBitmap(INT16 x, INT16 y, const UINT8 bitmap[], INT16 w,
INT16 h, UINT32 color);
void drawGrayscaleBitmap(INT16 x, INT16 y, UINT8 *bitmap, UINT8 *mask, INT16 w, INT16 h);
void drawRGBBitmap(INT16 x, INT16 y, UINT32 *bitmap, UINT8 *mask,
INT16 w, INT16 h);
void drawChar(INT16 x, INT16 y, unsigned char c, UINT32 color,
UINT32 bg, UINT8 size_x, UINT8 size_y);
void getTextBounds(const char *string, INT16 x, INT16 y, INT16 *x1,
INT16 *y1, UINT32 *w, UINT32 *h);
size_t write(UINT8 c);
void setTextSize(UINT8 s);
void setFont(const GFXfont *f);
void setCursor(INT16 x, INT16 y);
void setTextColor(UINT32 c);
// void setTextColor(UINT32 c, UINT32 bg);
void setTextWrap(bool w);
void cp437(bool x);
INT16 width(void);
INT16 height(void);
UINT8 getRotation(void);
INT16 getCursorX(void);
INT16 getCursorY(void);
void charBounds(unsigned char c, INT16 *x, INT16 *y, INT16 *minx,
INT16 *miny, INT16 *maxx, INT16 *maxy);
void print(char *s);
void print_16(UINT16 *s);
#endif // _ADAFRUIT_GFX_H

31
NesUEFI/ui/gfxfont.h Normal file
View File

@ -0,0 +1,31 @@
// Font structures for newer Adafruit_GFX (1.1 and later).
// Example fonts are included in 'Fonts' directory.
// To use a font in your Arduino sketch, #include the corresponding .h
// file and pass address of GFXfont struct to setFont(). Pass NULL to
// revert to 'classic' fixed-space bitmap font.
#ifndef _GFXFONT_H_
#define _GFXFONT_H_
#include <efi.h>
#include <efilib.h>
/// Font data stored PER GLYPH
typedef struct {
UINT32 bitmapOffset; ///< Pointer into GFXfont->bitmap
UINT8 width; ///< Bitmap dimensions in pixels
UINT8 height; ///< Bitmap dimensions in pixels
UINT8 xAdvance; ///< Distance to advance cursor (x axis)
INT8 xOffset; ///< X dist from cursor pos to UL corner
INT8 yOffset; ///< Y dist from cursor pos to UL corner
} GFXglyph;
/// Data stored for FONT AS A WHOLE
typedef struct {
UINT8 *bitmap; ///< Glyph bitmaps, concatenated
GFXglyph *glyph; ///< Glyph array
UINT32 first; ///< ASCII extents (first char)
UINT32 last; ///< ASCII extents (last char)
UINT8 yAdvance; ///< Newline distance (y axis)
} GFXfont;
#endif // _GFXFONT_H_

132
NesUEFI/ui/glcdfont.c Normal file
View File

@ -0,0 +1,132 @@
// This is the 'classic' fixed-space bitmap font for Adafruit_GFX since 1.0.
// See gfxfont.h for newer custom bitmap font info.
#ifndef FONT5X7_H
#define FONT5X7_H
#define PROGMEM
// Standard ASCII 5x7 font
static const unsigned char font[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x3E, 0x6B,
0x4F, 0x6B, 0x3E, 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x18, 0x3C, 0x7E, 0x3C,
0x18, 0x1C, 0x57, 0x7D, 0x57, 0x1C, 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 0x00,
0x18, 0x3C, 0x18, 0x00, 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 0x00, 0x18, 0x24,
0x18, 0x00, 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 0x30, 0x48, 0x3A, 0x06, 0x0E,
0x26, 0x29, 0x79, 0x29, 0x26, 0x40, 0x7F, 0x05, 0x05, 0x07, 0x40, 0x7F,
0x05, 0x25, 0x3F, 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 0x7F, 0x3E, 0x1C, 0x1C,
0x08, 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 0x14, 0x22, 0x7F, 0x22, 0x14, 0x5F,
0x5F, 0x00, 0x5F, 0x5F, 0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00, 0x66, 0x89,
0x95, 0x6A, 0x60, 0x60, 0x60, 0x60, 0x60, 0x94, 0xA2, 0xFF, 0xA2, 0x94,
0x08, 0x04, 0x7E, 0x04, 0x08, 0x10, 0x20, 0x7E, 0x20, 0x10, 0x08, 0x08,
0x2A, 0x1C, 0x08, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x1E, 0x10, 0x10, 0x10,
0x10, 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 0x30, 0x38, 0x3E, 0x38, 0x30, 0x06,
0x0E, 0x3E, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F,
0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x14, 0x7F, 0x14, 0x7F, 0x14,
0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62, 0x36, 0x49,
0x56, 0x20, 0x50, 0x00, 0x08, 0x07, 0x03, 0x00, 0x00, 0x1C, 0x22, 0x41,
0x00, 0x00, 0x41, 0x22, 0x1C, 0x00, 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x08,
0x08, 0x3E, 0x08, 0x08, 0x00, 0x80, 0x70, 0x30, 0x00, 0x08, 0x08, 0x08,
0x08, 0x08, 0x00, 0x00, 0x60, 0x60, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02,
0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x72, 0x49,
0x49, 0x49, 0x46, 0x21, 0x41, 0x49, 0x4D, 0x33, 0x18, 0x14, 0x12, 0x7F,
0x10, 0x27, 0x45, 0x45, 0x45, 0x39, 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x41,
0x21, 0x11, 0x09, 0x07, 0x36, 0x49, 0x49, 0x49, 0x36, 0x46, 0x49, 0x49,
0x29, 0x1E, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x34, 0x00, 0x00,
0x00, 0x08, 0x14, 0x22, 0x41, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x41,
0x22, 0x14, 0x08, 0x02, 0x01, 0x59, 0x09, 0x06, 0x3E, 0x41, 0x5D, 0x59,
0x4E, 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x3E,
0x41, 0x41, 0x41, 0x22, 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x49, 0x49,
0x49, 0x41, 0x7F, 0x09, 0x09, 0x09, 0x01, 0x3E, 0x41, 0x41, 0x51, 0x73,
0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x41, 0x7F, 0x41, 0x00, 0x20, 0x40,
0x41, 0x3F, 0x01, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x7F, 0x40, 0x40, 0x40,
0x40, 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x3E,
0x41, 0x41, 0x41, 0x3E, 0x7F, 0x09, 0x09, 0x09, 0x06, 0x3E, 0x41, 0x51,
0x21, 0x5E, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x26, 0x49, 0x49, 0x49, 0x32,
0x03, 0x01, 0x7F, 0x01, 0x03, 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x1F, 0x20,
0x40, 0x20, 0x1F, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x63, 0x14, 0x08, 0x14,
0x63, 0x03, 0x04, 0x78, 0x04, 0x03, 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00,
0x7F, 0x41, 0x41, 0x41, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41,
0x41, 0x7F, 0x04, 0x02, 0x01, 0x02, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40,
0x00, 0x03, 0x07, 0x08, 0x00, 0x20, 0x54, 0x54, 0x78, 0x40, 0x7F, 0x28,
0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x28, 0x38, 0x44, 0x44, 0x28,
0x7F, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x08, 0x7E, 0x09, 0x02, 0x18,
0xA4, 0xA4, 0x9C, 0x78, 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7D,
0x40, 0x00, 0x20, 0x40, 0x40, 0x3D, 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00,
0x00, 0x41, 0x7F, 0x40, 0x00, 0x7C, 0x04, 0x78, 0x04, 0x78, 0x7C, 0x08,
0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38, 0xFC, 0x18, 0x24, 0x24,
0x18, 0x18, 0x24, 0x24, 0x18, 0xFC, 0x7C, 0x08, 0x04, 0x04, 0x08, 0x48,
0x54, 0x54, 0x54, 0x24, 0x04, 0x04, 0x3F, 0x44, 0x24, 0x3C, 0x40, 0x40,
0x20, 0x7C, 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x3C, 0x40, 0x30, 0x40, 0x3C,
0x44, 0x28, 0x10, 0x28, 0x44, 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x44, 0x64,
0x54, 0x4C, 0x44, 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x77, 0x00,
0x00, 0x00, 0x41, 0x36, 0x08, 0x00, 0x02, 0x01, 0x02, 0x04, 0x02, 0x3C,
0x26, 0x23, 0x26, 0x3C, 0x1E, 0xA1, 0xA1, 0x61, 0x12, 0x3A, 0x40, 0x40,
0x20, 0x7A, 0x38, 0x54, 0x54, 0x55, 0x59, 0x21, 0x55, 0x55, 0x79, 0x41,
0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut
0x21, 0x55, 0x54, 0x78, 0x40, 0x20, 0x54, 0x55, 0x79, 0x40, 0x0C, 0x1E,
0x52, 0x72, 0x12, 0x39, 0x55, 0x55, 0x55, 0x59, 0x39, 0x54, 0x54, 0x54,
0x59, 0x39, 0x55, 0x54, 0x54, 0x58, 0x00, 0x00, 0x45, 0x7C, 0x41, 0x00,
0x02, 0x45, 0x7D, 0x42, 0x00, 0x01, 0x45, 0x7C, 0x40, 0x7D, 0x12, 0x11,
0x12, 0x7D, // A-umlaut
0xF0, 0x28, 0x25, 0x28, 0xF0, 0x7C, 0x54, 0x55, 0x45, 0x00, 0x20, 0x54,
0x54, 0x7C, 0x54, 0x7C, 0x0A, 0x09, 0x7F, 0x49, 0x32, 0x49, 0x49, 0x49,
0x32, 0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut
0x32, 0x4A, 0x48, 0x48, 0x30, 0x3A, 0x41, 0x41, 0x21, 0x7A, 0x3A, 0x42,
0x40, 0x20, 0x78, 0x00, 0x9D, 0xA0, 0xA0, 0x7D, 0x3D, 0x42, 0x42, 0x42,
0x3D, // O-umlaut
0x3D, 0x40, 0x40, 0x40, 0x3D, 0x3C, 0x24, 0xFF, 0x24, 0x24, 0x48, 0x7E,
0x49, 0x43, 0x66, 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 0xFF, 0x09, 0x29, 0xF6,
0x20, 0xC0, 0x88, 0x7E, 0x09, 0x03, 0x20, 0x54, 0x54, 0x79, 0x41, 0x00,
0x00, 0x44, 0x7D, 0x41, 0x30, 0x48, 0x48, 0x4A, 0x32, 0x38, 0x40, 0x40,
0x22, 0x7A, 0x00, 0x7A, 0x0A, 0x0A, 0x72, 0x7D, 0x0D, 0x19, 0x31, 0x7D,
0x26, 0x29, 0x29, 0x2F, 0x28, 0x26, 0x29, 0x29, 0x29, 0x26, 0x30, 0x48,
0x4D, 0x40, 0x20, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x38, 0x2F, 0x10, 0xC8, 0xAC, 0xBA, 0x2F, 0x10, 0x28, 0x34, 0xFA, 0x00,
0x00, 0x7B, 0x00, 0x00, 0x08, 0x14, 0x2A, 0x14, 0x22, 0x22, 0x14, 0x2A,
0x14, 0x08, 0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old
// code
0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block
0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block
0x00, 0x00, 0x00, 0xFF, 0x00, 0x10, 0x10, 0x10, 0xFF, 0x00, 0x14, 0x14,
0x14, 0xFF, 0x00, 0x10, 0x10, 0xFF, 0x00, 0xFF, 0x10, 0x10, 0xF0, 0x10,
0xF0, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x14, 0x14, 0x17,
0x10, 0x1F, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0x1F, 0x00,
0x10, 0x10, 0x10, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x10,
0x10, 0x1F, 0x10, 0x10, 0x10, 0x10, 0xF0, 0x10, 0x00, 0x00, 0x00, 0xFF,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0xFF, 0x10, 0x00,
0x00, 0x00, 0xFF, 0x14, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x1F,
0x10, 0x17, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14, 0x17, 0x10, 0x17,
0x14, 0x14, 0xF4, 0x04, 0xF4, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14,
0x14, 0x14, 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x17,
0x14, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0xF4, 0x14, 0x10,
0x10, 0xF0, 0x10, 0xF0, 0x00, 0x00, 0x1F, 0x10, 0x1F, 0x00, 0x00, 0x00,
0x1F, 0x14, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x00, 0x00, 0xF0, 0x10, 0xF0,
0x10, 0x10, 0xFF, 0x10, 0xFF, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x10, 0x10,
0x10, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x10, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x38, 0x44, 0x44,
0x38, 0x44, 0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta
0x7E, 0x02, 0x02, 0x06, 0x06, 0x02, 0x7E, 0x02, 0x7E, 0x02, 0x63, 0x55,
0x49, 0x41, 0x63, 0x38, 0x44, 0x44, 0x3C, 0x04, 0x40, 0x7E, 0x20, 0x1E,
0x20, 0x06, 0x02, 0x7E, 0x02, 0x02, 0x99, 0xA5, 0xE7, 0xA5, 0x99, 0x1C,
0x2A, 0x49, 0x2A, 0x1C, 0x4C, 0x72, 0x01, 0x72, 0x4C, 0x30, 0x4A, 0x4D,
0x4D, 0x30, 0x30, 0x48, 0x78, 0x48, 0x30, 0xBC, 0x62, 0x5A, 0x46, 0x3D,
0x3E, 0x49, 0x49, 0x49, 0x00, 0x7E, 0x01, 0x01, 0x01, 0x7E, 0x2A, 0x2A,
0x2A, 0x2A, 0x2A, 0x44, 0x44, 0x5F, 0x44, 0x44, 0x40, 0x51, 0x4A, 0x44,
0x40, 0x40, 0x44, 0x4A, 0x51, 0x40, 0x00, 0x00, 0xFF, 0x01, 0x03, 0xE0,
0x80, 0xFF, 0x00, 0x00, 0x08, 0x08, 0x6B, 0x6B, 0x08, 0x36, 0x12, 0x36,
0x24, 0x36, 0x06, 0x0F, 0x09, 0x0F, 0x06, 0x00, 0x00, 0x18, 0x18, 0x00,
0x00, 0x00, 0x10, 0x10, 0x00, 0x30, 0x40, 0xFF, 0x01, 0x01, 0x00, 0x1F,
0x01, 0x01, 0x1E, 0x00, 0x19, 0x1D, 0x17, 0x12, 0x00, 0x3C, 0x3C, 0x3C,
0x3C, 0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP
};
// allow clean compilation with [-Wunused-const-variable=] and [-Wall]
static inline void avoid_unused_const_variable_compiler_warning(void) {
(void)font;
}
#endif // FONT5X7_H

206
NesUEFI/ui/ui_gfx.c Normal file
View File

@ -0,0 +1,206 @@
#include "./driver/timer.h"
#include "./driver/kernel.h"
#include "./driver/bmp_header.h"
#include "./driver/keyboard.h"
#include "./driver/graphics.h"
#include "./driver/sfs.h"
#include "./hal/nes.h"
#include "./hal/nes_key_hal.h"
#include "./emulator/fce.h"
#include "./emulator/ppu.h"
#include "./emulator/cpu.h"
#include "adafruit_gfx.h"
#include "ui_gfx.h"
CHAR16 rom_collection[MAX_FILE_NUMBER][MAX_FILE_NAME_SIZE];
ui_state_t g_ui_state = UI_STATE_SPLASH;
static int total_roms = 0;
static int current_rom_index = 0;
CHAR16 * strstr_16(CHAR16 * string, CHAR16 * substring){
CHAR16 *a, *b;
// https://opensource.apple.com/source/tcl/tcl-10/tcl/compat/strstr.c.auto.html
/* First scan quickly through the two strings looking for a
* single-character match. When it's found, then compare the
* rest of the substring.
*/
b = substring;
if (*b == 0) {
return string;
}
for ( ; *string != 0; string += 1) {
if (*string != *b) {
continue;
}
a = string;
while (1) {
if (*b == 0) {
return string;
}
if (*a++ != *b++) {
break;
}
}
b = substring;
}
return NULL;
}
void draw_menu(CHAR16 * item_name){
int x_offset = (kernel.graphics->Mode->Info->HorizontalResolution - 601) / 2;
int y_offset = (kernel.graphics->Mode->Info->VerticalResolution - 573) / 2 + 495;
fillRect( x_offset, y_offset, 573, 20, 0x0f4106);
setCursor(x_offset + 30, y_offset + 6);
setTextColor(0xFFFFFFFF);
setTextSize(1);
write(17);
print(" ");
print_16( item_name);
print(" ");
write(16);
}
bool load_current_nes_file(){
bool rc = false;;
EFI_DEVICE_PATH * dp;
EFI_FILE_HANDLE root = get_volume(kernel.image_handle, (void*)&dp);
EFI_FILE_HANDLE FileHandle;
uefi_call_wrapper(root->Open, 5, root, &FileHandle, rom_collection[current_rom_index], EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
UINT64 ReadSize = file_size(FileHandle);
EFI_HANDLE * Buffer = AllocatePool(ReadSize);
uefi_call_wrapper(FileHandle->Read, 3, FileHandle, &ReadSize, Buffer);
if(fce_load_rom((char*)Buffer) == 0){
// Print(L"ROM LOADED\n\r");
draw_menu(L"ROM LOADED");
rc = true;
} else{
draw_menu(L"ROM LOAD ERROR");
}
// uefi_call_wrapper(FileHandle->Close, 0);
FreePool(Buffer);
return rc;
}
void render_menu(UINT16 key){
switch(key){
case KEY_UP:
case KEY_RIGHT:{
current_rom_index++;
current_rom_index = current_rom_index % total_roms;
break;
}
case KEY_DOWN:
case KEY_LEFT:{
current_rom_index--;
if(current_rom_index < 0){
current_rom_index = total_roms - 1;
}
break;
}
case KEY_START:{
g_ui_state = UI_STATE_LOAD_ROM;
break;
}
default:
break;
}
draw_menu(rom_collection[current_rom_index]);
}
void save_rom_info(EFI_FILE_INFO * info){
// Print(L"FileName = %s\n", info -> FileName);
if((strstr_16(info -> FileName, L".nes")) || (strstr_16(info -> FileName, L".NES"))){
StrCpy(rom_collection[total_roms], info -> FileName);
total_roms++;
}
}
int populate_menu_items(){
EFI_DEVICE_PATH * dp;
EFI_FILE_HANDLE root = get_volume(kernel.image_handle, (void*)&dp);
list_directory(root, dp, save_rom_info);
return total_roms;
}
ui_state_t ui_manage_states(){
switch(g_ui_state){
case UI_STATE_SPLASH:{
render_splash_screen();
g_ui_state = UI_STATE_MENU;
break;
}
case UI_STATE_MENU:{
break;
}
case UI_STATE_LOAD_ROM:{
if(load_current_nes_file()){
fce_init();
graphics_clear_framebuffer(kernel.graphics);
g_ui_state = UI_STATE_PLAY;
} else{
g_ui_state = UI_STATE_MENU;
}
break;
}
case UI_STATE_PLAY:{
fce_run();
// hal_nes_ctrl_key_clear();
break;
}
}
return g_ui_state;
}
void render_splash_screen(){
EFI_DEVICE_PATH * dp;
EFI_FILE_HANDLE root = get_volume(kernel.image_handle, (void*)&dp);
CHAR16 *FileName = L"splash.bmp";
EFI_FILE_HANDLE FileHandle;
graphics_clear_framebuffer(kernel.graphics);
/* open the file */
uefi_call_wrapper(root->Open, 5, root, &FileHandle, FileName, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
/* read from the file */
UINT64 ReadSize = file_size(FileHandle);
EFI_HANDLE * Buffer = AllocatePool(ReadSize);
uefi_call_wrapper(FileHandle->Read, 3, FileHandle, &ReadSize, Buffer);
BMP_IMAGE_HEADER * bmp_header = (BMP_IMAGE_HEADER *) Buffer;
int x_offset = (kernel.graphics->Mode->Info->HorizontalResolution - bmp_header->PixelHeight) / 2;
int y_offset = (kernel.graphics->Mode->Info->VerticalResolution - bmp_header->PixelWidth) / 2;
graphics_draw_bitmap( kernel.graphics, x_offset, y_offset, (void**)Buffer);
// uefi_call_wrapper(FileHandle->Close, 0);
FreePool(Buffer);
// WaitForSingleEvent(ST->ConIn->WaitForKey, 0);
}
void ui_key_handler(uint32_t key){
if(g_ui_state == UI_STATE_MENU){render_menu(key);}
else if(g_ui_state == UI_STATE_PLAY){
hal_nes_set_key(key);
if(key == 'r'){
g_ui_state = UI_STATE_SPLASH;
}
}
}

19
NesUEFI/ui/ui_gfx.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef UI_GFX_H
#define UI_GFX_H
#include <stdbool.h>
typedef enum {
UI_STATE_SPLASH,
UI_STATE_MENU,
UI_STATE_LOAD_ROM,
UI_STATE_PLAY
} ui_state_t;
void render_menu(UINT16 key);
void render_splash_screen();
int populate_menu_items();
ui_state_t ui_manage_states();
void ui_key_handler(uint32_t key);
#endif