Initial commit
This commit is contained in:
commit
9d8ec29538
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
*.exe
|
||||||
|
*.o
|
||||||
|
*.elf
|
||||||
|
*~
|
||||||
|
*.shbin
|
||||||
|
*.vsh.h
|
||||||
|
*.bat
|
||||||
|
build/
|
19
COPYING
Normal file
19
COPYING
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2014-2015, fincs
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
78
Makefile
Normal file
78
Makefile
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# This Makefile was blatantly ripped off from the devkitPro project
|
||||||
|
|
||||||
|
.SUFFIXES:
|
||||||
|
|
||||||
|
TARGET := $(notdir $(CURDIR))
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := source
|
||||||
|
|
||||||
|
export CC := gcc
|
||||||
|
export CXX := g++
|
||||||
|
export STRIP := strip
|
||||||
|
|
||||||
|
CFLAGS := -g0 -Wall -O2
|
||||||
|
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++0x
|
||||||
|
|
||||||
|
LDFLAGS := -g0
|
||||||
|
|
||||||
|
UNAME := $(shell uname -s)
|
||||||
|
|
||||||
|
ifneq (,$(findstring MINGW,$(UNAME)))
|
||||||
|
EXEEXT := .exe
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(findstring Darwin,$(UNAME)))
|
||||||
|
SDK := /Developer/SDKs/MacOSX10.4u.sdk
|
||||||
|
OSXCFLAGS := -mmacosx-version-min=10.4 -isysroot $(SDK) -arch i386 -arch ppc
|
||||||
|
OSXCXXFLAGS := -fvisibility=hidden -mmacosx-version-min=10.4 -isysroot $(SDK) -arch i386 -arch ppc
|
||||||
|
OSXLDFLAGS := -mmacosx-version-min=10.4 -Wl,-syslibroot,$(SDK) -arch i386 -arch ppc
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||||
|
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGET)$(EXEEXT)
|
||||||
|
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||||
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
|
||||||
|
|
||||||
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
|
|
||||||
|
ifeq ($(strip $(CPPFILES)),)
|
||||||
|
export LD := $(CC)
|
||||||
|
else
|
||||||
|
export LD := $(CXX)
|
||||||
|
endif
|
||||||
|
|
||||||
|
export OFILES := $(CFILES:.c=.o) $(CPPFILES:.cpp=.o)
|
||||||
|
|
||||||
|
.PHONY: $(BUILD) clean
|
||||||
|
|
||||||
|
$(BUILD):
|
||||||
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm -fr $(BUILD) $(OUTPUT)
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
$(OUTPUT): $(OFILES)
|
||||||
|
|
||||||
|
-include $(DEPSDIR)/*.d
|
||||||
|
|
||||||
|
$(OUTPUT):
|
||||||
|
@echo Linking...
|
||||||
|
@$(LD) $(OSXLDFLAGS) $(LDFLAGS) $(OFILES) -o $@
|
||||||
|
@$(STRIP) $@
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(CC) -E -MMD -MF $(DEPSDIR)/$(*).d $(CFLAGS) $< > /dev/null
|
||||||
|
@$(CC) $(OSXCFLAGS) $(CFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
|
%.o: %.cpp
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(CXX) -E -MMD -MF $(DEPSDIR)/$(*).d $(CXXFLAGS) $< > /dev/null
|
||||||
|
@$(CXX) $(OSXCXXFLAGS) $(CXXFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
|
endif
|
16
README.md
Normal file
16
README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# picasso
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
`picasso` is a PICA200 shader assembler, written in C++. The PICA200 is the GPU used by the Nintendo 3DS.
|
||||||
|
|
||||||
|
Currently there's no documentation; refer to `example.vsh` in order to figure out the syntax.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
A working C++11 compiler for the host is required (Windows users: use TDM-GCC). Use `make` to build the program.
|
||||||
|
|
||||||
|
## Shout-outs
|
||||||
|
|
||||||
|
- **smea** for reverse-engineering the PICA200, writing documentation, working hard & making `aemstro_as.py` (the original homebrew PICA200 shader assembler)
|
||||||
|
- **neobrain** for making `nihstro-assemble`, whose syntax inspired that of `picasso` and whose usage of boost inspired me to make my own assembler without hefty dependencies.
|
53
example.vsh
Normal file
53
example.vsh
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
; Really simple & stupid PICA200 shader
|
||||||
|
; Also serves as an example of picasso syntax
|
||||||
|
|
||||||
|
; Uniforms
|
||||||
|
.uniform projMtx(4), mdlvMtx(4)
|
||||||
|
|
||||||
|
; Constants
|
||||||
|
.const myconst(0.0, 1.0, -1.0, 0.0)
|
||||||
|
.alias zeros myconst.xxxx
|
||||||
|
.alias ones myconst.yyyy
|
||||||
|
.alias negones myconst.zzzz
|
||||||
|
.alias dummytcoord myconst.xxxy ; (0,0,0,1)
|
||||||
|
|
||||||
|
; Outputs
|
||||||
|
.out outpos position
|
||||||
|
.out outtc0 texcoord0
|
||||||
|
.out outtc1 texcoord1
|
||||||
|
.out outtc2 texcoord2
|
||||||
|
.out outclr color
|
||||||
|
|
||||||
|
; Inputs
|
||||||
|
.alias inpos v0
|
||||||
|
.alias intex v1
|
||||||
|
.alias inarg v2
|
||||||
|
|
||||||
|
.proc main
|
||||||
|
; r0 = (inpos.x, inpos.y, inpos.z, 1.0)
|
||||||
|
mov r0.xyz, inpos
|
||||||
|
mov r0.w, ones
|
||||||
|
|
||||||
|
; r1 = mdlvMtx * r0
|
||||||
|
dp4 r1.x, mdlvMtx(0), r0
|
||||||
|
dp4 r1.y, mdlvMtx(1), r0
|
||||||
|
dp4 r1.z, mdlvMtx(2), r0
|
||||||
|
dp4 r1.w, mdlvMtx(3), r0
|
||||||
|
|
||||||
|
; outpos = projMtx * r1
|
||||||
|
dp4 outpos.x, projMtx(0), r1
|
||||||
|
dp4 outpos.y, projMtx(1), r1
|
||||||
|
dp4 outpos.z, projMtx(2), r1
|
||||||
|
dp4 outpos.w, projMtx(3), r1
|
||||||
|
|
||||||
|
; Set texcoords
|
||||||
|
mov outtc0, intex
|
||||||
|
mov outtc1, dummytcoord
|
||||||
|
mov outtc2, dummytcoord
|
||||||
|
|
||||||
|
; Set vertex color
|
||||||
|
mov outclr.xyz, inarg
|
||||||
|
mov outclr.w, ones
|
||||||
|
|
||||||
|
end
|
||||||
|
.end
|
132
source/FileClass.h
Normal file
132
source/FileClass.h
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdio.h>
|
||||||
|
#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;
|
||||||
|
}
|
31
source/maestro_opcodes.h
Normal file
31
source/maestro_opcodes.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
MAESTRO_ADD = 0x00,
|
||||||
|
MAESTRO_DP3,
|
||||||
|
MAESTRO_DP4,
|
||||||
|
|
||||||
|
MAESTRO_MUL = 0x08,
|
||||||
|
|
||||||
|
MAESTRO_MAX = 0x0C,
|
||||||
|
MAESTRO_MIN,
|
||||||
|
MAESTRO_RCP,
|
||||||
|
MAESTRO_RSQ,
|
||||||
|
|
||||||
|
MAESTRO_MOV = 0x13,
|
||||||
|
|
||||||
|
MAESTRO_NOP = 0x21,
|
||||||
|
MAESTRO_END,
|
||||||
|
|
||||||
|
MAESTRO_CALL = 0x24,
|
||||||
|
|
||||||
|
MAESTRO_CALLC = 0x26,
|
||||||
|
MAESTRO_IFB,
|
||||||
|
MAESTRO_IF, // ???
|
||||||
|
|
||||||
|
MAESTRO_EMIT = 0x2A, // Geometry shader related
|
||||||
|
MAESTRO_SETEMIT, // Geometry shader related
|
||||||
|
|
||||||
|
MAESTRO_CMP = 0x2E,
|
||||||
|
MAESTRO_CMP2, // ???
|
||||||
|
};
|
104
source/picasso.h
Normal file
104
source/picasso.h
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "FileClass.h"
|
||||||
|
|
||||||
|
#include "maestro_opcodes.h"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
COMP_X = 0,
|
||||||
|
COMP_Y,
|
||||||
|
COMP_Z,
|
||||||
|
COMP_W,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SWIZZLE_COMP(n,v) ((v) << (6-(n)*2))
|
||||||
|
#define OPDESC_MAKE(out, src1, src2) ((out) | ((src1) << 5) | ((src2) << (5+8+1)))
|
||||||
|
#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))
|
||||||
|
|
||||||
|
extern std::vector<u32> g_outputBuf;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
SE_PROC,
|
||||||
|
SE_IFB,
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
#define MAX_OPDESC 128
|
||||||
|
extern int g_opdescTable[MAX_OPDESC];
|
||||||
|
extern int g_opdescCount;
|
||||||
|
|
||||||
|
struct Uniform
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
int pos, size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_UNIFORM 0x60
|
||||||
|
extern Uniform g_uniformTable[MAX_UNIFORM];
|
||||||
|
extern int g_uniformCount;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
OUTTYPE_POS = 0,
|
||||||
|
OUTTYPE_CLR = 2,
|
||||||
|
OUTTYPE_TCOORD0,
|
||||||
|
OUTTYPE_TCOORD1 = 5,
|
||||||
|
OUTTYPE_TCOORD2,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_OUTPUT 8
|
||||||
|
extern u64 g_outputTable[MAX_OUTPUT];
|
||||||
|
extern int g_outputCount;
|
||||||
|
|
||||||
|
struct Constant
|
||||||
|
{
|
||||||
|
int regId;
|
||||||
|
float param[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_CONSTANT 0x60
|
||||||
|
extern Constant g_constantTable[MAX_CONSTANT];
|
||||||
|
extern int g_constantCount;
|
||||||
|
|
||||||
|
typedef std::pair<size_t, size_t> procedure; // position, size
|
||||||
|
typedef std::pair<size_t, const char*> relocation;
|
||||||
|
extern std::map<std::string, procedure> g_procTable;
|
||||||
|
extern std::map<std::string, size_t> g_labels;
|
||||||
|
extern std::map<std::string, int> g_aliases;
|
||||||
|
|
||||||
|
int AssembleString(char* str, const char* initialFilename);
|
764
source/picasso_assembler.cpp
Normal file
764
source/picasso_assembler.cpp
Normal file
@ -0,0 +1,764 @@
|
|||||||
|
#include "picasso.h"
|
||||||
|
|
||||||
|
#define BUF g_outputBuf
|
||||||
|
#define NO_MORE_STACK (g_stackPos==MAX_STACK)
|
||||||
|
|
||||||
|
static const char* curFile = nullptr;
|
||||||
|
static int curLine = -1;
|
||||||
|
|
||||||
|
std::vector<u32> g_outputBuf;
|
||||||
|
|
||||||
|
StackEntry g_stack[MAX_STACK];
|
||||||
|
int g_stackPos;
|
||||||
|
|
||||||
|
int g_opdescTable[MAX_OPDESC];
|
||||||
|
int g_opdescCount;
|
||||||
|
|
||||||
|
Uniform g_uniformTable[MAX_UNIFORM];
|
||||||
|
int g_uniformCount;
|
||||||
|
static int uniformPos = 0x20;
|
||||||
|
|
||||||
|
Constant g_constantTable[MAX_CONSTANT];
|
||||||
|
int g_constantCount;
|
||||||
|
|
||||||
|
u64 g_outputTable[MAX_OUTPUT];
|
||||||
|
int g_outputCount;
|
||||||
|
|
||||||
|
std::map<std::string, procedure> g_procTable;
|
||||||
|
std::map<std::string, size_t> g_labels;
|
||||||
|
std::map<std::string, int> g_aliases;
|
||||||
|
|
||||||
|
static char* mystrtok_pos;
|
||||||
|
static char* mystrtok(char* str, const char* delim)
|
||||||
|
{
|
||||||
|
if (!str) str = mystrtok_pos;
|
||||||
|
if (!*str) return nullptr;
|
||||||
|
|
||||||
|
size_t pos = strcspn(str, delim);
|
||||||
|
auto ret = str;
|
||||||
|
str += pos;
|
||||||
|
if (*str)
|
||||||
|
*str++ = 0;
|
||||||
|
mystrtok_pos = str;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* mystrtok_spc(char* str)
|
||||||
|
{
|
||||||
|
auto ret = mystrtok(str, " \t");
|
||||||
|
if (!ret) return nullptr;
|
||||||
|
if (*mystrtok_pos)
|
||||||
|
for (; *mystrtok_pos && isspace(*mystrtok_pos); mystrtok_pos++);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* remove_comment(char* buf)
|
||||||
|
{
|
||||||
|
char* pos = strchr(buf, ';');
|
||||||
|
if (pos) *pos = 0;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* trim_whitespace(char* buf)
|
||||||
|
{
|
||||||
|
if (!buf)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Remove trailing whitespace
|
||||||
|
int pos;
|
||||||
|
for(pos = strlen(buf)-1; pos >= 0 && isspace(buf[pos]); pos --) buf[pos] = '\0';
|
||||||
|
|
||||||
|
// Remove leading whitespace
|
||||||
|
char* newbuf = buf;
|
||||||
|
for(; isspace(*newbuf); newbuf ++);
|
||||||
|
|
||||||
|
return newbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool validateIdentifier(const char* id)
|
||||||
|
{
|
||||||
|
int len = strlen(id);
|
||||||
|
bool valid = true;
|
||||||
|
for (int i = 0; valid && i < len; i ++)
|
||||||
|
{
|
||||||
|
int c = id[i];
|
||||||
|
valid = isalpha(c) || c == '_' || c == '.' || (i > 0 && isdigit(c));
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int throwError(const char* msg, ...)
|
||||||
|
{
|
||||||
|
va_list v;
|
||||||
|
|
||||||
|
fprintf(stderr, "%s:%d: error: ", curFile, curLine);
|
||||||
|
|
||||||
|
va_start(v, msg);
|
||||||
|
vfprintf(stderr, msg, v);
|
||||||
|
va_end(v);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parseInt(char* pos, int& out, long long min, long long max)
|
||||||
|
{
|
||||||
|
char* endptr = nullptr;
|
||||||
|
long long res = strtoll(pos, &endptr, 0);
|
||||||
|
if (pos == endptr)
|
||||||
|
return throwError("Invalid value: %s\n", pos);
|
||||||
|
if (res < min || res > max)
|
||||||
|
return throwError("Value out of range (%d..%u): %d\n", (int)min, (unsigned int)max, (int)res);
|
||||||
|
out = res;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define safe_call(x) do \
|
||||||
|
{ \
|
||||||
|
int _ = (x); \
|
||||||
|
if (_ != 0) return _; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
static int ProcessCommand(const char* cmd);
|
||||||
|
|
||||||
|
int AssembleString(char* str, const char* initialFilename)
|
||||||
|
{
|
||||||
|
curFile = initialFilename;
|
||||||
|
curLine = 1;
|
||||||
|
|
||||||
|
int nextLineIncr = 0;
|
||||||
|
char* nextStr = nullptr;
|
||||||
|
for (; str; str = nextStr, curLine += nextLineIncr)
|
||||||
|
{
|
||||||
|
size_t len = strcspn(str, "\n");
|
||||||
|
int linedelim = str[len];
|
||||||
|
str[len] = 0;
|
||||||
|
nextStr = linedelim ? (str + len + 1) : nullptr;
|
||||||
|
nextLineIncr = linedelim == '\n' ? 1 : 0;
|
||||||
|
|
||||||
|
char* line = trim_whitespace(remove_comment(str));
|
||||||
|
|
||||||
|
char* colonPos = nullptr;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
colonPos = strchr(line, ':');
|
||||||
|
if (!colonPos)
|
||||||
|
break;
|
||||||
|
*colonPos = 0;
|
||||||
|
char* labelName = line;
|
||||||
|
line = trim_whitespace(colonPos + 1);
|
||||||
|
|
||||||
|
if (!validateIdentifier(labelName))
|
||||||
|
return throwError("invalid label name: %s\n", labelName);
|
||||||
|
|
||||||
|
auto ret = g_labels.insert( std::pair<std::string,size_t>(labelName, BUF.size()) );
|
||||||
|
if (!ret.second)
|
||||||
|
return throwError("duplicate label: %s\n", labelName);
|
||||||
|
|
||||||
|
//printf("Label: %s\n", labelName);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!*line)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (*line == '#')
|
||||||
|
{
|
||||||
|
line = trim_whitespace(line + 1);
|
||||||
|
nextLineIncr = 0;
|
||||||
|
size_t pos = strcspn(line, " \t");
|
||||||
|
line[pos] = 0;
|
||||||
|
curLine = atoi(line);
|
||||||
|
line = trim_whitespace(line + pos + 1);
|
||||||
|
if (*line == '"')
|
||||||
|
{
|
||||||
|
line ++;
|
||||||
|
line[strlen(line)-1] = 0;
|
||||||
|
}
|
||||||
|
curFile = line;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* tok = mystrtok_spc(line);
|
||||||
|
safe_call(ProcessCommand(tok));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_stackPos)
|
||||||
|
return throwError("unclosed block(s)\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < g_opdescCount; i ++)
|
||||||
|
g_opdescTable[i] &= ~BIT(31);
|
||||||
|
|
||||||
|
//safe_call(FixupRelocations());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
// Commands
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
static char* nextArg()
|
||||||
|
{
|
||||||
|
return trim_whitespace(mystrtok(nullptr, ","));
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* nextArgCParen()
|
||||||
|
{
|
||||||
|
return trim_whitespace(mystrtok(nullptr, "("));
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* nextArgSpc()
|
||||||
|
{
|
||||||
|
return trim_whitespace(mystrtok_spc(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int missingParam()
|
||||||
|
{
|
||||||
|
return throwError("missing parameter\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
int (* func) (const char*, int);
|
||||||
|
int opcode;
|
||||||
|
} cmdTableType;
|
||||||
|
|
||||||
|
#define NEXT_ARG(_varName) char* _varName; do \
|
||||||
|
{ \
|
||||||
|
_varName = nextArg(); \
|
||||||
|
if (!_varName) return missingParam(); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define NEXT_ARG_SPC(_varName) char* _varName; do \
|
||||||
|
{ \
|
||||||
|
_varName = nextArgSpc(); \
|
||||||
|
if (!_varName) return missingParam(); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define NEXT_ARG_CPAREN(_varName) char* _varName; do \
|
||||||
|
{ \
|
||||||
|
_varName = nextArgCParen(); \
|
||||||
|
if (!_varName) return missingParam(); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define NEXT_ARG_OPT(_varName, _opt) char* _varName; do \
|
||||||
|
{ \
|
||||||
|
_varName = nextArg(); \
|
||||||
|
if (!_varName) _varName = (char*)(_opt); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define DEF_COMMAND(name) \
|
||||||
|
static int cmd_##name(const char* cmdName, int opcode)
|
||||||
|
|
||||||
|
#define DEC_COMMAND(name, fun) \
|
||||||
|
{ #name, cmd_##fun, MAESTRO_##name }
|
||||||
|
|
||||||
|
#define DEF_DIRECTIVE(name) \
|
||||||
|
static int dir_##name(const char* cmdName, int _unused)
|
||||||
|
|
||||||
|
#define DEC_DIRECTIVE(name) \
|
||||||
|
{ #name, dir_##name, 0 }
|
||||||
|
|
||||||
|
static int ensureNoMoreArgs()
|
||||||
|
{
|
||||||
|
return nextArg() ? throwError("too many parameters") : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
static int ensureLabel(const char* lbl)
|
||||||
|
{
|
||||||
|
if (!validateIdentifier(lbl))
|
||||||
|
return throwError("invalid target label: %s\n", lbl);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int ensure_valid_dest(int reg, const char* name)
|
||||||
|
{
|
||||||
|
if (reg < 0x00 || reg >= 0x20)
|
||||||
|
return throwError("invalid destination register: %s\n", name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ensure_valid_src1(int reg, const char* name)
|
||||||
|
{
|
||||||
|
if (reg < 0x00 || reg >= 0x80)
|
||||||
|
return throwError("invalid source1 register: %s\n", name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ensure_valid_src2(int reg, const char* name)
|
||||||
|
{
|
||||||
|
if (reg < 0x00 || reg >= 0x20)
|
||||||
|
return throwError("invalid source2 register: %s\n", name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ENSURE_NO_MORE_ARGS() safe_call(ensureNoMoreArgs())
|
||||||
|
|
||||||
|
#define ARG_TO_INT(_varName, _argName, _min, _max) \
|
||||||
|
int _varName = 0; \
|
||||||
|
safe_call(parseInt(_argName, _varName, _min, _max))
|
||||||
|
|
||||||
|
#define ARG_TO_REG(_varName, _argName) \
|
||||||
|
int _varName = 0, _varName##Sw = 0; \
|
||||||
|
safe_call(parseReg(_argName, _varName, _varName##Sw));
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define ARG_LABEL(_argName) \
|
||||||
|
safe_call(ensureLabel(_argName))
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ARG_TO_DEST_REG(_reg, _name) \
|
||||||
|
ARG_TO_REG(_reg, _name); \
|
||||||
|
safe_call(ensure_valid_dest(_reg, _name))
|
||||||
|
|
||||||
|
#define ARG_TO_SRC1_REG(_reg, _name) \
|
||||||
|
ARG_TO_REG(_reg, _name); \
|
||||||
|
safe_call(ensure_valid_src1(_reg, _name))
|
||||||
|
|
||||||
|
#define ARG_TO_SRC2_REG(_reg, _name) \
|
||||||
|
ARG_TO_REG(_reg, _name); \
|
||||||
|
safe_call(ensure_valid_src2(_reg, _name))
|
||||||
|
|
||||||
|
static int parseSwizzling(const char* b)
|
||||||
|
{
|
||||||
|
int i, out = 0, q = COMP_X;
|
||||||
|
for (i = 0; b[i] && i < 4; i ++)
|
||||||
|
{
|
||||||
|
switch (tolower(b[i]))
|
||||||
|
{
|
||||||
|
case 'x': q = COMP_X; break;
|
||||||
|
case 'y': q = COMP_Y; break;
|
||||||
|
case 'z': q = COMP_Z; break;
|
||||||
|
case 'w': q = COMP_W; break;
|
||||||
|
default: return -1;
|
||||||
|
}
|
||||||
|
out |= SWIZZLE_COMP(i, q);
|
||||||
|
}
|
||||||
|
if (b[i])
|
||||||
|
return -1;
|
||||||
|
// Fill in missing bits
|
||||||
|
for (int j = i; j < 4; j ++)
|
||||||
|
out |= SWIZZLE_COMP(j, q);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int maskFromSwizzling(int sw)
|
||||||
|
{
|
||||||
|
int out = 0;
|
||||||
|
for (int i = 0; i < 4; i ++)
|
||||||
|
out |= BIT(3-((sw>>(i*2))&3));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int findOrAddOpdesc(int& out, int opdesc, bool ignoreOp2=false)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < g_opdescCount; i ++)
|
||||||
|
{
|
||||||
|
int cur_opdesc = g_opdescTable[i];
|
||||||
|
if (ignoreOp2)
|
||||||
|
cur_opdesc &= ~((0xFF << (5+8+1)) | BIT(31)); // clear bits we don't want to compare
|
||||||
|
else if (cur_opdesc & BIT(31))
|
||||||
|
{
|
||||||
|
// We can recycle this opdesc which didn't have an explicit Op2
|
||||||
|
cur_opdesc &= ~BIT(31);
|
||||||
|
int cmp = opdesc &~ (0xFF << (5+8+1)); // partial opdesc used for comparison
|
||||||
|
if (cmp == cur_opdesc)
|
||||||
|
{
|
||||||
|
g_opdescTable[i] = cur_opdesc | (opdesc & (0xFF << (5+8+1)));
|
||||||
|
out = i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opdesc == cur_opdesc)
|
||||||
|
{
|
||||||
|
out = i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (g_opdescCount == MAX_OPDESC)
|
||||||
|
return throwError("too many operand descriptors (limit is %d)\n", MAX_OPDESC);
|
||||||
|
if (ignoreOp2)
|
||||||
|
opdesc |= BIT(31); // will be removed
|
||||||
|
g_opdescTable[g_opdescCount] = opdesc;
|
||||||
|
out = g_opdescCount++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parseReg(char* pos, int& outReg, int& outSw)
|
||||||
|
{
|
||||||
|
outReg = 0;
|
||||||
|
outSw = DEFAULT_SWIZZLE;
|
||||||
|
auto dotPos = strchr(pos, '.');
|
||||||
|
if (dotPos)
|
||||||
|
{
|
||||||
|
*dotPos++ = 0;
|
||||||
|
outSw = parseSwizzling(dotPos);
|
||||||
|
if (outSw < 0)
|
||||||
|
return throwError("invalid swizzling mask: %s\n", dotPos);
|
||||||
|
}
|
||||||
|
int regOffset = 0;
|
||||||
|
auto parenPos = strchr(pos, '(');
|
||||||
|
if (parenPos)
|
||||||
|
{
|
||||||
|
auto closePos = strchr(parenPos, ')');
|
||||||
|
if (!closePos)
|
||||||
|
return throwError("missing close paren: %s\n", pos);
|
||||||
|
*closePos = 0;
|
||||||
|
*parenPos++ = 0;
|
||||||
|
parenPos = trim_whitespace(parenPos);
|
||||||
|
// TODO: support (idx1[+n]), (idx2[+n]), (lcnt[+n])
|
||||||
|
regOffset = atoi(parenPos);
|
||||||
|
if (regOffset < 0)
|
||||||
|
return throwError("invalid register offset: %s\n", parenPos);
|
||||||
|
}
|
||||||
|
auto it = g_aliases.find(pos);
|
||||||
|
if (it != g_aliases.end())
|
||||||
|
{
|
||||||
|
int x = it->second;
|
||||||
|
outReg = x & 0xFF;
|
||||||
|
outReg += regOffset;
|
||||||
|
x >>= 8;
|
||||||
|
// Combine swizzling
|
||||||
|
int temp = 0;
|
||||||
|
for (int j = 0; j < 4; j ++)
|
||||||
|
{
|
||||||
|
int comp = (outSw >> (6 - j*2)) & 3;
|
||||||
|
comp = (x >> (6 - comp*2)) & 3;
|
||||||
|
temp |= SWIZZLE_COMP(j, comp);
|
||||||
|
}
|
||||||
|
outSw = temp;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_call(parseInt(pos+1, outReg, 0, 255));
|
||||||
|
switch (*pos)
|
||||||
|
{
|
||||||
|
case 'o': // Output registers
|
||||||
|
case 'v': // Input attributes
|
||||||
|
if (outReg < 0x00 || outReg >= 0x08)
|
||||||
|
return throwError("invalid input/output register: %s(%d)\n", pos);
|
||||||
|
break;
|
||||||
|
case 'r': // Temporary registers
|
||||||
|
outReg += 0x10;
|
||||||
|
if (outReg < 0x10 || outReg >= 0x20)
|
||||||
|
return throwError("invalid temporary register: %s(%d)\n", pos);
|
||||||
|
break;
|
||||||
|
case 'c': // Vector uniform registers
|
||||||
|
outReg += 0x20;
|
||||||
|
if (outReg < 0x20 || outReg >= 0x80)
|
||||||
|
return throwError("invalid vector uniform register: %s(%d)\n", pos);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return throwError("invalid register: %s\n", pos);
|
||||||
|
}
|
||||||
|
outReg += regOffset;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF_COMMAND(format0)
|
||||||
|
{
|
||||||
|
ENSURE_NO_MORE_ARGS();
|
||||||
|
|
||||||
|
BUF.push_back(FMT_OPCODE(opcode));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF_COMMAND(format1)
|
||||||
|
{
|
||||||
|
NEXT_ARG(destName);
|
||||||
|
NEXT_ARG(src1Name);
|
||||||
|
NEXT_ARG(src2Name);
|
||||||
|
ENSURE_NO_MORE_ARGS();
|
||||||
|
|
||||||
|
ARG_TO_DEST_REG(rDest, destName);
|
||||||
|
ARG_TO_SRC1_REG(rSrc1, src1Name);
|
||||||
|
ARG_TO_SRC2_REG(rSrc2, src2Name);
|
||||||
|
|
||||||
|
int opdesc = 0;
|
||||||
|
safe_call(findOrAddOpdesc(opdesc, OPDESC_MAKE(maskFromSwizzling(rDestSw), rSrc1Sw, rSrc2Sw)));
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("%s:%02X d%02X, d%02X, d%02X (0x%X)\n", cmdName, opcode, rDest, rSrc1, rSrc2, opdesc);
|
||||||
|
#endif
|
||||||
|
BUF.push_back(FMT_OPCODE(opcode) | opdesc | (rSrc2<<7) | (rSrc1<<12) | (rDest<<21));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF_COMMAND(format2)
|
||||||
|
{
|
||||||
|
NEXT_ARG(destName);
|
||||||
|
NEXT_ARG(src1Name);
|
||||||
|
ENSURE_NO_MORE_ARGS();
|
||||||
|
|
||||||
|
ARG_TO_DEST_REG(rDest, destName);
|
||||||
|
ARG_TO_SRC1_REG(rSrc1, src1Name);
|
||||||
|
|
||||||
|
int opdesc = 0;
|
||||||
|
safe_call(findOrAddOpdesc(opdesc, OPDESC_MAKE(maskFromSwizzling(rDestSw), rSrc1Sw, 0), true));
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("%s:%02X d%02X, d%02X (0x%X)\n", cmdName, opcode, rDest, rSrc1, opdesc);
|
||||||
|
#endif
|
||||||
|
BUF.push_back(FMT_OPCODE(opcode) | opdesc | (rSrc1<<12) | (rDest<<21));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const cmdTableType cmdTable[] =
|
||||||
|
{
|
||||||
|
DEC_COMMAND(NOP, format0),
|
||||||
|
DEC_COMMAND(END, format0),
|
||||||
|
|
||||||
|
DEC_COMMAND(ADD, format1),
|
||||||
|
DEC_COMMAND(DP3, format1),
|
||||||
|
DEC_COMMAND(DP4, format1),
|
||||||
|
DEC_COMMAND(MUL, format1),
|
||||||
|
DEC_COMMAND(MAX, format1),
|
||||||
|
DEC_COMMAND(MIN, format1),
|
||||||
|
|
||||||
|
DEC_COMMAND(RCP, format2),
|
||||||
|
DEC_COMMAND(RSQ, format2),
|
||||||
|
DEC_COMMAND(MOV, format2),
|
||||||
|
|
||||||
|
{ nullptr, nullptr },
|
||||||
|
};
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
// Directives
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
DEF_DIRECTIVE(proc)
|
||||||
|
{
|
||||||
|
NEXT_ARG(procName);
|
||||||
|
ENSURE_NO_MORE_ARGS();
|
||||||
|
|
||||||
|
if (NO_MORE_STACK)
|
||||||
|
return throwError("too many nested blocks\n");
|
||||||
|
|
||||||
|
auto& elem = g_stack[g_stackPos++];
|
||||||
|
elem.type = SE_PROC;
|
||||||
|
elem.pos = BUF.size();
|
||||||
|
elem.strExtra = procName;
|
||||||
|
|
||||||
|
if (g_procTable.find(procName) != g_procTable.end())
|
||||||
|
return throwError("proc already exists: %s\n", procName);
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("Defining %s\n", procName);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF_DIRECTIVE(end)
|
||||||
|
{
|
||||||
|
ENSURE_NO_MORE_ARGS();
|
||||||
|
if (!g_stackPos)
|
||||||
|
return throwError(".end with unmatched block\n");
|
||||||
|
|
||||||
|
auto& elem = g_stack[--g_stackPos];
|
||||||
|
u32 curPos = BUF.size();
|
||||||
|
u32 size = curPos - elem.pos;
|
||||||
|
|
||||||
|
switch (elem.type)
|
||||||
|
{
|
||||||
|
case SE_PROC:
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("proc: %s(%u, size:%u)\n", elem.strExtra, elem.pos, size);
|
||||||
|
#endif
|
||||||
|
g_procTable.insert( std::pair<std::string, procedure>(elem.strExtra, procedure(elem.pos, size)) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool isregp(int x)
|
||||||
|
{
|
||||||
|
x = tolower(x);
|
||||||
|
return x=='o' || x=='v' || x=='r' || x=='c';
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF_DIRECTIVE(alias)
|
||||||
|
{
|
||||||
|
NEXT_ARG_SPC(aliasName);
|
||||||
|
NEXT_ARG_SPC(aliasReg);
|
||||||
|
ENSURE_NO_MORE_ARGS();
|
||||||
|
|
||||||
|
if (!validateIdentifier(aliasName))
|
||||||
|
return throwError("invalid alias name: %s\n", aliasName);
|
||||||
|
if (isregp(aliasName[0]) && isdigit(aliasName[1]))
|
||||||
|
return throwError("cannot redefine register\n");
|
||||||
|
ARG_TO_REG(rAlias, aliasReg);
|
||||||
|
|
||||||
|
if (g_aliases.find(aliasName) != g_aliases.end())
|
||||||
|
return throwError("identifier already used: %s\n", aliasName);
|
||||||
|
|
||||||
|
g_aliases.insert( std::pair<std::string,int>(aliasName, rAlias | (rAliasSw<<8)) );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF_DIRECTIVE(uniform)
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
char* argText = nextArg();
|
||||||
|
if (!argText) break;
|
||||||
|
|
||||||
|
int uSize = 1;
|
||||||
|
char* parenPos = strchr(argText, '(');
|
||||||
|
if (parenPos)
|
||||||
|
{
|
||||||
|
char* closePos = strchr(parenPos, ')');
|
||||||
|
if (!closePos)
|
||||||
|
return throwError("missing close paren: %s\n", argText);
|
||||||
|
*closePos = 0;
|
||||||
|
*parenPos++ = 0;
|
||||||
|
parenPos = trim_whitespace(parenPos);
|
||||||
|
uSize = atoi(parenPos);
|
||||||
|
if (uSize < 1)
|
||||||
|
return throwError("invalid uniform size: %s(%s)\n", argText, parenPos);
|
||||||
|
}
|
||||||
|
if (!validateIdentifier(argText))
|
||||||
|
return throwError("invalid uniform name: %s\n", argText);
|
||||||
|
if ((uniformPos+uSize) >= 0x80)
|
||||||
|
return throwError("not enough uniform registers: %s(%d)\n", argText, uSize);
|
||||||
|
if (g_uniformCount == MAX_UNIFORM)
|
||||||
|
return throwError("too many uniforms: %s(%d)\n", argText, uSize);
|
||||||
|
if (g_aliases.find(argText) != g_aliases.end())
|
||||||
|
return throwError("identifier already used: %s\n", argText);
|
||||||
|
|
||||||
|
auto& uniform = g_uniformTable[g_uniformCount++];
|
||||||
|
uniform.name = argText;
|
||||||
|
uniform.pos = uniformPos;
|
||||||
|
uniform.size = uSize;
|
||||||
|
uniformPos += uSize;
|
||||||
|
g_aliases.insert( std::pair<std::string,int>(argText, uniform.pos | (DEFAULT_SWIZZLE<<8)) );
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("uniform %s(%d) @ d%02X:d%02X\n", argText, uSize, uniform.pos, uniform.pos+uSize-1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF_DIRECTIVE(const)
|
||||||
|
{
|
||||||
|
NEXT_ARG_CPAREN(constName);
|
||||||
|
NEXT_ARG(arg0Text);
|
||||||
|
NEXT_ARG(arg1Text);
|
||||||
|
NEXT_ARG(arg2Text);
|
||||||
|
auto arg3Text = mystrtok_pos;
|
||||||
|
if (!mystrtok_pos) return missingParam();
|
||||||
|
auto parenPos = strchr(arg3Text, ')');
|
||||||
|
if (!parenPos) return throwError("invalid syntax\n");
|
||||||
|
*parenPos = 0;
|
||||||
|
arg3Text = trim_whitespace(arg3Text);
|
||||||
|
|
||||||
|
if (g_constantCount == MAX_CONSTANT || uniformPos>=0x80)
|
||||||
|
return throwError("not enough space for constant\n");
|
||||||
|
|
||||||
|
if (g_aliases.find(constName) != g_aliases.end())
|
||||||
|
return throwError("identifier already used: %s\n", constName);
|
||||||
|
|
||||||
|
auto& ct = g_constantTable[g_constantCount++];
|
||||||
|
ct.regId = uniformPos++;
|
||||||
|
ct.param[0] = atof(arg0Text);
|
||||||
|
ct.param[1] = atof(arg1Text);
|
||||||
|
ct.param[2] = atof(arg2Text);
|
||||||
|
ct.param[3] = atof(arg3Text);
|
||||||
|
|
||||||
|
g_aliases.insert( std::pair<std::string,int>(constName, ct.regId | (DEFAULT_SWIZZLE<<8)) );
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("constant %s(%f, %f, %f, %f) @ d%02X\n", constName, ct.param[0], ct.param[1], ct.param[2], ct.param[3], ct.regId);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int parseOutType(const char* text)
|
||||||
|
{
|
||||||
|
if (stricmp(text,"pos")==0 || stricmp(text,"position")==0)
|
||||||
|
return OUTTYPE_POS;
|
||||||
|
if (stricmp(text,"clr")==0 || stricmp(text,"color")==0)
|
||||||
|
return OUTTYPE_CLR;
|
||||||
|
if (stricmp(text,"tcoord0")==0 || stricmp(text,"texcoord0")==0)
|
||||||
|
return OUTTYPE_TCOORD0;
|
||||||
|
if (stricmp(text,"tcoord1")==0 || stricmp(text,"texcoord1")==0)
|
||||||
|
return OUTTYPE_TCOORD1;
|
||||||
|
if (stricmp(text,"tcoord2")==0 || stricmp(text,"texcoord2")==0)
|
||||||
|
return OUTTYPE_TCOORD2;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF_DIRECTIVE(out)
|
||||||
|
{
|
||||||
|
NEXT_ARG_SPC(outName);
|
||||||
|
NEXT_ARG_SPC(outType);
|
||||||
|
ENSURE_NO_MORE_ARGS();
|
||||||
|
|
||||||
|
if (!validateIdentifier(outName))
|
||||||
|
return throwError("invalid identifier: %s\n", outName);
|
||||||
|
|
||||||
|
int sw = DEFAULT_SWIZZLE;
|
||||||
|
auto dotPos = strchr(outType, '.');
|
||||||
|
if (dotPos)
|
||||||
|
{
|
||||||
|
*dotPos++ = 0;
|
||||||
|
sw = parseSwizzling(dotPos);
|
||||||
|
if (sw < 0)
|
||||||
|
return throwError("invalid output mask: %s\n", dotPos);
|
||||||
|
}
|
||||||
|
int mask = maskFromSwizzling(sw);
|
||||||
|
int type = parseOutType(outType);
|
||||||
|
if (type < 0)
|
||||||
|
return throwError("invalid output type: %s\n", outType);
|
||||||
|
|
||||||
|
if (g_outputCount==MAX_OUTPUT)
|
||||||
|
return throwError("too many outputs\n");
|
||||||
|
|
||||||
|
if (g_aliases.find(outName) != g_aliases.end())
|
||||||
|
return throwError("identifier already used: %s\n", outName);
|
||||||
|
|
||||||
|
int oid = g_outputCount;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("output %s <- o%d (%d:%X)\n", outName, oid, type, mask);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
g_outputTable[g_outputCount++] = OUTPUT_MAKE(type, oid, mask);
|
||||||
|
g_aliases.insert( std::pair<std::string,int>(outName, oid | (sw<<8)) );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const cmdTableType dirTable[] =
|
||||||
|
{
|
||||||
|
DEC_DIRECTIVE(proc),
|
||||||
|
DEC_DIRECTIVE(end),
|
||||||
|
DEC_DIRECTIVE(alias),
|
||||||
|
DEC_DIRECTIVE(uniform),
|
||||||
|
DEC_DIRECTIVE(const),
|
||||||
|
DEC_DIRECTIVE(out),
|
||||||
|
{ nullptr, nullptr },
|
||||||
|
};
|
||||||
|
|
||||||
|
int ProcessCommand(const char* cmd)
|
||||||
|
{
|
||||||
|
const cmdTableType* table = cmdTable;
|
||||||
|
if (*cmd == '.')
|
||||||
|
{
|
||||||
|
cmd ++;
|
||||||
|
table = dirTable;
|
||||||
|
} else if (!g_stackPos)
|
||||||
|
return throwError("instruction outside block\n");
|
||||||
|
|
||||||
|
for (int i = 0; table[i].name; i ++)
|
||||||
|
if (stricmp(table[i].name, cmd) == 0)
|
||||||
|
return table[i].func(cmd, table[i].opcode);
|
||||||
|
|
||||||
|
return throwError("invalid instruction: %s\n", cmd);
|
||||||
|
}
|
204
source/picasso_frontend.cpp
Normal file
204
source/picasso_frontend.cpp
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#include "picasso.h"
|
||||||
|
|
||||||
|
// !! Taken from ctrulib !!
|
||||||
|
u32 f32tof24(float vf)
|
||||||
|
{
|
||||||
|
if (!vf) return 0;
|
||||||
|
|
||||||
|
union { float f; u32 v; } q;
|
||||||
|
q.f=vf;
|
||||||
|
|
||||||
|
u8 s = q.v>>31;
|
||||||
|
u32 exp = ((q.v>>23) & 0xFF) - 0x40;
|
||||||
|
u32 man = (q.v>>7) & 0xFFFF;
|
||||||
|
|
||||||
|
if (exp >= 0)
|
||||||
|
return man | (exp<<16) | (s<<23);
|
||||||
|
else
|
||||||
|
return s<<23;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
static inline void FixMinGWPath(char* buf)
|
||||||
|
{
|
||||||
|
if (buf && *buf == '/')
|
||||||
|
{
|
||||||
|
buf[0] = buf[1];
|
||||||
|
buf[1] = ':';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int usage(const char* prog)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage:\n\n"
|
||||||
|
"%s shbinFile vshFile [hFile]\n", prog);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
if (argc < 3 || argc > 4)
|
||||||
|
return usage(argv[0]);
|
||||||
|
|
||||||
|
char* shbinFile = argv[1];
|
||||||
|
char* vshFile = argv[2];
|
||||||
|
char* hFile = argc > 3 ? argv[3] : NULL;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
FixMinGWPath(shbinFile);
|
||||||
|
FixMinGWPath(vshFile);
|
||||||
|
FixMinGWPath(hFile);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char* sourceCode = StringFromFile(vshFile);
|
||||||
|
if (!sourceCode)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Cannot open input file!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rc = AssembleString(sourceCode, vshFile);
|
||||||
|
free(sourceCode);
|
||||||
|
if (rc != 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
auto mainIt = g_procTable.find("main");
|
||||||
|
if (mainIt == g_procTable.end())
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error: main proc not defined\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (g_opdescCount > 9)
|
||||||
|
// printf("WARNING: currently using more than 9 opdescs -- libctru has a bug\n");
|
||||||
|
|
||||||
|
FileClass f(shbinFile, "wb");
|
||||||
|
|
||||||
|
if (f.openerror())
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Can't open output file!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
f.WriteWord(0x424C5644); // DVLB
|
||||||
|
f.WriteWord(1); // 1 DVLE
|
||||||
|
f.WriteWord(3*4 + 0x28); // offset to DVLE
|
||||||
|
|
||||||
|
u32 dvlpStart = f.Tell();
|
||||||
|
u32 shaderSize = g_outputBuf.size();
|
||||||
|
u32 paramStart = 0x28 + 0x40;
|
||||||
|
|
||||||
|
f.WriteWord(0x504C5644); // DVLP
|
||||||
|
f.WriteWord(0); // version
|
||||||
|
f.WriteWord(paramStart); // offset to shader binary blob
|
||||||
|
f.WriteWord(shaderSize); // size of shader binary blob
|
||||||
|
paramStart += shaderSize*4;
|
||||||
|
f.WriteWord(paramStart); // offset to opdesc table
|
||||||
|
f.WriteWord(g_opdescCount); // number of opdescs
|
||||||
|
paramStart += g_opdescCount*8;
|
||||||
|
f.WriteWord(paramStart); // offset to symtable (TODO)
|
||||||
|
f.WriteWord(0); // ????
|
||||||
|
f.WriteWord(0); // ????
|
||||||
|
f.WriteWord(0); // ????
|
||||||
|
|
||||||
|
u32 dvleStart = f.Tell();
|
||||||
|
paramStart -= dvleStart - dvlpStart;
|
||||||
|
|
||||||
|
f.WriteWord(0x454C5644); // DVLE
|
||||||
|
f.WriteHword(0); // padding?
|
||||||
|
f.WriteHword(0); // Vertex shader
|
||||||
|
f.WriteWord(mainIt->second.first); // offset to main
|
||||||
|
f.WriteWord(mainIt->second.first+mainIt->second.second); // offset to end of main
|
||||||
|
f.WriteWord(0); // ???
|
||||||
|
f.WriteWord(0); // ???
|
||||||
|
f.WriteWord(paramStart); // offset to constant table
|
||||||
|
f.WriteWord(g_constantCount); // size of constant table
|
||||||
|
paramStart += g_constantCount*0x14;
|
||||||
|
f.WriteWord(paramStart); // offset to label table (TODO)
|
||||||
|
f.WriteWord(0); // size of label table (TODO)
|
||||||
|
f.WriteWord(paramStart); // offset to output table
|
||||||
|
f.WriteWord(g_outputCount); // size of output table
|
||||||
|
paramStart += g_outputCount*8;
|
||||||
|
f.WriteWord(paramStart); // offset to uniform table
|
||||||
|
f.WriteWord(g_uniformCount); // size of uniform table
|
||||||
|
paramStart += g_uniformCount*8;
|
||||||
|
f.WriteWord(paramStart); // offset to symbol table
|
||||||
|
u32 temp = f.Tell();
|
||||||
|
f.WriteWord(0); // size of symbol table
|
||||||
|
|
||||||
|
// Write program
|
||||||
|
for (u32 p : g_outputBuf)
|
||||||
|
f.WriteWord(p);
|
||||||
|
|
||||||
|
// Write opdescs
|
||||||
|
for (int i = 0; i < g_opdescCount; i ++)
|
||||||
|
{
|
||||||
|
f.WriteWord(g_opdescTable[i]);
|
||||||
|
f.WriteWord(0x0000000F); // unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write constants
|
||||||
|
for (int i = 0; i < g_constantCount; i ++)
|
||||||
|
{
|
||||||
|
auto& ct = g_constantTable[i];
|
||||||
|
f.WriteHword(0);
|
||||||
|
f.WriteByte(ct.regId-0x20);
|
||||||
|
f.WriteByte(0);
|
||||||
|
for (int j = 0; j < 4; j ++)
|
||||||
|
f.WriteWord(f32tof24(ct.param[j]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write outputs
|
||||||
|
for (int i = 0; i < g_outputCount; i ++)
|
||||||
|
f.WriteDword(g_outputTable[i]);
|
||||||
|
|
||||||
|
// Write uniforms
|
||||||
|
size_t sp = 0;
|
||||||
|
for (int i = 0; i < g_uniformCount; i ++)
|
||||||
|
{
|
||||||
|
auto& u = g_uniformTable[i];
|
||||||
|
size_t l = strlen(u.name)+1;
|
||||||
|
f.WriteWord(sp); sp += l;
|
||||||
|
f.WriteHword(u.pos-0x20);
|
||||||
|
f.WriteHword(u.pos+u.size-1-0x20);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write size of symbol table
|
||||||
|
u32 temp2 = f.Tell();
|
||||||
|
f.Seek(temp, SEEK_SET);
|
||||||
|
f.WriteWord(sp);
|
||||||
|
f.Seek(temp2, SEEK_SET);
|
||||||
|
|
||||||
|
// Write symbols
|
||||||
|
for (int i = 0; i < g_uniformCount; i ++)
|
||||||
|
{
|
||||||
|
auto u = g_uniformTable[i].name;
|
||||||
|
size_t l = strlen(u)+1;
|
||||||
|
f.WriteRaw(u, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hFile)
|
||||||
|
{
|
||||||
|
auto f2 = fopen(hFile, "w");
|
||||||
|
if (!f2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Can't open header file!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(f2, "// Generated by picasso\n");
|
||||||
|
fprintf(f2, "#pragma once\n");
|
||||||
|
for (int i = 0; i < g_uniformCount; i ++)
|
||||||
|
{
|
||||||
|
auto& u = g_uniformTable[i];
|
||||||
|
fprintf(f2, "#define SHADER_UREG_%s 0x%02X\n", u.name, u.pos-0x20);
|
||||||
|
fprintf(f2, "#define SHADER_ULEN_%s %d\n", u.name, u.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
35
source/types.h
Normal file
35
source/types.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
#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
|
Loading…
Reference in New Issue
Block a user