diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0737d71..c4e76f4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,11 +1,11 @@
cmake_minimum_required(VERSION 3.24)
-project(HansTheGatherer)
+set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/riscv-toolchain.cmake)
+
+project(HansTheGatherer C CXX ASM)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
-# Option to switch real platform vs. SDL implementation...
-
include(FetchContent)
FetchContent_Declare(
@@ -14,8 +14,24 @@ FetchContent_Declare(
OVERRIDE_FIND_PACKAGE
)
FetchContent_MakeAvailable(entt)
+
find_package(entt CONFIG REQUIRED)
+add_library(RISCV_Options INTERFACE)
+target_compile_options(RISCV_Options INTERFACE
+ -fno-exceptions
+ -fno-unwind-tables
+ -fno-rtti
+ -fno-pic # PIC?
+ -mno-relax
+ -march=rv32im
+ -mabi=ilp32
+ -std=c++20
+)
+
+add_subdirectory(lib)
+add_subdirectory(wuehans)
+
add_executable(HansTheGatherer
src/main.cpp
src/audio.cpp
@@ -25,6 +41,20 @@ add_executable(HansTheGatherer
src/render.cpp
)
-target_link_libraries(HansTheGatherer EnTT)
+target_link_options(HansTheGatherer PRIVATE
+ -nostartfiles
+ -Wl,--gc-sections
+ -march=rv32im
+ -mabi=ilp32
+ -lstdc++
+ -lc
+ -lgcc
+ -mcmodel=medany
+)
-set_property(TARGET HansTheGatherer PROPERTY CXX_STANDARD 20)
+target_link_libraries(HansTheGatherer PRIVATE
+ RISCV_Options
+ WueHans
+ Hall
+ EnTT
+)
diff --git a/cmake/riscv-toolchain.cmake b/cmake/riscv-toolchain.cmake
new file mode 100644
index 0000000..91fa1dc
--- /dev/null
+++ b/cmake/riscv-toolchain.cmake
@@ -0,0 +1,19 @@
+set(CMAKE_SYSTEM_NAME Generic)
+set(CMAKE_SYSTEM_PROCESSOR riscv)
+
+set(RISCV_PREFIX riscv32-unknown-elf)
+set(CMAKE_C_COMPILER ${RISCV_PREFIX}-gcc)
+set(CMAKE_CXX_COMPILER ${RISCV_PREFIX}-g++)
+set(CMAKE_ASM_COMPILER ${RISCV_PREFIX}-gcc)
+
+set(CMAKE_FIND_ROOT_PATH root)
+
+# Don't run the linker on compiler check
+set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+
+# adjust the default behavior of the FIND_XXX() commands:
+# search programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+# search headers and libraries in the target environment
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
new file mode 100644
index 0000000..62032de
--- /dev/null
+++ b/lib/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(Hall)
diff --git a/lib/Hall/CMakeLists.txt b/lib/Hall/CMakeLists.txt
new file mode 100644
index 0000000..435cd42
--- /dev/null
+++ b/lib/Hall/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_library(Hall STATIC
+ source/Audio.cpp
+ source/System.cpp
+ source/Video.cpp
+)
+
+target_include_directories(Hall
+ PUBLIC SYSTEM include
+)
+
+target_link_libraries(Hall PRIVATE RISCV_Options)
diff --git a/lib/Hall/README.md b/lib/Hall/README.md
new file mode 100644
index 0000000..790ef60
--- /dev/null
+++ b/lib/Hall/README.md
@@ -0,0 +1,2 @@
+# Hall
+Hallo, hier ist Hall
diff --git a/lib/Hall/include/Hall/Audio.h b/lib/Hall/include/Hall/Audio.h
new file mode 100644
index 0000000..b3a7cb3
--- /dev/null
+++ b/lib/Hall/include/Hall/Audio.h
@@ -0,0 +1,63 @@
+#pragma once
+
+namespace Hall
+{
+ ///
+ /// Sets the global volume. 0 is muted, 128 is the default, 255 is max
+ ///
+ /// 0 is muted, 128 is the default, 255 is max
+ void SetGlobalVolume(unsigned char volume);
+
+ ///
+ /// Prepares a channel to be used to play a non-looped single-channel audio
+ ///
+ /// The ID of the channel. Must be within [0,7]
+ /// A pointer to the first sample of the audio data
+ /// The total number of samples in the audio data
+ /// The channel's local volume. 0 is muted, 128 is the default, 255 is max
+ void SetupMono(int channelID, short* data, int sampleCount, unsigned char volume = 128);
+
+ ///
+ /// Prepares a channel to be used to play a looped single-channel audio
+ ///
+ /// The ID of the channel. Must be within [0,7]
+ /// A pointer to the first sample of the audio data
+ /// The total number of samples in the audio data
+ /// The index of the first sample of the loop (inclusive)
+ /// The index of the last sample of the loop (exclusive?)
+ /// The channel's local volume. 0 is muted, 128 is the default, 255 is max
+ void SetupMono(int channelID, short* data, int sampleCount, unsigned int loopStart, unsigned int loopEnd, unsigned char volume = 128);
+
+ ///
+ /// Prepares two channels to be used to play a non-looped stereo audio
+ ///
+ /// The ID of the channel for the left audio. Must be within [0,7]
+ /// The ID of the channel for the right audio. Must be within [0,7]
+ /// A pointer to the first sample of the audio data
+ /// The total number of samples PER CHANNEL in the audio data
+ /// The channel's local volume. 0 is muted, 128 is the default, 255 is max
+ void SetupStereo(int channelID_left, int channelID_right, short* data, int sampleCount, unsigned char volume = 128);
+
+ ///
+ /// Prepares two channels to be used to play a looped stereo audio
+ ///
+ /// The ID of the channel for the left audio. Must be within [0,7]
+ /// The ID of the channel for the right audio. Must be within [0,7]
+ /// A pointer to the first sample of the audio data
+ /// The total number of samples PER CHANNEL in the audio data
+ /// The index of the first sample of the loop (inclusive)
+ /// The index of the last sample of the loop (exclusive?)
+ /// The channel's local volume. 0 is muted, 128 is the default, 255 is max
+ void SetupStereo(int channelID_left, int channelID_right, short* data, int sampleCount, unsigned int loopStart, unsigned int loopEnd, unsigned char volume = 128);
+
+
+ void Play(unsigned char channelSelect);
+ void Pause(unsigned char channelSelect);
+ void SetData(unsigned char channelSelect, short* data);
+ void SetLoop(unsigned char channelSelect, bool isLooping);
+ void SetLoopBoundaries(unsigned char channelSelect, int start, int end);
+ void SetPosition(unsigned char channelSelect, int position);
+ void SetSample(unsigned char channelSelect, short sample);
+ void SetVolume(unsigned char channelSelect, unsigned char volume);
+
+}
diff --git a/lib/Hall/include/Hall/Hall.h b/lib/Hall/include/Hall/Hall.h
new file mode 100644
index 0000000..673d7f8
--- /dev/null
+++ b/lib/Hall/include/Hall/Hall.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include
+#include
+#include
diff --git a/lib/Hall/include/Hall/System.h b/lib/Hall/include/Hall/System.h
new file mode 100644
index 0000000..41de630
--- /dev/null
+++ b/lib/Hall/include/Hall/System.h
@@ -0,0 +1,36 @@
+#pragma once
+
+namespace Hall
+{
+ static const int SYSTEM_CLK_FREQUENCY = 50000000; //50 MHz
+ static void* const SDRAM_START = 0x0; //Start of the RAM. It is 32 MB large
+ static void* const BOOTLOADER_START = (void* const) 0x02010000; //Start of the bootloader. It is 32 kB large
+ static void* const SD_CARD_START = (void* const) 0x80000000; //Start of the SD-Card
+
+ ///
+ /// Returns an 8-Byte int, that represents the number of ticks that passed since the system's boot-up. Divide by SYSTEM_CLK_FREQUENCY to get seconds
+ ///
+ /// The system time since boot-up in ticks
+ unsigned long long GetSystemTime();
+
+ ///
+ /// Returns the state of all buttons on the controller.
+ ///
+ /// Must be in the range [0,1]
+ /// Each button corresponds to one bit: 0 - B, 1 - Y, 2 - Select, 3 - Start, 4 - Up, 5 - Down, 6 - Left, 7 - Right, 8 - A, 9 - X, 10 - L, 11 - R
+ unsigned short GetController(int id);
+
+ unsigned int GetSystemTimeExcerpt(int precision);
+ bool GetA(unsigned short controller);
+ bool GetB(unsigned short controller);
+ bool GetX(unsigned short controller);
+ bool GetY(unsigned short controller);
+ bool GetStart(unsigned short controller);
+ bool GetSelect(unsigned short controller);
+ bool GetUp(unsigned short controller);
+ bool GetDown(unsigned short controller);
+ bool GetLeft(unsigned short controller);
+ bool GetRight(unsigned short controller);
+ bool GetL(unsigned short controller);
+ bool GetR(unsigned short controller);
+}
diff --git a/lib/Hall/include/Hall/Video.h b/lib/Hall/include/Hall/Video.h
new file mode 100644
index 0000000..0ce4634
--- /dev/null
+++ b/lib/Hall/include/Hall/Video.h
@@ -0,0 +1,43 @@
+#pragma once
+
+namespace Hall
+{
+ const int SCREEN_HEIGHT = 240;
+ const int SCREEN_WIDTH = 400;
+
+ ///
+ /// Draws an excerpt of the given data onto the screen. All coordinates describe the top-left corner
+ ///
+ /// A pointer to the first pixel of an image
+ /// The x offset within that image
+ /// The y offset within that image
+ /// The x position on screen.
+ /// The y position on screen.
+ /// The width of the excerpt
+ /// The height of the excerpt
+ /// The width of the image (NOT EXCERPT)
+ void Draw(unsigned short* data, unsigned short xOffset, unsigned short yOffset, unsigned short screenX, unsigned short screenY, unsigned short width, unsigned short height, unsigned short dataWidth);
+
+ ///
+ /// Clears the whole screen with the given color
+ ///
+ /// The format is R5G5B5A1, with the alpha bit being lsb
+ void Clear(unsigned short color);
+
+ void SetData(unsigned short* data);
+ void SetXOffset(unsigned short xOffset);
+ void SetYOffset(unsigned short yOffset);
+ void SetImageWidth(unsigned short imageWidth);
+ void SetWidth(unsigned short width);
+ void SetHeight(unsigned short height);
+ void SetScreenX(unsigned short x);
+ void SetScreenY(unsigned short y);
+ void SetClearColor(unsigned short color);
+ void SetCommandDraw();
+ void SetCommandClear();
+ void SetCommandSwapBuffers();
+
+ bool GetIsGPUBusy();
+ bool GetVSync();
+ bool GetHSync();
+}
diff --git a/lib/Hall/source/Audio.cpp b/lib/Hall/source/Audio.cpp
new file mode 100644
index 0000000..2537289
--- /dev/null
+++ b/lib/Hall/source/Audio.cpp
@@ -0,0 +1,132 @@
+#include
+
+volatile char* AUDIO_START = (char*) 0x2000100;
+volatile short** AUDIO_START_ADDRESS = (volatile short**)(AUDIO_START + 4);
+volatile int* AUDIO_SAMPLE_COUNT = (volatile int*)(AUDIO_START + 8);
+volatile int* AUDIO_LOOP_START = (volatile int*)(AUDIO_START + 12);
+volatile int* AUDIO_LOOP_END = (volatile int*)(AUDIO_START + 16);
+volatile int* AUDIO_CURRENT_POSITION = (volatile int*)(AUDIO_START + 20);
+volatile short* AUDIO_LAST_SAMPLE = (volatile short*)(AUDIO_START + 24);
+volatile unsigned char* AUDIO_VOLUME = (volatile unsigned char*)(AUDIO_START + 28);
+volatile bool* AUDIO_IS_LOOPING = (volatile bool*)(AUDIO_START + 32);
+volatile bool* AUDIO_IS_PLAYING = (volatile bool*)(AUDIO_START + 36);
+volatile bool* AUDIO_IS_MONO = (volatile bool*)(AUDIO_START + 40);
+volatile bool* AUDIO_IS_RIGHT = (volatile bool*)(AUDIO_START + 44);
+unsigned char* AUDIO_GLOBAL_VOLUME = (unsigned char*)(AUDIO_START + 48); //I think we can skip volatile for these two
+unsigned char* AUDIO_CHANNEL_SELECT = (unsigned char*)(AUDIO_START + 52); //Because they will never change and always address the same value
+
+
+void Hall::SetGlobalVolume(unsigned char volume)
+{
+ *AUDIO_GLOBAL_VOLUME = volume;
+}
+
+void Hall::SetupMono(int channelID, short* data, int sampleCount, unsigned char volume)
+{
+ *AUDIO_CHANNEL_SELECT = 1 << channelID;
+ *AUDIO_START_ADDRESS = data;
+ *AUDIO_SAMPLE_COUNT = sampleCount;
+ *AUDIO_CURRENT_POSITION = 0;
+ *AUDIO_VOLUME = volume;
+ *AUDIO_IS_LOOPING = false;
+ *AUDIO_IS_PLAYING = false;
+ *AUDIO_IS_MONO = true;
+}
+
+void Hall::SetupMono(int channelID, short* data, int sampleCount, unsigned int loopStart, unsigned int loopEnd, unsigned char volume)
+{
+ *AUDIO_CHANNEL_SELECT = 1 << channelID;
+ *AUDIO_START_ADDRESS = data;
+ *AUDIO_SAMPLE_COUNT = sampleCount;
+ *AUDIO_CURRENT_POSITION = 0;
+ *AUDIO_VOLUME = volume;
+ *AUDIO_IS_LOOPING = true;
+ *AUDIO_LOOP_START = loopStart;
+ *AUDIO_LOOP_END = loopEnd;
+ *AUDIO_IS_PLAYING = false;
+ *AUDIO_IS_MONO = true;
+}
+
+void Hall::SetupStereo(int channelID_left, int channelID_right, short* data, int sampleCount, unsigned char volume)
+{
+ *AUDIO_CHANNEL_SELECT = (1 << channelID_left) | (1 << channelID_right);
+ *AUDIO_START_ADDRESS = data;
+ *AUDIO_SAMPLE_COUNT = sampleCount;
+ *AUDIO_CURRENT_POSITION = 0;
+ *AUDIO_VOLUME = volume;
+ *AUDIO_IS_LOOPING = false;
+ *AUDIO_IS_PLAYING = false;
+ *AUDIO_IS_MONO = false;
+ *AUDIO_IS_RIGHT = false;
+
+ *AUDIO_CHANNEL_SELECT = 1 << channelID_right;
+ *AUDIO_IS_RIGHT = true;
+}
+
+void Hall::SetupStereo(int channelID_left, int channelID_right, short* data, int sampleCount, unsigned int loopStart, unsigned int loopEnd, unsigned char volume)
+{
+ *AUDIO_CHANNEL_SELECT = (1 << channelID_left) | (1 << channelID_right);
+ *AUDIO_START_ADDRESS = data;
+ *AUDIO_SAMPLE_COUNT = sampleCount;
+ *AUDIO_CURRENT_POSITION = 0;
+ *AUDIO_VOLUME = volume;
+ *AUDIO_LOOP_START = loopStart;
+ *AUDIO_LOOP_END = loopEnd;
+ *AUDIO_IS_LOOPING = true;
+ *AUDIO_IS_PLAYING = false;
+ *AUDIO_IS_MONO = false;
+ *AUDIO_IS_RIGHT = false;
+
+ *AUDIO_CHANNEL_SELECT = 1 << channelID_right;
+ *AUDIO_IS_RIGHT = true;
+
+}
+
+void Hall::Play(unsigned char channelSelect)
+{
+ *AUDIO_CHANNEL_SELECT = channelSelect;
+ *AUDIO_IS_PLAYING = true;
+}
+
+void Hall::Pause(unsigned char channelSelect)
+{
+ *AUDIO_CHANNEL_SELECT = channelSelect;
+ *AUDIO_IS_PLAYING = false;
+}
+
+void Hall::SetData(unsigned char channelSelect, short* data)
+{
+ *AUDIO_CHANNEL_SELECT = channelSelect;
+ *AUDIO_START_ADDRESS = data;
+}
+
+void Hall::SetLoop(unsigned char channelSelect, bool isLooping)
+{
+ *AUDIO_CHANNEL_SELECT = channelSelect;
+ *AUDIO_IS_LOOPING;
+}
+
+void Hall::SetLoopBoundaries(unsigned char channelSelect, int start, int end)
+{
+ *AUDIO_CHANNEL_SELECT = channelSelect;
+ *AUDIO_LOOP_START = start;
+ *AUDIO_LOOP_END = end;
+}
+
+void Hall::SetPosition(unsigned char channelSelect, int position)
+{
+ *AUDIO_CHANNEL_SELECT = channelSelect;
+ *AUDIO_CURRENT_POSITION = position;
+}
+
+void Hall::SetSample(unsigned char channelSelect, short sample)
+{
+ *AUDIO_CHANNEL_SELECT = channelSelect;
+ *AUDIO_LAST_SAMPLE = sample;
+}
+
+void Hall::SetVolume(unsigned char channelSelect, unsigned char volume)
+{
+ *AUDIO_CHANNEL_SELECT = channelSelect;
+ *AUDIO_VOLUME = volume;
+}
diff --git a/lib/Hall/source/System.cpp b/lib/Hall/source/System.cpp
new file mode 100644
index 0000000..5faa125
--- /dev/null
+++ b/lib/Hall/source/System.cpp
@@ -0,0 +1,90 @@
+#include
+
+volatile char* SYSTEM_TIME = (volatile char*)0x02000300;
+volatile unsigned int* SYSTEM_TIME_0 = (volatile unsigned int*)(SYSTEM_TIME + 0); //Least precise
+volatile unsigned int* SYSTEM_TIME_1 = (volatile unsigned int*)(SYSTEM_TIME + 4);
+volatile unsigned int* SYSTEM_TIME_2 = (volatile unsigned int*)(SYSTEM_TIME + 8);
+volatile unsigned int* SYSTEM_TIME_3 = (volatile unsigned int*)(SYSTEM_TIME + 12);
+volatile unsigned int* SYSTEM_TIME_4 = (volatile unsigned int*)(SYSTEM_TIME + 16);//Most precise
+
+volatile char* CONTROLLER_START = (volatile char*)0x02000200;
+volatile unsigned short* CONTROLLER_0 = (volatile unsigned short*)(CONTROLLER_START + 0);
+volatile unsigned short* CONTROLLER_1 = (volatile unsigned short*)(CONTROLLER_START + 2);
+
+unsigned long long Hall::GetSystemTime()
+{
+ unsigned long long result = *SYSTEM_TIME_0;
+ result = result << 32;
+ result += *SYSTEM_TIME_4;
+ return result;
+}
+
+unsigned int Hall::GetSystemTimeExcerpt(int precision)
+{
+ return *(SYSTEM_TIME_0 + precision);
+}
+
+unsigned short Hall::GetController(int id)
+{
+ return *(CONTROLLER_0 + id);
+}
+
+bool Hall::GetA(unsigned short controller)
+{
+ return controller & (1 << 8);
+}
+
+bool Hall::GetB(unsigned short controller)
+{
+ return controller & (1 << 0);
+}
+
+bool Hall::GetX(unsigned short controller)
+{
+ return controller & (1 << 9);
+}
+
+bool Hall::GetY(unsigned short controller)
+{
+ return controller & (1 << 1);
+}
+
+bool Hall::GetStart(unsigned short controller)
+{
+ return controller & (1 << 3);
+}
+
+bool Hall::GetSelect(unsigned short controller)
+{
+ return controller & (1 << 2);
+}
+
+bool Hall::GetUp(unsigned short controller)
+{
+ return controller & (1 << 4);
+}
+
+bool Hall::GetDown(unsigned short controller)
+{
+ return controller & (1 << 5);
+}
+
+bool Hall::GetLeft(unsigned short controller)
+{
+ return controller & (1 << 6);
+}
+
+bool Hall::GetRight(unsigned short controller)
+{
+ return controller & (1 << 7);
+}
+
+bool Hall::GetL(unsigned short controller)
+{
+ return controller & (1 << 10);
+}
+
+bool Hall::GetR(unsigned short controller)
+{
+ return controller & (1 << 11);
+}
diff --git a/lib/Hall/source/Video.cpp b/lib/Hall/source/Video.cpp
new file mode 100644
index 0000000..532d855
--- /dev/null
+++ b/lib/Hall/source/Video.cpp
@@ -0,0 +1,117 @@
+#include
+
+volatile char* GPU_START = (char*)0x02000000;
+volatile unsigned short** GPU_IMAGE_START = (volatile unsigned short**)(GPU_START + 0);
+volatile unsigned short* GPU_IMAGE_X_OFFSET = (volatile unsigned short*)(GPU_START + 4);
+volatile unsigned short* GPU_IMAGE_Y_OFFSET = (volatile unsigned short*)(GPU_START + 8);
+volatile unsigned short* GPU_IMAGE_WIDTH = (volatile unsigned short*)(GPU_START + 12);
+volatile unsigned short* GPU_WIDTH = (volatile unsigned short*)(GPU_START + 16);
+volatile unsigned short* GPU_HEIGHT = (volatile unsigned short*)(GPU_START + 20);
+volatile unsigned short* GPU_SCREEN_X = (volatile unsigned short*)(GPU_START + 24);
+volatile unsigned short* GPU_SCREEN_Y = (volatile unsigned short*)(GPU_START + 28);
+volatile unsigned short* GPU_CLEAR_COLOR = (volatile unsigned short*)(GPU_START + 32);
+volatile bool* GPU_COMMAND_DRAW = (volatile bool*)(GPU_START + 36);
+volatile bool* GPU_COMMAND_CLEAR = (volatile bool*)(GPU_START + 40);
+volatile bool* GPU_IS_BUSY = (volatile bool*)(GPU_START + 44);
+volatile bool* GPU_VSYNC = (volatile bool*)(GPU_START + 48);
+volatile bool* GPU_HSYNC = (volatile bool*)(GPU_START + 52);
+volatile bool* GPU_COMMAND_SWAP_BUFFERS = (volatile bool*)(GPU_START + 56);
+volatile bool* VSYNC_BUFFER_SWAP = (volatile bool*)(GPU_START + 60);
+
+
+void Hall::Draw(unsigned short* data, unsigned short xOffset, unsigned short yOffset, unsigned short screenX, unsigned short screenY, unsigned short width, unsigned short height, unsigned short dataWidth)
+{
+ *GPU_IMAGE_START = data;
+ *GPU_IMAGE_X_OFFSET = xOffset;
+ *GPU_IMAGE_Y_OFFSET = yOffset;
+ *GPU_IMAGE_WIDTH = dataWidth;
+ *GPU_WIDTH = width;
+ *GPU_HEIGHT = height;
+ *GPU_SCREEN_X = screenX;
+ *GPU_SCREEN_Y = screenY;
+ *GPU_COMMAND_DRAW = true;
+}
+
+
+void Hall::Clear(unsigned short color)
+{
+ *GPU_CLEAR_COLOR = color;
+ *GPU_COMMAND_CLEAR = true;
+}
+
+void Hall::SetData(unsigned short* data)
+{
+ *GPU_IMAGE_START = data;
+}
+
+void Hall::SetXOffset(unsigned short xOffset)
+{
+ *GPU_IMAGE_X_OFFSET = xOffset;
+}
+
+void Hall::SetYOffset(unsigned short yOffset)
+{
+ *GPU_IMAGE_Y_OFFSET = yOffset;
+}
+
+void Hall::SetImageWidth(unsigned short imageWidth)
+{
+ *GPU_IMAGE_WIDTH = imageWidth;
+}
+
+void Hall::SetWidth(unsigned short width)
+{
+ *GPU_WIDTH = width;
+}
+
+void Hall::SetHeight(unsigned short height)
+{
+ *GPU_HEIGHT = height;
+}
+
+
+void Hall::SetScreenX(unsigned short x)
+{
+ *GPU_SCREEN_X = x;
+}
+
+
+void Hall::SetScreenY(unsigned short y)
+{
+ *GPU_SCREEN_Y = y;
+}
+
+void Hall::SetClearColor(unsigned short color)
+{
+ *GPU_CLEAR_COLOR = color;
+}
+
+void Hall::SetCommandDraw()
+{
+ *GPU_COMMAND_DRAW = true;
+}
+
+void Hall::SetCommandClear()
+{
+ *GPU_COMMAND_CLEAR = true;
+}
+
+void Hall::SetCommandSwapBuffers()
+{
+ *GPU_COMMAND_SWAP_BUFFERS = true;
+}
+
+bool Hall::GetIsGPUBusy()
+{
+ return *GPU_IS_BUSY;
+}
+
+bool Hall::GetVSync()
+{
+ return *GPU_VSYNC;
+}
+
+bool Hall::GetHSync()
+{
+ return *GPU_HSYNC;
+}
diff --git a/wuehans/CMakeLists.txt b/wuehans/CMakeLists.txt
new file mode 100644
index 0000000..2ada51e
--- /dev/null
+++ b/wuehans/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_library(WueHans STATIC start.S syscall.c)
+
+target_include_directories(WueHans PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+target_link_libraries(WueHans PRIVATE RISCV_Options)
+
+target_link_options(WueHans PUBLIC
+ -T ${CMAKE_CURRENT_SOURCE_DIR}/wuehans.ld
+)
diff --git a/wuehans/start.S b/wuehans/start.S
new file mode 100644
index 0000000..bc20c74
--- /dev/null
+++ b/wuehans/start.S
@@ -0,0 +1,54 @@
+.section .text.startSection
+.global _Z4mainv
+.global _start
+
+_start:
+/* zero-initialize all registers */
+addi x1, zero, 0
+addi x2, zero, 0
+addi x3, zero, 0
+addi x4, zero, 0
+addi x5, zero, 0
+addi x6, zero, 0
+addi x7, zero, 0
+addi x8, zero, 0
+addi x9, zero, 0
+addi x10, zero, 0
+addi x11, zero, 0
+addi x12, zero, 0
+addi x13, zero, 0
+addi x14, zero, 0
+addi x15, zero, 0
+addi x16, zero, 0
+addi x17, zero, 0
+addi x18, zero, 0
+addi x19, zero, 0
+addi x20, zero, 0
+addi x21, zero, 0
+addi x22, zero, 0
+addi x23, zero, 0
+addi x24, zero, 0
+addi x25, zero, 0
+addi x26, zero, 0
+addi x27, zero, 0
+addi x28, zero, 0
+addi x29, zero, 0
+addi x30, zero, 0
+addi x31, zero, 0
+
+/* set stack pointer */
+.equ STACK_START, 0x02000000
+lui sp, %hi(STACK_START)
+addi sp, sp, %lo(STACK_START)
+
+/* push zeros on the stack for argc and argv */
+/* (stack is aligned to 16 bytes in riscv calling convention) */
+addi sp,sp,-16
+sw zero,0(sp)
+sw zero,4(sp)
+sw zero,8(sp)
+sw zero,12(sp)
+
+/* jump to libc init */
+// j _Z4mainv
+j main
diff --git a/wuehans/syscall.c b/wuehans/syscall.c
new file mode 100644
index 0000000..5c78bf8
--- /dev/null
+++ b/wuehans/syscall.c
@@ -0,0 +1,186 @@
+// #include
+#include
+#include
+#include
+#include
+/* #include */
+
+void *__dso_handle = 0;
+
+// const TCHAR STDOUT[30] = "stdout.txt";
+// const TCHAR STDERR[30] = "stderr.txt";
+
+// FIL fp = {0};
+// FATFS FatFs = {0};
+// BYTE is_mounted = 0;
+
+// struct FD_Data {
+// FIL fp;
+// BYTE mode;
+// BYTE is_open;
+// } FD_Data_default = {NULL, FA_READ, 0};
+// typedef struct FD_Data FD_Data;
+
+// #define FILE_AMOUNT 4
+// static FD_Data fd_data[FILE_AMOUNT];
+
+// int _write(int fd, char *ptr, int len) {
+// if (is_mounted == 0) {
+// f_mount(&FatFs, "", 0);
+// is_mounted = 1;
+// }
+// // stdout
+// if (fd == 1) {
+
+// FIL write_file;
+// FRESULT fr = f_open(&write_file, STDOUT, FA_OPEN_APPEND | FA_WRITE);
+// if (fr != 0) {
+// return fr;
+// }
+
+// UINT written = 0;
+
+// fr = f_write(&write_file, ptr, len, &written);
+// if (fr != 0) {
+// // TODO: Set errno
+// return -1;
+// }
+
+// f_close(&write_file);
+
+// // HACK: Hackermann
+// volatile int* x = (int*) 0x80000000;
+// int b = *x;
+// // HACK: Hackermann Ende
+
+// return written;
+// } else if (fd > 2 && (fd-3) < FILE_AMOUNT && fd_data[fd-3].is_open) {
+// UINT bw = 0;
+// f_write(&fd_data[fd-3].fp, ptr, len, &bw);
+
+// return bw;
+// }
+
+// return -1;
+// }
+
+// int _read(int fd, char *ptr, int len) {
+// if (fd > 2 && (fd-3) < FILE_AMOUNT && fd_data[fd-3].is_open) {
+// UINT bytesRead = 0;
+// FRESULT result = f_read(&fd_data[fd-3].fp, ptr, len, &bytesRead);
+// /* ScreenPrint("_read"); */
+// return bytesRead;
+// }
+
+// return -1;
+// }
+
+// void *_sbrk(int incr) {
+// extern int heap_begin;
+// static unsigned char *heap = NULL;
+// unsigned char *prev_heap;
+
+// if (heap == NULL) {
+// heap = (unsigned char *)&heap_begin;
+// }
+// prev_heap = heap;
+
+// if ((int)heap + incr < heap_begin + HEAP_SIZE) {
+// heap += incr;
+// }
+
+// /* ScreenPrint("_sbrk"); */
+// return prev_heap;
+// }
+
+// int _fstat(int fd, struct stat *st) {
+// st->st_mode = S_IFREG;
+// /* ScreenPrint("_fstat"); */
+// return 0;
+// }
+
+// int _lseek(int fd, int offset, int whence) {
+// // Invalid lseek call
+// if (fd <= 2 || (fd-3) >= FILE_AMOUNT || !fd_data[fd-3].is_open) {
+// return -1;
+// }
+
+// // Calculate the correct offset for f_lseek()
+// FSIZE_t ofs = 0;
+// switch (whence) {
+// // Set to offset directly
+// case SEEK_SET:
+// ofs = offset;
+// break;
+
+// // Increment by offset
+// case SEEK_CUR:
+// ofs = f_tell(&fd_data[fd-3].fp) + offset;
+// break;
+
+// // Append offset to EOF
+// case SEEK_END:
+// ofs = f_size(&fd_data[fd-3].fp) + offset;
+// break;
+// default:
+// return -1;
+// }
+
+// FRESULT result = f_lseek(&fd_data[fd-3].fp, ofs);
+// return result;
+// }
+
+// int _open(const char *name, int flags, int mode) {
+// if (is_mounted == 0) {
+// f_mount(&FatFs, "", 0);
+// is_mounted = 1;
+// }
+
+// int i;
+// for (i = 0; i < FILE_AMOUNT; i++) {
+// if (fd_data[i].is_open == 1) {
+// // Entry already in use
+// continue;
+// }
+// mode = FA_READ;
+// FRESULT fr = f_open(&fd_data[i].fp, name, mode);
+// if (fr != FR_OK) {
+// return -1;
+// }
+
+// fd_data[i].mode = mode;
+// fd_data[i].is_open = 1;
+// // Exclude stdout, stderr, stdin
+// return i+3;
+// }
+
+// return -1;
+// }
+
+// int _close(int fd) {
+// if (fd > 2 && (fd-3) < FILE_AMOUNT) {
+// fd_data[fd-3].is_open = 0;
+// return 0;
+// }
+
+// return -1;
+// }
+
+int _isatty(int fd)
+{
+ errno = ENOTTY;
+ /* ScreenPrint("isatty"); */
+ return 0;
+}
+
+void _exit(int status) {
+ while (1) {
+ }
+}
+
+int _kill(int pid, int sig) {
+ errno = EINVAL;
+ return -1;
+}
+
+int _getpid() { return -1; }
diff --git a/wuehans/wuehans.ld b/wuehans/wuehans.ld
new file mode 100644
index 0000000..abe0e1c
--- /dev/null
+++ b/wuehans/wuehans.ld
@@ -0,0 +1,207 @@
+
+/* ---- Original Script: /opt/riscv32i/riscv32-unknown-elf/lib/ldscripts/elf32lriscv.x ---- */
+/* Default linker script, for normal executables */
+/* Copyright (C) 2014-2017 Free Software Foundation, Inc.
+Copying and distribution of this script, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved. */
+OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv",
+"elf32-littleriscv")
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+SECTIONS
+{
+ . = 0x00000000;
+
+ .startSection :
+ {
+ KEEP(*(.text.startSection))
+ }
+ .text :
+ {
+ *(.text)
+ *(.text.unlikely .text.*_unlikely .text.unlikely.*)
+ *(.text.exit .text.exit.*)
+ *(.text.startup .text.startup.*)
+ *(.text.hot .text.hot.*)
+ *(.stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+}
+.init :
+{
+ KEEP (*(SORT_NONE(.init)))
+}
+.plt : { *(.plt) }
+.iplt : { *(.iplt) }
+.fini :
+{
+ KEEP (*(SORT_NONE(.fini)))
+}
+PROVIDE (__etext = .);
+PROVIDE (_etext = .);
+PROVIDE (etext = .);
+.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+.rodata1 : { *(.rodata1) }
+.sdata2 :
+{
+ *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+}
+.sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+.eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
+.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
+.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table
+ .gcc_except_table.*) }
+.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) }
+/* These sections are generated by the Sun/Oracle C++ compiler. */
+.exception_ranges : ONLY_IF_RO { *(.exception_ranges
+ .exception_ranges*) }
+/* Adjust the address for the data segment. We want to adjust up to
+the same address within the page on the next page up. */
+. = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
+/* Exception handling */
+.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
+.gnu_extab : ONLY_IF_RW { *(.gnu_extab) }
+.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
+.exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) }
+/* Thread Local Storage sections */
+.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+.preinit_array :
+{
+ PROVIDE_HIDDEN (__preinit_array_start = .);
+ KEEP (*(.preinit_array))
+ PROVIDE_HIDDEN (__preinit_array_end = .);
+}
+.init_array :
+{
+ PROVIDE_HIDDEN (__init_array_start = .);
+ KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
+ KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
+ PROVIDE_HIDDEN (__init_array_end = .);
+}
+.fini_array :
+{
+ PROVIDE_HIDDEN (__fini_array_start = .);
+ KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
+ KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
+ PROVIDE_HIDDEN (__fini_array_end = .);
+}
+.ctors :
+{
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+KEEP (*crtbegin.o(.ctors))
+KEEP (*crtbegin?.o(.ctors))
+/* We don't want to include the .ctor section from
+the crtend.o file until after the sorted ctors.
+The .ctor section from the crtend file contains the
+end of ctors marker and it must be last */
+KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
+KEEP (*(SORT(.ctors.*)))
+KEEP (*(.ctors))
+}
+.dtors :
+{
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*crtbegin?.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+}
+.jcr : { KEEP (*(.jcr)) }
+.data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
+.dynamic : { *(.dynamic) }
+. = DATA_SEGMENT_RELRO_END (0, .);
+.data :
+{
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+}
+.data1 : { *(.data1) }
+.got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }
+/* We want the small data sections together, so single-instruction offsets
+can access them all, and initialized data all before uninitialized, so
+we can shorten the on-disk segment size. */
+.sdata :
+{
+ __global_pointer$ = . + 0x800;
+ *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*)
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+}
+_edata = .; PROVIDE (edata = .);
+. = .;
+__bss_start = .;
+.sbss :
+{
+ *(.dynsbss)
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+}
+.bss :
+{
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ PROVIDE(_bss_end = .);
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections. FIXME: Why do we need it? When there is no .bss section, we don't
+ pad the .data section. */
+. = ALIGN(. != 0 ? 32 / 8 : 1);
+}
+. = ALIGN(32 / 8);
+. = SEGMENT_START("ldata-segment", .);
+. = ALIGN(32 / 8);
+_end = .; PROVIDE (end = .);
+. = DATA_SEGMENT_END (.);
+/* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* DWARF 3 */
+ .debug_pubtypes 0 : { *(.debug_pubtypes) }
+ .debug_ranges 0 : { *(.debug_ranges) }
+ /* DWARF Extension. */
+ .debug_macro 0 : { *(.debug_macro) }
+ .debug_addr 0 : { *(.debug_addr) }
+ .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
+ /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
+ PROVIDE(heap_begin = .);
+}
diff --git a/wuehans/wuehans_config.h b/wuehans/wuehans_config.h
new file mode 100644
index 0000000..237a2d9
--- /dev/null
+++ b/wuehans/wuehans_config.h
@@ -0,0 +1,7 @@
+#ifndef WUEHANS_CONFIG_H
+#define WUEHANS_CONFIG_H
+
+// Heap Size in Bytes
+#define HEAP_SIZE 0x800000
+
+#endif