diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..788ba45 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,21 @@ +{ + "configurations": [ + { + "name": "3ds", + "includePath": [ + "${workspaceFolder}/**", + //"C:/devkitpro/libnx/include/**", + "C:/devkitpro/libctru/include/**", + "/opt/devkitpro/libctru/include/**", + //"C:/devkitpro/portlibs/switch/include/**", + "/opt/devkitpro/portlibs/3ds/include/**", + "C:/devkitpro/portlibs/3ds/include/**" + ], + "defines": [], + "cStandard": "gnu17", + "cppStandard": "gnu++17", + "intelliSenseMode": "linux-gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/3ds.cmake b/3ds.cmake new file mode 100644 index 0000000..0a7d00a --- /dev/null +++ b/3ds.cmake @@ -0,0 +1,20 @@ +######################################################################################### +set(DEVKITPRO $ENV{DEVKITPRO}) + +set(CMAKE_SYSTEM_NAME "Nintendo 3ds") +set(CMAKE_C_COMPILER "${DEVKITPRO}/devkitARM/bin/arm-none-eabi-gcc") +set(CMAKE_CXX_COMPILER "${DEVKITPRO}/devkitARM/bin/arm-none-eabi-g++") +set(CMAKE_AR "${DEVKITPRO}/devkitARM/bin/arm-none-eabi-gcc-ar" CACHE STRING "") +set(CMAKE_RANLIB "${DEVKITPRO}/devkitARM/bin/arm-none-eabi-gcc-ranlib" CACHE STRING "") +set(CMAKE_ASM_COMPILER "${DEVKITPRO}/devkitARM/bin/arm-none-eabi-gcc") + +set(ARCH "-march=armv6k -mtune=mpcore -mfloat-abi=hard -mfpu=vfp -mtp=soft -D__3DS__") +set(CMAKE_C_FLAGS "${ARCH} -Wall -mword-relocations -O3 -fomit-frame-pointer -ffunction-sections -fdata-sections" CACHE STRING "C flags") +set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -std=gnu++20" CACHE STRING "C++ flags") +set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_FIND_ROOT_PATH ${DEVKITPRO}/devkitARM ${DEVKITPRO}/libctru ${DEVKITARM}/portlibs/3ds) + +set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Shared libs not available") + +link_directories(${DEVKITPRO}/libcrtu/lib ${DEVKITPRO}/portlibs/3ds/lib) +######################################################################################### \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0dfc6d2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.22) + +project(picasso) + +set(CMAKE_EXE_LINKER_FLAGS "-L${DEVKITPRO}/libctru/lib -L${DEVKITPRO}/picaGL/lib -L${DEVKITPRO}/portlibs/3ds/lib -specs=3dsx.specs -Wl,--gc-sections") + +include_directories(${DEVKITPRO}/libctru/include ${DEVKITPRO}/picaGL/include ${DEVKITPRO}/portlibs/3ds/include) +add_definitions("-D__3DS__") + +enable_language(ASM) + +add_library(${PROJECT_NAME} STATIC source/picasso_assembler.cpp source/picasso_library.cpp) + +target_include_directories(${PROJECT_NAME} PRIVATE include) \ No newline at end of file diff --git a/compile b/compile new file mode 100755 index 0000000..df363c8 --- /dev/null +++ b/compile @@ -0,0 +1,348 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2018-03-07.03; # UTC + +# Copyright (C) 1999-2021 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN* | MSYS*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/* | msys/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ + icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..2378216 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.22) + +project(linpicasso_sample) + +set(CMAKE_EXE_LINKER_FLAGS "-L${DEVKITPRO}/libctru/lib -L${DEVKITPRO}/picaGL/lib -L${DEVKITPRO}/portlibs/3ds/lib -specs=3dsx.specs -Wl,--gc-sections") + +include_directories(${DEVKITPRO}/libctru/include ${DEVKITPRO}/picaGL/include ${DEVKITPRO}/portlibs/3ds/include) +add_definitions("-D__3DS__") + + +set(APP_TITLE "${PROJECT_NAME}") +set(APP_DESCRIPTION "Example of Lib Picasso") +set(APP_AUTHOR "Tobi-D7, tobid7vx") + +#set(APP_ICON "${PROJECT_SOURCE_DIR}/app/icon.png") +#set(APP_ROMFS "${PROJECT_SOURCE_DIR}/romfs") + +enable_language(ASM) + +set(BASE_CTR ON CACHE BOOL "Enable 3ds") +add_subdirectory(../ picasso) + +add_executable(${PROJECT_NAME}.elf src/main.cpp) + +target_include_directories(${PROJECT_NAME}.elf PRIVATE src ../include) + +target_link_libraries(${PROJECT_NAME}.elf citro2d citro3d ctru m picasso) +#"${APP_ICON}" +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.smdh + COMMAND smdhtool --create "${APP_TITLE}" "${APP_DESCRIPTION}" "${APP_AUTHOR}" "/opt/devkitpro/libctru/default_icon.png" ${PROJECT_NAME}.smdh + DEPENDS ${PROJECT_NAME}.elf +) +#--romfs=${APP_ROMFS} +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.3dsx + COMMAND 3dsxtool ${PROJECT_NAME}.elf ${PROJECT_NAME}.3dsx --smdh=${PROJECT_NAME}.smdh + DEPENDS ${PROJECT_NAME}.elf +) + +add_custom_target( 3ds ALL + DEPENDS ${PROJECT_NAME}.smdh ${PROJECT_NAME}.3dsx +) \ No newline at end of file diff --git a/example/src/main.cpp b/example/src/main.cpp new file mode 100644 index 0000000..d97762c --- /dev/null +++ b/example/src/main.cpp @@ -0,0 +1,172 @@ +#include <3ds.h> +#include + +#include + +static const char *const vertShader = R"text( +; Example PICA200 vertex shader + +; Uniforms +.fvec projection[4] + +; Constants +.constf myconst(0.0, 1.0, -1.0, 0.1) +.constf myconst2(0.3, 0.0, 0.0, 0.0) +.alias zeros myconst.xxxx ; Vector full of zeros +.alias ones myconst.yyyy ; Vector full of ones + +; Outputs +.out outpos position +.out outclr color + +; Inputs (defined as aliases for convenience) +.alias inpos v0 +.alias inclr v1 + +.proc main + ; Force the w component of inpos to be 1.0 + mov r0.xyz, inpos + mov r0.w, ones + + ; outpos = projectionMatrix * inpos + dp4 outpos.x, projection[0], r0 + dp4 outpos.y, projection[1], r0 + dp4 outpos.z, projection[2], r0 + dp4 outpos.w, projection[3], r0 + + ; outclr = inclr + mov outclr, inclr + + ; We're finished + end +.end +)text"; + +#define CLEAR_COLOR 0x68B0D8FF + +#define DISPLAY_TRANSFER_FLAGS \ + (GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | \ + GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \ + GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)) + +typedef struct { float x, y, z; } vertex; + +static const vertex vertex_list[] = +{ + { 200.0f, 200.0f, 0.5f }, + { 100.0f, 40.0f, 0.5f }, + { 300.0f, 40.0f, 0.5f }, +}; + +#define vertex_list_count (sizeof(vertex_list)/sizeof(vertex_list[0])) + +static DVLB_s* vshader_dvlb; +static shaderProgram_s program; +static int uLoc_projection; +static C3D_Mtx projection; + + +static char* vshader_shbin; +static int vshader_shbin_size; + +static void* vbo_data; + +static void sceneInit(void) +{ + // Load the vertex shader, create a shader program and bind it + vshader_dvlb = DVLB_ParseFile((u32*)vshader_shbin, vshader_shbin_size); + shaderProgramInit(&program); + shaderProgramSetVsh(&program, &vshader_dvlb->DVLE[0]); + C3D_BindProgram(&program); + + // Get the location of the uniforms + uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection"); + + // Configure attributes for use with the vertex shader + C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position + AttrInfo_AddFixed(attrInfo, 1); // v1=color + + // Set the fixed attribute (color) to solid white + C3D_FixedAttribSet(1, 1.0, 1.0, 1.0, 1.0); + + // Compute the projection matrix + Mtx_OrthoTilt(&projection, 0.0, 400.0, 0.0, 240.0, 0.0, 1.0, true); + + // Create the VBO (vertex buffer object) + vbo_data = linearAlloc(sizeof(vertex_list)); + memcpy(vbo_data, vertex_list, sizeof(vertex_list)); + + // Configure buffers + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, vbo_data, sizeof(vertex), 1, 0x0); + + // Configure the first fragment shading substage to just pass through the vertex color + // See https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml for more insight + C3D_TexEnv* env = C3D_GetTexEnv(0); + C3D_TexEnvInit(env); + C3D_TexEnvSrc(env, C3D_Both, GPU_PRIMARY_COLOR, (GPU_TEVSRC)0, (GPU_TEVSRC)0); + C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE); +} + +static void sceneRender(void) +{ + // Update the uniforms + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection); + + // Draw the VBO + C3D_DrawArrays(GPU_TRIANGLES, 0, vertex_list_count); +} + +static void sceneExit(void) +{ + // Free the VBO + linearFree(vbo_data); + + // Free the shader program + shaderProgramFree(&program); + DVLB_Free(vshader_dvlb); +} + +int main() +{ + // Initialize graphics + gfxInitDefault(); + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + vshader_shbin = Pica::AssembleCode(vertShader, vshader_shbin_size); + + // Initialize the render target + C3D_RenderTarget* target = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetOutput(target, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + + // Initialize the scene + sceneInit(); + + // Main loop + while (aptMainLoop()) + { + hidScanInput(); + + // Respond to user input + u32 kDown = hidKeysDown(); + if (kDown & KEY_START) + break; // break in order to return to hbmenu + + // Render the scene + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C3D_RenderTargetClear(target, C3D_CLEAR_ALL, CLEAR_COLOR, 0); + C3D_FrameDrawOn(target); + sceneRender(); + C3D_FrameEnd(0); + } + + // Deinitialize the scene + sceneExit(); + + // Deinitialize graphics + C3D_Fini(); + gfxExit(); + return 0; +} \ No newline at end of file diff --git a/include/picasso.hpp b/include/picasso.hpp new file mode 100644 index 0000000..7dbd2aa --- /dev/null +++ b/include/picasso.hpp @@ -0,0 +1,9 @@ +//#pragma once +#include +#include + +namespace Pica +{ + void InstallErrorCallback(void(*ErrorHandler)(const char* top, const char* message)); + char* AssembleCode(const char* vertex, int &res_size); +} \ No newline at end of file diff --git a/source/FileClass.h b/source/FileClass.h deleted file mode 100644 index ccdd096..0000000 --- a/source/FileClass.h +++ /dev/null @@ -1,132 +0,0 @@ -#pragma once -#include -#include "types.h" - -class FileClass -{ - FILE* f; - bool LittleEndian, own; - int filePos; - - size_t _RawRead(void* buffer, size_t size) - { - size_t x = fread(buffer, 1, size, f); - filePos += x; - return x; - } - - size_t _RawWrite(const void* buffer, size_t size) - { - size_t x = fwrite(buffer, 1, size, f); - filePos += x; - return x; - } - -public: - FileClass(const char* file, const char* mode) : LittleEndian(true), own(true), filePos(0) - { - f = fopen(file, mode); - } - FileClass(FILE* inf) : f(inf), LittleEndian(true), own(false), filePos(0) { } - ~FileClass() - { - if (f && own) fclose(f); - } - - void SetLittleEndian() { LittleEndian = true; } - void SetBigEndian() { LittleEndian = false; } - - FILE* get_ptr() { return f; } - bool openerror() { return f == NULL; } - - dword_t ReadDword() - { - dword_t value; - _RawRead(&value, sizeof(dword_t)); - return LittleEndian ? le_dword(value) : be_dword(value); - } - - void WriteDword(dword_t value) - { - value = LittleEndian ? le_dword(value) : be_dword(value); - _RawWrite(&value, sizeof(dword_t)); - } - - word_t ReadWord() - { - word_t value; - _RawRead(&value, sizeof(word_t)); - return LittleEndian ? le_word(value) : be_word(value); - } - - void WriteWord(word_t value) - { - value = LittleEndian ? le_word(value) : be_word(value); - _RawWrite(&value, sizeof(word_t)); - } - - hword_t ReadHword() - { - hword_t value; - _RawRead(&value, sizeof(hword_t)); - return LittleEndian ? le_hword(value) : be_hword(value); - } - - void WriteHword(hword_t value) - { - value = LittleEndian ? le_hword(value) : be_hword(value); - _RawWrite(&value, sizeof(hword_t)); - } - - byte_t ReadByte() - { - byte_t value; - _RawRead(&value, sizeof(byte_t)); - return value; - } - - void WriteByte(byte_t value) - { - _RawWrite(&value, sizeof(byte_t)); - } - - float ReadFloat() - { - union { word_t w; float f; } t; - t.w = ReadWord(); - return t.f; - } - - void WriteFloat(float value) - { - union { word_t w; float f; } t; - t.f = value; - WriteWord(t.w); - } - - bool ReadRaw(void* buffer, size_t size) { return _RawRead(buffer, size) == size; } - bool WriteRaw(const void* buffer, size_t size) { return _RawWrite(buffer, size) == size; } - - void Seek(int pos, int mode) { fseek(f, pos, mode); } - int Tell() { return filePos /*ftell(f)*/; } - void Flush() { fflush(f); } -}; - -static inline char* StringFromFile(const char* filename) -{ - FILE* f = fopen(filename, "rb"); - if (!f) return NULL; - fseek(f, 0, SEEK_END); - int size = ftell(f); - rewind(f); - char* buf = (char*)malloc(size+1); - if (!buf) - { - fclose(f); - return NULL; - } - fread(buf, 1, size, f); - buf[size] = 0; - fclose(f); - return buf; -} diff --git a/source/maestro_opcodes.h b/source/maestro_opcodes.h deleted file mode 100644 index de8cff0..0000000 --- a/source/maestro_opcodes.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once -enum -{ - MAESTRO_ADD = 0x00, - MAESTRO_DP3, - MAESTRO_DP4, - MAESTRO_DPH, - MAESTRO_DST, - MAESTRO_EX2, - MAESTRO_LG2, - MAESTRO_LITP, - MAESTRO_MUL, - MAESTRO_SGE, - MAESTRO_SLT, - MAESTRO_FLR, - MAESTRO_MAX, - MAESTRO_MIN, - MAESTRO_RCP, - MAESTRO_RSQ, - - MAESTRO_unk10, - MAESTRO_unk11, - MAESTRO_MOVA, - MAESTRO_MOV, - MAESTRO_unk14, - MAESTRO_unk15, - MAESTRO_unk16, - MAESTRO_unk17, - MAESTRO_DPHI, - MAESTRO_DSTI, - MAESTRO_SGEI, - MAESTRO_SLTI, - MAESTRO_unk1C, - MAESTRO_unk1D, - MAESTRO_unk1E, - MAESTRO_unk1F, - - MAESTRO_BREAK, - MAESTRO_NOP, - MAESTRO_END, - MAESTRO_BREAKC, - MAESTRO_CALL, - MAESTRO_CALLC, - MAESTRO_CALLU, - MAESTRO_IFU, - MAESTRO_IFC, - MAESTRO_FOR, - MAESTRO_EMIT, // Geometry shader related - MAESTRO_SETEMIT, // Geometry shader related - MAESTRO_JMPC, - MAESTRO_JMPU, - MAESTRO_CMP, // only the upper 5 bits are used for the opcode - - // Only the upper 3 bits are used for the following opcodes - MAESTRO_MADI = 0x30, - MAESTRO_MAD = 0x38, -}; diff --git a/source/picasso.h b/source/picasso.h deleted file mode 100644 index 7fdf008..0000000 --- a/source/picasso.h +++ /dev/null @@ -1,256 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#ifdef WIN32 -#include -#endif -#include "types.h" - -#include -#include -#include -#include -#include - -#include "FileClass.h" - -#include "maestro_opcodes.h" - -#if !defined(WIN32) && !defined(stricmp) -#define stricmp strcasecmp -#endif - -enum -{ - COMP_X = 0, - COMP_Y, - COMP_Z, - COMP_W, -}; - -#define SWIZZLE_COMP(n,v) ((v) << (6-(n)*2)) -#define OPSRC_MAKE(neg, sw) ((neg) | ((sw) << 1)) -#define OPDESC_MAKE(out, src1, src2, src3) ((out) | ((src1) << 4) | ((src2) << (4+9)) | ((src3) << (4+9*2))) -#define FMT_OPCODE(n) ((n)<<26) -#define OUTPUT_MAKE(i, reg, mask) ((i) | ((reg)<<16) | ((u64)(mask)<<32)) - -#define DEFAULT_SWIZZLE (SWIZZLE_COMP(0,COMP_X) | SWIZZLE_COMP(1,COMP_Y) | SWIZZLE_COMP(2,COMP_Z) | SWIZZLE_COMP(3,COMP_W)) -#define DEFAULT_OPSRC OPSRC_MAKE(0, DEFAULT_SWIZZLE) - -#define OPDESC_MASK_D123 OPDESC_MAKE(0xF, 0x1FF, 0x1FF, 0x1FF) -#define OPDESC_MASK_D12 OPDESC_MAKE(0xF, 0x1FF, 0x1FF, 0) -#define OPDESC_MASK_D1 OPDESC_MAKE(0xF, 0x1FF, 0, 0) -#define OPDESC_MASK_1 OPDESC_MAKE(0, 0x1FF, 0, 0) -#define OPDESC_MASK_12 OPDESC_MAKE(0, 0x1FF, 0x1FF, 0) - -enum -{ - COND_EQ = 0, - COND_NE, - COND_LT, - COND_LE, - COND_GT, - COND_GE, -}; - -//----------------------------------------------------------------------------- -// Global data -//----------------------------------------------------------------------------- - -// Output buffer -#define MAX_VSH_SIZE 512 -typedef std::vector outputBufType; -typedef outputBufType::iterator outputBufIter; -extern outputBufType g_outputBuf; - -enum -{ - SE_PROC, - SE_FOR, - SE_IF, - SE_ARRAY, -}; - -struct StackEntry -{ - int type; - size_t pos; - union - { - const char* strExtra; - size_t uExtra; - }; -}; - -// Stack used to keep track of stuff. -#define MAX_STACK 32 -extern StackEntry g_stack[MAX_STACK]; -extern int g_stackPos; - -// Operand descriptor stuff. -#define MAX_OPDESC 128 -extern int g_opdescTable[MAX_OPDESC]; -extern int g_opdeskMasks[MAX_OPDESC]; // used to keep track of used bits -extern int g_opdescCount; - -enum -{ - UTYPE_BOOL = 0, - UTYPE_IVEC, - UTYPE_FVEC, -}; - -struct Uniform -{ - std::string name; - int pos, size; - int type; - - inline bool operator <(const Uniform& rhs) const - { - return pos < rhs.pos; - } - - void init(const char* name, int pos, int size, int type) - { - this->name = name; - this->pos = pos; - this->size = size; - this->type = type; - } -}; - -// List of uniforms -#define MAX_UNIFORM 0x60 -extern Uniform g_uniformTable[MAX_UNIFORM]; -extern int g_uniformCount; - -struct DVLEData; // Forward declaration - -typedef std::pair procedure; // position, size -typedef std::pair relocation; // position, name - -typedef std::map procTableType; -typedef std::map labelTableType; -typedef std::map aliasTableType; -typedef std::vector relocTableType; -typedef std::list dvleTableType; - -typedef procTableType::iterator procTableIter; -typedef labelTableType::iterator labelTableIter; -typedef aliasTableType::iterator aliasTableIter; -typedef relocTableType::iterator relocTableIter; -typedef dvleTableType::iterator dvleTableIter; - -extern procTableType g_procTable; -extern dvleTableType g_dvleTable; -extern relocTableType g_procRelocTable; -extern int g_totalDvleCount; - -// The following are cleared before each file is processed -extern labelTableType g_labels; -extern relocTableType g_labelRelocTable; -extern aliasTableType g_aliases; - -extern bool g_autoNop; - -int AssembleString(char* str, const char* initialFilename); -int RelocateProduct(void); - -//----------------------------------------------------------------------------- -// Local data -//----------------------------------------------------------------------------- - -enum -{ - OUTTYPE_POS = 0, - OUTTYPE_NQUAT = 1, - OUTTYPE_CLR = 2, - OUTTYPE_TCOORD0 = 3, - OUTTYPE_TCOORD0W = 4, - OUTTYPE_TCOORD1 = 5, - OUTTYPE_TCOORD2 = 6, - OUTTYPE_VIEW = 8, - OUTTYPE_DUMMY = 9, -}; - -enum -{ - GSHTYPE_POINT = 0, - GSHTYPE_VARIABLE = 1, - GSHTYPE_FIXED = 2, -}; - -struct Constant -{ - int regId; - int type; - union - { - float fparam[4]; - u8 iparam[4]; - bool bparam; - }; -}; - -struct DVLEData -{ - // General config - std::string filename; - std::string entrypoint; - size_t entryStart, entryEnd; - bool nodvle, isGeoShader, isCompatGeoShader, isMerge; - u16 inputMask, outputMask; - u8 geoShaderType; - u8 geoShaderFixedStart; - u8 geoShaderVariableNum; - u8 geoShaderFixedNum; - - // Uniforms - Uniform uniformTable[MAX_UNIFORM]; - int uniformCount; - size_t symbolSize; - - // Constants - #define MAX_CONSTANT 0x60 - Constant constantTable[MAX_CONSTANT]; - int constantCount; - - // Outputs - #define MAX_OUTPUT 16 - u64 outputTable[MAX_OUTPUT]; - u32 outputUsedReg; - int outputCount; - - bool usesGshSpace() const { return isGeoShader && !isCompatGeoShader; } - int findFreeOutput() const - { - for (int i = 0; i < maxOutputReg(); i ++) - if (!(outputMask & BIT(i))) - return i; - return -1; - } - - int findFreeInput() const - { - for (int i = 0; i < 16; i ++) - if (!(inputMask & BIT(i))) - return i; - return -1; - } - - int maxOutputReg() const - { - return isGeoShader ? 0x07 : 0x10; - } - - DVLEData(const char* filename) : - filename(filename), entrypoint("main"), - nodvle(false), isGeoShader(false), isCompatGeoShader(false), isMerge(false), - inputMask(0), outputMask(0), geoShaderType(0), geoShaderFixedStart(0), geoShaderVariableNum(0), geoShaderFixedNum(0), - uniformCount(0), symbolSize(0), constantCount(0), outputUsedReg(0), outputCount(0) { } -}; diff --git a/source/picasso_assembler.cpp b/source/picasso_assembler.cpp index a651815..250cbec 100644 --- a/source/picasso_assembler.cpp +++ b/source/picasso_assembler.cpp @@ -1,4 +1,4 @@ -#include "picasso.h" +#include //#define DEBUG #define BUF g_outputBuf diff --git a/source/picasso_frontend.cpp b/source/picasso_frontend.cxx similarity index 100% rename from source/picasso_frontend.cpp rename to source/picasso_frontend.cxx diff --git a/source/picasso_library.cpp b/source/picasso_library.cpp new file mode 100644 index 0000000..d8a41c0 --- /dev/null +++ b/source/picasso_library.cpp @@ -0,0 +1,192 @@ +#include +#include +// f24 has: +// - 1 sign bit +// - 7 exponent bits +// - 16 mantissa bits +uint32_t f32tof24(float f) { + uint32_t i; + memcpy(&i, &f, sizeof(f)); + + uint32_t mantissa = (i << 9) >> 9; + int32_t exponent = (i << 1) >> 24; + uint32_t sign = (i << 0) >> 31; + + // Truncate mantissa + mantissa >>= 7; + + // Re-bias exponent + exponent = exponent - 127 + 63; + if (exponent < 0) { + // Underflow: flush to zero + return sign << 23; + } else if (exponent > 0x7F) { + // Overflow: saturate to infinity + return (sign << 23) | (0x7F << 16); + } + + return (sign << 23) | (exponent << 16) | mantissa; +} + +void BasicHandler(const char *top, const char *message) +{ + std::cout << top << std::endl << message << std::endl; +} + +static void (*EHND)(const char *top, const char *message) = BasicHandler; + +namespace Pica { + +void InstallErrorCallback(void (*ErrorHandler)(const char *top, + const char *message)) { + EHND = ErrorHandler; +} + +char *AssembleCode(const char *vertex, int &res_size) { + int rc = 0; + rc = AssembleString((char *)vertex, "llc_npi"); + if (rc) { + EHND("Error when Assembling Code", vertex); + } + + rc = RelocateProduct(); + if (rc) { + EHND("Error when Relocating Product", "0"); + } + FileClass f("Dont Care", "wb"); + + u32 progSize = g_outputBuf.size(); + u32 dvlpSize = 10 * 4 + progSize * 4 + g_opdescCount * 8; + + // Write DVLB header + f.WriteWord(0x424C5644); // DVLB + f.WriteWord(g_totalDvleCount); // Number of DVLEs + + // Calculate and write DVLE offsets + u32 curOff = 2 * 4 + g_totalDvleCount * 4 + dvlpSize; + for (dvleTableIter dvle = g_dvleTable.begin(); dvle != g_dvleTable.end(); + ++dvle) { + if (dvle->nodvle) + continue; + f.WriteWord(curOff); + curOff += 16 * 4; // Header + curOff += dvle->constantCount * 20; + curOff += dvle->outputCount * 8; + curOff += dvle->uniformCount * 8; + curOff += dvle->symbolSize; + curOff = (curOff + 3) & ~3; // Word alignment + } + + // Write DVLP header + f.WriteWord(0x504C5644); // DVLP + f.WriteWord(0); // version + f.WriteWord(10 * 4); // offset to shader binary blob + f.WriteWord(progSize); // size of shader binary blob + f.WriteWord(10 * 4 + progSize * 4); // offset to opdesc table + f.WriteWord(g_opdescCount); // number of opdescs + f.WriteWord(dvlpSize); // offset to symtable (TODO) + f.WriteWord(0); // ???? + f.WriteWord(0); // ???? + f.WriteWord(0); // ???? + + // Write program + for (outputBufIter it = g_outputBuf.begin(); it != g_outputBuf.end(); ++it) + f.WriteWord(*it); + + // Write opdescs + for (int i = 0; i < g_opdescCount; i++) + f.WriteDword(g_opdescTable[i]); + + // Write DVLEs + for (dvleTableIter dvle = g_dvleTable.begin(); dvle != g_dvleTable.end(); + ++dvle) { + if (dvle->nodvle) + continue; + curOff = 16 * 4; + + f.WriteWord(0x454C5644); // DVLE + f.WriteHword(0x1002); // maybe version? + f.WriteByte(dvle->isGeoShader ? 1 : 0); // Shader type + f.WriteByte(dvle->isMerge ? 1 : 0); + f.WriteWord(dvle->entryStart); // offset to main + f.WriteWord(dvle->entryEnd); // offset to end of main + f.WriteHword(dvle->inputMask); + f.WriteHword(dvle->outputMask); + f.WriteByte(dvle->geoShaderType); + f.WriteByte(dvle->geoShaderFixedStart); + f.WriteByte(dvle->geoShaderVariableNum); + f.WriteByte(dvle->geoShaderFixedNum); + f.WriteWord(curOff); // offset to constant table + f.WriteWord(dvle->constantCount); // size of constant table + curOff += dvle->constantCount * 5 * 4; + f.WriteWord(curOff); // offset to label table (TODO) + f.WriteWord(0); // size of label table (TODO) + f.WriteWord(curOff); // offset to output table + f.WriteWord(dvle->outputCount); // size of output table + curOff += dvle->outputCount * 8; + f.WriteWord(curOff); // offset to uniform table + f.WriteWord(dvle->uniformCount); // size of uniform table + curOff += dvle->uniformCount * 8; + f.WriteWord(curOff); // offset to symbol table + f.WriteWord(dvle->symbolSize); // size of symbol table + + // Sort uniforms by position + std::sort(dvle->uniformTable, dvle->uniformTable + dvle->uniformCount); + + // Write constants + for (int i = 0; i < dvle->constantCount; i++) { + Constant &ct = dvle->constantTable[i]; + f.WriteHword(ct.type); + if (ct.type == UTYPE_FVEC) { + f.WriteHword(ct.regId - 0x20); + for (int j = 0; j < 4; j++) + f.WriteWord(f32tof24(ct.fparam[j])); + } else if (ct.type == UTYPE_IVEC) { + f.WriteHword(ct.regId - 0x80); + for (int j = 0; j < 4; j++) + f.WriteByte(ct.iparam[j]); + } else if (ct.type == UTYPE_BOOL) { + f.WriteHword(ct.regId - 0x88); + f.WriteWord(ct.bparam ? 1 : 0); + } + if (ct.type != UTYPE_FVEC) + for (int j = 0; j < 3; j++) + f.WriteWord(0); // Padding + } + + // Write outputs + for (int i = 0; i < dvle->outputCount; i++) + f.WriteDword(dvle->outputTable[i]); + + // Write uniforms + size_t sp = 0; + for (int i = 0; i < dvle->uniformCount; i++) { + Uniform &u = dvle->uniformTable[i]; + size_t l = u.name.length() + 1; + f.WriteWord(sp); + sp += l; + int pos = u.pos; + if (pos >= 0x20) + pos -= 0x10; + f.WriteHword(pos); + f.WriteHword(pos + u.size - 1); + } + + // Write symbols + for (int i = 0; i < dvle->uniformCount; i++) { + std::string u(dvle->uniformTable[i].name); + std::replace(u.begin(), u.end(), '$', '.'); + size_t l = u.length() + 1; + f.WriteRaw(u.c_str(), l); + } + + // Word alignment + int pos = f.Tell(); + int pad = ((pos + 3) & ~3) - pos; + for (int i = 0; i < pad; i++) + f.WriteByte(0); + } + res_size = f.Tell(); + return (char *)f.get_ptr()->str().c_str(); +} +} // namespace Pica \ No newline at end of file diff --git a/source/types.h b/source/types.h deleted file mode 100644 index ffc1c0f..0000000 --- a/source/types.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once -#include - -typedef uint64_t dword_t; -typedef uint32_t word_t; -typedef uint16_t hword_t; -typedef uint8_t byte_t; -typedef int64_t dlong_t; -typedef int32_t long_t; -typedef int16_t short_t; -typedef int8_t char_t; -typedef uint64_t u64; -typedef uint32_t u32; -typedef uint16_t u16; -typedef uint8_t u8; - -#define BIT(n) (1U << (n)) - -#ifndef __BYTE_ORDER__ -#include -#define __BYTE_ORDER__ BYTE_ORDER -#define __ORDER_LITTLE_ENDIAN__ LITTLE_ENDIAN -#define __ORDER_BIG_ENDIAN__ BIG_ENDIAN -#endif - -#ifndef __llvm__ -#if !defined(__GNUC__) || (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) - -static inline uint16_t __builtin_bswap16(uint16_t x) -{ - return ((x << 8) & 0xff00) | ((x >> 8) & 0x00ff); -} - -#if defined(__GNUC__) && (__GNUC__ == 4 && __GNUC_MINOR__ < 7) -static inline uint32_t __builtin_bswap32(uint32_t x) -{ - return ((x << 24) & 0xff000000) | - ((x << 8) & 0x00ff0000) | - ((x >> 8) & 0x0000ff00) | - ((x >> 24) & 0x000000ff); -} - -static inline uint64_t __builtin_bswap64(uint64_t x) -{ - return (uint64_t)__builtin_bswap32(x>>32) | - ((uint64_t)__builtin_bswap32(x&0xFFFFFFFF) << 32); -} -#endif -#endif -#endif - -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define be_dword(a) __builtin_bswap64(a) -#define be_word(a) __builtin_bswap32(a) -#define be_hword(a) __builtin_bswap16(a) -#define le_dword(a) (a) -#define le_word(a) (a) -#define le_hword(a) (a) -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -#define be_dword(a) (a) -#define be_word(a) (a) -#define be_hword(a) (a) -#define le_dword(a) __builtin_bswap64(a) -#define le_word(a) __builtin_bswap32(a) -#define le_hword(a) __builtin_bswap16(a) -#else -#error "What's the endianness of the platform you're targeting?" -#endif