From b393e20996b8bf2d469c7ebea4a126997a252379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Parrino?= Date: Wed, 1 Jul 2020 22:44:53 -0300 Subject: [PATCH 01/56] libHttp changes (#97) * Fixed http typedefs Used changes from Estwald v2 sdk (http connections were failing due to wrong typedefs) * Fix definitions to match exports.h ppu/sprx/libhttp/exports.h * Change size_t to u32 --- ppu/include/http/http.h | 68 ++++++++++++++++++++-------------------- ppu/include/http/https.h | 8 ++--- ppu/include/http/util.h | 40 +++++++++++------------ ppu/include/ssl/ssl.h | 15 +++++---- 4 files changed, 65 insertions(+), 66 deletions(-) mode change 100755 => 100644 ppu/include/http/http.h diff --git a/ppu/include/http/http.h b/ppu/include/http/http.h old mode 100755 new mode 100644 index b768b008..0d29554e --- a/ppu/include/http/http.h +++ b/ppu/include/http/http.h @@ -100,13 +100,10 @@ static const char HTTP_METHOD_TRACE[] = "TRACE"; * structures */ -struct httpClient; -struct httpTransaction; +typedef s32 httpClientId; +typedef s32 httpTransId; -typedef struct httpClient* httpClientId; -typedef struct httpTransaction* httpTransId; - -typedef const void* httpSslId; +typedef s32 httpSslId; /* @@ -125,41 +122,44 @@ typedef int (*httpCookieRecvCallback)(httpTransId tid,const httpUri *uri,const c */ /* initialization */ -s32 httpInit(void *pool,size_t poolSize); +s32 httpInit(void *pool,u32 poolSize); s32 httpEnd(void); /* transaction request */ -s32 httpSendRequest(httpTransId tid,const char *buf,size_t size,size_t *sent); -s32 httpRecvResponse(httpTransId tid,char *buf,size_t size,size_t *recvd); +s32 httpSendRequest(httpTransId tid,const char *buf,u32 size,u32 *sent); +s32 httpRecvResponse(httpTransId tid,char *buf,u32 size,u32 *recvd); /* proxy */ s32 httpSetProxy(const httpUri *proxy); -s32 httpGetProxy(httpUri *proxy,void *pool,size_t poolSize,size_t *required); +s32 httpGetProxy(httpUri *proxy,void *pool,u32 poolSize,u32 *required); /* request content length */ s32 httpRequestSetContentLength(httpTransId tid,u64 totalSize); s32 httpRequestGetContentLength(httpTransId tid,u64 *totalSize); /* request headers */ -s32 httpRequestGetAllHeaders(httpTransId tid,httpHeader **headers,size_t *items,void *pool,size_t poolSize,size_t *required); +s32 httpRequestGetAllHeaders(httpTransId tid,httpHeader **headers,u32 *items,void *pool,u32 poolSize,u32 *required); s32 httpRequestSetHeader(httpTransId tid,const httpHeader *header); -s32 httpRequestGetHeader(httpTransId tid,httpHeader *header,const char *name,void *pool,size_t poolSize,size_t *required); +s32 httpRequestGetHeader(httpTransId tid,httpHeader *header,const char *name,void *pool,u32 poolSize,u32 *required); s32 httpRequestAddHeader(httpTransId tid,const httpHeader *header); s32 httpRequestDeleteHeader(httpTransId tid,const char *name); /* response status code */ s32 httpResponseGetStatusCode(httpTransId tid,int32_t *code); +/* response content length */ +s32 httpResponseGetContentLength(httpTransId tid,u64 *totalSize); + /* response status line */ -s32 httpResponseGetStatusLine(httpTransId tid,httpStatusLine *status,void *pool,size_t poolSize,size_t *required); +s32 httpResponseGetStatusLine(httpTransId tid,httpStatusLine *status,void *pool,u32 poolSize,u32 *required); /* cookies */ -s32 httpInitCookie(void *pool,size_t poolSize); +s32 httpInitCookie(void *pool,u32 poolSize); s32 httpEndCookie(void); s32 httpAddCookieWithClientId(const httpUri *uri,const char *cookie,httpClientId cid); s32 httpSessionCookieFlush(httpClientId cid); -s32 httpCookieExportWithClientId(void *buf,size_t size,size_t *exportSize,httpClientId cid); -s32 httpCookieImportWithClientId(const void *buf,size_t size,httpClientId cid); +s32 httpCookieExportWithClientId(void *buf,u32 size,u32 *exportSize,httpClientId cid); +s32 httpCookieImportWithClientId(const void *buf,u32 size,httpClientId cid); /* cookie callbacks */ s32 httpClientSetCookieSendCallback(httpClientId cid,httpCookieSendCallback cb,void *arg); @@ -176,7 +176,7 @@ s32 httpDestroyClient(httpClientId cid); /* proxy */ s32 httpClientSetProxy(httpClientId cid,const httpUri *proxy); -s32 httpClientGetProxy(httpClientId cid,httpUri *proxy,void *pool,size_t poolSize,size_t *required); +s32 httpClientGetProxy(httpClientId cid,httpUri *proxy,void *pool,u32 poolSize,u32 *required); /* version */ s32 httpClientSetVersion(httpClientId cid,u32 major,u32 minor); @@ -208,11 +208,11 @@ s32 httpClientGetCookieStatus(httpClientId cid,u32 *enable); /* user agent */ s32 httpClientSetUserAgent(httpClientId cid,const char *userAgent); -s32 httpClientGetUserAgent(httpClientId cid,char *userAgent,size_t size,size_t *required); +s32 httpClientGetUserAgent(httpClientId cid,char *userAgent,u32 size,u32 *required); /* buffer max */ -s32 httpClientSetResponseBufferMax(httpClientId cid,size_t max); -s32 httpClientGetResponseBufferMax(httpClientId cid,size_t *max); +s32 httpClientSetResponseBufferMax(httpClientId cid,u32 max); +s32 httpClientGetResponseBufferMax(httpClientId cid,u32 *max); /* close connections */ s32 httpClientCloseAllConnections(httpClientId cid); @@ -234,24 +234,24 @@ s32 httpClientSetConnTimeout(httpClientId cid,s64 usec); s32 httpClientGetConnTimeout(httpClientId cid,s64 *usec); /* pool size */ -s32 httpClientSetTotalPoolSize(httpClientId cid,size_t poolSize); -s32 httpClientGetTotalPoolSize(httpClientId cid,size_t *poolSize); -s32 httpClientSetPerHostPoolSize(httpClientId cid,size_t poolSize); -s32 httpClientGetPerHostPoolSize(httpClientId cid,size_t *poolSize); +s32 httpClientSetTotalPoolSize(httpClientId cid,u32 poolSize); +s32 httpClientGetTotalPoolSize(httpClientId cid,u32 *poolSize); +s32 httpClientSetPerHostPoolSize(httpClientId cid,u32 poolSize); +s32 httpClientGetPerHostPoolSize(httpClientId cid,u32 *poolSize); /* keep alive */ -s32 httpClientSetPerHostKeepAliveMax(httpClientId cid,size_t maxSize); -s32 httpClientGetPerHostKeepAliveMax(httpClientId cid,size_t *maxSize); +s32 httpClientSetPerHostKeepAliveMax(httpClientId cid,u32 maxSize); +s32 httpClientGetPerHostKeepAliveMax(httpClientId cid,u32 *maxSize); /* pipeline */ -s32 httpClientSetPerPipelineMax(httpClientId cid,size_t pipeMax); -s32 httpClientGetPerPipelineMax(httpClientId cid,size_t *pipeMax); +s32 httpClientSetPerPipelineMax(httpClientId cid,u32 pipeMax); +s32 httpClientGetPerPipelineMax(httpClientId cid,u32 *pipeMax); /* client headers */ -s32 httpClientGetAllHeaders(httpClientId cid,httpHeader **headers,size_t *items,void *pool,size_t poolSize,size_t *required); +s32 httpClientGetAllHeaders(httpClientId cid,httpHeader **headers,u32 *items,void *pool,u32 poolSize,u32 *required); s32 httpClientSetHeader(httpClientId cid,const httpHeader *header); -s32 httpClientGetHeader(httpClientId cid,httpHeader *header,const char *name,void *pool,size_t poolSize,size_t *required); +s32 httpClientGetHeader(httpClientId cid,httpHeader *header,const char *name,void *pool,u32 poolSize,u32 *required); s32 httpClientAddHeader(httpClientId cid,const httpHeader *header); s32 httpClientDeleteHeader(httpClientId cid,const char *name); @@ -267,17 +267,17 @@ s32 httpClientSetRedirectCallback(httpClientId cid,httpRedirectCallback cb,void /* general transactions */ s32 httpCreateTransaction(httpTransId *tid,httpClientId cid,const char *method,const httpUri *uri); s32 httpDestroyTransaction(httpTransId tid); -s32 httpTransactionGetUri(httpTransId tid,httpUri *uri,void *pool,size_t poolSize,size_t *required); +s32 httpTransactionGetUri(httpTransId tid,httpUri *uri,void *pool,u32 poolSize,u32 *required); s32 httpTransactionCloseConnection(httpTransId tid); s32 httpTransactionReleaseConnection(httpTransId tid,int *sid); s32 httpTransactionAbortConnection(httpTransId tid); /* SSL transactions */ -s32 httpTransactionGetSslCipherName(httpTransId tid,char *name,size_t size,size_t *required); +s32 httpTransactionGetSslCipherName(httpTransId tid,char *name,u32 size,u32 *required); s32 httpTransactionGetSslCipherId(httpTransId tid,int32_t *id); -s32 httpTransactionGetSslCipherVersion(httpTransId tid,char *version,size_t size,size_t *required); +s32 httpTransactionGetSslCipherVersion(httpTransId tid,char *version,u32 size,u32 *required); s32 httpTransactionGetSslCipherBits(httpTransId tid,int32_t *effectiveBits,int32_t *algorithmBits); -s32 httpTransactionGetSslCipherString(httpTransId tid,char *buffer,size_t size); +s32 httpTransactionGetSslCipherString(httpTransId tid,char *buffer,u32 size); s32 httpTransactionGetSslVersion(httpTransId tid,int32_t *version); s32 httpTransactionGetSslId(httpTransId tid,httpSslId *id); diff --git a/ppu/include/http/https.h b/ppu/include/http/https.h index 5158d8aa..17517ab0 100644 --- a/ppu/include/http/https.h +++ b/ppu/include/http/https.h @@ -18,7 +18,7 @@ extern "C" { typedef struct _https_data { char *ptr ATTRIBUTE_PRXPTR; - size_t size; + u32 size; } httpsData; @@ -34,14 +34,14 @@ typedef int (*httpsSslCallback)(s32 verErr,sslCert const sslCerts[],int certNum, */ /* initialization */ -s32 httpsInit(size_t caCertNum,const httpsData *caList); +s32 httpsInit(u32 caCertNum,const httpsData *caList); s32 httpsEnd(void); /* SSL certificate */ -s32 httpsClientSetSslClientCertificate(httpClientId cid,const httpsData *cert,const httpsData *privKey); +s32 httpClientSetSslClientCertificate(httpClientId cid,const httpsData *cert,const httpsData *privKey); /* SSL callback */ -s32 httpsClientSetSslCallback(httpClientId cid,httpsSslCallback cb,void *arg); +s32 httpClientSetSslCallback(httpClientId cid,httpsSslCallback cb,void *arg); #ifdef __cplusplus diff --git a/ppu/include/http/util.h b/ppu/include/http/util.h index 922417fc..1f49ee94 100644 --- a/ppu/include/http/util.h +++ b/ppu/include/http/util.h @@ -73,32 +73,32 @@ typedef struct _http_header */ /* build */ -s32 httpUtilBuildRequestLine(const httpRequestLine *req,char *buf,size_t len,size_t *required); -s32 httpUtilBuildHeader(const httpHeader *header,char *buf,size_t len,size_t *required); -s32 httpUtilBuildUri(const httpUri *uri,char *buf,size_t len,size_t *required,int32_t flags); -s32 httpUtilSweepPath(char *dst,const char *src,size_t srcSize); +s32 httpUtilBuildRequestLine(const httpRequestLine *req,char *buf,u32 len,u32 *required); +s32 httpUtilBuildHeader(const httpHeader *header,char *buf,u32 len,u32 *required); +s32 httpUtilBuildUri(const httpUri *uri,char *buf,u32 len,u32 *required,int32_t flags); +s32 httpUtilSweepPath(char *dst,const char *src,u32 srcSize); /* encode */ -s32 httpUtilEscapeUri(char *out,size_t outSize,const unsigned char *in,size_t inSize,size_t *required); -s32 httpUtilUnescapeUri(unsigned char *out,size_t size,const char *in,size_t *required); -s32 httpUtilFormUrlEncode(char *out,size_t outSize,const unsigned char *in,size_t inSize,size_t *required); -s32 httpUtilFormUrlDecode(unsigned char *out,size_t size,const char *in,size_t *required); -s32 httpUtilBase64Encoder(char *out,const void *in,size_t len); -s32 httpUtilBase64Decoder(char *out,const void *in,size_t len); +s32 httpUtilEscapeUri(char *out,u32 outSize,const unsigned char *in,u32 inSize,u32 *required); +s32 httpUtilUnescapeUri(unsigned char *out,u32 size,const char *in,u32 *required); +s32 httpUtilFormUrlEncode(char *out,u32 outSize,const unsigned char *in,u32 inSize,u32 *required); +s32 httpUtilFormUrlDecode(unsigned char *out,u32 size,const char *in,u32 *required); +s32 httpUtilBase64Encoder(char *out,const void *in,u32 len); +s32 httpUtilBase64Decoder(char *out,const void *in,u32 len); /* copy */ -s32 httpUtilCopyUri(httpUri *dest,const httpUri *src,void *pool,size_t poolSize,size_t *required); -s32 httpUtilCopyHeader(httpHeader *dest,const httpHeader *src,void *pool,size_t poolSize,size_t *required); -s32 httpUtilCopyStatusLine(httpStatusLine *dest,const httpStatusLine *src,void *pool,size_t poolSize,size_t *required); -s32 httpUtilMergeUriPath(httpUri *uri,const httpUri *src,const char *path,void *pool,size_t poolSize,size_t *required); -s32 httpUtilAppendHeaderValue(httpHeader *dest,const httpHeader *src,const char *value,void *pool,size_t poolSize,size_t *required); +s32 httpUtilCopyUri(httpUri *dest,const httpUri *src,void *pool,u32 poolSize,u32 *required); +s32 httpUtilCopyHeader(httpHeader *dest,const httpHeader *src,void *pool,u32 poolSize,u32 *required); +s32 httpUtilCopyStatusLine(httpStatusLine *dest,const httpStatusLine *src,void *pool,u32 poolSize,u32 *required); +s32 httpUtilMergeUriPath(httpUri *uri,const httpUri *src,const char *path,void *pool,u32 poolSize,u32 *required); +s32 httpUtilAppendHeaderValue(httpHeader *dest,const httpHeader *src,const char *value,void *pool,u32 poolSize,u32 *required); /* parse */ -s32 httpUtilParseUri(httpUri *uri,const char *str,void *pool,size_t size,size_t *required); -s32 httpUtilParseUriPath(httpUriPath *path,const char *str,void *pool,size_t size,size_t *required); -s32 httpUtilParseProxy(httpUri *uri,const char *str,void *pool,size_t size,size_t *required); -s32 httpUtilParseStatusLine(httpStatusLine *resp,const char *str,size_t len,void *pool,size_t size,size_t *required,size_t *parsedLength); -s32 httpUtilParseHeader(httpHeader *header,const char *str,size_t len,void *pool,size_t size,size_t *required,size_t *parsedLength); +s32 httpUtilParseUri(httpUri *uri,const char *str,void *pool,u32 size,u32 *required); +s32 httpUtilParseUriPath(httpUriPath *path,const char *str,void *pool,u32 size,u32 *required); +s32 httpUtilParseProxy(httpUri *uri,const char *str,void *pool,u32 size,u32 *required); +s32 httpUtilParseStatusLine(httpStatusLine *resp,const char *str,u32 len,void *pool,u32 size,u32 *required,u32 *parsedLength); +s32 httpUtilParseHeader(httpHeader *header,const char *str,u32 len,void *pool,u32 size,u32 *required,u32 *parsedLength); #ifdef __cplusplus diff --git a/ppu/include/ssl/ssl.h b/ppu/include/ssl/ssl.h index 41b70a6e..d4d80844 100644 --- a/ppu/include/ssl/ssl.h +++ b/ppu/include/ssl/ssl.h @@ -166,17 +166,17 @@ typedef void* sslCert; typedef void* sslCertName; -int sslInit(void *pool, size_t poolSize); +int sslInit(void *pool, uint32_t poolSize); int sslEnd(void); /* SSL certificate loader */ -int sslCertificateLoader(uint64_t flag, char *buffer, size_t size, size_t *required); +int sslCertificateLoader(uint64_t flag, char *buffer, uint32_t size, uint32_t *required); /* SSL certificate information get functions */ -int sslCertGetSerialNumber(const sslCert cert, const uint8_t **sboData, size_t *sboLength); -int sslCertGetPublicKey(const sslCert cert, const uint8_t **sboData, size_t *sboLength); -int sslCertGetRsaPublicKeyModulus(const sslCert cert, const uint8_t **sboData, size_t *sboLength); -int sslCertGetRsaPublicKeyExponent(const sslCert cert, const uint8_t **sboData, size_t *sboLength); +int sslCertGetSerialNumber(const sslCert cert, const uint8_t **sboData, uint32_t *sboLength); +int sslCertGetPublicKey(const sslCert cert, const uint8_t **sboData, uint32_t *sboLength); +int sslCertGetRsaPublicKeyModulus(const sslCert cert, const uint8_t **sboData, uint32_t *sboLength); +int sslCertGetRsaPublicKeyExponent(const sslCert cert, const uint8_t **sboData, uint32_t *sboLength); //int sslCertGetNotBefore(const sslCert cert, CellRtcTick *begin); //int sslCertGetNotAfter(const sslCert cert, CellRtcTick *limit); @@ -184,7 +184,7 @@ int sslCertGetSubjectName(const sslCert cert, const sslCertName *name); int sslCertGetIssuerName(const sslCert cert, const sslCertName *name); int sslCertGetNameEntryCount(const sslCert cert, uint32_t *entryCount); -int sslCertGetNameEntryInfo(const sslCert cert, uint32_t entryNum, const char **oidName, const uint8_t **value, size_t *valueLength, int32_t flag); +int sslCertGetNameEntryInfo(const sslCert cert, uint32_t entryNum, const char **oidName, const uint8_t **value, uint32_t *valueLength, int32_t flag); int sslCertGetMd5Fingerprint(const sslCert cert, const uint8_t *buf, uint32_t *plen); @@ -193,4 +193,3 @@ int sslCertGetMd5Fingerprint(const sslCert cert, const uint8_t *buf, uint32_t *p #endif /* __cplusplus */ #endif /* __SSL_H__ */ - From 30299eb407be941b6e04a8233e48fc181b35fb59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Parrino?= Date: Thu, 2 Jul 2020 23:40:31 -0300 Subject: [PATCH 02/56] Simple HTTP example (#101) --- samples/network/httptest/Makefile | 140 +++++++++++ samples/network/httptest/source/http.c | 319 +++++++++++++++++++++++++ 2 files changed, 459 insertions(+) create mode 100644 samples/network/httptest/Makefile create mode 100644 samples/network/httptest/source/http.c diff --git a/samples/network/httptest/Makefile b/samples/network/httptest/Makefile new file mode 100644 index 00000000..f93ba9e4 --- /dev/null +++ b/samples/network/httptest/Makefile @@ -0,0 +1,140 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PSL1GHT)),) +$(error "Please set PSL1GHT in your environment. export PSL1GHT=") +endif + +include $(PSL1GHT)/ppu_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include + +TITLE := HTTP sample - PSL1GHT +APPID := DEBUGPR01 +CONTENTID := UP0001-$(APPID)_00-0000000000000000 +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +CFLAGS = -O2 -Wall -mcpu=cell $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lrsx -lgcm_sys -lio -lsysutil -lrt -llv2 -lm -lnet -lsysmodule -lssl -lhttp -lhttputil + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export BUILDDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES), -I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(LIBPSL1GHT_INC) \ + -I$(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + $(LIBPSL1GHT_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).self + +#--------------------------------------------------------------------------------- +run: + ps3load $(OUTPUT).self + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).self: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .bin extension +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/samples/network/httptest/source/http.c b/samples/network/httptest/source/http.c new file mode 100644 index 00000000..d3b68ae8 --- /dev/null +++ b/samples/network/httptest/source/http.c @@ -0,0 +1,319 @@ +#include +#include +#include +#include + +#include +#include +#include + +#define HTTP_YES 1 +#define HTTP_NO 0 +#define HTTP_SUCCESS 1 +#define HTTP_FAILED 0 + +#define HTTP_USER_AGENT "Mozilla/5.0 (PLAYSTATION 3; 1.00)" + +typedef struct +{ + void* http_pool; + void* ssl_pool; + httpsData* caList; + void* cert_buffer; +} t_http_pools; + +static t_http_pools http_pools; +u8 cancel = HTTP_NO; + +static char getBuffer[64*1024]; + + +int http_init(void) +{ + int ret; + u32 cert_size=0; + u8 module_https_loaded=HTTP_NO; + u8 module_http_loaded=HTTP_NO; + u8 module_net_loaded=HTTP_NO; + u8 module_ssl_loaded=HTTP_NO; + + u8 https_init=HTTP_NO; + u8 http_init=HTTP_NO; + u8 net_init=HTTP_NO; + u8 ssl_init=HTTP_NO; + + //init + ret = sysModuleLoad(SYSMODULE_NET); + if (ret < 0) { + printf("Error : sysModuleLoad(SYSMODULE_NET) HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } else module_net_loaded=HTTP_YES; + + ret = netInitialize(); + if (ret < 0) { + printf("Error : netInitialize HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } else net_init=HTTP_YES; + + ret = sysModuleLoad(SYSMODULE_HTTP); + if (ret < 0) { + printf("Error : sysModuleLoad(SYSMODULE_HTTP) HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } else module_http_loaded=HTTP_YES; + + http_pools.http_pool = malloc(0x10000); + if (http_pools.http_pool == NULL) { + printf("Error : out of memory (http_pool)"); + ret=HTTP_FAILED; + goto end; + } + + ret = httpInit(http_pools.http_pool, 0x10000); + if (ret < 0) { + printf("Error : httpInit HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } else http_init=HTTP_YES; + + ret = sysModuleLoad(SYSMODULE_HTTPS); + if (ret < 0) { + printf("Error : sysModuleLoad(SYSMODULE_HTTP) HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } else module_https_loaded=HTTP_YES; + + ret = sysModuleLoad(SYSMODULE_SSL); + if (ret < 0) { + printf("Error : sysModuleLoad(SYSMODULE_HTTP) HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } else module_ssl_loaded=HTTP_YES; + + http_pools.ssl_pool = malloc(0x40000); + if (http_pools.ssl_pool == NULL) { + printf("Error : out of memory (ssl_pool)"); + ret=HTTP_FAILED; + goto end; + } + + ret = sslInit(http_pools.ssl_pool, 0x40000); + if (ret < 0) { + printf("Error : sslInit HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } else ssl_init=HTTP_YES; + + http_pools.caList = (httpsData *)malloc(sizeof(httpsData)); + ret = sslCertificateLoader(SSL_LOAD_CERT_ALL, NULL, 0, &cert_size); + if (ret < 0) { + printf("Error : sslCertificateLoader HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } + + http_pools.cert_buffer = malloc(cert_size); + if (http_pools.cert_buffer==NULL) { + printf("Error : out of memory (cert_buffer)"); + ret=HTTP_FAILED; + goto end; + } + + ret = sslCertificateLoader(SSL_LOAD_CERT_ALL, http_pools.cert_buffer, cert_size, NULL); + if (ret < 0) { + printf("Error : sslCertificateLoader HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } + + (&http_pools.caList[0])->ptr = http_pools.cert_buffer; + (&http_pools.caList[0])->size = cert_size; + + ret = httpsInit(1, (httpsData *) http_pools.caList); + if (ret < 0) { + printf("Error : httpsInit HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } else https_init=HTTP_YES; + + return HTTP_SUCCESS; + +end: + if(http_pools.caList) free(http_pools.caList); + if(https_init) httpsEnd(); + if(ssl_init) sslEnd(); + if(http_init) httpEnd(); + if(net_init) netDeinitialize(); + + if(module_http_loaded) sysModuleUnload(SYSMODULE_HTTP); + if(module_https_loaded) sysModuleUnload(SYSMODULE_HTTPS); + if(module_ssl_loaded) sysModuleUnload(SYSMODULE_SSL); + if(module_net_loaded) sysModuleUnload(SYSMODULE_NET); + + if(http_pools.http_pool) free(http_pools.http_pool); + if(http_pools.ssl_pool) free(http_pools.ssl_pool); + if(http_pools.cert_buffer) free(http_pools.cert_buffer); + + return ret; +} + +void http_end(void) +{ + if(http_pools.caList) free(http_pools.caList); + httpsEnd(); + sslEnd(); + httpEnd(); + netDeinitialize(); + + sysModuleUnload(SYSMODULE_HTTP); + sysModuleUnload(SYSMODULE_HTTPS); + sysModuleUnload(SYSMODULE_SSL); + sysModuleUnload(SYSMODULE_NET); + + if(http_pools.http_pool) free(http_pools.http_pool); + if(http_pools.ssl_pool) free(http_pools.ssl_pool); + if(http_pools.cert_buffer) free(http_pools.cert_buffer); + + return; +} + +char* escape_filename(const char* filename) +{ + int len = strlen(filename); + char* ret = (char *)calloc(1, len*3); + + httpUtilEscapeUri(ret, len*3, (uint8_t*) filename, len, 0); + + return ret; +} + +int http_download(const char* url, const char* filename, const char* local_dst) +{ + int ret = 0, httpCode = 0; + httpUri uri; + httpClientId httpClient = 0; + httpTransId httpTrans = 0; + FILE* fp=NULL; + u32 nRecv = 1; + u32 size = 0; + uint64_t length = 0; + void *uri_pool = NULL; + char* escaped_name = NULL; + char* escaped_url = NULL; + + ret = httpCreateClient(&httpClient); + if (ret < 0) { + printf("Error : httpCreateClient HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } + httpClientSetConnTimeout(httpClient, 10 * 1000 * 1000); + httpClientSetUserAgent(httpClient, HTTP_USER_AGENT); + httpClientSetAutoRedirect(httpClient, 1); + + // Escape URL file name characters + escaped_name = escape_filename(filename); + asprintf(&escaped_url, "%s%s", url, escaped_name); + + printf("Downloading (%s) -> (%s)", escaped_url, local_dst); + + //URI + ret = httpUtilParseUri(&uri, escaped_url, NULL, 0, &size); + if (ret < 0) { + printf("Error : httpUtilParseUri() HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } + + uri_pool = malloc(size); + if (uri_pool == NULL) { + printf("Error : out of memory (uri_pool)"); + ret=HTTP_FAILED; + goto end; + } + + ret = httpUtilParseUri(&uri, escaped_url, uri_pool, size, 0); + if (ret < 0) { + printf("Error : httpUtilParseUri() HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } + //END of URI + + //SEND REQUEST + ret = httpCreateTransaction(&httpTrans, httpClient, HTTP_METHOD_GET, &uri); + if (ret < 0) { + printf("Error : httpCreateTransaction() HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } + + ret = httpSendRequest(httpTrans, NULL, 0, NULL); + if (ret < 0) { + printf("Error : httpSendRequest() HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } + + //GET SIZE + httpResponseGetContentLength(httpTrans, &length); + + ret = httpResponseGetStatusCode(httpTrans, &httpCode); + if (ret < 0) { + printf("Error : cellHttpResponseGetStatusCode() HTTP_FAILED (%x)", ret); + ret=HTTP_FAILED; + goto end; + } + + if(httpCode != HTTP_STATUS_CODE_OK && httpCode >= 400 ) { + printf("Error : Status code (%d)", httpCode); + ret=HTTP_FAILED; + goto end; + } + + //TRANSFER + fp = fopen(local_dst, "wb"); + if(fp == NULL) { + printf("Error : fopen() HTTP_FAILED : %s", local_dst); + ret=HTTP_FAILED; + goto end; + } + + while(nRecv != 0) { + if(httpRecvResponse(httpTrans, (void*) getBuffer, sizeof(getBuffer)-1, &nRecv) > 0) break; + if(nRecv == 0) break; + fwrite((char*) getBuffer, nRecv, 1, fp); + if(cancel==HTTP_YES) break; + } + fclose(fp); + + if(cancel==HTTP_YES) { + unlink((char*)local_dst); + ret=HTTP_FAILED; + cancel=HTTP_NO; + } + + //END of TRANSFER + ret=HTTP_SUCCESS; + +end: + if(httpTrans) httpDestroyTransaction(httpTrans); + if(httpClient) httpDestroyClient(httpClient); + if(uri_pool) free(uri_pool); + if(escaped_url) free(escaped_url); + if(escaped_name) free(escaped_name); + + return ret; +} + +s32 main(s32 argc, const char* argv[]) +{ + if (http_init() == HTTP_SUCCESS) + { + http_download("https://google.com/", "robots.txt", "/dev_hdd0/tmp/file.txt"); + http_end(); + } + return 0; +} From 766f6ace9d5efa6349cd987f29ea850790000981 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Tue, 7 Jul 2020 08:49:32 +0200 Subject: [PATCH 03/56] - add libspumars (Multicore Application Runtime System). A library set to issue tasks onto the SPU's (similar to SPURS) - add real fake self tool - add more to .gitignore - few changes and additions to current libraries. --- .gitignore | 34 + Makefile | 6 +- common/Makefile | 3 + common/libspumars/Makefile | 19 + .../base/common/callback_internal_types.h | 22 + .../base/common/kernel_internal_types.h | 95 + .../base/common/workload_internal_types.h | 159 ++ .../include/common/mars/callback_types.h | 30 + common/libspumars/include/common/mars/error.h | 25 + .../include/common/mars/mutex_types.h | 34 + .../include/common/mars/task_barrier_types.h | 33 + .../common/mars/task_event_flag_types.h | 67 + .../include/common/mars/task_queue_types.h | 49 + .../common/mars/task_semaphore_types.h | 25 + .../include/common/mars/task_types.h | 36 + .../include/common/mars/workload_types.h | 38 + common/libspumars/include/ppu/mars/base.h | 261 ++ common/libspumars/include/ppu/mars/context.h | 95 + common/libspumars/include/ppu/mars/mutex.h | 130 + common/libspumars/include/ppu/mars/task.h | 132 + .../include/ppu/mars/task_barrier.h | 78 + .../include/ppu/mars/task_event_flag.h | 96 + .../libspumars/include/ppu/mars/task_queue.h | 120 + .../include/ppu/mars/task_semaphore.h | 60 + .../libspumars/include/ppu/mars/task_signal.h | 20 + .../include/ppu/mars/workload_queue.h | 330 +++ common/libspumars/include/spu/mars/module.h | 408 +++ common/libspumars/include/spu/mars/task.h | 315 +++ .../include/spu/mars/task_barrier.h | 117 + .../include/spu/mars/task_event_flag.h | 158 ++ .../libspumars/include/spu/mars/task_queue.h | 554 ++++ .../include/spu/mars/task_semaphore.h | 58 + .../libspumars/include/spu/mars/task_signal.h | 73 + common/libspumars/ppu/Makefile | 119 + common/libspumars/ppu/base/lib/alloc.c | 18 + .../libspumars/ppu/base/lib/callback_cell.c | 272 ++ common/libspumars/ppu/base/lib/cond_cell.c | 58 + common/libspumars/ppu/base/lib/context.c | 339 +++ .../ppu/base/lib/context_internal.h | 62 + common/libspumars/ppu/base/lib/ea_cell.c | 141 + common/libspumars/ppu/base/lib/elf.h | 2459 +++++++++++++++++ .../ppu/base/lib/host_mutex_psl1ght.c | 39 + common/libspumars/ppu/base/lib/mpu_cell.c | 163 ++ common/libspumars/ppu/base/lib/mutex_cell.c | 159 ++ .../libspumars/ppu/base/lib/numa_internal.h | 9 + .../libspumars/ppu/base/lib/workload_queue.c | 878 ++++++ common/libspumars/ppu/ppu/mars_kernel.c | 724 +++++ common/libspumars/ppu/ppu/mars_task_module.c | 475 ++++ common/libspumars/ppu/task/lib/elf.h | 2459 +++++++++++++++++ common/libspumars/ppu/task/lib/task.c | 391 +++ common/libspumars/ppu/task/lib/task_barrier.c | 111 + .../libspumars/ppu/task/lib/task_event_flag.c | 314 +++ common/libspumars/ppu/task/lib/task_queue.c | 372 +++ .../libspumars/ppu/task/lib/task_semaphore.c | 73 + common/libspumars/ppu/task/lib/task_signal.c | 28 + common/libspumars/spu/Makefile | 136 + common/libspumars/spu/base/kernel/dma.c | 59 + common/libspumars/spu/base/kernel/kernel.c | 1065 +++++++ .../libspumars/spu/base/kernel/kernel_crt.S | 46 + common/libspumars/spu/base/kernel/mutex.c | 105 + common/libspumars/spu/base/kernel/switch.S | 39 + common/libspumars/spu/base/lib/module.S | 307 ++ common/libspumars/spu/task/lib/task.c | 177 ++ common/libspumars/spu/task/lib/task_barrier.c | 164 ++ .../libspumars/spu/task/lib/task_event_flag.c | 207 ++ common/libspumars/spu/task/lib/task_queue.c | 455 +++ .../libspumars/spu/task/lib/task_semaphore.c | 93 + common/libspumars/spu/task/lib/task_signal.c | 33 + .../libspumars/spu/task/module/task_module.c | 358 +++ .../libspumars/spu/task/module/task_module.h | 179 ++ .../libspumars/spu/task/module/task_switch.S | 195 ++ .../task/common/task_barrier_internal_types.h | 22 + .../common/task_event_flag_internal_types.h | 22 + .../task/common/task_internal_types.h | 44 + .../task/common/task_queue_internal_types.h | 34 + .../common/task_semaphore_internal_types.h | 20 + ppu/include/lv2/spu.h | 2 +- ppu/include/sys/cond.h | 8 + ppu/include/sys/mutex.h | 13 +- ppu/include/sys/spu.h | 91 +- ppu/sprx/liblv2/exports.h | 2 +- ppu_rules | 2 +- spu/Makefile | 6 + spu/include/dma/spu_dma.h | 391 +++ spu/include/sys/spu_atomic.h | 85 + spu/include/sys/spu_printf.h | 19 + spu/libspuatomic/Makefile | 81 + spu/libspuatomic/spu_atomic.c | 409 +++ spu/libspudma/Makefile | 81 + spu/libspudma/spu_dma.c | 28 + spu/libsputhread/Makefile | 2 +- spu/libsputhread/spu_call_event_va_arg.S | 37 + tools/Makefile | 3 + tools/fself/Makefile | 182 ++ tools/fself/include/common.h | 53 + tools/fself/include/self.h | 159 ++ tools/fself/include/sha1.h | 72 + tools/fself/include/tools.h | 63 + tools/fself/include/types.h | 20 + tools/fself/source/main.c | 94 + tools/fself/source/self.c | 419 +++ tools/fself/source/sha1.c | 380 +++ tools/fself/source/tools.c | 241 ++ 103 files changed, 20031 insertions(+), 15 deletions(-) create mode 100644 common/libspumars/Makefile create mode 100644 common/libspumars/base/common/callback_internal_types.h create mode 100644 common/libspumars/base/common/kernel_internal_types.h create mode 100644 common/libspumars/base/common/workload_internal_types.h create mode 100644 common/libspumars/include/common/mars/callback_types.h create mode 100644 common/libspumars/include/common/mars/error.h create mode 100644 common/libspumars/include/common/mars/mutex_types.h create mode 100644 common/libspumars/include/common/mars/task_barrier_types.h create mode 100644 common/libspumars/include/common/mars/task_event_flag_types.h create mode 100644 common/libspumars/include/common/mars/task_queue_types.h create mode 100644 common/libspumars/include/common/mars/task_semaphore_types.h create mode 100644 common/libspumars/include/common/mars/task_types.h create mode 100644 common/libspumars/include/common/mars/workload_types.h create mode 100644 common/libspumars/include/ppu/mars/base.h create mode 100644 common/libspumars/include/ppu/mars/context.h create mode 100644 common/libspumars/include/ppu/mars/mutex.h create mode 100644 common/libspumars/include/ppu/mars/task.h create mode 100644 common/libspumars/include/ppu/mars/task_barrier.h create mode 100644 common/libspumars/include/ppu/mars/task_event_flag.h create mode 100644 common/libspumars/include/ppu/mars/task_queue.h create mode 100644 common/libspumars/include/ppu/mars/task_semaphore.h create mode 100644 common/libspumars/include/ppu/mars/task_signal.h create mode 100644 common/libspumars/include/ppu/mars/workload_queue.h create mode 100644 common/libspumars/include/spu/mars/module.h create mode 100644 common/libspumars/include/spu/mars/task.h create mode 100644 common/libspumars/include/spu/mars/task_barrier.h create mode 100644 common/libspumars/include/spu/mars/task_event_flag.h create mode 100644 common/libspumars/include/spu/mars/task_queue.h create mode 100644 common/libspumars/include/spu/mars/task_semaphore.h create mode 100644 common/libspumars/include/spu/mars/task_signal.h create mode 100644 common/libspumars/ppu/Makefile create mode 100644 common/libspumars/ppu/base/lib/alloc.c create mode 100644 common/libspumars/ppu/base/lib/callback_cell.c create mode 100644 common/libspumars/ppu/base/lib/cond_cell.c create mode 100644 common/libspumars/ppu/base/lib/context.c create mode 100644 common/libspumars/ppu/base/lib/context_internal.h create mode 100644 common/libspumars/ppu/base/lib/ea_cell.c create mode 100644 common/libspumars/ppu/base/lib/elf.h create mode 100644 common/libspumars/ppu/base/lib/host_mutex_psl1ght.c create mode 100644 common/libspumars/ppu/base/lib/mpu_cell.c create mode 100644 common/libspumars/ppu/base/lib/mutex_cell.c create mode 100644 common/libspumars/ppu/base/lib/numa_internal.h create mode 100644 common/libspumars/ppu/base/lib/workload_queue.c create mode 100644 common/libspumars/ppu/ppu/mars_kernel.c create mode 100644 common/libspumars/ppu/ppu/mars_task_module.c create mode 100644 common/libspumars/ppu/task/lib/elf.h create mode 100644 common/libspumars/ppu/task/lib/task.c create mode 100644 common/libspumars/ppu/task/lib/task_barrier.c create mode 100644 common/libspumars/ppu/task/lib/task_event_flag.c create mode 100644 common/libspumars/ppu/task/lib/task_queue.c create mode 100644 common/libspumars/ppu/task/lib/task_semaphore.c create mode 100644 common/libspumars/ppu/task/lib/task_signal.c create mode 100644 common/libspumars/spu/Makefile create mode 100644 common/libspumars/spu/base/kernel/dma.c create mode 100644 common/libspumars/spu/base/kernel/kernel.c create mode 100644 common/libspumars/spu/base/kernel/kernel_crt.S create mode 100644 common/libspumars/spu/base/kernel/mutex.c create mode 100644 common/libspumars/spu/base/kernel/switch.S create mode 100644 common/libspumars/spu/base/lib/module.S create mode 100644 common/libspumars/spu/task/lib/task.c create mode 100644 common/libspumars/spu/task/lib/task_barrier.c create mode 100644 common/libspumars/spu/task/lib/task_event_flag.c create mode 100644 common/libspumars/spu/task/lib/task_queue.c create mode 100644 common/libspumars/spu/task/lib/task_semaphore.c create mode 100644 common/libspumars/spu/task/lib/task_signal.c create mode 100644 common/libspumars/spu/task/module/task_module.c create mode 100644 common/libspumars/spu/task/module/task_module.h create mode 100644 common/libspumars/spu/task/module/task_switch.S create mode 100644 common/libspumars/task/common/task_barrier_internal_types.h create mode 100644 common/libspumars/task/common/task_event_flag_internal_types.h create mode 100644 common/libspumars/task/common/task_internal_types.h create mode 100644 common/libspumars/task/common/task_queue_internal_types.h create mode 100644 common/libspumars/task/common/task_semaphore_internal_types.h create mode 100644 spu/include/dma/spu_dma.h create mode 100644 spu/include/sys/spu_atomic.h create mode 100644 spu/include/sys/spu_printf.h create mode 100644 spu/libspuatomic/Makefile create mode 100755 spu/libspuatomic/spu_atomic.c create mode 100644 spu/libspudma/Makefile create mode 100755 spu/libspudma/spu_dma.c create mode 100644 spu/libsputhread/spu_call_event_va_arg.S create mode 100644 tools/fself/Makefile create mode 100644 tools/fself/include/common.h create mode 100644 tools/fself/include/self.h create mode 100644 tools/fself/include/sha1.h create mode 100644 tools/fself/include/tools.h create mode 100644 tools/fself/include/types.h create mode 100644 tools/fself/source/main.c create mode 100644 tools/fself/source/self.c create mode 100644 tools/fself/source/sha1.c create mode 100644 tools/fself/source/tools.c diff --git a/.gitignore b/.gitignore index 72d4ab69..cddced40 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,39 @@ *.o *.a *.d +*.so +*.dll +*.elf.map build/ .idea/ +.vscode/ +ppu/sprx/libaudio/ppu/ +ppu/sprx/libcamera/ppu/ +ppu/sprx/libgcm_sys/ppu/ +ppu/sprx/libgem/ppu/ +ppu/sprx/libhttp/ppu/ +ppu/sprx/libhttputil/ppu/ +ppu/sprx/libio/ppu/ +ppu/sprx/libjpgdec/ppu/ +ppu/sprx/liblv2/ppu/ +ppu/sprx/libnet/ppu/ +ppu/sprx/libpngdec/ppu/ +ppu/sprx/libresc/ppu/ +ppu/sprx/libspurs/ppu/ +ppu/sprx/liblv2dbg/ppu/ +ppu/sprx/libnetctl/ppu/ +ppu/sprx/libssl/ppu/ +ppu/sprx/libsysfs/ppu/ +ppu/sprx/libsysmodule/ppu/ +ppu/sprx/libsysutil/ppu/ +ppu/sprx/libusb/ppu/ +ppu/sprx/libvdec/ppu/ +tools/generic/bin2s +tools/cgcomp/cgcomp +tools/geohot/make_self +tools/geohot/make_self_npdrm +tools/geohot/package_finalize +tools/ps3load/ps3load +tools/raw2h/raw2h +tools/sprxlinker/sprxlinker +tools/fself/fself diff --git a/Makefile b/Makefile index 11c5821a..b96c7eb9 100644 --- a/Makefile +++ b/Makefile @@ -11,9 +11,9 @@ endif .PHONY: samples all: - @$(MAKE) -C common --no-print-directory @$(MAKE) -C ppu --no-print-directory @$(MAKE) -C spu --no-print-directory + @$(MAKE) -C common --no-print-directory @$(MAKE) -C tools --no-print-directory samples: @@ -33,15 +33,15 @@ install-socat: @$(MAKE) -C tools install-socat --no-print-directory install: - @$(MAKE) -C common install --no-print-directory @$(MAKE) -C ppu install --no-print-directory @$(MAKE) -C spu install --no-print-directory + @$(MAKE) -C common install --no-print-directory @$(MAKE) -C tools install --no-print-directory clean: - @$(MAKE) -C common clean --no-print-directory @$(MAKE) -C ppu clean --no-print-directory @$(MAKE) -C spu clean --no-print-directory + @$(MAKE) -C common clean --no-print-directory @$(MAKE) -C tools clean --no-print-directory @rm -rf doc diff --git a/common/Makefile b/common/Makefile index 8782180b..e6779355 100644 --- a/common/Makefile +++ b/common/Makefile @@ -7,13 +7,16 @@ all: @$(MAKE) -C libsimdmath --no-print-directory @$(MAKE) -C vectormath --no-print-directory + @$(MAKE) -C libspumars --no-print-directory install: @$(MAKE) -C libsimdmath install --no-print-directory @$(MAKE) -C vectormath install --no-print-directory + @$(MAKE) -C libspumars install --no-print-directory clean: @$(MAKE) -C libsimdmath clean --no-print-directory @$(MAKE) -C vectormath clean --no-print-directory + @$(MAKE) -C libspumars clean --no-print-directory .PHONY: all clean install diff --git a/common/libspumars/Makefile b/common/libspumars/Makefile new file mode 100644 index 00000000..43269e1c --- /dev/null +++ b/common/libspumars/Makefile @@ -0,0 +1,19 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +all: + @$(MAKE) -C spu --no-print-directory + @$(MAKE) -C ppu --no-print-directory + +install: + @$(MAKE) -C spu install --no-print-directory + @$(MAKE) -C ppu install --no-print-directory + +clean: + @$(MAKE) -C spu clean --no-print-directory + @$(MAKE) -C ppu clean --no-print-directory + +.PHONY: all clean install diff --git a/common/libspumars/base/common/callback_internal_types.h b/common/libspumars/base/common/callback_internal_types.h new file mode 100644 index 00000000..77f43621 --- /dev/null +++ b/common/libspumars/base/common/callback_internal_types.h @@ -0,0 +1,22 @@ +#ifndef __MARS_CALLBACK_INTERNAL_TYPES_H__ +#define __MARS_CALLBACK_INTERNAL_TYPES_H__ + +#define MARS_CALLBACK_QUEUE_SIZE 128 /* size of 128 bytes */ +#define MARS_CALLBACK_QUEUE_ALIGN 128 /* align to 128 bytes */ +#define MARS_CALLBACK_QUEUE_MAX 54 /* max depth of queue */ + +#define MARS_CALLBACK_QUEUE_FLAG_NONE 0x0 /* no flag set */ +#define MARS_CALLBACK_QUEUE_FLAG_EXIT 0x1 /* exit flag */ +#define MARS_CALLBACK_QUEUE_FLAG_PUSH 0x2 /* push flag */ + +/* 128 byte callback queue structure */ +struct mars_callback_queue { + uint32_t lock; + uint32_t flag; + uint32_t count; + uint32_t head; + uint32_t tail; + uint16_t workload_id[MARS_CALLBACK_QUEUE_MAX]; +} __attribute__((aligned(MARS_CALLBACK_QUEUE_ALIGN))); + +#endif diff --git a/common/libspumars/base/common/kernel_internal_types.h b/common/libspumars/base/common/kernel_internal_types.h new file mode 100644 index 00000000..aa315838 --- /dev/null +++ b/common/libspumars/base/common/kernel_internal_types.h @@ -0,0 +1,95 @@ +#ifndef __KERNEL_INTERNAL_TYPES_H__ +#define __KERNEL_INTERNAL_TYPES_H__ + +#include + +#include "mars/mutex_types.h" + +#include "callback_internal_types.h" +#include "workload_internal_types.h" + +#define MARS_KERNEL_ID_NONE 0xffff + +#define MARS_KERNEL_TICKS_FLAG_SYNC_BEGIN 0x1 +#define MARS_KERNEL_TICKS_FLAG_SYNC_END 0x2 + +#define MARS_KERNEL_DMA_TAG 31 +#define MARS_KERNEL_SPU_EVENT_PORT 63 + +#define MARS_KERNEL_PARAMS_SIZE 128 +#define MARS_KERNEL_PARAMS_ALIGN 128 + +/* mars kernel syscalls */ +struct mars_kernel_syscalls { + uint32_t (*get_ticks)(void); + uint64_t (*get_mars_context_ea)(void); + uint16_t (*get_kernel_id)(void); + uint16_t (*get_workload_id)(void); + struct mars_workload_context * (*get_workload)(void); + struct mars_workload_context * (*get_workload_by_id)(uint16_t id); + + void (*workload_exit)(uint8_t state); + int (*workload_query)(uint16_t id, int query); + int (*workload_wait_set)(uint16_t id); + int (*workload_wait_reset)(void); + int (*workload_signal_set)(uint16_t id); + int (*workload_signal_reset)(void); + int (*workload_schedule_begin)(uint16_t id, uint8_t priority, + struct mars_workload_context **workload); + int (*workload_schedule_end)(uint16_t id, int cancel); + int (*workload_unschedule_begin)(uint16_t id, + struct mars_workload_context **workload); + int (*workload_unschedule_end)(uint16_t id); + + void (*host_signal_send)(uint64_t watch_point_ea); + int (*host_callback_set)(uint64_t callback_ea, + const struct mars_callback_args *in); + int (*host_callback_reset)(struct mars_callback_args *out); + + int (*mutex_lock_get)(uint64_t mutex_ea, struct mars_mutex *mutex); + int (*mutex_unlock_put)(uint64_t mutex_ea, struct mars_mutex *mutex); + + int (*dma_get)(void *ls, uint64_t ea, uint32_t size, uint32_t tag); + int (*dma_put)(const void *ls, uint64_t ea, uint32_t size, + uint32_t tag); + int (*dma_wait)(uint32_t tag); +}; + +/* mars kernel ticks */ +struct mars_kernel_ticks { + uint32_t flag; + uint32_t offset; +}; + +/* mars kernel parameters */ +struct mars_kernel_params { + struct mars_kernel_ticks kernel_ticks; + uint64_t mars_context_ea; + uint64_t workload_queue_ea; + uint64_t callback_queue_ea; + uint16_t kernel_id; + + uint8_t pad[MARS_KERNEL_PARAMS_SIZE - + (sizeof(struct mars_kernel_ticks) + + sizeof(uint64_t)*3 + + sizeof(uint16_t)) + ]; +} __attribute__((aligned(MARS_KERNEL_PARAMS_ALIGN))); + +/* mars kernel buffer */ +union mars_kernel_buffer { + struct mars_callback_queue callback_queue; + struct mars_workload_queue_header workload_queue_header; + struct mars_workload_queue_block workload_queue_block; +}; + +/* mars kernel mutex */ +int mutex_lock_get(uint64_t mutex_ea, struct mars_mutex *mutex); +int mutex_unlock_put(uint64_t mutex_ea, struct mars_mutex *mutex); + +/* mars kernel dma */ +int dma_get(void *ls, uint64_t ea, uint32_t size, uint32_t tag); +int dma_put(const void *ls, uint64_t ea, uint32_t size, uint32_t tag); +int dma_wait(uint32_t tag); + +#endif diff --git a/common/libspumars/base/common/workload_internal_types.h b/common/libspumars/base/common/workload_internal_types.h new file mode 100644 index 00000000..13dcde85 --- /dev/null +++ b/common/libspumars/base/common/workload_internal_types.h @@ -0,0 +1,159 @@ +#ifndef __WORKLOAD_INTERNAL_TYPES_H__ +#define __WORKLOAD_INTERNAL_TYPES_H__ + +#include + +#include "mars/callback_types.h" +#include "mars/workload_types.h" + +#define MARS_WORKLOAD_STATE_NONE 0x00 /* workload undefined */ +#define MARS_WORKLOAD_STATE_ADDING 0x01 /* adding now */ +#define MARS_WORKLOAD_STATE_REMOVING 0x02 /* removing now */ +#define MARS_WORKLOAD_STATE_SCHEDULING 0x04 /* scheduling now */ +#define MARS_WORKLOAD_STATE_UNSCHEDULING 0x08 /* unscheduling now */ +#define MARS_WORKLOAD_STATE_READY 0x10 /* ready to schedule */ +#define MARS_WORKLOAD_STATE_WAITING 0x20 /* waiting for sync */ +#define MARS_WORKLOAD_STATE_RUNNING 0x40 /* currently running */ +#define MARS_WORKLOAD_STATE_FINISHED 0x80 /* not allow schedule */ + +#define MARS_WORKLOAD_PRIORITY_MIN 0x00 /* minimum priority */ +#define MARS_WORKLOAD_PRIORITY_MAX 0xff /* maximum priority */ + +#define MARS_WORKLOAD_COUNTER_MIN 0x0000 /* minimum counter */ +#define MARS_WORKLOAD_COUNTER_MAX 0x7fff /* maximum counter */ + +#define MARS_WORKLOAD_SIGNAL_OFF 0x0 /* signal set off */ +#define MARS_WORKLOAD_SIGNAL_ON 0x1 /* signal set on */ + +#define MARS_WORKLOAD_ID_NONE 0xffff /* workload id none */ +#define MARS_WORKLOAD_ID_MAX 799 /* workload id max */ + +#define MARS_WORKLOAD_PER_BLOCK 16 /* wl/block (lock+15) */ +#define MARS_WORKLOAD_NUM_BLOCKS 50 /* total blocks */ +#define MARS_WORKLOAD_MAX 750 /* blocks * wl/block */ + +#define MARS_WORKLOAD_QUEUE_SIZE 198528 /* size 198528 bytes */ +#define MARS_WORKLOAD_QUEUE_ALIGN 128 /* align to 128 bytes */ +#define MARS_WORKLOAD_QUEUE_HEADER_SIZE 128 /* size of 128 bytes */ +#define MARS_WORKLOAD_QUEUE_HEADER_ALIGN 128 /* align to 128 bytes */ +#define MARS_WORKLOAD_QUEUE_BLOCK_SIZE 128 /* size to 128 bytes */ +#define MARS_WORKLOAD_QUEUE_BLOCK_ALIGN 128 /* align to 128 bytes */ + +#define MARS_WORKLOAD_QUEUE_FLAG_NONE 0x0 /* no flag set */ +#define MARS_WORKLOAD_QUEUE_FLAG_EXIT 0x1 /* exit flag */ + +#define MARS_WORKLOAD_BLOCK_PRIORITY_MIN MARS_WORKLOAD_PRIORITY_MIN +#define MARS_WORKLOAD_BLOCK_PRIORITY_MAX MARS_WORKLOAD_PRIORITY_MAX + +#define MARS_WORKLOAD_BLOCK_COUNTER_MIN 0x00 +#define MARS_WORKLOAD_BLOCK_COUNTER_MAX 0x3f + +#define MARS_WORKLOAD_BLOCK_READY_OFF 0x0 +#define MARS_WORKLOAD_BLOCK_READY_ON 0x1 + +#define MARS_WORKLOAD_BLOCK_WAITING_OFF 0x0 +#define MARS_WORKLOAD_BLOCK_WAITING_ON 0x1 +/* + * MARS workload queue header block bits (16-bits) + * ------------------------------------------ + * |[15.....8]|[7.....2]|[ 1 ]|[ 0 ]| + * ------------------------------------------ + * | 8-bits | 6-bits | 1-bit | 1-bit | + * ------------------------------------------ + * | PRIORITY | COUNTER | READY | WAITING | + * ------------------------------------------ + */ +#define MARS_BITS_SHIFT_BLOCK_PRIORITY 8 +#define MARS_BITS_SHIFT_BLOCK_COUNTER 2 +#define MARS_BITS_SHIFT_BLOCK_READY 1 +#define MARS_BITS_SHIFT_BLOCK_WAITING 0 + +#define MARS_BITS_MASK_BLOCK_PRIORITY 0x000000000000ff00ULL +#define MARS_BITS_MASK_BLOCK_COUNTER 0x00000000000000fcULL +#define MARS_BITS_MASK_BLOCK_READY 0x0000000000000002ULL +#define MARS_BITS_MASK_BLOCK_WAITING 0x0000000000000001ULL + +/* + * MARS workload queue block workload bits (64-bits) + * ------------------------------------------------------------------ + * |[63....56]|[55....48]|[47....33]|[ 32 ]|[31.....16]|[15......0]| + * ------------------------------------------------------------------ + * | 8-bits | 8-bits | 15-bits | 1-bit | 16-bits | 16-bits | + * ------------------------------------------------------------------ + * | STATE | PRIORITY | COUNTER | SIGNAL | WAIT_ID | KERNEL_ID | + * ------------------------------------------------------------------ + */ +#define MARS_BITS_SHIFT_WORKLOAD_STATE 56 +#define MARS_BITS_SHIFT_WORKLOAD_PRIORITY 48 +#define MARS_BITS_SHIFT_WORKLOAD_COUNTER 33 +#define MARS_BITS_SHIFT_WORKLOAD_SIGNAL 32 +#define MARS_BITS_SHIFT_WORKLOAD_WAIT_ID 16 +#define MARS_BITS_SHIFT_WORKLOAD_KERNEL_ID 0 + +#define MARS_BITS_MASK_WORKLOAD_STATE 0xff00000000000000ULL +#define MARS_BITS_MASK_WORKLOAD_PRIORITY 0x00ff000000000000ULL +#define MARS_BITS_MASK_WORKLOAD_COUNTER 0x0000fffe00000000ULL +#define MARS_BITS_MASK_WORKLOAD_SIGNAL 0x0000000100000000ULL +#define MARS_BITS_MASK_WORKLOAD_WAIT_ID 0x00000000ffff0000ULL +#define MARS_BITS_MASK_WORKLOAD_KERNEL_ID 0x000000000000ffffULL + +#define MARS_HOST_SIGNAL_EXIT 0x0 /* host exit flag */ + +#define MARS_WORKLOAD_MODULE_SIZE 64 +#define MARS_WORKLOAD_MODULE_ALIGN 16 + +#define MARS_WORKLOAD_CALLBACK_SIZE 64 +#define MARS_WORKLOAD_CALLBACK_ALIGN 16 + +#define MARS_BITS_GET(bits, name) \ + ((*(bits)&MARS_BITS_MASK_##name)>>MARS_BITS_SHIFT_##name) + +#define MARS_BITS_SET(bits, name, val) \ + (*bits) = ((*(bits)&~MARS_BITS_MASK_##name) | \ + ((uint64_t)(val)< + +#define MARS_CALLBACK_ARGS_SIZE 48 +#define MARS_CALLBACK_ARGS_ALIGN 16 + +#ifdef __cplusplus +extern "C" { +#endif + +struct mars_callback_args +{ + union + { + uint8_t u8[48]; + uint16_t u16[24]; + uint32_t u32[12]; + uint64_t u64[6]; + } type; +}__attribute__((aligned(MARS_CALLBACK_ARGS_ALIGN))); + +typedef int (*mars_callback)(const struct mars_callback_args *in, struct mars_callback_args *out); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/common/mars/error.h b/common/libspumars/include/common/mars/error.h new file mode 100644 index 00000000..7f46dbd3 --- /dev/null +++ b/common/libspumars/include/common/mars/error.h @@ -0,0 +1,25 @@ +#ifndef __MARS_ERROR_H__ +#define __MARS_ERROR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + MARS_SUCCESS = 0, /**< successful with no errors */ + MARS_ERROR_NULL, /**< null pointer passed in */ + MARS_ERROR_PARAMS, /**< invalid parameter specified */ + MARS_ERROR_INTERNAL, /**< internal library error */ + MARS_ERROR_MEMORY, /**< out of memory */ + MARS_ERROR_ALIGN, /**< bad memory alignment */ + MARS_ERROR_LIMIT, /**< some limit exceeded */ + MARS_ERROR_STATE, /**< something is in an invalid state */ + MARS_ERROR_FORMAT, /**< invalid format specified */ + MARS_ERROR_BUSY /**< operation returned due to being busy */ +}; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/common/mars/mutex_types.h b/common/libspumars/include/common/mars/mutex_types.h new file mode 100644 index 00000000..7e656a39 --- /dev/null +++ b/common/libspumars/include/common/mars/mutex_types.h @@ -0,0 +1,34 @@ +#ifndef __MARS_MUTEX_TYPES_H__ +#define __MARS_MUTEX_TYPES_H__ + +#include + +#define MARS_MUTEX_SIZE 128 +#define MARS_MUTEX_ALIGN 128 +#define MARS_MUTEX_ALIGN_MASK 0x7f +#define MARS_MUTEX_LOCKED 0x1 +#define MARS_MUTEX_UNLOCKED 0x0 + +#ifdef __cplusplus +extern "C" { +#endif + +struct mars_mutex_status +{ + uint8_t lock; + uint8_t pad; + uint8_t current_id; + uint8_t next_id; +}; + +struct mars_mutex +{ + struct mars_mutex_status status; + uint8_t pad[124]; +} __attribute__((aligned(MARS_MUTEX_ALIGN))); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/common/mars/task_barrier_types.h b/common/libspumars/include/common/mars/task_barrier_types.h new file mode 100644 index 00000000..cb99bf45 --- /dev/null +++ b/common/libspumars/include/common/mars/task_barrier_types.h @@ -0,0 +1,33 @@ +#ifndef __MARS_TASK_BARRIER_TYPES_H__ +#define __MARS_TASK_BARRIER_TYPES_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \ingroup group_mars_task_barrier + * \brief [host/MPU] MARS Task Barrier Types + */ + +/** + * \ingroup group_mars_task_barrier + * \brief Maximum tasks allowed for single barrier + */ +#define MARS_TASK_BARRIER_WAIT_MAX 25 + +/** + * \ingroup group_mars_task_barrier + * \brief MARS task barrier structure + * + * An instance of this structure must be created when using any of the + * MARS task barrier API. + */ +struct mars_task_barrier; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/common/mars/task_event_flag_types.h b/common/libspumars/include/common/mars/task_event_flag_types.h new file mode 100644 index 00000000..1a80b058 --- /dev/null +++ b/common/libspumars/include/common/mars/task_event_flag_types.h @@ -0,0 +1,67 @@ +#ifndef __MARS_TASK_EVENT_FLAG_TYPES_H__ +#define __MARS_TASK_EVENT_FLAG_TYPES_H__ + +/** + * \file + * \ingroup group_mars_task_event_flag + * \brief [host/MPU] MARS Task Event Flag Types + */ + +/** + * \ingroup group_mars_task_event_flag + * \brief Event flag direction from PPU to SPU + */ +#define MARS_TASK_EVENT_FLAG_HOST_TO_MPU 0x10 + +/** + * \ingroup group_mars_task_event_flag + * \brief Event flag direction from SPU to PPU + */ +#define MARS_TASK_EVENT_FLAG_MPU_TO_HOST 0x11 + +/** + * \ingroup group_mars_task_event_flag + * \brief Event flag direction from SPU to SPU + */ +#define MARS_TASK_EVENT_FLAG_MPU_TO_MPU 0x12 + +/** + * \ingroup group_mars_task_event_flag + * \brief Event flag clear mode automatic + */ +#define MARS_TASK_EVENT_FLAG_CLEAR_AUTO 0x20 + +/** + * \ingroup group_mars_task_event_flag + * \brief Event flag clear mode manual + */ +#define MARS_TASK_EVENT_FLAG_CLEAR_MANUAL 0x21 + +/** + * \ingroup group_mars_task_event_flag + * \brief Event flag mask mode bitwise OR + */ +#define MARS_TASK_EVENT_FLAG_MASK_OR 0x30 + +/** + * \ingroup group_mars_task_event_flag + * \brief Event flag mask mode bitwise AND + */ +#define MARS_TASK_EVENT_FLAG_MASK_AND 0x31 + +/** + * \ingroup group_mars_task_event_flag + * \brief Maximum tasks allowed to wait on a single event flag + */ +#define MARS_TASK_EVENT_FLAG_WAIT_MAX 15 + +/** + * \ingroup group_mars_task_event_flag + * \brief MARS task event flag structure + * + * An instance of this structure must be created when using any of the + * MARS event flag API. + */ +struct mars_task_event_flag; + +#endif diff --git a/common/libspumars/include/common/mars/task_queue_types.h b/common/libspumars/include/common/mars/task_queue_types.h new file mode 100644 index 00000000..decbb2bf --- /dev/null +++ b/common/libspumars/include/common/mars/task_queue_types.h @@ -0,0 +1,49 @@ +#ifndef __MARS_TASK_QUEUE_TYPES_H__ +#define __MARS_TASK_QUEUE_TYPES_H__ + +/** + * \file + * \ingroup group_mars_task_queue + * \brief [host/MPU] MARS Task Queue Types + */ + +/** + * \ingroup group_mars_task_queue + * \brief Queue direction from PPU to SPU + */ +#define MARS_TASK_QUEUE_HOST_TO_MPU 0x10 + +/** + * \ingroup group_mars_task_queue + * \brief Queue direction from SPU to PPU + */ +#define MARS_TASK_QUEUE_MPU_TO_HOST 0x11 + +/** + * \ingroup group_mars_task_queue + * \brief Queue direction from SPU to SPU + */ +#define MARS_TASK_QUEUE_MPU_TO_MPU 0x12 + +/** + * \ingroup group_mars_task_queue + * \brief Maximum tasks allowed to wait on a queue + */ +#define MARS_TASK_QUEUE_WAIT_MAX 18 + +/** + * \ingroup group_mars_task_queue + * \brief Maximum size allowed for queue entry + */ +#define MARS_TASK_QUEUE_ENTRY_SIZE_MAX 16384 + +/** + * \ingroup group_mars_task_queue + * \brief MARS task queue structure + * + * An instance of this structure must be created when using any of the + * MARS queue API. + */ +struct mars_task_queue; + +#endif diff --git a/common/libspumars/include/common/mars/task_semaphore_types.h b/common/libspumars/include/common/mars/task_semaphore_types.h new file mode 100644 index 00000000..9994be3c --- /dev/null +++ b/common/libspumars/include/common/mars/task_semaphore_types.h @@ -0,0 +1,25 @@ +#ifndef __MARS_TASK_SEMAPHORE_TYPES_H__ +#define __MARS_TASK_SEMAPHORE_TYPES_H__ + +/** + * \file + * \ingroup group_mars_task_semaphore + * \brief [host/MPU] MARS Task Semaphore Types + */ + +/** + * \ingroup group_mars_task_semaphore + * \brief Maximum task accesses allowed for single semaphore + */ +#define MARS_TASK_SEMAPHORE_WAIT_MAX 54 + +/** + * \ingroup group_mars_task_semaphore + * \brief MARS task semaphore structure + * + * An instance of this structure must be created when using any of the + * MARS semaphore API. + */ +struct mars_task_semaphore; + +#endif diff --git a/common/libspumars/include/common/mars/task_types.h b/common/libspumars/include/common/mars/task_types.h new file mode 100644 index 00000000..38f144ea --- /dev/null +++ b/common/libspumars/include/common/mars/task_types.h @@ -0,0 +1,36 @@ +#ifndef __MARS_TASK_TYPES_H__ +#define __MARS_TASK_TYPES_H__ + +#include + +#include + +#define MARS_TASK_BASE_ADDR 0x4000 +#define MARS_TASK_CONTEXT_SAVE_SIZE_MAX 0x3c000 +#define MARS_TASK_NAME_LEN_MAX 21 + +#ifdef __cplusplus +extern "C" { +#endif + + +struct mars_task_id { + uint64_t mars_context_ea; + uint16_t workload_id; + uint8_t name[MARS_TASK_NAME_LEN_MAX + 1]; +}; + +struct mars_task_args { + union { + uint8_t u8[16]; + uint16_t u16[8]; + uint32_t u32[4]; + uint64_t u64[2]; + } type; +}; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/common/mars/workload_types.h b/common/libspumars/include/common/mars/workload_types.h new file mode 100644 index 00000000..a9bae3ed --- /dev/null +++ b/common/libspumars/include/common/mars/workload_types.h @@ -0,0 +1,38 @@ +#ifndef __MARS_WORKLOAD_TYPES_H__ +#define __MARS_WORKLOAD_TYPES_H__ + +#include + +#define MARS_WORKLOAD_MODULE_BASE_ADDR 0x2a00 +#define MARS_WORKLOAD_MODULE_NAME_LEN_MAX 23 +#define MARS_WORKLOAD_RESERVED_SIZE 128 +#define MARS_WORKLOAD_CONTEXT_SIZE 256 +#define MARS_WORKLOAD_CONTEXT_ALIGN 128 + +#ifdef __cplusplus +extern "C" { +#endif + +struct mars_workload_context +{ + uint8_t reserved[MARS_WORKLOAD_RESERVED_SIZE]; + uint8_t data[MARS_WORKLOAD_CONTEXT_SIZE - MARS_WORKLOAD_RESERVED_SIZE]; +} __attribute__((aligned(MARS_WORKLOAD_CONTEXT_ALIGN))); + +enum mars_workload_query +{ + MARS_WORKLOAD_QUERY_IS_MODULE_CACHED = 0, /**< module cached? */ + MARS_WORKLOAD_QUERY_IS_CONTEXT_CACHED, /**< context cached? */ + MARS_WORKLOAD_QUERY_IS_INITIALIZED, /**< is initialized? */ + MARS_WORKLOAD_QUERY_IS_READY, /**< is ready? */ + MARS_WORKLOAD_QUERY_IS_WAITING, /**< is waiting? */ + MARS_WORKLOAD_QUERY_IS_RUNNING, /**< is running? */ + MARS_WORKLOAD_QUERY_IS_FINISHED, /**< is finished? */ + MARS_WORKLOAD_QUERY_IS_SIGNAL_SET /**< has signal set? */ +}; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/ppu/mars/base.h b/common/libspumars/include/ppu/mars/base.h new file mode 100644 index 00000000..2084060f --- /dev/null +++ b/common/libspumars/include/ppu/mars/base.h @@ -0,0 +1,261 @@ +#ifndef __MARS_BASE_H__ +#define __MARS_BASE_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_base + * \brief [host] Allocates memory in host storage. + * + * \param[in] size - size of memory block to allocate + * \return + * void * - pointer to allocated memory block + */ +void *mars_malloc(size_t size); + +/** + * \ingroup group_mars_base + * \brief [host] Re-allocates memory in host storage. + * + * \param[in] ptr - ptr to memory block to re-allocate + * \param[in] size - size of memory block to resize to + * \return + * void * - pointer to re-allocated memory block + */ +void *mars_realloc(void *ptr, size_t size); + +/** + * \ingroup group_mars_base + * \brief [host] Allocates memory on the stack + * + * \param[in] boundary - memory address will be a multiple of boundary + * \param[in] size - size of memory block to allocate + * \return + * void * - pointer to allocated memory block + */ +#define mars_alloca_align(boundary, size) \ + ( (void *)( ( (uintptr_t)alloca((size) + (boundary) - 1) + \ + (boundary) - 1) & \ + ~(uintptr_t)((boundary) - 1) ) ) + +/** + * \ingroup group_mars_base + * \brief [host] Frees memory allocated in host storage. + * + * \param[in] ptr - ptr to memory block to free + */ +void mars_free(void *ptr); + +/** + * \ingroup group_mars_base + * \brief [host] Allocates memory in shared storage accessible from MPU. + * + * \param[in] boundary - memory address will be a multiple of boundary + * \param[in] size - size of memory block to allocate + * \return + * uint64_t - 64-bit address of allocated memory block + */ +uint64_t mars_ea_memalign(size_t boundary, size_t size); + +/** + * \ingroup group_mars_base + * \brief [host] Frees memory allocated in shared storage. + * + * \param[in] ea - 64-bit address of allocated memory block to free + */ +void mars_ea_free(uint64_t ea); + +/** + * \ingroup group_mars_base + * \brief [host] Copy memory block from shared memory to host memory. + * + * \param[in] ea - 64-bit address of source + * \param[in] ptr - pointer to destination + * \param[in] size - size of memory block to copy + */ +void mars_ea_get(uint64_t ea, void *ptr, size_t size); + +/** + * \ingroup group_mars_base + * \brief [host] Get 8-bit integer value from shared memory + * + * \param[in] ea - 64-bit address of source + * + * \return + * uint8_t - 8-bit result, no guarantee that it is loaded atomically + */ +uint8_t mars_ea_get_uint8(uint64_t ea); + +/** + * \ingroup group_mars_base + * \brief [host] Get 16-bit integer value from shared memory + * + * \param[in] ea - 64-bit address of source + * + * \return + * uint16_t - 16-bit result, no guarantee that it is loaded atomically + */ +uint16_t mars_ea_get_uint16(uint64_t ea); + +/** + * \ingroup group_mars_base + * \brief [host] Get 32-bit integer value from shared memory atomically + * + * \param[in] ea - 64-bit address of source + * + * \return + * uint32_t - 32-bit result + */ + +uint32_t mars_ea_get_uint32(uint64_t ea); + +/** + * \ingroup group_mars_base + * \brief [host] Get 64-bit integer value from shared memory + * + * \param[in] ea - 64-bit address of source + * + * \return + * uint64_t - 64-bit result, no guarantee that it is loaded atomically + */ +uint64_t mars_ea_get_uint64(uint64_t ea); + +/** + * \ingroup group_mars_base + * \brief [host] Copy memory block from host memory to shared memory. + * + * \param[in] ea - 64-bit address of destination + * \param[in] ptr - pointer to source + * \param[in] size - size of memory block to copy + */ +void mars_ea_put(uint64_t ea, const void *ptr, size_t size); + +/** + * \ingroup group_mars_base + * \brief [host] Put 8-bit integer value to shared memory atomically + * + * \param[in] ea - 64-bit address of destination + * \param[in] value - 8-bit value to be stored in shared memory, no guarantee that it is stored atomically + */ +void mars_ea_put_uint8(uint64_t ea, uint8_t value); + +/** + * \ingroup group_mars_base + * \brief [host] Put 16-bit integer value to shared memory atomically + * + * \param[in] ea - 64-bit address of destination + * \param[in] value - 16-bit value to be stored in shared memory, no guarantee that it is stored atomically + */ +void mars_ea_put_uint16(uint64_t ea, uint16_t value); + +/** + * \ingroup group_mars_base + * \brief [host] Put 32-bit integer value to shared memory atomically + * + * \param[in] ea - 64-bit address of destination + * \param[in] value - 32-bit value to be stored in shared memory + */ +void mars_ea_put_uint32(uint64_t ea, uint32_t value); + +/** + * \ingroup group_mars_base + * \brief [host] Put 64-bit integer value to shared memory atomically + * + * \param[in] ea - 64-bit address of destination + * \param[in] value - 64-bit value to be stored in shared memory, no guarantee that it is stored atomically + */ +void mars_ea_put_uint64(uint64_t ea, uint64_t value); + +/** + * \ingroup group_mars_base + * \brief [host] Enable read access to data in specified memory block from MPU + * + * \param[in] ptr - pointer to source + * \param[in] size - size of memory block + * + * \return + * uint64_t - 64-bit address of memory block + */ +uint64_t mars_ea_map(void *ptr, size_t size); + +/** + * \ingroup group_mars_base + * \brief [host] Disable data access enabled by mars_ea_map + * + * \param[in] ea - 64-bit address of memory block + * \param[in] size - size of memory block + */ +void mars_ea_unmap(uint64_t ea, size_t size); + +/* test_cond: + * MARS_SUCCESS: the condition is satisfied + * <0: the condition is not satisfied + * >0: error code + */ +int mars_ea_cond_wait(uint64_t watch_point_ea, + int (*test_cond)(uint32_t , void *), + void *test_cond_param); + +int mars_ea_cond_signal(uint64_t watch_point_ea, int broadcast); + +#ifdef MARS_ENABLE_DISCRETE_SHARED_MEMORY +# define mars_ea_work_area_get(ea, boundary, size) mars_alloca_align(boundary, size) +#else /* !MARS_ENABLE_DISCRETE_SHARED_MEMORY */ + /* work area is unnecessary for non-discrete shared memory model */ +# define mars_ea_work_area_get(ea, boundary, size) mars_ea_to_ptr(ea) +#endif /* !MARS_ENABLE_DISCRETE_SHARED_MEMORY */ + +/** + * \ingroup group_mars_base + * \brief [host] Memory barrier + */ +void mars_ea_sync(void); + +/** + * \ingroup group_mars_base + * \brief [host] Converts a 64-bit address to pointer. + * + * \param[in] ea - 64-bit address + * \return + * void * - pointer value + */ +static inline void *mars_ea_to_ptr(uint64_t ea) +{ + return (void *)(uintptr_t)ea; +} + +/** + * \ingroup group_mars_base + * \brief [host] Converts a pointer to 64-bit address. + * + * \param[in] ptr - pointer value + * \return + * uint64_t - 64-bit address + */ +static inline uint64_t mars_ptr_to_ea(const void *ptr) +{ + return (uint64_t)(const uintptr_t)ptr; +} + +/** + * \ingroup group_mars_base + * \brief [host/MPU] Returns tick counter value. + * + * \note Counter's frequency depends on runtime environment. + * + * \return + * uint32_t - 32-bit tick counter value + */ +uint32_t mars_get_ticks(void); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/ppu/mars/context.h b/common/libspumars/include/ppu/mars/context.h new file mode 100644 index 00000000..5b3495e4 --- /dev/null +++ b/common/libspumars/include/ppu/mars/context.h @@ -0,0 +1,95 @@ +#ifndef __MARS_CONTEXT_H__ +#define __MARS_CONTEXT_H__ + +#include + +/** + * \ingroup group_mars_context + * \brief MARS context structure + * + * An instance of this structure must be created and initialized before + * using any of the MARS API. + */ +struct mars_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_context + * \brief [host] Creates a single MARS context. + * + * This function creates a single MARS context. A MARS context must be + * created before any of the MARS functionality can be used. This will + * create the MPU contexts that are each loaded with and run the MARS kernel. + * The MARS kernel on each MPU will continue to run until the MARS context + * is destroyed through \ref mars_context_destroy. + * + * Key Parameters: + * \n \n + * \e num_mpus + * - Specify total number of MPUs to be used by the MARS context + * - If 0 is specified, MARS will use the maximum number of MPUs available in + * the system. + * + * \e shared + * - Specify 1 to share the context with other libraries linked into the + * application that also utilize MARS. + * - Specify 0 to create an independent MARS context that is not shared with + * other libraries linked into the application that also utilize MARS. + * - Sharing a single MARS context within an application with other libraries + * will maximize the MARS benefits of MPU utilization. + * + * \note If there are multiple MARS contexts created in the system, then + * each MARS context will suffer the large over head of MPU context switches. + * + * \param[out] mars - address of pointer to MARS context + * \param[in] num_mpus - number of mpus requested by MARS context + * \param[in] spu_prio - priority of SPU thread group + * \param[in] ppuPriority - priority of MARS handler PPU thread + * \return + * MARS_SUCCESS - successfully created MARS context + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_LIMIT - no more available MPUs or system threads + * \n MARS_ERROR_PARAMS - more MPUs requested than there are available + * \n MARS_ERROR_MEMORY - not enough memory + * \n MARS_ERROR_INTERNAL - some internal error occurred + */ +int mars_context_create(struct mars_context **mars, uint32_t num_mpus, uint32_t spu_prio, uint32_t ppu_prio); + +/** + * \ingroup group_mars_context + * \brief [host] Destroys a single MARS context. + * + * This function destroys a single MARS context that was previously + * created by \ref mars_context_create. In order to successfully destroy + * a MARS context, all workloads added to the workload queue must be + * completed and destroyed so that the workload queue is empty. + * + * \param[in] mars - pointer to MARS context + * \return + * MARS_SUCCESS - successfully destroyed MARS context + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_STATE - workload queue is not empty + */ +int mars_context_destroy(struct mars_context *mars); + +/** + * \ingroup group_mars_context + * \brief [host] Returns number of MPUs allocated for MARS context. + * + * This function returns the number of MPUs allocated for the MARS context. + * + * \param[in] mars - pointer to MARS context + * \return + * non-zero - number of MPUs + * \n 0 - invalid MARS context + */ +uint32_t mars_context_get_num_mpus(struct mars_context *mars); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/ppu/mars/mutex.h b/common/libspumars/include/ppu/mars/mutex.h new file mode 100644 index 00000000..92d35a1e --- /dev/null +++ b/common/libspumars/include/ppu/mars/mutex.h @@ -0,0 +1,130 @@ +#ifndef __MARS_MUTEX_H__ +#define __MARS_MUTEX_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_mutex + * \brief [host] Creates a mutex. + * + * This function creates a mutex instance that can be locked or unlocked + * from both host and MPU to restrict concurrent accesses. + * + * \param[in] mutex_ea - ea of mutex instance + * \return + * MARS_SUCCESS - successfully created mutex + * \n MARS_ERROR_NULL - null pointer is specified + * \n MARS_ERROR_MEMORY - instance not aligned properly + */ +int mars_mutex_create(uint64_t *mutex_ea); + +/** + * \ingroup group_mars_mutex + * \brief [host] Destroys a mutex. + * + * This function destroys a mutex instance. + * + * \param[in] mutex_ea - ea of mutex instance + * \return + * MARS_SUCCESS - successfully destroyed mutex + * \n MARS_ERROR_NULL - null pointer is specified + * \n MARS_ERROR_ALIGN - instance not aligned properly + */ +int mars_mutex_destroy(uint64_t mutex_ea); + +/** + * \ingroup group_mars_mutex + * \brief [host] Resets a mutex. + * + * This function resets a mutex instance and forces it into an unlocked state + * regardless of whether it is locked or unlocked. + * + * \param[in] mutex_ea - ea of mutex instance + * \return + * MARS_SUCCESS - successfully reset mutex + * \n MARS_ERROR_NULL - null pointer is specified + * \n MARS_ERROR_ALIGN - instance not aligned properly + */ +int mars_mutex_reset(uint64_t mutex_ea); + +/** + * \ingroup group_mars_mutex + * \brief [host] Locks a mutex. + * + * This function locks a mutex and blocks other requests to lock it. + * + * \param[in] mutex_ea - ea of mutex instance + * \return + * MARS_SUCCESS - successfully locked mutex + * \n MARS_ERROR_NULL - null pointer is specified + * \n MARS_ERROR_ALIGN - instance not aligned properly + */ +int mars_mutex_lock(uint64_t mutex_ea); + +/** + * \ingroup group_mars_mutex + * \brief [host] Unlocks a mutex. + * + * This function unlocks a previously locked mutex to allow other lock requests. + * + * \param[in] mutex_ea - ea of mutex instance + * \return + * MARS_SUCCESS - successfully unlocked mutex + * \n MARS_ERROR_NULL - null pointer is specified + * \n MARS_ERROR_ALIGN - instance not aligned properly + * \n MARS_ERROR_STATE - instance not in locked state + */ +int mars_mutex_unlock(uint64_t mutex_ea); + +/** + * \ingroup group_mars_mutex + * \brief [host] Locks a mutex. + * + * This function locks a mutex and blocks other requests to lock it. + * It also loads the mutex instance from the effective address specified + * into the local mutex instance. + * + * \note This call should only be used when MARS_ENABLE_DISCRETE_SHARED_MEMORY + * is enabled. Otherwise, the mutex parameter is ignored and the function + * behaves the same as \ref mars_mutex_lock. + * + * \param[in] mutex_ea - ea of mutex instance to lock + * \param[in] mutex - pointer to local mutex instance + * \return + * MARS_SUCCESS - successfully locked mutex + * \n MARS_ERROR_NULL - ea is 0 or mutex is NULL + * \n MARS_ERROR_ALIGN - ea or mutex not aligned properly + */ +int mars_mutex_lock_get(uint64_t mutex_ea, struct mars_mutex *mutex); + +/** + * \ingroup group_mars_mutex + * \brief [host] Unlocks a mutex. + * + * This function unlocks a previously locked mutex to allow other lock requests. + * It also stores the local mutex instance into the effective address specified. + * + * \note This call should only be used when MARS_ENABLE_DISCRETE_SHARED_MEMORY + * is enabled. Otherwise, the mutex parameter is ignored and the function + * behaves the same as \ref mars_mutex_unlock. + * + * \param[in] mutex_ea - ea of mutex instance to unlock + * \param[in] mutex - pointer to local mutex instance + * \return + * MARS_SUCCESS - successfully unlocked mutex + * \n MARS_ERROR_NULL - ea is 0 or mutex is NULL + * \n MARS_ERROR_ALIGN - ea or mutex not aligned properly + * \n MARS_ERROR_STATE - instance not in locked state + */ +int mars_mutex_unlock_put(uint64_t mutex_ea, struct mars_mutex *mutex); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/ppu/mars/task.h b/common/libspumars/include/ppu/mars/task.h new file mode 100644 index 00000000..1c7437bc --- /dev/null +++ b/common/libspumars/include/ppu/mars/task.h @@ -0,0 +1,132 @@ +#ifndef __MARS_TASK_H__ +#define __MARS_TASK_H__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_task + * \brief [host] Creates a MARS task. + * + * This function creates a single task and adds it the MARS context's + * workload queue. + * Upon success, a valid task id will be returned. + * You must call \ref mars_task_schedule in order for it to be scheduled for + * execution by the kernel. + * The task is in the finished state upon creation and may be destroyed by + * \ref mars_task_destroy without ever being scheduled for execution. + * + * Key Parameters: + * \n \n + * \e name + * - The name is optional, but if specified to other than NULL its + * length must not exceed \ref MARS_TASK_NAME_LEN_MAX. + * - An empty string will be treated the same as passing NULL, and will return + * NULL if \ref mars_task_get_name is called on the task. + * - The name does need to be kept allocated after this function returns. + + * \e elf_image + * - The address of MPU program elf image in host storage that will be run + * by this task. + * - The elf image must remain allocated until the task is destroyed. + + * \e context_save_size + * - If 0 is specified, then no context save area will be allocated for the task + * and therefore the task must be a run-complete task. + * - A run-complete task will run and occupy an MPU until it has completed + * running and exits. + * - A run-complete task cannot context switch, and therefore cannot call + * functions that will enter that task into a wait state. + * - If \ref MARS_TASK_CONTEXT_SAVE_SIZE_MAX is specified, the maximum size + * context save area required will be allocated and all of the MPU storage area + * used by the task will be saved and restored. + * - Advanced users can specify a size between 0 and + * \ref MARS_TASK_CONTEXT_SAVE_SIZE_MAX in order to minimize memory usage for + * the context save area. + * - The size specified must be large enough to hold all of the task program's + * text, data, heap, stack and non-volatile registers 80-127. + * + * \param[in] mars - pointer to MARS context + * \param[out] id - address of pointer to task id instance + * \param[in] name - name of task + * \param[in] elf_image - address of MPU program elf image + * \param[in] context_save_size - [ 0 ~ \ref MARS_TASK_CONTEXT_SAVE_SIZE_MAX ] + * \return + * MARS_SUCCESS - successfully created MARS task + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - bad params specified + * \n MARS_ERROR_MEMORY - not enough memory + * \n MARS_ERROR_LIMIT - task queue is currently full + */ +int mars_task_create(struct mars_context *mars, + struct mars_task_id *id, + const char *name, + const void *elf_image, + uint32_t context_save_size); + +/** + * \ingroup group_mars_task + * \brief [host] Destroys a MARS task. + * + * This function destroys a task created by \ref mars_task_create. + * The task will only be destroyed if the task is in the finished state. + * Once this function returns successfully and the task is destroyed, the task + * id is no longer valid. + * To guarantee the task has finished before calling this function, you should + * wait for task completion by calling \ref mars_task_wait or + * \ref mars_task_try_wait. + * + * \param[in] id - pointer to task id instance + * \return + * MARS_SUCCESS - successfully destroyed MARS task + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - bad task id specified + * \n MARS_ERROR_STATE - task is in an invalid state + */ +int mars_task_destroy(struct mars_task_id *id); + +/** + * \ingroup group_mars_task + */ +int mars_task_schedule(const struct mars_task_id *id, + const struct mars_task_args *args, + uint8_t priority); + +/** + * \ingroup group_mars_task + */ +int mars_task_unschedule(const struct mars_task_id *id, int32_t exit_code); + +/** + * \ingroup group_mars_task + */ +int mars_task_wait(const struct mars_task_id *id, int32_t *exit_code); + +/** + * \ingroup group_mars_task + */ +int mars_task_try_wait(const struct mars_task_id *id, int32_t *exit_code); + +/** + * \ingroup group_mars_task + */ +uint32_t mars_task_get_ticks(void); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/ppu/mars/task_barrier.h b/common/libspumars/include/ppu/mars/task_barrier.h new file mode 100644 index 00000000..35cac4de --- /dev/null +++ b/common/libspumars/include/ppu/mars/task_barrier.h @@ -0,0 +1,78 @@ +#ifndef __MARS_TASK_BARRIER_H__ +#define __MARS_TASK_BARRIER_H__ + +#include +#include + +struct mars_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_task_barrier + * \brief [host] Creates a task barrier. + * + * This function will allocate an instance of the task barrier. + * The barrier allows for tasks within the barrier group to wait until all + * tasks arrive at some synchronization point and notify the barrier. + * All tasks included in the barrier group should call the notify and wait in + * pairs. + * + * Key Parameters: + * \n \n + * \e total + * - Specify total number of tasks that will be associated with this barrier. + * - Total must be a value between 1 and \ref MARS_TASK_BARRIER_WAIT_MAX. + * + * \param[in] mars - pointer to MARS context + * \param[out] barrier_ea - ea of barrier instance + * \param[in] total - number of notifies before barrier released + * \return + * MARS_SUCCESS - successfully created barrier + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - total is 0 or exceeds allowed limit + */ +int mars_task_barrier_create(struct mars_context *mars, + uint64_t *barrier_ea, + uint32_t total); + + +/** + * \ingroup group_mars_task_barrier + * \brief [host] Initialize a task barrier. + * + * This function will initialize a task barrier to a new number + * of notifies before barrier release. + * + * \param[in] barrier_ea - ea of barrier instance + * \param[in] total - number of notifies before barrier released + * \return + * MARS_SUCCESS - successfully destroyed barrier + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - total is 0 or exceeds allowed limit + */ +int mars_task_barrier_initialize(uint64_t barrier_ea, + uint32_t total); +/** + * \ingroup group_mars_task_barrier + * \brief [host] Destroys a task barrier. + * + * This function will free any resources allocated during creation of the task + * barrier. + * + * \param[in] barrier_ea - ea of barrier instance + * \return + * MARS_SUCCESS - successfully destroyed barrier + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_ALIGN - ea not properly aligned + * \n MARS_ERROR_STATE - tasks still waiting + */ +int mars_task_barrier_destroy(uint64_t barrier_ea); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/ppu/mars/task_event_flag.h b/common/libspumars/include/ppu/mars/task_event_flag.h new file mode 100644 index 00000000..b08bc474 --- /dev/null +++ b/common/libspumars/include/ppu/mars/task_event_flag.h @@ -0,0 +1,96 @@ +#ifndef __MARS_TASK_EVENT_FLAG_H__ +#define __MARS_TASK_EVENT_FLAG_H__ + +#include +#include + +struct mars_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_task_event_flag + * \brief [host] Creates a task event flag. + * + * This function will allocate an instance of the task event flag. + * The event flag allows for tasks to wait on specific events. + * The event flag should be used in pairs with calls to set an event flag and + * wait for an event flag. + * + * Key Parameters: + * \n \n + * \e direction + * - Specify the communication direction of the event flag. + * - Must be one of \ref MARS_TASK_EVENT_FLAG_HOST_TO_MPU, + * \ref MARS_TASK_EVENT_FLAG_MPU_TO_HOST, \ref MARS_TASK_EVENT_FLAG_MPU_TO_MPU. + * + * \e clear_mode + * - Specify when the event flag is cleared. + * - Specify \ref MARS_TASK_EVENT_FLAG_CLEAR_AUTO to have event flag bits + * cleared automatically when a task waiting on the event flag bits has been + * notified of the event. + * - Specify \ref MARS_TASK_EVENT_FLAG_CLEAR_MANUAL to have the event flag bits + * remain set until the user manually clears them through + * \ref mars_task_event_flag_clear. + * + * \param[in] mars - pointer to MARS context + * \param[out] event_flag_ea - ea of event flag instance + * \param[in] direction - direction of the event flag + * \param[in] clear_mode - behavior of how the event flag is cleared + * \return + * MARS_SUCCESS - successfully created event flag + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - invalid direction or clear mode specified + */ +int mars_task_event_flag_create(struct mars_context *mars, + uint64_t *event_flag_ea, + uint8_t direction, + uint8_t clear_mode); + +/** + * \ingroup group_mars_task_event_flag + * \brief [host] Destroys a task event flag. + * + * This function will free any resources allocated during creation of the task + * event flag. + * + * \param[in] event_flag_ea - ea of event flag instance + * \return + * MARS_SUCCESS - successfully destroyed event flag + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_ALIGN - ea not properly aligned + * \n MARS_ERROR_STATE - tasks still waiting + */ +int mars_task_event_flag_destroy(uint64_t event_flag_ea); + +/** + * \ingroup group_mars_task_event_flag + */ +int mars_task_event_flag_clear(uint64_t event_flag_ea, uint32_t bits); + +/** + * \ingroup group_mars_task_event_flag + */ +int mars_task_event_flag_set(uint64_t event_flag_ea, uint32_t bits); + +/** + * \ingroup group_mars_task_event_flag + */ +int mars_task_event_flag_wait(uint64_t event_flag_ea, + uint32_t mask, uint8_t mask_mode, + uint32_t *bits); + +/** + * \ingroup group_mars_task_event_flag + */ +int mars_task_event_flag_try_wait(uint64_t event_flag_ea, + uint32_t mask, uint8_t mask_mode, + uint32_t *bits); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/ppu/mars/task_queue.h b/common/libspumars/include/ppu/mars/task_queue.h new file mode 100644 index 00000000..a405be85 --- /dev/null +++ b/common/libspumars/include/ppu/mars/task_queue.h @@ -0,0 +1,120 @@ +#ifndef __MARS_TASK_QUEUE_H__ +#define __MARS_TASK_QUEUE_H__ + +#include +#include + +struct mars_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_task_queue + * \brief [host] Creates a task queue. + * + * This function will allocate an instance of the task queue. + * The queue allows for tasks to wait until data is available from a FIFO + * data queue. + * The queue should be used in pairs with various calls to push and pop the + * the queue + * + * Key Parameters: + * \n \n + * \e size + * - Specify the size of each data item in the queue. + * - The size needs to be a multiple of 16 bytes and no larger than + * \ref MARS_TASK_QUEUE_ENTRY_SIZE_MAX. + * + * \e depth + * - Specify the depth of the total queue. + * - The number specified here is the total number of data items the queue + * can hold at a time. + * - The size of the total queue is \e size * \e depth. + * + * \e direction + * - Specify the communication direction of the queue. + * - Must be one of \ref MARS_TASK_QUEUE_HOST_TO_MPU, + * \ref MARS_TASK_QUEUE_MPU_TO_HOST, \ref MARS_TASK_QUEUE_MPU_TO_MPU. + * + * \param[in] mars - pointer to MARS context + * \param[out] queue_ea - address of 64-bit address of queue instance + * \param[in] size - size of each data item in data buffer + * \param[in] depth - maximum number of data entries in data buffer + * \param[in] direction - direction of the event flag + * \return + * MARS_SUCCESS - successfully created queue + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - size is not multiple of 16 bytes or is greater + * than 16KB maximum + * \n MARS_ERROR_PARAMS - invalid direction specified + * \n MARS_ERROR_MEMORY - not enough memory for queue buffer allocation + */ +int mars_task_queue_create(struct mars_context *mars, + uint64_t *queue_ea, + uint32_t size, + uint32_t depth, + uint8_t direction); + +/** + * \ingroup group_mars_task_queue + * \brief [host] Destroys a task queue. + * + * This function will free any resources allocated during creation of the task + * queue. + * + * \param[in] queue_ea - ea of queue instance + * \return + * MARS_SUCCESS - successfully destroyed queue + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_ALIGN - ea not properly aligned + * \n MARS_ERROR_STATE - tasks still waiting + */ +int mars_task_queue_destroy(uint64_t queue_ea); + +/** + * \ingroup group_mars_task_queue + */ +int mars_task_queue_count(uint64_t queue_ea, uint32_t *count); + +/** + * \ingroup group_mars_task_queue + */ +int mars_task_queue_clear(uint64_t queue_ea); + +/** + * \ingroup group_mars_task_queue + */ +int mars_task_queue_push(uint64_t queue_ea, const void *data); + +/** + * \ingroup group_mars_task_queue + */ +int mars_task_queue_try_push(uint64_t queue_ea, const void *data); + +/** + * \ingroup group_mars_task_queue + */ +int mars_task_queue_pop(uint64_t queue_ea, void *data); + +/** + * \ingroup group_mars_task_queue + */ +int mars_task_queue_try_pop(uint64_t queue_ea, void *data); + +/** + * \ingroup group_mars_task_queue + */ +int mars_task_queue_peek(uint64_t queue_ea, void *data); + +/** + * \ingroup group_mars_task_queue + */ +int mars_task_queue_try_peek(uint64_t queue_ea, void *data); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/ppu/mars/task_semaphore.h b/common/libspumars/include/ppu/mars/task_semaphore.h new file mode 100644 index 00000000..3112f53e --- /dev/null +++ b/common/libspumars/include/ppu/mars/task_semaphore.h @@ -0,0 +1,60 @@ +#ifndef __MARS_TASK_SEMAPHORE_H__ +#define __MARS_TASK_SEMAPHORE_H__ + +#include +#include + +struct mars_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_task_semaphore + * \brief [host] Creates a task semaphore. + * + * This function will allocate an instance of the task semaphore. + * The semaphore allows for tasks to wait until a semaphore can be obtained. + * The semaphore should be used in pairs with calls to acquire and release. + * + * Key Parameters: + * \n \n + * \e count + * - Specify the total number of entities that can have access to the semaphore + * simultaneously. + * - Must not be greater than \ref MARS_TASK_SEMAPHORE_WAIT_MAX. + * + * \param[in] mars - pointer to MARS context + * \param[out] semaphore_ea - ea of semaphore instance + * \param[in] count - initial number of task accesses allowed + * \return + * MARS_SUCCESS - successfully created semaphore + * \n MARS_ERROR_NULL - null pointer is specified + * \n MARS_ERROR_PARAMS - count exceeds allowed limit + */ +int mars_task_semaphore_create(struct mars_context *mars, + uint64_t *semaphore_ea, + int32_t count); + +/** + * \ingroup group_mars_task_semaphore + * \brief [host] Destroys a task semaphore. + * + * This function will free any resources allocated during creation of the task + * semaphore. + * + * \param[in] semaphore_ea - ea of semaphore instance + * \return + * MARS_SUCCESS - successfully destroyed semaphore + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_ALIGN - ea not properly aligned + * \n MARS_ERROR_STATE - tasks still waiting + */ +int mars_task_semaphore_destroy(uint64_t semaphore_ea); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/ppu/mars/task_signal.h b/common/libspumars/include/ppu/mars/task_signal.h new file mode 100644 index 00000000..4381de24 --- /dev/null +++ b/common/libspumars/include/ppu/mars/task_signal.h @@ -0,0 +1,20 @@ +#ifndef __MARS_TASK_SIGNAL_H__ +#define __MARS_TASK_SIGNAL_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_task_signal + */ +int mars_task_signal_send(struct mars_task_id *id); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/ppu/mars/workload_queue.h b/common/libspumars/include/ppu/mars/workload_queue.h new file mode 100644 index 00000000..4860f0c5 --- /dev/null +++ b/common/libspumars/include/ppu/mars/workload_queue.h @@ -0,0 +1,330 @@ +#ifndef __MARS_WORKLOAD_QUEUE_H__ +#define __MARS_WORKLOAD_QUEUE_H__ + +#include +#include + +struct mars_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_workload_queue + * \brief [host] Returns whether or not specified query is satisfied. + * + * \note Query type \ref MARS_WORKLOAD_QUERY_IS_MODULE_CACHED and + * \ref MARS_WORKLOAD_QUERY_IS_CONTEXT_CACHED are only valid queries for the + * MPU-side call to \ref mars_module_workload_query. + * Calling \ref mars_workload_queue_query with these queries will always return + * 0. + * + * \param[in] mars - address of pointer to MARS context + * \param[in] id - id of workload + * \param[in] query - query type + * \return + * int - non-zero if query satisfied + */ +int mars_workload_queue_query(struct mars_context *mars, + uint16_t id, + int query); + +/** + * \ingroup group_mars_workload_queue + * \brief [host] Begins adding workload to workload queue. + * + * This function will begin the process to add a workload to the workload queue. + * This only initiates the add operation. + * This function must be completed with a matching call to + * \ref mars_workload_queue_add_end to guarantee the completion of the add + * operation. + * + * If workload_ea is not NULL, the ea of the workload will be returned. + * + * The workload adding process is not completed until the matching call to + * \ref mars_workload_queue_add_end is made. + * The user should make any necessary updates to the returned workload context + * in between this begin call and the end call. + * + * \param[in] mars - address of pointer to MARS context + * \param[out] id - pointer to return workload id + * \param[out] workload_ea - address of pointer to workload context ea + * \param[in] module_elf - pointer to workload module elf image + * \param[in] module_name - name of module + * \return + * MARS_SUCCESS - workload adding started + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - invalid mars context or module specified + * \n MARS_ERROR_LIMIT - workload queue is full + */ +int mars_workload_queue_add_begin(struct mars_context *mars, + uint16_t *id, + uint64_t *workload_ea, + const void *module_elf, + const char *module_name); + +/** + * \ingroup group_mars_workload_queue + * \brief [host] Ends adding of specified workload. + * + * This function will complete a add operation previously initiated with + * \ref mars_workload_queue_add_begin. + * This function must be called in pair for each call to + * \ref mars_workload_queue_add_begin to guarantee the completion of the + * initiated add operation. + * + * \param[in] mars - address of pointer to MARS context + * \param[in] id - id of workload to end add + * \param[in] cancel - cancels the add operation + * \return + * MARS_SUCCESS - workload adding complete + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - invalid mars context or workload id specified + * \n MARS_ERROR_STATE - workload adding not started + */ +int mars_workload_queue_add_end(struct mars_context *mars, + uint16_t id, + int cancel); + +/** + * \ingroup group_mars_workload_queue + * \brief [host] Begins removing workload from workload queue. + * + * This function will begin the process to remove a workload from the workload + * queue. + * This only initiates the remove operation. + * This function must be completed with a matching call to + * \ref mars_workload_queue_remove_end to guarantee the completion of the remove + * operation. + * + * If workload_ea is not NULL, the ea of the workload will be returned. + * + * The workload removing process is not completed until the matching call to + * \ref mars_workload_queue_remove_end is made. + * The user should make any necessary updates to the returned workload context + * in between this begin call and the end call. + * + * \param[in] mars - address of pointer to MARS context + * \param[in] id - id of workload to begin remove + * \param[out] workload_ea - address of pointer to workload context ea + * \return + * MARS_SUCCESS - workload removing started + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - invalid mars context specified + * \n MARS_ERROR_STATE - specified workload not added or finished + */ +int mars_workload_queue_remove_begin(struct mars_context *mars, + uint16_t id, + uint64_t *workload_ea); + +/** + * \ingroup group_mars_workload_queue + * \brief [host] Ends removing of specified workload. + * + * This function will complete a remove operation previously initiated with + * \ref mars_workload_queue_remove_begin. + * This function must be called in pair for each call to + * \ref mars_workload_queue_remove_begin to guarantee the completion of the + * initiated remove operation. + * + * \param[in] mars - address of pointer to MARS context + * \param[in] id - id of workload + * \param[in] cancel - cancels the remove operation + * \return + * MARS_SUCCESS - workload removing complete + * \n MARS_ERROR_PARAMS - invalid mars context or workload id specified + * \n MARS_ERROR_STATE - workload removing not started + */ +int mars_workload_queue_remove_end(struct mars_context *mars, + uint16_t id, + int cancel); + +/** + * \ingroup group_mars_workload_queue + * \brief [host] Begins scheduling of specified workload. + * + * This function will begin scheduling the workload specified. + * This only initiates the scheduling of the workload. + * This function must be completed with a matching call to + * \ref mars_workload_queue_schedule_end to guarantee the completion of the + * scheduling. + * + * If workload_ea is not NULL, the ea of the workload will be returned. + * + * The workload scheduling process is not completed until the matching call to + * \ref mars_workload_queue_schedule_end is made. + * The user should make any necessary updates to the returned workload context + * in between this begin call and the end call. + * + * \param[in] mars - address of pointer to MARS context + * \param[in] id - id of workload + * \param[in] priority - scheduling priority of workload + * \param[out] workload_ea - address of pointer to workload context ea + * \return + * MARS_SUCCESS - workload scheduling started + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - invalid mars context or workload id specified + * \n MARS_ERROR_STATE - specified workload not added or finished + */ +int mars_workload_queue_schedule_begin(struct mars_context *mars, + uint16_t id, uint8_t priority, + uint64_t *workload_ea); + +/** + * \ingroup group_mars_workload_queue + * \brief [host] Ends scheduling of specified workload. + * + * This function will complete a schedule operation previously initiated with + * \ref mars_workload_queue_schedule_begin. + * This function must be called in pair for each call to + * \ref mars_workload_queue_schedule_begin to guarantee the completion of the + * initiated schedule operation. + * + * \param[in] mars - address of pointer to MARS context + * \param[in] id - id of workload + * \param[in] cancel - cancels the schedule operation + * \return + * MARS_SUCCESS - workload scheduling complete + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - invalid mars context or workload id specified + * \n MARS_ERROR_STATE - workload scheduling not started + */ +int mars_workload_queue_schedule_end(struct mars_context *mars, + uint16_t id, + int cancel); + +/** + * \ingroup group_mars_workload_queue + * \brief [host] Begins unscheduling of specified workload. + * + * This function will begin unscheduling the workload specified. + * This only initiates the unscheduling of the workload. + * This function must be completed with a matching call to + * \ref mars_workload_queue_unschedule_end to guarantee the completion of the + * unscheduling. + * + * If workload_ea is not NULL, the ea of the workload will be returned. + * + * The workload unscheduling process is not completed until the matching call to + * \ref mars_workload_queue_unschedule_end is made. + * The user should make any necessary updates to the returned workload context + * in between this begin call and the end call. + * + * When a workload is unscheduled, it will be put into a finished state and any + * entities waiting on the workload to finish will be resumed. + * + * If a scheduled workload is unscheduled before execution, the workload will + * not be executed until a subsequent scheduling request is made. + * + * If the workload is currently in a waiting state, calling unschedule will + * finish the workload and will not be resumed from the waiting state. + * + * If the workload is currently in a running state, calling unschedule will + * immediately put the workload into a finished state. However, execution of the + * workload will only be suspended when the workload yields, waits, or finishes. + * + * \note + * Trying to unschedule a workload that has not yet been scheduled, or has + * already finished a previously scheduled execution will return an error. + * + * \param[in] mars - address of pointer to MARS context + * \param[in] id - id of workload + * \param[out] workload_ea - address of pointer to workload context ea + * \return + * MARS_SUCCESS - workload aborted + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - invalid mars context or workload id specified + * \n MARS_ERROR_STATE - workload is not scheduled or has finished + */ +int mars_workload_queue_unschedule_begin(struct mars_context *mars, + uint16_t id, + uint64_t *workload_ea); + +/** + * \ingroup group_mars_workload_queue + * \brief [host] Ends unscheduling of specified workload. + * + * This function will complete an unschedule operation previously initiated with + * \ref mars_workload_queue_unschedule_begin. + * This function must be called in pair for each call to + * \ref mars_workload_queue_unschedule_begin to guarantee the completion of the + * initiated unschedule operation. + * + * \param[in] mars - address of pointer to MARS context + * \param[in] id - id of workload + * \return + * MARS_SUCCESS - workload unscheduling complete + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - invalid mars context or workload id specified + * \n MARS_ERROR_STATE - workload unscheduling not started + */ +int mars_workload_queue_unschedule_end(struct mars_context *mars, + uint16_t id); + +/** + * \ingroup group_mars_workload_queue + * \brief [host] Waits for specified workload to finish. + * + * This function will block and wait until the specified workload finishes. + * + * If workload_ea is not NULL, the ea of the workload will be returned. + * + * \param[in] mars - address of pointer to MARS context + * \param[in] id - id of workload + * \param[out] workload_ea - address of pointer to workload context ea + * \return + * MARS_SUCCESS - workload is finished + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - invalid mars context or workload id specified + * \n MARS_ERROR_STATE - invalid workload specified + */ +int mars_workload_queue_wait(struct mars_context *mars, + uint16_t id, + uint64_t *workload_ea); + +/** + * \ingroup group_mars_workload_queue + * \brief [host] Waits for specified workload to finish. + * + * This function will check whether the workload specified is finished or not + * and return immediately without blocking. + * + * If workload_ea is not NULL, the ea of the workload will be returned. + * + * \param[in] mars - address of pointer to MARS context + * \param[in] id - id of workload + * \param[out] workload_ea - address of pointer to workload context ea + * \return + * MARS_SUCCESS - workload is finished + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - invalid mars context or workload id specified + * \n MARS_ERROR_STATE - invalid workload specified + * \n MARS_ERROR_BUSY - workload has not yet finished + */ +int mars_workload_queue_try_wait(struct mars_context *mars, + uint16_t id, + uint64_t *workload_ea); + +/** + * \ingroup group_mars_workload_queue + * \brief [host] Sends signal to specified workload. + * + * This function will send a signal to the specified workload. + * + * \param[in] mars - address of pointer to MARS context + * \param[in] id - id of workload + * \return + * MARS_SUCCESS - signalled workload + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - invalid mars context or workload id specified + * \n MARS_ERROR_STATE - invalid workload specified + */ +int mars_workload_queue_signal_send(struct mars_context *mars, + uint16_t id); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/spu/mars/module.h b/common/libspumars/include/spu/mars/module.h new file mode 100644 index 00000000..46a70cb9 --- /dev/null +++ b/common/libspumars/include/spu/mars/module.h @@ -0,0 +1,408 @@ +#ifndef __MARS_MODULE_H__ +#define __MARS_MODULE_H__ + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Entry point for workload module. + * + * This function is the main entry point for the workload module. All workload + * modules will need to have a definition of this function. This function is + * called from the MARS kernel when a workload context that specifies this + * workload module is scheduled for execution. + * + * \note Returning from this function is equivalent to calling + * \ref mars_module_workload_finish. + */ +void mars_module_main(void); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Gets tick counter value. + * + * \note Counter's frequency depends on runtime environment. + * + * \return + * uint32_t - 32-bit tick counter value + */ +uint32_t mars_module_get_ticks(void); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Gets ea of MARS context. + * + * \return + * uint64_t - ea of MARS context + */ +uint64_t mars_module_get_mars_context_ea(void); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Gets id of kernel that the module is being executed on. + * + * \return + * uint16_t - id of MARS kernel + */ +uint16_t mars_module_get_kernel_id(void); + + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Gets id of current workload context. + * + * \return + * uint16_t - id of workload + */ +uint16_t mars_module_get_workload_id(void); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Gets pointer to current workload context. + * + * \return + * struct mars_workload_context * - pointer to current workload context + */ +struct mars_workload_context *mars_module_get_workload(void); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Gets pointer to workload context specified by id. + * + * \param[in] id - id of workload + * \return + * struct mars_workload_context * - pointer to specified workload context + */ +struct mars_workload_context *mars_module_get_workload_by_id(uint16_t id); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Returns whether or not specified query is satisfied. + * + * \param[in] id - id of workload + * \param[in] query - query type + * \return + * int - non-zero if query satisfied + + */ +int mars_module_workload_query(uint16_t id, int query); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Sets calling workload to wait for completion of specified workload. + * + * \note This function only sets the id of workload to wait for completion. + * The caller should also \ref mars_module_workload_wait immediately after this + * call so the calling workload yields execution and enters the waiting state. + * + * \param[in] id - id of workload + * \return + * MARS_SUCCESS - id of workload to wait for set + * \n MARS_ERROR_PARAMS - invalid workload id specified + */ +int mars_module_workload_wait_set(uint16_t id); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Sets calling workload to not wait for completion of any workloads. + * + * \return + * MARS_SUCCESS - id of workload to wait for reset + * \n MARS_ERROR_PARAMS - invalid workload id specified + */ +int mars_module_workload_wait_reset(void); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Sets signal for specified workload. + * + * \param[in] id - id of workload + * \return + * MARS_SUCCESS - signal set + * \n MARS_ERROR_PARAMS - invalid workload id specified + */ +int mars_module_workload_signal_set(uint16_t id); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Resets signal for specified workload. + * + * \return + * MARS_SUCCESS - signal reset + * \n MARS_ERROR_PARAMS - invalid workload id specified + */ +int mars_module_workload_signal_reset(void); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Begins scheduling of specified workload. + * + * This function will begin scheduling the workload specified. + * This only initiates the scheduling of the workload. + * This function must be completed with a matching call to + * \ref mars_module_workload_schedule_end to guarantee the completion of the + * scheduling. + * + * The workload scheduling process is not complete until the matching call to + * \ref mars_module_workload_schedule_end is made. + * The user should make any necessary updates to the returned workload context + * in between this begin call and the end call. + * + * \param[in] id - id of workload + * \param[in] priority - scheduling priority of workload + * \param[out] workload - address of pointer to workload context + * \return + * MARS_SUCCESS - workload scheduling started + * \n MARS_ERROR_PARAMS - invalid workload id specified + * \n MARS_ERROR_STATE - specified workload not added or finished + */ +int mars_module_workload_schedule_begin(uint16_t id, uint8_t priority, + struct mars_workload_context **workload); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Ends scheduling of specified workload. + * + * This function will complete a schedule operation previously initiated with + * \ref mars_module_workload_schedule_begin. + * This function must be called in pair for each call to + * \ref mars_module_workload_schedule_begin to guarantee the completion of the + * initiated schedule operation. + * + * \param[in] id - id of workload + * \param[in] cancel - cancels the schedule operation + * \return + * MARS_SUCCESS - workload scheduling complete + * \n MARS_ERROR_PARAMS - invalid workload id specified + * \n MARS_ERROR_STATE - workload scheduling not started + */ +int mars_module_workload_schedule_end(uint16_t id, int cancel); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Begins unscheduling of specified workload. + * + * This function will begin unscheduling the workload specified. + * This only initiates the unscheduling of the workload. + * This function must be completed with a matching call to + * \ref mars_module_workload_unschedule_end to guarantee the completion of the + * unscheduling. + * + * The workload unscheduling process is not complete until the matching call to + * \ref mars_module_workload_unschedule_end is made. + * The user should make any necessary updates to the returned workload context + * in between this begin call and the end call. + * + * When a workload is unscheduled, it will be put into a finished state and any + * entities waiting on the workload to finish will be resumed. + * + * If a scheduled workload is unscheduled before execution, the workload will + * not be executed until a subsequent scheduling request is made. + * + * If the workload is currently in a waiting state, calling unschedule will + * finish the workload and will not be resumed from the waiting state. + * + * If the workload is currently in a running state, calling unschedule will + * immediately put the workload into a finished state. However, execution of the + * workload will only be suspended when the workload yields, waits, or finishes. + * + * \note + * Trying to unschedule a workload that has not yet been scheduled, or has + * already finished a previously scheduled execution will return an error. + * + * \param[in] id - id of workload + * \param[out] workload - address of pointer to workload context + * \return + * MARS_SUCCESS - workload unscheduling started + * \n MARS_ERROR_PARAMS - invalid workload id specified + * \n MARS_ERROR_STATE - workload is not scheduled or has finished + */ +int mars_module_workload_unschedule_begin(uint16_t id, + struct mars_workload_context **workload); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Ends unscheduling of specified workload. + * + * This function will complete an unschedule operation previously initiated with + * \ref mars_module_workload_unschedule_begin. + * This function must be called in pair for each call to + * \ref mars_module_workload_unschedule_begin to guarantee the completion of the + * initiated unschedule operation. + * + * \return + * MARS_SUCCESS - workload unscheduling complete + * \n MARS_ERROR_PARAMS - invalid workload id specified + * \n MARS_ERROR_STATE - workload unscheduling not started + */ +int mars_module_workload_unschedule_end(uint16_t id); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Returns execution to kernel with workload in wait state. + * + * This function will yield execution of the calling workload module and return + * execution back to the kernel. The workload currently being processed will be + * put into a waiting state. + * + * \note This function will exit the workload module and is not re-entrant. + */ +void mars_module_workload_wait(void); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Returns execution to kernel with workload in ready state. + * + * This function will yield execution of the calling workload module and return + * execution back to the kernel. The workload currently being processed will be + * put into a ready state. + * + * \note This function will exit the workload module and is not re-entrant. + */ +void mars_module_workload_yield(void); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Returns execution to kernel with workload in finished state. + * + * This function will yield execution of the calling workload module and return + * execution back to the kernel. The workload currently being processed will be + * put into a finished state. + * + * \note This function will exit the workload module and is not re-entrant. + */ +void mars_module_workload_finish(void); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Notify host a particular 32-bit area is modified. + * + * \param[in] watch_point_ea - ea of modified area + * + */ +void mars_module_host_signal_send(uint64_t watch_point_ea); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Request host to call registered callback. + * + * \param[in] callback_ea - ea of function of type \ref mars_callback + * \param[in] in - pointer to input args in MPU storage + * + * \return + * MARS_SUCCESS - callback requested to host + */ +int mars_module_host_callback_set(uint64_t callback_ea, + const struct mars_callback_args *in); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Resets a host callback request and requests result. + * + * \param[out] out - pointer to output args to store result + * + * \return + * MARS_SUCCESS - callback request reset + */ +int mars_module_host_callback_reset(struct mars_callback_args *out); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Locks a mutex. + * + * This function locks a mutex and blocks other requests to lock it. + * It also loads the mutex instance from the effective address specified + * into the local mutex instance. + * + * \param[in] mutex_ea - ea of mutex instance to lock + * \param[in] mutex - pointer to local mutex instance + * \return + * MARS_SUCCESS - successfully locked mutex + * \n MARS_ERROR_NULL - ea is 0 or mutex is NULL + * \n MARS_ERROR_ALIGN - ea or mutex not aligned properly + */ +int mars_module_mutex_lock_get(uint64_t mutex_ea, struct mars_mutex *mutex); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Unlocks a mutex. + * + * This function unlocks a previously locked mutex to allow other lock requests. + * It also stores the local mutex instance into the effective address specified. + * + * \param[in] mutex_ea - ea of mutex instance to unlock + * \param[in] mutex - pointer to local mutex instance + * \return + * MARS_SUCCESS - successfully unlocked mutex + * \n MARS_ERROR_NULL - ea is 0 or mutex is NULL + * \n MARS_ERROR_ALIGN - ea or mutex not aligned properly + * \n MARS_ERROR_STATE - instance not in locked state + */ +int mars_module_mutex_unlock_put(uint64_t mutex_ea, struct mars_mutex *mutex); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] DMA transfer from host storage to MPU storage. + * + * This function begins a DMA transfer request from host storage to MPU storage. + * Transfer completion is not guaranteed until calling \ref mars_module_dma_wait + * with the corresponding tag used to request the transfer. + * + * \param[in] ls - address of MPU storage to transfer to + * \param[in] ea - ea of host storage to transfer from + * \param[in] size - size of dma transfer + * \param[in] tag - tag of dma transfer + * \return + * MARS_SUCCESS - successfully tranferred data + * \n MARS_ERROR_PARAMS - invalid tag specified + * \n MARS_ERROR_ALIGN - ls or ea not aligned properly + */ +int mars_module_dma_get(void *ls, uint64_t ea, uint32_t size, uint32_t tag); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] DMA transfer from MPU storage to host storage. + * + * This function begins a DMA transfer request from MPU storage to host storage. + * Transfer completion is not guaranteed until calling \ref mars_module_dma_wait + * with the corresponding tag used to request the transfer. + * + * \param[in] ls - address of MPU storage to transfer to + * \param[in] ea - ea of host storage to transfer from + * \param[in] size - size of dma transfer + * \param[in] tag - tag of dma transfer + * \return + * MARS_SUCCESS - successfully tranferred data + * \n MARS_ERROR_PARAMS - invalid tag specified + * \n MARS_ERROR_ALIGN - ls or ea not aligned properly + */ +int mars_module_dma_put(const void *ls, uint64_t ea, uint32_t size, + uint32_t tag); + +/** + * \ingroup group_mars_workload_module + * \brief [MPU] Waits for completion of requested DMA transfer. + * + * This function waits until completion of all previously started DMA transfer + * requests with the same tag. + * + * \param[in] tag - tag of dma transfer + * \return + * MARS_SUCCESS - successfully waited for transfer completion + * \n MARS_ERROR_PARAMS - invalid tag specified + */ +int mars_module_dma_wait(uint32_t tag); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/spu/mars/task.h b/common/libspumars/include/spu/mars/task.h new file mode 100644 index 00000000..f1b2d7ec --- /dev/null +++ b/common/libspumars/include/spu/mars/task.h @@ -0,0 +1,315 @@ +#ifndef __MARS_TASK_H__ +#define __MARS_TASK_H__ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_task + * \brief [MPU] Entry point for task. + * + * This function is the main entry point for the task program. All task + * programs will need to have a definition of this function. The arguments + * passed into this function are specified during task scheduling through + * the call to \ref mars_task_schedule. + * + * \note If NULL was specified for \e args when calling \ref mars_task_schedule, + * the contents of args are undefined. + * + * \param[in] args - pointer to task args structure in MPU storage + * \return + * user specified + */ +int mars_task_main(const struct mars_task_args *args); + +/** + * \ingroup group_mars_task + * \brief [MPU] Exits and terminates task. + * + * This function causes the task to exit and terminate execution. Calling this + * function will cause the task to enter the finished state, and will no + * longer be scheduled to run. This function does not need to be called when + * returning from \ref mars_task_main since it is called automatically. + * + * \note This function is a scheduling call and will put the caller task into a + * finished state. + * + * Key Parameters: + * \n \n + * \e exit_code + * - The value passed into here can be obtained when calling \ref mars_task_wait + * or \ref mars_task_try_wait. + * + * \param[out] exit_code - value to be returned to the task wait call + */ +void mars_task_exit(int32_t exit_code); + +/** + * \ingroup group_mars_task + * \brief [MPU] Yields caller task so other workloads can run. + * (Task Switch Call) + * + * \note The [MPU] call may result in a task switch and put this + * task into the waiting state. Understand all the limitations before calling + * a Task Switch Call (See \ref sec_7_5). + * + * This function causes the task to yield and allow other workloads to be + * scheduled to run if available. The task's context state is saved and the + * kernel will reschedule the next available workload. If there are no other + * workloads to be scheduled, the task that called to yield will be rescheduled + * for resumed execution. + * + * \note This function is a scheduling call and may cause a task switch and put + * the caller task into a ready state. + * + * \return + * MARS_SUCCESS - successfully yielded task + * \n MARS_ERROR_FORMAT - no context save area specified + */ +int mars_task_yield(void); + +/** + * \ingroup group_mars_task + * \brief [host/MPU] Schedules a MARS task for execution. + * + * This function schedules the task specified for execution. + * The actual time of execution is determined by the scheduler. + * Once the task is scheduled for execution by this function, it may not be + * scheduled for execution until previous execution has finished. + * You can wait for task completion by calling \ref mars_task_wait or + * \ref mars_task_try_wait. + * + * You can call this function with a valid task id returned by + * \ref mars_task_create as many times as you want after each scheduled + * execution has completed. The task id is valid until the task is destroyed by + * \ref mars_task_destroy. + * + * Key Parameters: + * \n \n + * \e args + * - The task args (\ref mars_task_args) are optional, and only + * need to be passed in if the task executable expects it. + * - If NULL is specified the task args passed into the task executable's main + * function will not be initialized. + * - The args does need to be kept allocated after this function returns. + * + * \e priority + * - This is the priority of the task between 0 to 255, 0 being the lowest + * priority and 255 being the highest priority. + * - Tasks with higher priority will be prioritized during scheduling if both + * tasks are ready to run at the time of scheduling. + * + * \param[in] id - pointer to task id to schedule + * \param[in] args - pointer to task args to pass into task main + * \param[in] priority - priority of scheduling for the task + * \return + * MARS_SUCCESS - successfully scheduled MARS task for execution + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - bad task id specified + * \n MARS_ERROR_STATE - task is in an invalid state + */ +int mars_task_schedule(const struct mars_task_id *id, + const struct mars_task_args *args, + uint8_t priority); + +/** + * \ingroup group_mars_task + * \brief [host/MPU] Unschedules a MARS task from being executed. + * + * This function unschedules a previously scheduled task. + * + * When a task is unscheduled, it will behave as if the task has completed + * execution. The user is responsible for specifying a unique exit code when + * unscheduling tasks to handle the condition accordingly. + * + * If a scheduled task is unscheduled before execution, the workload will + * not be executed until a subsequent scheduling request is made. + * + * If the task is currently in a waiting state, calling unschedule will + * finish the workload and will not be resumed from the waiting state. + * + * If the task is currently in a running state, calling unschedule will + * immediately put the task into a finished state. However, execution of the + * task will only be suspended when the task yields, waits, or exits. + * + * \note + * Trying to unschedule a task that has not yet been scheduled, or has + * already finished a previously scheduled execution will return an error. + * + * Key Parameters: + * \n \n + * \e exit_code + * - The value passed into here can be obtained when calling \ref mars_task_wait + * or \ref mars_task_try_wait. + * + * \param[in] id - pointer to task id to abort + * \param[out] exit_code - value to be returned to the task wait call + * \return + * MARS_SUCCESS - successfully scheduled MARS task for execution + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - bad task id specified + * \n MARS_ERROR_STATE - task is in an invalid state + */ +int mars_task_unschedule(const struct mars_task_id *id, int32_t exit_code); + +/** + * \ingroup group_mars_task + * \brief [host/MPU] Waits for task completion. + * (Task Switch Call) + * + * \note The [MPU] call may result in a task switch and put this + * task into the waiting state. Understand all the limitations before calling + * a Task Switch Call (See \ref sec_7_5). + * + * This function will block until the scheduled task specified is finished. + * + * Any number of host threads or tasks can wait for a specific task to complete + * execution as long as it holds the task's id. However, the task being waited + * on should not be re-scheduled until all wait calls for the task have + * returned. Otherwise it is not guaranteed that all wait calls will return + * after the completion of the initial schedule call. + * + * Key Parameters: + * \n \n + * \e exit_code + * - Pass in a pointer to store the task exit code. + * - If NULL is specified, no exit code can be obtained when the task + * completes and returns. + * + * \note This function is a scheduling call and may cause a task switch and put + * the caller task into a waiting state. + * + * \param[in] id - pointer to task id to wait for + * \param[out] exit_code - pointer to variable to store task exit code + * \return + * MARS_SUCCESS - task execution finished + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - bad task id specified + * \n MARS_ERROR_STATE - task is in an invalid state + * \n MARS_ERROR_FORMAT - no context save area specified + */ +int mars_task_wait(const struct mars_task_id *id, int32_t *exit_code); + +/** + * \ingroup group_mars_task + * \brief [host/MPU] Waits for a task completion. + * + * This function will check whether the scheduled task specified is finished + * or not and return immediately without blocking. + * + * Key Parameters: + * \n \n + * \e exit_code + * - Pass in a pointer to store the task exit code. + * - If NULL is specified, no exit code can be obtained when the task + * completes and returns. + * + * \param[in] id - pointer to task id to wait for + * \param[out] exit_code - pointer to variable to store task exit code + * \return + * MARS_SUCCESS - task execution finished + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - bad task id specified + * \n MARS_ERROR_STATE - task is in an invalid state + * \n MARS_ERROR_BUSY - task has not yet finished execution + */ +int mars_task_try_wait(const struct mars_task_id *id, int32_t *exit_code); + +/** + * \ingroup group_mars_task + * \brief [MPU] Calls the specified host callback. + * (Task Switch Call) + * + * \note The [MPU] call may result in a task switch and put this + * task into the waiting state. Understand all the limitations before calling + * a Task Switch Call (See \ref sec_7_5). + * + * This function will block until the requested host callback function returns. + * + * Key Parameters: + * \n \n + * \e callback_ea + * - Pass in the EA of the host callback function you want called. This function + * should be of type \ref mars_callback. + * - If an invalid EA is specified, the resulting behavior is undetermined. + * + * \e in + * - Pass in a pointer to an initialized callback argument structure instance. + * - If NULL is specified, a pointer to an uninitialized callback argument + * structure will be passed into the host callback function. + * + * \e out + * - Pass in a pointer to a callback argument structure instance, which can be + * initialized by the host callback function. + * - If NULL is specified, the output argument structure initialized by the host + * callback function will not be returned to the caller. + * + * \param[in] callback_ea - ea of host callback of type \ref mars_callback + * \param[in] in - pointer to args, passed into to host callback + * \param[out] out - pointer to args, initialized by host callback + * \return + * MARS_SUCCESS - host callback successful + * \n MARS_ERROR_LIMIT - host callback queue is full + * \n MARS_ERROR_FORMAT - no context save area specified + */ +int mars_task_call_host(uint64_t callback_ea, + const struct mars_callback_args *in, + struct mars_callback_args *out); + +/** + * \ingroup group_mars_task + * \brief [host/MPU] Gets tick counter value. + * + * \note Counter's frequency depends on runtime environment. + * + * \return + * uint32_t - 32-bit tick counter value + */ +uint32_t mars_task_get_ticks(void); + +/** + * \ingroup group_mars_task + * \brief [MPU] Gets id of kernel that the task is being executed on. + * + * \note The kernel id refers to an index of the MPU it is being executed on and + * will range from 0 to (# of MPUs initiialized for MARS context) - 1. + * + * \return + * uint16_t - id of MARS kernel + */ +uint16_t mars_task_get_kernel_id(void); + +/** + * \ingroup group_mars_task + * \brief [MPU] Gets id of caller task. + * + * \return + * const struct mars_task_id * - pointer to task id in MPU storage + */ +const struct mars_task_id *mars_task_get_id(void); + +/** + * \ingroup group_mars_task + * \brief [MPU] Gets name of caller task. + * + * \return + * const char * - pointer to task name in MPU storage + */ +const char *mars_task_get_name(void); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/spu/mars/task_barrier.h b/common/libspumars/include/spu/mars/task_barrier.h new file mode 100644 index 00000000..b30f2866 --- /dev/null +++ b/common/libspumars/include/spu/mars/task_barrier.h @@ -0,0 +1,117 @@ +#ifndef __MARS_TASK_BARRIER_H__ +#define __MARS_TASK_BARRIER_H__ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_task_barrier + * \brief [MPU] Notifies a task barrier. + * (Task Switch Call) + * + * \note The [MPU] call may result in a task switch and put this + * task into the waiting state. Understand all the limitations before calling + * a Task Switch Call (See \ref sec_7_5). + * + * This function notifies the barrier that the caller task has reached the + * synchronization point. Once a task reaches the synchronization point and + * notifies the barrier, it may handle other processing before calling the + * required \ref mars_task_barrier_wait. + * + * If all tasks from the previous barrier cycle have not reached the + * synchronization point and notified/waited on the barrier yet, the caller task + * will enter a waiting state until the previous barrier cycle is released. + * + * \param[in] barrier_ea - ea of initialized barrier instance + * \return + * MARS_SUCCESS - successfully notified barrier + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not properly aligned + * \n MARS_ERROR_LIMIT - maximum number of tasks already waiting + * \n MARS_ERROR_FORMAT - no context save area specified + */ +int mars_task_barrier_notify(uint64_t barrier_ea); + +/** + * \ingroup group_mars_task_barrier + * \brief [MPU] Notifies a task barrier. + * + * This function notifies the barrier that the caller task has reached the + * synchronization point. Once a task reaches the synchronization point and + * notifies the barrier, it may handle other processing before calling the + * required \ref mars_task_barrier_wait. + * + * If all tasks from the previous barrier cycle have not reached the + * synchronization point and notified the barrier yet, this function will + * return immediately with \ref MARS_ERROR_BUSY. + * + * \param[in] barrier_ea - ea of initialized barrier instance + * \return + * MARS_SUCCESS - successfully notified barrier + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not properly aligned + * \n MARS_ERROR_BUSY - previous waits have not completed + */ +int mars_task_barrier_try_notify(uint64_t barrier_ea); + +/** + * \ingroup group_mars_task_barrier + * \brief [MPU] Waits on a task barrier. + * (Task Switch Call) + * + * \note The [MPU] call may result in a task switch and put this + * task into the waiting state. Understand all the limitations before calling + * a Task Switch Call (See \ref sec_7_5). + * + * This function should be called after the caller task has previously called + * \ref mars_task_barrier_notify. This function should be called when the task + * that reached the synchronization point is ready to wait for others to arrive + * at the synchronization point. + * + * If not all tasks associated with the barrier have reached the synchronization + * point and notified the barrier at the time of this call, the caller task will + * enter a waiting state until all notifications are received from the other + * tasks and the barrier is released. + * + * \param[in] barrier_ea - ea of initialized barrier instance + * \return + * MARS_SUCCESS - barrier is released + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not properly aligned + * \n MARS_ERROR_LIMIT - maximum number of tasks already waiting + * \n MARS_ERROR_FORMAT - no context save area specified + */ +int mars_task_barrier_wait(uint64_t barrier_ea); + +/** + * \ingroup group_mars_task_barrier + * \brief [MPU] Waits on a task barrier. + * + * This function should be called after the caller task has previously called + * \ref mars_task_barrier_notify. This function should be called when the task + * that reached the synchronization point is ready to wait for others to arrive + * at the synchronization point. + * + * If not all tasks associated with the barrier have reached the synchronization + * point and notified the barrier at the time of this call, this function will + * return immediately with \ref MARS_ERROR_BUSY. + * + * \param[in] barrier_ea - ea of initialized barrier instance + * \return + * MARS_SUCCESS - barrier is released + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not properly aligned + * \n MARS_ERROR_BUSY - not all notifications arrived yet + */ +int mars_task_barrier_try_wait(uint64_t barrier_ea); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/spu/mars/task_event_flag.h b/common/libspumars/include/spu/mars/task_event_flag.h new file mode 100644 index 00000000..1e6ec743 --- /dev/null +++ b/common/libspumars/include/spu/mars/task_event_flag.h @@ -0,0 +1,158 @@ +#ifndef __MARS_TASK_EVENT_FLAG_H__ +#define __MARS_TASK_EVENT_FLAG_H__ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_task_event_flag + * \brief [host/MPU] Clears the bits specified in the task event flag. + * + * This function will clear all bits currently set in the event flag. + * Tasks waiting on some events will remain in the waiting state. + * + * Key Parameters: + * \n \n + * \e bits + * - Specify only the bits you want to be cleared from the event flag bits. + * + * \param[in] event_flag_ea - ea of initialized event flag instance + * \param[in] bits - bits to clear + * \return + * MARS_SUCCESS - successfully cleared event flag bits + * \n MARS_ERROR_NULL - ea is 0\n + * \n MARS_ERROR_ALIGN - ea not aligned properly + */ +int mars_task_event_flag_clear(uint64_t event_flag_ea, uint32_t bits); + +/** + * \ingroup group_mars_task_event_flag + * \brief [host/MPU] Sets the bits specified in the task event flag. + * + * This function will set the bits specified. The bits are OR'd with bits + * already set in the event flag at the time of this call. + * If there are any tasks in the waiting state, and this call satisfies the + * release condition of those tasks, all those tasks will be returned to the + * ready state. + * + * Key Parameters: + * \n \n + * \e bits + * - Specify only the bits you want to set in the event flag bits. + * + * \param[in] event_flag_ea - ea of initialized event flag instance + * \param[in] bits - bits to set + * \return + * MARS_SUCCESS - successfully cleared event flag bits + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not aligned properly + * \n MARS_ERROR_STATE - invalid direction + */ +int mars_task_event_flag_set(uint64_t event_flag_ea, uint32_t bits); + +/** + * \ingroup group_mars_task_event_flag + * \brief [host/MPU] Waits on a task event flag. + * (Task Switch Call) + * + * \note The [MPU] call may result in a task switch and put this + * task into the waiting state. Understand all the limitations before calling + * a Task Switch Call (See \ref sec_7_5). + * + * This call will put the task into a waiting state until the specified event + * flag condition is met. + * + * Key Parameters: + * \n \n + * \e mask + * - Specify only the bits you want to test against in the event flag bits set. + * - Whether the test condition is satisfied or not will depend on \e mask_mode. + * + * \e mask_mode + * - Specify how to test the mask bits against the event flag bits set. + * - Specify \ref MARS_TASK_EVENT_FLAG_MASK_OR (\e mask | event flag bits), so + * the condition is satisfied if any bits specified in \e mask is set in the + * event flag bits. + * - Specify \ref MARS_TASK_EVENT_FLAG_MASK_AND (\e mask & event flag bits) so + * the condition is satisfied if all bits specified in \e mask are set in the + * event flag bits. + * + * \e bits + * - Specify a pointer to a variable where the event flag bits can be returned. + * - The resulting bits will be the state of bits in the event flag prior to any + * automatic clearing of bits (if \ref MARS_TASK_EVENT_FLAG_CLEAR_AUTO was + * specified at creation). + * - The returned bits are only valid when MARS_SUCCESS is returned. + * + * \param[in] event_flag_ea - ea of initialized event flag instance + * \param[in] mask - bit mask to test event flag bits against + * \param[in] mask_mode - specifies how to mask bits (AND, OR) + * \param[out] bits - pointer of instance to return bits state + * \return + * MARS_SUCCESS - successfully received event + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not aligned properly + * \n MARS_ERROR_PARAMS - invalid mask_mode + * \n MARS_ERROR_STATE - invalid direction + * \n MARS_ERROR_LIMIT - exceeded limit of max waiting tasks + * \n MARS_ERROR_FORMAT - no context save area specified + */ +int mars_task_event_flag_wait(uint64_t event_flag_ea, + uint32_t mask, uint8_t mask_mode, + uint32_t *bits); + +/** + * \ingroup group_mars_task_event_flag + * \brief [host/MPU] Waits on a task event flag. + * + * This call will test to see if the specified event flag condition is met + * returns immediately with \ref MARS_ERROR_BUSY if it did not. + * + * Key Parameters: + * \n \n + * \e mask + * - Specify only the bits you want to test against in the event flag bits set. + * - Whether the test condition is satisfied or not will depend on \e mask_mode. + * + * \e mask_mode + * - Specify how to test the mask bits against the event flag bits set. + * - Specify \ref MARS_TASK_EVENT_FLAG_MASK_OR (\e mask | event flag bits), so + * the condition is satisfied if any bits specified in \e mask is set in the + * event flag bits. + * - Specify \ref MARS_TASK_EVENT_FLAG_MASK_AND (\e mask & event flag bits) so + * the condition is satisfied if all bits specified in \e mask are set in the + * event flag bits. + * + * \e bits + * - Specify a pointer to a variable where the event flag bits can be returned. + * - The resulting bits will be the state of bits in the event flag prior to any + * automatic clearing of bits (if \ref MARS_TASK_EVENT_FLAG_CLEAR_AUTO was + * specified at creation). + * - The returned bits are only valid when MARS_SUCCESS is returned. + * + * \param[in] event_flag_ea - ea of initialized event flag instance + * \param[in] mask - bit mask to test event flag bits against + * \param[in] mask_mode - specifies how to mask bits (AND, OR) + * \param[out] bits - pointer of instance to return bits state + * \return + * MARS_SUCCESS - successfully received event + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not aligned properly + * \n MARS_ERROR_PARAMS - invalid mask_mode + * \n MARS_ERROR_STATE - invalid direction + * \n MARS_ERROR_BUSY - event not yet received + */ +int mars_task_event_flag_try_wait(uint64_t event_flag_ea, + uint32_t mask, uint8_t mask_mode, + uint32_t *bits); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/spu/mars/task_queue.h b/common/libspumars/include/spu/mars/task_queue.h new file mode 100644 index 00000000..c2c251ca --- /dev/null +++ b/common/libspumars/include/spu/mars/task_queue.h @@ -0,0 +1,554 @@ +#ifndef __MARS_TASK_QUEUE_H__ +#define __MARS_TASK_QUEUE_H__ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_task_queue + * \brief [host/MPU] Returns the number of data items in the task queue. + * + * This function will return the total number of data items in the queue at the + * time of the call. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[out] count - pointer to variable to store return count + * \return + * MARS_SUCCESS - successfully returned count + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not aligned properly + */ +int mars_task_queue_count(uint64_t queue_ea, uint32_t *count); + +/** + * \ingroup group_mars_task_queue + * \brief [host/MPU] Clears the data items in the task queue. + * + * This function will clear all data items currently in the queue. + * Entities waiting to pop data from the queue will remain waiting. + * Entities waiting to push data into the queue will resume. + * + * \param[in] queue_ea - ea of initialized queue instance + * \return + * MARS_SUCCESS - successfully cleared queue + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not aligned properly + */ +int mars_task_queue_clear(uint64_t queue_ea); + +/** + * \ingroup group_mars_task_queue + * \brief [host/MPU] Pushes the data specified into the task queue. + * (Task Switch Call) + * + * \note The [MPU] call may result in a task switch and put this + * task into the waiting state. Understand all the limitations before calling + * a Task Switch Call (See \ref sec_7_5). + * + * This function will push the data specified into the queue. + * The entity waiting longest to pop data from the queue can resume. + * + * If the queue is full at the time of this call, this function will cause the + * caller task to enter a waiting state until there is room in the queue to push + * the data. + * + * Key Parameters: + * \n \n + * \e data + * - Specify the pointer to user data to copy into the queue. + * - The size of data should be equal to the size specified at queue creation. + * - Note: In the Cell B.E. processor, data must be aligned to 16-bytes. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[in] data - address of data to be pushed into queue + * \return + * MARS_SUCCESS - successfully pushed data into queue + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not aligned properly + * \n MARS_ERROR_LIMIT - exceeded limit of max waiting tasks + * \n MARS_ERROR_STATE - invalid direction + * \n MARS_ERROR_FORMAT - no context save area specified + */ +int mars_task_queue_push(uint64_t queue_ea, const void *data); + +/** + * \ingroup group_mars_task_queue + * \brief [MPU] Begins push operation on a task queue. + * (Task Switch Call) + * + * \note The [MPU] call may result in a task switch and put this + * task into the waiting state. Understand all the limitations before calling + * a Task Switch Call (See \ref sec_7_5). + * + * This function will begin pushing the data specified into the queue. + * This only initiates the memory transfer of data into the queue. + * This function must be completed with a matching call to + * \ref mars_task_queue_push_end to guarantee the completion of the push. + * + * If the queue is full at the time of this call, this function will cause the + * caller task to enter a waiting state until there is room in the queue to push + * the data. + * + * Key Parameters: + * \n \n + * \e data + * - Specify the pointer to user data to copy into the queue. + * - The size of data should be equal to the size specified at queue creation. + * - Note: In the Cell B.E. processor, data must be aligned to 16-bytes. + * + * \e tag + * - Specify a memory transfer tag value between 0-31. + * - Multiple push operations can be initiated concurrently, and each can be + * waited for independently if different memory tranfer tags are specified. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[in] data - address of data to be pushed into queue + * \param[in] tag - tag identifier for memory transfer + * \return + * MARS_SUCCESS - successfully pushed data into queue + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not aligned properly + * \n MARS_ERROR_LIMIT - exceeded limit of max waiting tasks + * \n MARS_ERROR_STATE - invalid direction + * \n MARS_ERROR_PARAMS - invalid tag + * \n MARS_ERROR_FORMAT - no context save area specified + */ +int mars_task_queue_push_begin(uint64_t queue_ea, const void *data, + uint32_t tag); + +/** + * \ingroup group_mars_task_queue + * \brief [MPU] Completes push operation on a task queue. + * + * This function will complete a push operation initiated with + * \ref mars_task_queue_push_begin or \ref mars_task_queue_try_push_begin. + * This function must be called in pair for each call to + * \ref mars_task_queue_push_begin or \ref mars_task_queue_try_push_begin to + * guarantee the completion of the initiated push operation. + * + * Key Parameters: + * \n \n + * \e tag + * - Specify the memory transfer tag specified at push operation initialization. + * - If multiple push operations were initiated concurrently, this call must + * wait for all memory transfers initiated with the same tag to complete. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[in] tag - tag identifier for memory transfer + * \return + * MARS_SUCCESS - successfully pushed data into queue + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not aligned properly + * \n MARS_ERROR_STATE - invalid direction + * \n MARS_ERROR_PARAMS - invalid tag + */ +int mars_task_queue_push_end(uint64_t queue_ea, uint32_t tag); + +/** + * \ingroup group_mars_task_queue + * \brief [host/MPU] Pushes the data specified into the task queue. + * + * This function will push the data specified into the queue. + * The entity waiting longest to pop data from the queue can resume. + * + * If the queue is full at the time of this call, this function will return + * immedately with \ref MARS_ERROR_BUSY. + * + * Key Parameters: + * \n \n + * \e data + * - Specify the pointer to user data to copy into the queue. + * - The size of data should be equal to the size specified at queue creation. + * - Note: In the Cell B.E. processor, data must be aligned to 16-bytes. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[in] data - address of data to be pushed into queue + * \return + * MARS_SUCCESS - successfully pushed data into queue + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not aligned properly + * \n MARS_ERROR_BUSY - queue is full + * \n MARS_ERROR_STATE - invalid direction + */ +int mars_task_queue_try_push(uint64_t queue_ea, const void *data); + +/** + * \ingroup group_mars_task_queue + * \brief [MPU] Begins push operation on a task queue. + * + * This function will begin pushing the data specified into the queue. + * This only initiates the memory transfer of data into the queue. + * This function must be completed with a matching call to + * \ref mars_task_queue_push_end to guarantee the completion of the push. + * + * If the queue is full at the time of this call, this function will return + * immedately with \ref MARS_ERROR_BUSY. + * + * Key Parameters: + * \n \n + * \e data + * - Specify the pointer to user data to copy into the queue. + * - The size of data should be equal to the size specified at queue creation. + * - Note: In the Cell B.E. processor, data must be aligned to 16-bytes. + * + * \e tag + * - Specify a memory transfer tag value between 0-31. + * - Multiple push operations can be initiated concurrently, and each can be + * waited for independently if different memory tranfer tags are specified. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[in] data - address of data to be pushed into queue + * \param[in] tag - tag identifier for memory transfer + * \return + * MARS_SUCCESS - successfully pushed data into queue + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not aligned properly + * \n MARS_ERROR_BUSY - queue is full + * \n MARS_ERROR_STATE - invalid direction + * \n MARS_ERROR_PARAMS - invalid tag + */ +int mars_task_queue_try_push_begin(uint64_t queue_ea, const void *data, + uint32_t tag); + +/** + * \ingroup group_mars_task_queue + * \brief [host/MPU] Pops data from a task queue. + * (Task Switch Call) + * + * \note The [MPU] call may result in a task switch and put this + * task into the waiting state. Understand all the limitations before calling + * a Task Switch Call (See \ref sec_7_5). + * + * This function will pop data from the queue. + * + * If the queue is empty at the time of this call, this function will cause the + * caller task to enter a waiting state until there is data in the queue to + * pop. + * + * Key Parameters: + * \n \n + * \e data + * - Specify the pointer to some allocated memory to copy the popped data. + * - The size of the allocated memory area should be equal to the size specified + * at queue creation. + * - Note: In the Cell B.E. processor, data must be aligned to 16-bytes. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[out] data - address of data to be popped from queue + * \return + * MARS_SUCCESS - successfully popped data from queue + * \n MARS_ERROR_NULL - ea or data is 0 + * \n MARS_ERROR_ALIGN - ea or data not aligned properly + * \n MARS_ERROR_LIMIT - exceeded limit of max waiting tasks + * \n MARS_ERROR_STATE - invalid direction + * \n MARS_ERROR_FORMAT - no context save area specified + */ +int mars_task_queue_pop(uint64_t queue_ea, void *data); + +/** + * \ingroup group_mars_task_queue + * \brief [MPU] Begins pop operation on a task queue. + * (Task Switch Call) + * + * \note The [MPU] call may result in a task switch and put this + * task into the waiting state. Understand all the limitations before calling + * a Task Switch Call (See \ref sec_7_5). + * + * This function will begin popping data from the queue. + * This only initiates the memory transfer of data from the queue. + * This function must be completed with a matching call to + * \ref mars_task_queue_pop_end to guarantee the completion of the pop. + * + * If the queue is empty at the time of this call, this function will cause the + * caller task to enter a waiting state until there is data in the queue to + * pop. + * + * Key Parameters: + * \n \n + * \e data + * - Specify the pointer to some allocated memory to copy the popped data. + * - The size of the allocated memory area should be equal to the size specified + * at queue creation. + * - Note: In the Cell B.E. processor, data must be aligned to 16-bytes. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[out] data - address of data to be popped from queue + * \param[in] tag - tag identifier for memory transfer + * \return + * MARS_SUCCESS - successfully popped data from queue + * \n MARS_ERROR_NULL - ea or data is 0 + * \n MARS_ERROR_ALIGN - ea or data not aligned properly + * \n MARS_ERROR_LIMIT - exceeded limit of max waiting tasks + * \n MARS_ERROR_STATE - invalid direction + * \n MARS_ERROR_PARAMS - invalid tag + * \n MARS_ERROR_FORMAT - no context save area specified + */ +int mars_task_queue_pop_begin(uint64_t queue_ea, void *data, uint32_t tag); + +/** + * \ingroup group_mars_task_queue + * \brief [MPU] Completes pop operation on a task queue. + * + * This function will complete a pop operation initiated with + * \ref mars_task_queue_pop_begin or \ref mars_task_queue_try_pop_begin. + * This function must be called in pair for each call to + * \ref mars_task_queue_pop_begin or \ref mars_task_queue_try_pop_begin to + * guarantee the completion of the initiated pop operation. + * + * Key Parameters: + * \n \n + * \e tag + * - Specify the memory transfer tag specified at pop operation initialization. + * - If multiple pop operations were initiated concurrently, this call must + * wait for all memory transfers initiated with the same tag to complete. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[in] tag - tag identifier for memory transfer + * \return + * MARS_SUCCESS - successfully popped data from queue + * \n MARS_ERROR_NULL - ea or data is 0 + * \n MARS_ERROR_ALIGN - ea or data not aligned properly + * \n MARS_ERROR_STATE - invalid direction + * \n MARS_ERROR_PARAMS - invalid tag + */ +int mars_task_queue_pop_end(uint64_t queue_ea, uint32_t tag); + +/** + * \ingroup group_mars_task_queue + * \brief [host/MPU] Pops data from a task queue. + * + * This function will pop data from the queue. + * + * If the queue is empty at the time of this call, this function will return + * immedately with \ref MARS_ERROR_BUSY. + * + * Key Parameters: + * \n \n + * \e data + * - Specify the pointer to some allocated memory to copy the popped data. + * - The size of the allocated memory area should be equal to the size specified + * at queue creation. + * - Note: In the Cell B.E. processor, data must be aligned to 16-bytes. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[out] data - address for data to be popped from queue + * \return + * MARS_SUCCESS - successfully popped data from queue + * \n MARS_ERROR_NULL - ea or data is 0 + * \n MARS_ERROR_ALIGN - ea or data not aligned properly + * \n MARS_ERROR_BUSY - queue is empty + * \n MARS_ERROR_STATE - invalid direction + */ +int mars_task_queue_try_pop(uint64_t queue_ea, void *data); + +/** + * \ingroup group_mars_task_queue + * \brief [MPU] Begins pop operation on a task queue. + * + * This function will begin popping data from the queue. + * This only initiates the memory transfer of data from the queue. + * This function must be completed with a matching call to + * \ref mars_task_queue_pop_end to guarantee the completion of the pop. + * + * If the queue is empty at the time of this call, this function will return + * immedately with \ref MARS_ERROR_BUSY. + * + * Key Parameters: + * \n \n + * \e data + * - Specify the pointer to some allocated memory to copy the popped data. + * - The size of the allocated memory area should be equal to the size specified + * at queue creation. + * - Note: In the Cell B.E. processor, data must be aligned to 16-bytes. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[out] data - address for data to be popped from queue + * \param[in] tag - tag identifier for memory transfer + * \return + * MARS_SUCCESS - successfully popped data from queue + * \n MARS_ERROR_NULL - ea or data is 0 + * \n MARS_ERROR_ALIGN - ea or data not aligned properly + * \n MARS_ERROR_BUSY - queue is empty + * \n MARS_ERROR_STATE - invalid direction + * \n MARS_ERROR_PARAMS - invalid tag + */ +int mars_task_queue_try_pop_begin(uint64_t queue_ea, void *data, uint32_t tag); + +/** + * \ingroup group_mars_task_queue + * \brief [host/MPU] Pops data from a task queue without removing it. + * (Task Switch Call) + * + * \note The [MPU] call may result in a task switch and put this + * task into the waiting state. Understand all the limitations before calling + * a Task Switch Call (See \ref sec_7_5). + * + * This function will retrieve data from the queue without removing it from the + * queue. + * + * If the queue is empty at the time of this call, this function will cause the + * caller task to enter a waiting state until there is data in the queue to + * be retrieved. + * + * Key Parameters: + * \n \n + * \e data + * - Specify the pointer to some allocated memory to copy the popped data. + * - The size of the allocated memory area should be equal to the size specified + * at queue creation. + * - Note: In the Cell B.E. processor, data must be aligned to 16-bytes. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[out] data - address of data to be popped from queue + * \return + * MARS_SUCCESS - successfully popped data from queue + * \n MARS_ERROR_NULL - ea or data is 0 + * \n MARS_ERROR_ALIGN - ea or data not aligned properly + * \n MARS_ERROR_LIMIT - exceeded limit of max waiting tasks + * \n MARS_ERROR_STATE - invalid direction + * \n MARS_ERROR_FORMAT - no context save area specified + */ +int mars_task_queue_peek(uint64_t queue_ea, void *data); + +/** + * \ingroup group_mars_task_queue + * \brief [MPU] Begins peek operation on a task queue. + * (Task Switch Call) + * + * \note The [MPU] call may result in a task switch and put this + * task into the waiting state. Understand all the limitations before calling + * a Task Switch Call (See \ref sec_7_5). + * + * This function will begin retriveing data from the queue without removing the + * data from the queue. + * This only initiates the memory transfer of data from the queue. + * This function must be completed with a matching call to + * \ref mars_task_queue_peek_end to guarantee the completion of the peek. + * + * If the queue is empty at the time of this call, this function will cause the + * caller task to enter a waiting state until there is data in the queue to + * be retrived. + * + * Key Parameters: + * \n \n + * \e data + * - Specify the pointer to some allocated memory to copy the popped data. + * - The size of the allocated memory area should be equal to the size specified + * at queue creation. + * - Note: In the Cell B.E. processor, data must be aligned to 16-bytes. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[out] data - address of data to be popped from queue + * \param[in] tag - tag identifier for memory transfer + * \return + * MARS_SUCCESS - successfully popped data from queue + * \n MARS_ERROR_NULL - ea or data is 0 + * \n MARS_ERROR_ALIGN - ea or data not aligned properly + * \n MARS_ERROR_LIMIT - exceeded limit of max waiting tasks + * \n MARS_ERROR_STATE - invalid direction + * \n MARS_ERROR_PARAMS - invalid tag + * \n MARS_ERROR_FORMAT - no context save area specified + */ +int mars_task_queue_peek_begin(uint64_t queue_ea, void *data, uint32_t tag); + +/** + * \ingroup group_mars_task_queue + * \brief [MPU] Completes peek operation on a task queue. + * + * This function will complete a peek operation initiated with + * \ref mars_task_queue_peek_begin or \ref mars_task_queue_try_peek_begin. + * This function must be called in pair for each call to + * \ref mars_task_queue_peek_begin or \ref mars_task_queue_try_peek_begin to + * guarantee the completion of the initiated peek operation. + * + * Key Parameters: + * \n \n + * \e tag + * - Specify the memory transfer tag specified at peek operation initialization. + * - If multiple peek operations were initiated concurrently, this call must + * wait for all memory transfers initiated with the same tag to complete. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[in] tag - tag identifier for memory transfer + * \return + * MARS_SUCCESS - successfully popped data from queue + * \n MARS_ERROR_NULL - ea or data is 0 + * \n MARS_ERROR_ALIGN - ea or data not aligned properly + * \n MARS_ERROR_STATE - invalid direction + * \n MARS_ERROR_PARAMS - invalid tag + */ +int mars_task_queue_peek_end(uint64_t queue_ea, uint32_t tag); + +/** + * \ingroup group_mars_task_queue + * \brief [host/MPU] Pops data from a task queue without removing it. + * + * This function will retrieve data from the queue without removing it from the + * queue. + * + * If the queue is empty at the time of this call, this function will return + * immedately with \ref MARS_ERROR_BUSY. + * + * Key Parameters: + * \n \n + * \e data + * - Specify the pointer to some allocated memory to copy the popped data. + * - The size of the allocated memory area should be equal to the size specified + * at queue creation. + * - Note: In the Cell B.E. processor, data must be aligned to 16-bytes. + * . + * \param[in] queue_ea - ea of initialized queue instance + * \param[out] data - address of data to be popped from queue + * \return + * MARS_SUCCESS - successfully popped data from queue + * \n MARS_ERROR_NULL - ea or data is 0 + * \n MARS_ERROR_ALIGN - ea or data not aligned properly + * \n MARS_ERROR_BUSY - queue is empty + * \n MARS_ERROR_STATE - invalid direction + */ +int mars_task_queue_try_peek(uint64_t queue_ea, void *data); + +/** + * \ingroup group_mars_task_queue + * \brief [MPU] Begins peek operation on a task queue. + * + * This function will begin retriveing data from the queue without removing the + * data from the queue. + * This only initiates the memory transfer of data from the queue. + * This function must be completed with a matching call to + * \ref mars_task_queue_peek_end to guarantee the completion of the peek. + * + * If the queue is empty at the time of this call, this function will return + * immedately with \ref MARS_ERROR_BUSY. + * + * Key Parameters: + * \n \n + * \e data + * - Specify the pointer to some allocated memory to copy the popped data. + * - The size of the allocated memory area should be equal to the size specified + * at queue creation. + * - Note: In the Cell B.E. processor, data must be aligned to 16-bytes. + * + * \param[in] queue_ea - ea of initialized queue instance + * \param[out] data - address of data to be popped from queue + * \param[in] tag - tag identifier for memory transfer + * \return + * MARS_SUCCESS - successfully popped data from queue + * \n MARS_ERROR_NULL - ea or data is 0 + * \n MARS_ERROR_ALIGN - ea or data not aligned properly + * \n MARS_ERROR_BUSY - queue is empty + * \n MARS_ERROR_STATE - invalid direction + * \n MARS_ERROR_PARAMS - invalid tag + */ +int mars_task_queue_try_peek_begin(uint64_t queue_ea, void *data, uint32_t tag); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/spu/mars/task_semaphore.h b/common/libspumars/include/spu/mars/task_semaphore.h new file mode 100644 index 00000000..526ba3b3 --- /dev/null +++ b/common/libspumars/include/spu/mars/task_semaphore.h @@ -0,0 +1,58 @@ +#ifndef __MARS_TASK_SEMAPHORE_H__ +#define __MARS_TASK_SEMAPHORE_H__ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_task_semaphore + * \brief [MPU] Acquires a task semaphore. + * (Task Switch Call) + * + * \note The [MPU] call may result in a task switch and put this + * task into the waiting state. Understand all the limitations before calling + * a Task Switch Call (See \ref sec_7_5). + * + * This function will attempt to acquire the semaphore. + * If the total number of current accesses of the semaphore is greater than or + * equal to the total allowed specified at semaphore creation, the caller task + * will enter a waiting state until some other tasks release the semaphore and + * becomes available for acquiring. + * + * \param[in] semaphore_ea - ea of initialized semaphore instance + * \return + * MARS_SUCCESS - successfully acquired semaphore + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not aligned properly + * \n MARS_ERROR_LIMIT - maximum number of tasks already waiting + * \n MARS_ERROR_FORMAT - no context save area specified + */ +int mars_task_semaphore_acquire(uint64_t semaphore_ea); + +/** + * \ingroup group_mars_task_semaphore + * \brief [MPU] Releases a task semaphore. + * + * This function will release a previously acquired semaphore. + * If their are other tasks currently waiting to acquire a semaphore, calling + * this function will resume a waiting task to allow it to acquire the + * semaphore. + * + * \param[in] semaphore_ea - ea of initialized semaphore instance + * \return + * MARS_SUCCESS - successfully released semaphore + * \n MARS_ERROR_NULL - ea is 0 + * \n MARS_ERROR_ALIGN - ea not aligned properly + */ +int mars_task_semaphore_release(uint64_t semaphore_ea); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/include/spu/mars/task_signal.h b/common/libspumars/include/spu/mars/task_signal.h new file mode 100644 index 00000000..9376c19a --- /dev/null +++ b/common/libspumars/include/spu/mars/task_signal.h @@ -0,0 +1,73 @@ +#ifndef __MARS_TASK_SIGNAL_H__ +#define __MARS_TASK_SIGNAL_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup group_mars_task_signal + * \brief [host/MPU] Sends a signal to the specified task. + * + * This function sends a signal to the task specified. If the task had + * previously called \ref mars_task_signal_wait and was in the waiting state, + * this function will cause that task to switch to a ready state and scheduled + * to run accordingly. If the task has not yet called + * \ref mars_task_signal_wait, the tasks's signal buffer will be set and the + * same order of events will occur as previously explained once + * \ref mars_task_signal_wait is called. The task signal buffer depth is 1. + * Therefore if the signal buffer is already set when a another signal is + * received, it has no effect. + * + * \param[in] id - pointer to task id of task to signal + * \return + * MARS_SUCCESS - successfully yielded task + * \n MARS_ERROR_NULL - null pointer specified + * \n MARS_ERROR_PARAMS - bad task id specified + * \n MARS_ERROR_STATE - task is in an invalid state + */ +int mars_task_signal_send(struct mars_task_id *id); + +/** + * \ingroup group_mars_task_signal + * \brief [MPU] Waits and yields caller task until receiving signal. + * (Task Switch Call) + * + * \note The [MPU] call may result in a task switch and put this + * task into the waiting state. Understand all the limitations before calling + * a Task Switch Call (See \ref sec_7_5). + * + * This function will cause the task to enter a waiting state until some other + * entity calls \ref mars_task_signal_send to this task to return it to the + * ready state. The task will not be scheduled to run until it receives the + * signal and returned to the ready state. Once the task receives the signal and + * this function returns MARS_SUCCESS, the signal buffer is cleared. + * + * \return + * MARS_SUCCESS - successfully yielded task + * \n MARS_ERROR_FORMAT - no context save area specified + */ +int mars_task_signal_wait(void); + +/** + * \ingroup group_mars_task_signal + * \brief [MPU] Waits for task until receiving signal. + * + * This function will check the state of a task to see if some other entity + * has called \ref mars_task_signal_send to this task and returns immediately + * with the result. Once the task receives the signal and this function returns + * MARS_SUCCESS, the signal buffer is cleared. + * + * \return + * MARS_SUCCESS - successfully yielded task + * \n MARS_ERROR_BUSY - signal not yet received + */ +int mars_task_signal_try_wait(void); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/ppu/Makefile b/common/libspumars/ppu/Makefile new file mode 100644 index 00000000..12e4a476 --- /dev/null +++ b/common/libspumars/ppu/Makefile @@ -0,0 +1,119 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(PSL1GHT)),) +$(error "Please set PSL1GHT in your environment. export PSL1GHT=") +endif + +include $(PSL1GHT)/ppu_rules + +BUILD := build + +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PLATFORM)),) +#--------------------------------------------------------------------------------- +export BASEDIR := $(CURDIR) +export DEPS := $(BASEDIR)/deps +export LIBS := $(BASEDIR)/lib + +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + +export LIBDIR := $(LIBS)/$(PLATFORM) +export DEPSDIR := $(DEPS)/$(PLATFORM) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +LIBRARY := $(LIBDIR)/libspumars + +KERNELMODULE := $(BASEDIR)/../spu/lib/mars_kernel.elf +TASKMODULE := $(BASEDIR)/../spu/lib/mars_task_module.elf + +#--------------------------------------------------------------------------------- +INCLUDES := -I$(BASEDIR) \ + -I$(BASEDIR)/../include/common \ + -I$(BASEDIR)/../include/ppu \ + -I$(BASEDIR)/../base/common \ + -I$(BASEDIR)/../task/common \ + -I$(BASEDIR)/../../../ppu/include + +CFLAGS := -O2 -mregnames -Wall -mcpu=cell $(MACHDEP) -DLIBSPUMARS_INTERNAL $(INCLUDES) -Wa,-mcell +ASFLAGS := $(MACHDEP) -mregnames -mcpu=cell -D__ASSEMBLY__ -Wa,-mcell $(INCLUDES) + +#--------------------------------------------------------------------------------- +VPATH := $(BASEDIR) \ + $(BASEDIR)/base/lib \ + $(BASEDIR)/task/lib + +#--------------------------------------------------------------------------------- +OBJS := alloc.o ea_cell.o workload_queue.o context.o \ + mpu_cell.o mutex_cell.o cond_cell.o callback_cell.o \ + host_mutex_psl1ght.o mars_kernel.o \ + task.o task_signal.o task_semaphore.o task_queue.o \ + task_event_flag.o task_barrier.o mars_task_module.o + +all: ppu + +#--------------------------------------------------------------------------------- +ppu: +#--------------------------------------------------------------------------------- + @[ -d $(LIBS)/ppu ] || mkdir -p $(LIBS)/ppu + @[ -d $(DEPS)/ppu ] || mkdir -p $(DEPS)/ppu + @[ -d ppu ] || mkdir -p ppu + @$(MAKE) PLATFORM=ppu lib -C ppu -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +install-header: +#--------------------------------------------------------------------------------- + @[ -d $(PSL1GHT)/ppu/include ] || mkdir -p $(PSL1GHT)/ppu/include + @cp -frv $(CURDIR)/../include/common/mars $(PSL1GHT)/ppu/include + @cp -frv $(CURDIR)/../include/ppu/mars $(PSL1GHT)/ppu/include + +#--------------------------------------------------------------------------------- +install: all install-header +#--------------------------------------------------------------------------------- + @[ -d $(PSL1GHT)/ppu/lib ] || mkdir -p $(PSL1GHT)/ppu/lib + @cp -frv $(CURDIR)/lib/ppu/*.a $(PSL1GHT)/ppu/lib + +#--------------------------------------------------------------------------------- +$(LIBRARY).a: $(OBJS) + +mars_kernel.c: $(KERNELMODULE) + @echo generating ... $(notdir $@) + @echo '__attribute__((aligned(128))) const unsigned char mars_kernel_entry[] = {' > $@.tmp + @od -v -A n -t x1 $(KERNELMODULE) | \ + sed -e '/^$$/d' -e 's/ */,0x/g' -e 's/^,//' -e 's/ *$$/,/' -e 's/,0x,$$//' >> $@.tmp + @echo '};' >> $@.tmp + @mv -f $@.tmp $@ + +mars_task_module.c: $(TASKMODULE) + @echo generating ... $(notdir $@) + @echo '__attribute__((aligned(128))) const unsigned char mars_task_module_entry[] = {' > $@.tmp + @od -v -A n -t x1 $(TASKMODULE) | \ + sed -e '/^$$/d' -e 's/ */,0x/g' -e 's/^,//' -e 's/ *$$/,/' -e 's/,0x,$$//' >> $@.tmp + @echo '};' >> $@.tmp + @mv -f $@.tmp $@ + +#--------------------------------------------------------------------------------- + +.PHONY: lib ppu install + +#--------------------------------------------------------------------------------- +lib: mars_kernel.c mars_task_module.c $(LIBRARY).a +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +clean: +#--------------------------------------------------------------------------------- + @echo clean ... + @rm -rf ppu + @rm -rf $(DEPS) + @rm -rf $(LIBS) + +-include $(DEPSDIR)/*.d diff --git a/common/libspumars/ppu/base/lib/alloc.c b/common/libspumars/ppu/base/lib/alloc.c new file mode 100644 index 00000000..6e2f6520 --- /dev/null +++ b/common/libspumars/ppu/base/lib/alloc.c @@ -0,0 +1,18 @@ +#include + +#include + +void *mars_malloc(size_t size) +{ + return malloc(size); +} + +void *mars_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +void mars_free(void *ptr) +{ + free(ptr); +} diff --git a/common/libspumars/ppu/base/lib/callback_cell.c b/common/libspumars/ppu/base/lib/callback_cell.c new file mode 100644 index 00000000..a7a5c3af --- /dev/null +++ b/common/libspumars/ppu/base/lib/callback_cell.c @@ -0,0 +1,272 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "callback_internal_types.h" +#include "kernel_internal_types.h" +#include "workload_internal_types.h" + +#include "context_internal.h" + +static inline uint64_t get_workload_ea(uint64_t queue_ea, int workload_id) +{ + uint64_t context_ea = + mars_ea_get_uint64(queue_ea + + offsetof(struct mars_workload_queue_header, + context_ea)); + int context_index = + workload_id - (workload_id / MARS_WORKLOAD_PER_BLOCK) - 1; + + return context_ea + context_index * MARS_WORKLOAD_CONTEXT_SIZE; +} + +static void callback_process(struct mars_context *mars, uint16_t workload_id) +{ + uint64_t workload_callback_ea; + struct mars_workload_callback *workload_callback; + struct mars_callback_args out; + + /* busy loop until workload is put into wait state */ + while (!mars_workload_queue_query(mars, workload_id, + MARS_WORKLOAD_QUERY_IS_WAITING)) { + } + + /* get ea of workload callback structure */ + workload_callback_ea = + get_workload_ea(mars->workload_queue_ea, workload_id) + + MARS_WORKLOAD_MODULE_SIZE; + + /* prepare work area for queue */ + workload_callback = + mars_ea_work_area_get(workload_callback_ea, + MARS_WORKLOAD_CALLBACK_ALIGN, + MARS_WORKLOAD_CALLBACK_SIZE); + + /* get workload callback structure from ea */ + mars_ea_get(workload_callback_ea, workload_callback, + MARS_WORKLOAD_CALLBACK_SIZE); + + /* call the callback and store the return value */ + workload_callback->callback_ret = + ((mars_callback) + mars_ea_to_ptr(workload_callback->callback_ea)) + (&workload_callback->callback_args, &out); + + /* copy output args back into callback structure */ + workload_callback->callback_args = out; + + /* update workload data on ea */ + mars_ea_put(workload_callback_ea, workload_callback, + MARS_WORKLOAD_CALLBACK_SIZE); +} + +static int callback_queue_pop(uint64_t queue_ea, uint16_t *workload_id) +{ + struct mars_callback_queue *queue; + + /* prepare work area for queue */ + queue = mars_ea_work_area_get(queue_ea, + MARS_CALLBACK_QUEUE_ALIGN, + MARS_CALLBACK_QUEUE_SIZE); + + /* lock the queue */ + mars_mutex_lock_get(queue_ea, (struct mars_mutex *)queue); + + /* queue is empty (false signal?) */ + if (!queue->count) { + mars_mutex_unlock(queue_ea); + return MARS_ERROR_STATE; + } + + /* get workload id from head of queue */ + *workload_id = queue->workload_id[queue->head]; + + /* decrement count */ + queue->count--; + + /* increment head */ + queue->head++; + + /* wrap head to front of queue if necessary */ + if (queue->head == MARS_CALLBACK_QUEUE_MAX) + queue->head = 0; + + /* if queue is empty reset the queue flag */ + if (!queue->count) + queue->flag = MARS_CALLBACK_QUEUE_FLAG_NONE; + + /* unlock the queue */ + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)queue); + + return MARS_SUCCESS; +} + +static int callback_queue_state(uint32_t flag, void *param) +{ + (void)param; + + /* exit flag is set so return */ + switch (flag) { + case MARS_CALLBACK_QUEUE_FLAG_EXIT: + return MARS_ERROR_STATE; + case MARS_CALLBACK_QUEUE_FLAG_PUSH: + return MARS_SUCCESS; + default: + return -1; + } +} + +static void callback_handler_thread(void *arg) +{ + struct mars_context *mars = (struct mars_context *)arg; + uint64_t queue_ea = mars->callback_queue_ea; + uint16_t workload_id; + int ret; + + while (1) { + /* wait until kernel requests callback processing */ + ret = mars_ea_cond_wait( + queue_ea + offsetof(struct mars_callback_queue, flag), + callback_queue_state, NULL); + if (ret != MARS_SUCCESS) + break; + + /* pop the workload id requesting callback */ + ret = callback_queue_pop(queue_ea, &workload_id); + if (ret != MARS_SUCCESS) + continue; + + /* process the callback requested by workload */ + callback_process(mars, workload_id); + + /* signal the workload for completion of callback */ + mars_workload_queue_signal_send(mars, workload_id); + } + + sysThreadExit(0); +} + +int mars_callback_queue_create(struct mars_context *mars, uint32_t ppu_prio) +{ + int ret; + int i; + uint64_t queue_ea; + struct mars_callback_queue *queue; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + if (mars->callback_queue_ea) + return MARS_ERROR_STATE; + + /* allocate queue instance */ + queue_ea = mars_ea_memalign(MARS_CALLBACK_QUEUE_ALIGN, + MARS_CALLBACK_QUEUE_SIZE); + if (!queue_ea) + return MARS_ERROR_MEMORY; + + /* prepare work area for queue */ + queue = mars_ea_work_area_get(queue_ea, + MARS_CALLBACK_QUEUE_ALIGN, + MARS_CALLBACK_QUEUE_SIZE); + + /* initialize queue structure */ + queue->flag = MARS_CALLBACK_QUEUE_FLAG_NONE; + queue->count = 0; + queue->head = 0; + queue->tail = 0; + + for (i = 0; i < MARS_CALLBACK_QUEUE_MAX; i++) + queue->workload_id[i] = MARS_WORKLOAD_ID_NONE; + + /* update queue on EA */ + mars_ea_put(queue_ea, queue, MARS_CALLBACK_QUEUE_SIZE); + + /* reset mutex portion of queue header */ + mars_mutex_reset(queue_ea); + + /* sync EA */ + mars_ea_sync(); + + /* set the host callback queue instance in the mars context */ + mars->callback_queue_ea = queue_ea; + + /* create the host callback handler thread */ + ret = sysThreadCreate(&mars->callback_handler, callback_handler_thread, mars, ppu_prio, 4096, THREAD_JOINABLE, "mars_callback_handler"); + if (ret) { + mars_ea_free(queue_ea); + return MARS_ERROR_INTERNAL; + } + + return MARS_SUCCESS; +} + +int mars_callback_queue_destroy(struct mars_context *mars) +{ + int ret; + uint64_t ret_val; + uint64_t queue_ea; + struct mars_callback_queue *queue; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + if (!mars->callback_queue_ea) + return MARS_ERROR_PARAMS; + + queue_ea = mars->callback_queue_ea; + + /* prepare work area for queue */ + queue = mars_ea_work_area_get(mars->callback_queue_ea, + MARS_CALLBACK_QUEUE_ALIGN, + MARS_CALLBACK_QUEUE_SIZE); + + /* get queue from ea */ + mars_ea_get(queue_ea, queue, MARS_CALLBACK_QUEUE_SIZE); + + /* make sure queue is empty */ + if (queue->count) + return MARS_ERROR_STATE; + + /* join the host callback handler thread */ + ret = sysThreadJoin(mars->callback_handler, &ret_val); + if (ret) + return MARS_ERROR_INTERNAL; + + /* free host callback queue instance */ + mars_ea_free(queue_ea); + + /* set the workload queue to NULL for error checking */ + mars->callback_queue_ea = 0; + + return MARS_SUCCESS; +} + +int mars_callback_queue_exit(struct mars_context *mars) +{ + uint64_t queue_ea; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + if (!mars->callback_queue_ea) + return MARS_ERROR_PARAMS; + + queue_ea = mars->callback_queue_ea; + + /* set callback queue exit flag */ + mars_ea_put_uint32( + queue_ea + offsetof(struct mars_callback_queue, flag), + MARS_CALLBACK_QUEUE_FLAG_EXIT); + + /* signal callback handler to wake up from wait */ + mars_ea_cond_signal( + queue_ea + offsetof(struct mars_callback_queue, flag), 1); + + return MARS_SUCCESS; +} diff --git a/common/libspumars/ppu/base/lib/cond_cell.c b/common/libspumars/ppu/base/lib/cond_cell.c new file mode 100644 index 00000000..0bf8d6d3 --- /dev/null +++ b/common/libspumars/ppu/base/lib/cond_cell.c @@ -0,0 +1,58 @@ +#include +#include + +#include + +#include +#include + +#include +#include + +static sys_cond_t eaCond; +static sys_mutex_t eaCondMutex; + +static void __mars_ea_cond_init() __attribute__((constructor(105))); +static void __mars_ea_cond_init() +{ + sys_cond_attr_t cattr; + sys_mutex_attr_t mattr; + + sysCondAttrInitialize(cattr); + sysMutexAttrInitialize(mattr); + + sysMutexCreate(&eaCondMutex, &mattr); + sysCondCreate(&eaCond, eaCondMutex, &cattr); +} + +int mars_ea_cond_wait(uint64_t watch_point_ea, + int (*test_cond)(uint32_t , void *), + void *test_cond_param) +{ + volatile int ret; + + sysMutexLock(eaCondMutex, 0); + do { + volatile uint32_t val = (volatile uint32_t)mars_ea_get_uint32(watch_point_ea); + + ret = (volatile int)(*test_cond)(val, test_cond_param); + if (ret >= 0) + break; + + sysCondWait(eaCond, 0); + } while(ret < 0); + sysMutexUnlock(eaCondMutex); + + return ret; +} + +int mars_ea_cond_signal(uint64_t watch_point_ea, int broadcast) +{ + sysMutexLock(eaCondMutex, 0); + if(broadcast) + sysCondBroadcast(eaCond); + else + sysCondSignal(eaCond); + sysMutexUnlock(eaCondMutex); + return MARS_SUCCESS; +} diff --git a/common/libspumars/ppu/base/lib/context.c b/common/libspumars/ppu/base/lib/context.c new file mode 100644 index 00000000..80d9a734 --- /dev/null +++ b/common/libspumars/ppu/base/lib/context.c @@ -0,0 +1,339 @@ +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "callback_internal_types.h" +#include "kernel_internal_types.h" +#include "workload_internal_types.h" + +#include "context_internal.h" + +uint32_t mars_get_ticks(void) +{ + return __mftb() & 0xffffffff; +} + +static void kernel_ticks_sync(uint64_t kernel_ticks_ea) +{ + uint64_t flag_ea = + kernel_ticks_ea + offsetof(struct mars_kernel_ticks, flag); + uint64_t offset_ea = + kernel_ticks_ea + offsetof(struct mars_kernel_ticks, offset); + + /* wait until kernel sets the sync begin flag */ + do { + } while (mars_ea_get_uint32(flag_ea) != + MARS_KERNEL_TICKS_FLAG_SYNC_BEGIN); + + mars_ea_sync(); + + mars_ea_put_uint32(offset_ea, mars_get_ticks()); + + mars_ea_sync(); + + /* set the sync end flag so kernel can finish sync */ + mars_ea_put_uint32(flag_ea, MARS_KERNEL_TICKS_FLAG_SYNC_END); +} + +static int kernel_params_init(struct mars_context *mars, uint64_t params_ea, + uint16_t kernel_id) +{ + struct mars_kernel_params *params = + mars_ea_work_area_get(params_ea, + MARS_KERNEL_PARAMS_ALIGN, + MARS_KERNEL_PARAMS_SIZE); + + if (!params) + return MARS_ERROR_MEMORY; + + /* zero kernel params */ + memset(params, 0, MARS_KERNEL_PARAMS_SIZE); + + params->kernel_id = kernel_id; + params->mars_context_ea = mars_ptr_to_ea(mars); + params->workload_queue_ea = mars->workload_queue_ea; + params->callback_queue_ea = mars->callback_queue_ea; + + /* update params on EA */ + mars_ea_put(params_ea, params, MARS_KERNEL_PARAMS_SIZE); + mars_ea_sync(); + + return MARS_SUCCESS; +} + +static int mpu_context_create(struct mars_context *mars, uint32_t num_mpus, uint32_t spu_prio, uint32_t ppu_prio) +{ + int ret; + uint16_t i; + mars_mpu_context_t *mpu = mars->mpu_context; + + mpu->num_spu = num_mpus; + mpu->spu_prio = spu_prio; + mpu->ppu_prio = ppu_prio; + + ret = mars_mpu_create(mpu, num_mpus, spu_prio, ppu_prio); + if(ret != MARS_SUCCESS) + return ret; + + /* create threads for mpu context */ + for (i = 0; i < num_mpus; i++) { + uint64_t params_ea = mars->kernel_params_ea + + MARS_KERNEL_PARAMS_SIZE * i; + + /* initialize kernel params for current mpu context */ + ret = kernel_params_init(mars, params_ea, i); + if (ret != MARS_SUCCESS) + return ret; + + /* run current mpu context */ + ret = mars_mpu_initialize(mpu, params_ea, i); + if (ret != MARS_SUCCESS) + return ret; + + /* increment mars context mpu context count */ + mars->mpu_context_count++; + } + mars_mpu_run(mpu); + + for(i = 0; i < num_mpus; i++) { + uint64_t params_ea = mars->kernel_params_ea + + MARS_KERNEL_PARAMS_SIZE * i; + /* sync kernel ticks for current mpu context */ + kernel_ticks_sync(params_ea + + offsetof(struct mars_kernel_params, kernel_ticks)); + } + + return MARS_SUCCESS; +} + +static int mpu_context_destroy(struct mars_context *mars) +{ + int ret; + + /* wait for mpu context */ + ret = mars_mpu_wait_all(mars->mpu_context); + if (ret != MARS_SUCCESS) + return ret; + + return MARS_SUCCESS; +} + +static int system_sanity_check(void) +{ + if ((MARS_CALLBACK_ARGS_SIZE != + sizeof(struct mars_callback_args)) || + (MARS_CALLBACK_QUEUE_SIZE != + sizeof(struct mars_callback_queue)) || + (MARS_KERNEL_PARAMS_SIZE != + sizeof(struct mars_kernel_params)) || + (MARS_MUTEX_SIZE != + sizeof(struct mars_mutex)) || + (MARS_WORKLOAD_CALLBACK_SIZE != + sizeof(struct mars_workload_callback)) || + (MARS_WORKLOAD_CONTEXT_SIZE != + sizeof(struct mars_workload_context)) || + (MARS_WORKLOAD_MODULE_SIZE != + sizeof(struct mars_workload_module)) || + (MARS_WORKLOAD_QUEUE_SIZE != + sizeof(struct mars_workload_queue)) || + (MARS_WORKLOAD_QUEUE_HEADER_SIZE != + sizeof(struct mars_workload_queue_header)) || + (MARS_WORKLOAD_QUEUE_BLOCK_SIZE != + sizeof(struct mars_workload_queue_block))) + return MARS_ERROR_INTERNAL; + + return MARS_SUCCESS; +} + +int mars_context_create(struct mars_context **mars_ret, uint32_t num_mpus, uint32_t spu_prio, uint32_t ppu_prio) +{ + int ret; + uint32_t num_mpus_max; + struct mars_context *mars = NULL; + + /* check function params */ + if (!mars_ret) + return MARS_ERROR_NULL; + + /* check number of mpus to use */ + ret = mars_mpu_max(&num_mpus_max); + if (ret != MARS_SUCCESS) + return ret; + if (!num_mpus_max) + return MARS_ERROR_LIMIT; + if (num_mpus > num_mpus_max) + return MARS_ERROR_PARAMS; + if (!num_mpus) + num_mpus = num_mpus_max; + + /* system sanity check */ + ret = system_sanity_check(); + if (ret != MARS_SUCCESS) + return ret; + + /* lock mutex */ + ret = mars_host_mutex_lock(&mars_shared_context_lock); + if (ret != MARS_SUCCESS) + return ret; + + /* allocate context */ + mars = mars_malloc(sizeof(struct mars_context)); + if (!mars) { + ret = MARS_ERROR_MEMORY; + goto error; + } + + /* zero context */ + memset(mars, 0, sizeof(struct mars_context)); + + /* increment reference count */ + mars->reference_count++; + + /* allocate kernel params */ + mars->kernel_params_ea = mars_ea_memalign( + MARS_KERNEL_PARAMS_ALIGN, + MARS_KERNEL_PARAMS_SIZE * num_mpus_max); + if (!mars->kernel_params_ea) { + ret = MARS_ERROR_MEMORY; + goto error_malloc_kernel_params; + } + + /* allocate mpu context thread array */ + mars->mpu_context = (mars_mpu_context_t *)mars_malloc(sizeof(mars_mpu_context_t)); + if (!mars->mpu_context) { + ret = MARS_ERROR_MEMORY; + goto error_malloc_mpu_context; + } + + /* create workload queue */ + ret = mars_workload_queue_create(mars); + if (ret != MARS_SUCCESS) + goto error_workload_queue_create; + + /* create callback queue */ + ret = mars_callback_queue_create(mars, ppu_prio); + if (ret != MARS_SUCCESS) + goto error_callback_queue_create; + + /* create mpu contexts */ + ret = mpu_context_create(mars, num_mpus, spu_prio, ppu_prio); + if (ret != MARS_SUCCESS) + goto error_mpu_context_create; + + /* return mars context pointer */ + *mars_ret = mars; + + /* unlock mutex */ + ret = mars_host_mutex_unlock(&mars_shared_context_lock); + if (ret != MARS_SUCCESS) + goto error_shared_context_unlock; + + return MARS_SUCCESS; + +error_shared_context_unlock: + mpu_context_destroy(mars); +error_mpu_context_create: + mars_callback_queue_exit(mars); + mars_callback_queue_destroy(mars); +error_callback_queue_create: + mars_workload_queue_exit(mars); + mars_workload_queue_destroy(mars); +error_workload_queue_create: + mars_free(mars->mpu_context); +error_malloc_mpu_context: + mars_ea_free(mars->kernel_params_ea); +error_malloc_kernel_params: + mars_free(mars); +error: + mars_host_mutex_unlock(&mars_shared_context_lock); + + return ret; +} + +int mars_context_destroy(struct mars_context *mars) +{ + int ret; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + + /* lock mutex */ + ret = mars_host_mutex_lock(&mars_shared_context_lock); + if (ret != MARS_SUCCESS) + return ret; + + /* decrement reference count */ + mars->reference_count--; + + /* reference count is not 0 so return */ + if (mars->reference_count) + goto done; + + /* shutdown the workload queue so mpu context threads exit */ + ret = mars_workload_queue_exit(mars); + if (ret != MARS_SUCCESS) + return ret; + + /* shutdown the callback queue so callback handler threads exit */ + ret = mars_callback_queue_exit(mars); + if (ret != MARS_SUCCESS) + return ret; + + /* destroy mpu contexts */ + if (mars->mpu_context_count) { + ret = mpu_context_destroy(mars); + if (ret != MARS_SUCCESS) + goto error; + } + + /* destroy callback queue */ + if (mars->callback_queue_ea) { + ret = mars_callback_queue_destroy(mars); + if (ret != MARS_SUCCESS) + goto error; + } + + /* destroy workload queue */ + if (mars->workload_queue_ea) { + ret = mars_workload_queue_destroy(mars); + if (ret != MARS_SUCCESS) + goto error; + } + + /* free allocated memory */ + mars_free(mars->mpu_context); + mars_ea_free(mars->kernel_params_ea); + mars_free(mars); + +done: + /* unlock mutex */ + ret = mars_host_mutex_unlock(&mars_shared_context_lock); + if (ret != MARS_SUCCESS) + return ret; + + return MARS_SUCCESS; + +error: + mars_host_mutex_unlock(&mars_shared_context_lock); + + return ret; +} + +uint32_t mars_context_get_num_mpus(struct mars_context *mars) +{ + /* check function params */ + if (!mars) + return 0; + + return mars->mpu_context_count; +} diff --git a/common/libspumars/ppu/base/lib/context_internal.h b/common/libspumars/ppu/base/lib/context_internal.h new file mode 100644 index 00000000..57b7746c --- /dev/null +++ b/common/libspumars/ppu/base/lib/context_internal.h @@ -0,0 +1,62 @@ +#ifndef __MARS_CONTEXT_INTERNAL_H__ +#define __MARS_CONTEXT_INTERNAL_H__ + +#include +#include +#include +#include +#include + +#define MARS_SHARED_CONTEXT_MAX 16 + +typedef struct { + uint32_t num_spu; + uint32_t spu_prio; + uint32_t ppu_prio; + sys_event_queue_t spu_ppu_event_queue; + sys_ppu_thread_t ppu_handler_thread; + sys_spu_group_t spu_thread_group; + sys_spu_thread_t *spu_threads; + sysSpuImage spu_image; +} mars_mpu_context_t; + +typedef sys_mutex_t mars_host_mutex_t; +typedef sys_ppu_thread_t mars_callback_t; + +struct mars_context { + /* parameters for the MARS kernel */ + uint64_t kernel_params_ea; + /* workload queue where workloads are added */ + uint64_t workload_queue_ea; + /* callback queue where host callback requests are added */ + uint64_t callback_queue_ea; + /* reference count */ + uint32_t reference_count; + /* num of mpu context threads */ + uint32_t mpu_context_count; + /* array of mpu contexts */ + mars_mpu_context_t *mpu_context; + /* callback handler */ + mars_callback_t callback_handler; +}; + +int mars_mpu_max(uint32_t *num); +int mars_mpu_run(mars_mpu_context_t *mpu); +int mars_mpu_initialize(mars_mpu_context_t *mpu, uint64_t params_ea, uint32_t idx); +int mars_mpu_create(mars_mpu_context_t *mpu, uint32_t num_mpus, uint32_t spu_prio, uint32_t ppu_prio); +int mars_mpu_wait_all(mars_mpu_context_t *mpu); + +int mars_workload_queue_create(struct mars_context *mars); +int mars_workload_queue_destroy(struct mars_context *mars); +int mars_workload_queue_exit(struct mars_context *mars); + +int mars_callback_queue_create(struct mars_context *mars, uint32_t ppu_prio); +int mars_callback_queue_destroy(struct mars_context *mars); +int mars_callback_queue_exit(struct mars_context *mars); + +int mars_host_mutex_lock(mars_host_mutex_t *mutex); +int mars_host_mutex_unlock(mars_host_mutex_t *mutex); + +extern mars_host_mutex_t mars_shared_context_lock; + +#endif diff --git a/common/libspumars/ppu/base/lib/ea_cell.c b/common/libspumars/ppu/base/lib/ea_cell.c new file mode 100644 index 00000000..9c591186 --- /dev/null +++ b/common/libspumars/ppu/base/lib/ea_cell.c @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#include + +#include + +#include "numa_internal.h" + +/* These functions must not be called */ +static uint64_t numa_ea_memalign(size_t boundary, size_t size) +{ + (void)boundary; + (void)size; + + return 0; +} + +static void numa_ea_free(uint64_t ea) +{ + (void)ea; +} + +uint64_t mars_ea_memalign(size_t boundary, size_t size) +{ + if (mars_numa_enabled()) + return numa_ea_memalign(boundary, size); + else + return mars_ptr_to_ea(memalign(boundary, size)); +} + +void mars_ea_free(uint64_t ea) +{ + if (mars_numa_enabled()) + numa_ea_free(ea); + else + free(mars_ea_to_ptr(ea)); +} + +/* copy data from EA to host */ +void mars_ea_get(uint64_t ea, void *mem, size_t size) +{ + const void *src = mars_ea_to_ptr(ea); + if (src != mem) + memcpy(mem, src, size); +} + +/* get uint8 value from EA */ +uint8_t mars_ea_get_uint8(uint64_t ea) +{ + return *(uint8_t *)mars_ea_to_ptr(ea); +} + +/* get uint16 value from EA */ +uint16_t mars_ea_get_uint16(uint64_t ea) +{ + return *(uint16_t *)mars_ea_to_ptr(ea); +} + +/* get uint32 value from EA */ +uint32_t mars_ea_get_uint32(uint64_t ea) +{ + return *(uint32_t *)mars_ea_to_ptr(ea); +} + +/* get uint64 value from EA */ +uint64_t mars_ea_get_uint64(uint64_t ea) +{ + return *(uint64_t *)mars_ea_to_ptr(ea); +} + +/* copy data from host to EA */ +void mars_ea_put(uint64_t ea, const void *mem, size_t size) +{ + void *dst = mars_ea_to_ptr(ea); + if (dst != mem) + memcpy(dst, mem, size); +} + +/* put uint8 value to EA */ +void mars_ea_put_uint8(uint64_t ea, uint8_t value) +{ + *(uint8_t *)mars_ea_to_ptr(ea) = value; +} + +/* put uint16 value to EA */ +void mars_ea_put_uint16(uint64_t ea, uint16_t value) +{ + *(uint16_t *)mars_ea_to_ptr(ea) = value; +} + +/* put uint32 value to EA */ +void mars_ea_put_uint32(uint64_t ea, uint32_t value) +{ + *(uint32_t *)mars_ea_to_ptr(ea) = value; +} + +/* put uint64 value to EA */ +void mars_ea_put_uint64(uint64_t ea, uint64_t value) +{ + *(uint64_t *)mars_ea_to_ptr(ea) = value; +} + +/* map readonly area on host memory to EA */ +uint64_t mars_ea_map(void *ptr, size_t size) +{ +#ifdef MARS_ENABLE_DISCRETE_SHARED_MEMORY + uint64_t copy_ea; + + copy_ea = mars_ea_memalign(16, size); /* FIXME: 16 -> any macro */ + if (!copy_ea) + return 0; + + mars_ea_put(copy_ea, ptr, size); + mars_ea_sync(); + + return copy_ea; +#else /* !MARS_ENABLE_DISCRETE_SHARED_MEMORY */ + (void)size; + return mars_ptr_to_ea(ptr); +#endif /* !MARS_ENABLE_DISCRETE_SHARED_MEMORY */ +} + +/* unmap area mapped by mars_ea_map */ +void mars_ea_unmap(uint64_t ea, size_t size) +{ + (void)size; + +#ifdef MARS_ENABLE_DISCRETE_SHARED_MEMORY + mars_ea_free(ea); +#else /* !MARS_ENABLE_DISCRETE_SHARED_MEMORY */ + (void)ea; +#endif /* !MARS_ENABLE_DISCRETE_SHARED_MEMORY */ +} + +/* sync EA */ +void mars_ea_sync(void) +{ + __lwsync(); +} diff --git a/common/libspumars/ppu/base/lib/elf.h b/common/libspumars/ppu/base/lib/elf.h new file mode 100644 index 00000000..b54dfc68 --- /dev/null +++ b/common/libspumars/ppu/base/lib/elf.h @@ -0,0 +1,2459 @@ +/* This file defines standard ELF types, structures, and macros. + Copyright (C) 1995-2003, 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef __ELF_H__ +#define __ELF_H__ + +//#include + +/* Standard ELF types. */ + +#include + +/* Type for a 16-bit quantity. */ +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +/* Types for signed and unsigned 32-bit quantities. */ +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; + +/* Types for signed and unsigned 64-bit quantities. */ +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* Type of addresses. */ +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; + +/* Type of file offsets. */ +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Off; + +/* Type for section indices, which are 16-bit quantities. */ +typedef uint16_t Elf32_Section; +typedef uint16_t Elf64_Section; + +/* Type for version symbol information. */ +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + + +/* The ELF file header. This appears at the start of every ELF file. */ + +#define EI_NIDENT (16) + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +/* Fields in the e_ident array. The EI_* macros are indices into the + array. The macros under each EI_* macro are the values the byte + may have. */ + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +/* Conglomeration of the identification bytes, for easy testing as a word. */ +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define ELFCLASSNUM 3 + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define ELFDATANUM 3 + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_SYSV 0 /* Alias. */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD. */ +#define ELFOSABI_LINUX 3 /* Linux. */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ +#define ELFOSABI_AIX 7 /* IBM AIX. */ +#define ELFOSABI_IRIX 8 /* SGI Irix. */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +/* Legal values for e_type (object file type). */ + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_NUM 5 /* Number of defined types */ +#define ET_LOOS 0xfe00 /* OS-specific range start */ +#define ET_HIOS 0xfeff /* OS-specific range end */ +#define ET_LOPROC 0xff00 /* Processor-specific range start */ +#define ET_HIPROC 0xffff /* Processor-specific range end */ + +/* Legal values for e_machine (architecture). */ + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* IBM System/370 */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC 64-bit */ +#define EM_S390 22 /* IBM S390 */ + +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ + +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_NUM 95 + +/* If it is necessary to assign new unofficial EM_* values, please + pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the + chances of collision with official or non-GNU unofficial values. */ + +#define EM_ALPHA 0x9026 + +/* Legal values for e_version (version). */ + +#define EV_NONE 0 /* Invalid ELF version */ +#define EV_CURRENT 1 /* Current version */ +#define EV_NUM 2 + +/* Section header. */ + +typedef struct +{ + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +typedef struct +{ + Elf64_Word sh_name; /* Section name (string tbl index) */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section flags */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Section size in bytes */ + Elf64_Word sh_link; /* Link to another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +/* Special section indices. */ + +#define SHN_UNDEF 0 /* Undefined section */ +#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ +#define SHN_LOPROC 0xff00 /* Start of processor-specific */ +#define SHN_BEFORE 0xff00 /* Order section before all others + (Solaris). */ +#define SHN_AFTER 0xff01 /* Order section after all others + (Solaris). */ +#define SHN_HIPROC 0xff1f /* End of processor-specific */ +#define SHN_LOOS 0xff20 /* Start of OS-specific */ +#define SHN_HIOS 0xff3f /* End of OS-specific */ +#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ +#define SHN_COMMON 0xfff2 /* Associated symbol is common */ +#define SHN_XINDEX 0xffff /* Index is in extra table. */ +#define SHN_HIRESERVE 0xffff /* End of reserved indices */ + +/* Legal values for sh_type (section type). */ + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* Number of defined types. */ +#define SHT_LOOS 0x60000000 /* Start OS-specific */ +#define SHT_GNU_HASH 0x6ffffff6 /* GNU style symbol hash table. */ +#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ +#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ +#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ +#define SHT_SUNW_move 0x6ffffffa +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ +#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ +#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ +#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ +#define SHT_HIOS 0x6fffffff /* End OS-specific type */ +#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ +#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ +#define SHT_LOUSER 0x80000000 /* Start of application-specific */ +#define SHT_HIUSER 0x8fffffff /* End of application-specific */ + +/* Legal values for sh_flags (section flags). */ + +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ +#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ +#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ +#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling + required */ +#define SHF_GROUP (1 << 9) /* Section is member of a group. */ +#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ +#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ +#define SHF_ORDERED (1 << 30) /* Special ordering requirement + (Solaris). */ +#define SHF_EXCLUDE (1 << 31) /* Section is excluded unless + referenced or allocated (Solaris).*/ + +/* Section group handling. */ +#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ + +/* Symbol table entry. */ + +typedef struct +{ + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +typedef struct +{ + Elf64_Word st_name; /* Symbol name (string tbl index) */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf64_Section st_shndx; /* Section index */ + Elf64_Addr st_value; /* Symbol value */ + Elf64_Xword st_size; /* Symbol size */ +} Elf64_Sym; + +/* The syminfo section if available contains additional information about + every dynamic symbol. */ + +typedef struct +{ + Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf32_Half si_flags; /* Per symbol flags */ +} Elf32_Syminfo; + +typedef struct +{ + Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf64_Half si_flags; /* Per symbol flags */ +} Elf64_Syminfo; + +/* Possible values for si_boundto. */ +#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ +#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ +#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ + +/* Possible bitmasks for si_flags. */ +#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ +#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ +#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ +#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy + loaded */ +/* Syminfo version values. */ +#define SYMINFO_NONE 0 +#define SYMINFO_CURRENT 1 +#define SYMINFO_NUM 2 + + +/* How to extract and insert information held in the st_info field. */ + +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) + +/* Legal values for ST_BIND subfield of st_info (symbol binding). */ + +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types. */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_NOTYPE 0 /* Symbol type is unspecified */ +#define STT_OBJECT 1 /* Symbol is a data object */ +#define STT_FUNC 2 /* Symbol is a code object */ +#define STT_SECTION 3 /* Symbol associated with a section */ +#define STT_FILE 4 /* Symbol's name is file name */ +#define STT_COMMON 5 /* Symbol is a common data object */ +#define STT_TLS 6 /* Symbol is thread-local data object*/ +#define STT_NUM 7 /* Number of defined types. */ +#define STT_LOOS 10 /* Start of OS-specific */ +#define STT_HIOS 12 /* End of OS-specific */ +#define STT_LOPROC 13 /* Start of processor-specific */ +#define STT_HIPROC 15 /* End of processor-specific */ + + +/* Symbol table indices are found in the hash buckets and chain table + of a symbol hash table section. This special index value indicates + the end of a chain, meaning no further symbols are found in that bucket. */ + +#define STN_UNDEF 0 /* End of a chain. */ + + +/* How to extract and insert information held in the st_other field. */ + +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) + +/* For ELF64 the definitions are the same. */ +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) + +/* Symbol visibility specification encoded in the st_other field. */ +#define STV_DEFAULT 0 /* Default symbol visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Sym unavailable in other modules */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation table entry without addend (in section of type SHT_REL). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +/* I have seen two different definitions of the Elf64_Rel and + Elf64_Rela structures, so we'll leave them out until Novell (or + whoever) gets their act together. */ +/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ +} Elf64_Rel; + +/* Relocation table entry with addend (in section of type SHT_RELA). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +} Elf32_Rela; + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ + Elf64_Sxword r_addend; /* Addend */ +} Elf64_Rela; + +/* How to extract and insert information held in the r_info field. */ + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val) & 0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) + +/* Program segment header. */ + +typedef struct +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +typedef struct +{ + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} Elf64_Phdr; + +/* Legal values for p_type (segment type). */ + +#define PT_NULL 0 /* Program header table entry unused */ +#define PT_LOAD 1 /* Loadable program segment */ +#define PT_DYNAMIC 2 /* Dynamic linking information */ +#define PT_INTERP 3 /* Program interpreter */ +#define PT_NOTE 4 /* Auxiliary information */ +#define PT_SHLIB 5 /* Reserved */ +#define PT_PHDR 6 /* Entry for header table itself */ +#define PT_TLS 7 /* Thread-local storage segment */ +#define PT_NUM 8 /* Number of defined types */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ +#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ +#define PT_LOSUNW 0x6ffffffa +#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ +#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ +#define PT_HISUNW 0x6fffffff +#define PT_HIOS 0x6fffffff /* End of OS-specific */ +#define PT_LOPROC 0x70000000 /* Start of processor-specific */ +#define PT_HIPROC 0x7fffffff /* End of processor-specific */ + +/* Legal values for p_flags (segment flags). */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ +#define PF_MASKOS 0x0ff00000 /* OS-specific */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Legal values for note segment descriptor types for core files. */ + +#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ +#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ +#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ +#define NT_PRXREG 4 /* Contains copy of prxregset struct */ +#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ +#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ +#define NT_AUXV 6 /* Contains copy of auxv array */ +#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ +#define NT_ASRS 8 /* Contains copy of asrset struct */ +#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ +#define NT_PSINFO 13 /* Contains copy of psinfo struct */ +#define NT_PRCRED 14 /* Contains copy of prcred struct */ +#define NT_UTSNAME 15 /* Contains copy of utsname struct */ +#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ +#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ +#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ + +/* Legal values for the note segment descriptor types for object files. */ + +#define NT_VERSION 1 /* Contains a version string. */ + + +/* Dynamic section entry. */ + +typedef struct +{ + Elf32_Sword d_tag; /* Dynamic entry type */ + union + { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +typedef struct +{ + Elf64_Sxword d_tag; /* Dynamic entry type */ + union + { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +} Elf64_Dyn; + +/* Legal values for d_tag (dynamic entry type). */ + +#define DT_NULL 0 /* Marks end of dynamic section */ +#define DT_NEEDED 1 /* Name of needed library */ +#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ +#define DT_PLTGOT 3 /* Processor defined value */ +#define DT_HASH 4 /* Address of symbol hash table */ +#define DT_STRTAB 5 /* Address of string table */ +#define DT_SYMTAB 6 /* Address of symbol table */ +#define DT_RELA 7 /* Address of Rela relocs */ +#define DT_RELASZ 8 /* Total size of Rela relocs */ +#define DT_RELAENT 9 /* Size of one Rela reloc */ +#define DT_STRSZ 10 /* Size of string table */ +#define DT_SYMENT 11 /* Size of one symbol table entry */ +#define DT_INIT 12 /* Address of init function */ +#define DT_FINI 13 /* Address of termination function */ +#define DT_SONAME 14 /* Name of shared object */ +#define DT_RPATH 15 /* Library search path (deprecated) */ +#define DT_SYMBOLIC 16 /* Start symbol search here */ +#define DT_REL 17 /* Address of Rel relocs */ +#define DT_RELSZ 18 /* Total size of Rel relocs */ +#define DT_RELENT 19 /* Size of one Rel reloc */ +#define DT_PLTREL 20 /* Type of reloc in PLT */ +#define DT_DEBUG 21 /* For debugging; unspecified */ +#define DT_TEXTREL 22 /* Reloc might modify .text */ +#define DT_JMPREL 23 /* Address of PLT relocs */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_NUM 34 /* Number used */ +#define DT_LOOS 0x6000000d /* Start of OS-specific */ +#define DT_HIOS 0x6ffff000 /* End of OS-specific */ +#define DT_LOPROC 0x70000000 /* Start of processor-specific */ +#define DT_HIPROC 0x7fffffff /* End of processor-specific */ +#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ + +/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the + Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's + approach. */ +#define DT_VALRNGLO 0x6ffffd00 +#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ +#define DT_CHECKSUM 0x6ffffdf8 +#define DT_PLTPADSZ 0x6ffffdf9 +#define DT_MOVEENT 0x6ffffdfa +#define DT_MOVESZ 0x6ffffdfb +#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ +#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting + the following DT_* entry. */ +#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ +#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ +#define DT_VALRNGHI 0x6ffffdff +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ +#define DT_VALNUM 12 + +/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the + Dyn.d_un.d_ptr field of the Elf*_Dyn structure. + + If any adjustment is made to the ELF object after it has been + built these entries will need to be adjusted. */ +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ +#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ +#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ +#define DT_CONFIG 0x6ffffefa /* Configuration information. */ +#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ +#define DT_AUDIT 0x6ffffefc /* Object auditing. */ +#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ +#define DT_MOVETAB 0x6ffffefe /* Move table. */ +#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ +#define DT_ADDRNUM 10 + +/* The versioning entry types. The next are defined as part of the + GNU extension. */ +#define DT_VERSYM 0x6ffffff0 + +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa + +/* These were chosen by Sun. */ +#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ +#define DT_VERDEF 0x6ffffffc /* Address of version definition + table */ +#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ +#define DT_VERNEED 0x6ffffffe /* Address of table with needed + versions */ +#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ +#define DT_VERSIONTAGNUM 16 + +/* Sun added these machine-independent extensions in the "processor-specific" + range. Be compatible. */ +#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ +#define DT_FILTER 0x7fffffff /* Shared object to get values from */ +#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) +#define DT_EXTRANUM 3 + +/* Values of `d_un.d_val' in the DT_FLAGS entry. */ +#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ +#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ + +/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 + entry in the dynamic section. */ +#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ +#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ +#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ +#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ +#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ +#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ +#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ +#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ +#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ +#define DF_1_TRANS 0x00000200 +#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ +#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ +#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ +#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ +#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ +#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ +#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ + +/* Flags for the feature selection in DT_FEATURE_1. */ +#define DTF_1_PARINIT 0x00000001 +#define DTF_1_CONFEXP 0x00000002 + +/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ +#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ +#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not + generally available. */ + +/* Version definition sections. */ + +typedef struct +{ + Elf32_Half vd_version; /* Version revision */ + Elf32_Half vd_flags; /* Version information */ + Elf32_Half vd_ndx; /* Version Index */ + Elf32_Half vd_cnt; /* Number of associated aux entries */ + Elf32_Word vd_hash; /* Version name hash value */ + Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf32_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf32_Verdef; + +typedef struct +{ + Elf64_Half vd_version; /* Version revision */ + Elf64_Half vd_flags; /* Version information */ + Elf64_Half vd_ndx; /* Version Index */ + Elf64_Half vd_cnt; /* Number of associated aux entries */ + Elf64_Word vd_hash; /* Version name hash value */ + Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf64_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf64_Verdef; + + +/* Legal values for vd_version (version revision). */ +#define VER_DEF_NONE 0 /* No version */ +#define VER_DEF_CURRENT 1 /* Current version */ +#define VER_DEF_NUM 2 /* Given version number */ + +/* Legal values for vd_flags (version information flags). */ +#define VER_FLG_BASE 0x1 /* Version definition of file itself */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + +/* Versym symbol index values. */ +#define VER_NDX_LOCAL 0 /* Symbol is local. */ +#define VER_NDX_GLOBAL 1 /* Symbol is global. */ +#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ +#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ + +/* Auxialiary version information. */ + +typedef struct +{ + Elf32_Word vda_name; /* Version or dependency names */ + Elf32_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf32_Verdaux; + +typedef struct +{ + Elf64_Word vda_name; /* Version or dependency names */ + Elf64_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf64_Verdaux; + + +/* Version dependency section. */ + +typedef struct +{ + Elf32_Half vn_version; /* Version of structure */ + Elf32_Half vn_cnt; /* Number of associated aux entries */ + Elf32_Word vn_file; /* Offset of filename for this + dependency */ + Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf32_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf32_Verneed; + +typedef struct +{ + Elf64_Half vn_version; /* Version of structure */ + Elf64_Half vn_cnt; /* Number of associated aux entries */ + Elf64_Word vn_file; /* Offset of filename for this + dependency */ + Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf64_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf64_Verneed; + + +/* Legal values for vn_version (version revision). */ +#define VER_NEED_NONE 0 /* No version */ +#define VER_NEED_CURRENT 1 /* Current version */ +#define VER_NEED_NUM 2 /* Given version number */ + +/* Auxiliary needed version information. */ + +typedef struct +{ + Elf32_Word vna_hash; /* Hash value of dependency name */ + Elf32_Half vna_flags; /* Dependency specific information */ + Elf32_Half vna_other; /* Unused */ + Elf32_Word vna_name; /* Dependency name string offset */ + Elf32_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf32_Vernaux; + +typedef struct +{ + Elf64_Word vna_hash; /* Hash value of dependency name */ + Elf64_Half vna_flags; /* Dependency specific information */ + Elf64_Half vna_other; /* Unused */ + Elf64_Word vna_name; /* Dependency name string offset */ + Elf64_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf64_Vernaux; + + +/* Legal values for vna_flags. */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + + +/* Auxiliary vector. */ + +/* This vector is normally only used by the program interpreter. The + usual definition in an ABI supplement uses the name auxv_t. The + vector is not usually defined in a standard file, but it + can't hurt. We rename it to avoid conflicts. The sizes of these + types are an arrangement between the exec server and the program + interpreter, so we don't fully specify them here. */ + +typedef struct +{ + int a_type; /* Entry type */ + union + { + long int a_val; /* Integer value */ + void *a_ptr; /* Pointer value */ + void (*a_fcn) (void); /* Function pointer value */ + } a_un; +} Elf32_auxv_t; + +typedef struct +{ + long int a_type; /* Entry type */ + union + { + long int a_val; /* Integer value */ + void *a_ptr; /* Pointer value */ + void (*a_fcn) (void); /* Function pointer value */ + } a_un; +} Elf64_auxv_t; + +/* Legal values for a_type (entry type). */ + +#define AT_NULL 0 /* End of vector */ +#define AT_IGNORE 1 /* Entry should be ignored */ +#define AT_EXECFD 2 /* File descriptor of program */ +#define AT_PHDR 3 /* Program headers for program */ +#define AT_PHENT 4 /* Size of program header entry */ +#define AT_PHNUM 5 /* Number of program headers */ +#define AT_PAGESZ 6 /* System page size */ +#define AT_BASE 7 /* Base address of interpreter */ +#define AT_FLAGS 8 /* Flags */ +#define AT_ENTRY 9 /* Entry point of program */ +#define AT_NOTELF 10 /* Program is not ELF */ +#define AT_UID 11 /* Real uid */ +#define AT_EUID 12 /* Effective uid */ +#define AT_GID 13 /* Real gid */ +#define AT_EGID 14 /* Effective gid */ +#define AT_CLKTCK 17 /* Frequency of times() */ + +/* Some more special a_type values describing the hardware. */ +#define AT_PLATFORM 15 /* String identifying platform. */ +#define AT_HWCAP 16 /* Machine dependent hints about + processor capabilities. */ + +/* This entry gives some information about the FPU initialization + performed by the kernel. */ +#define AT_FPUCW 18 /* Used FPU control word. */ + +/* Cache block sizes. */ +#define AT_DCACHEBSIZE 19 /* Data cache block size. */ +#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ +#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ + +/* A special ignored value for PPC, used by the kernel to control the + interpretation of the AUXV. Must be > 16. */ +#define AT_IGNOREPPC 22 /* Entry should be ignored. */ + +#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ + +/* Pointer to the global system page used for system calls and other + nice things. */ +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 + + +/* Note section contents. Each entry in the note section begins with + a header of a fixed form. */ + +typedef struct +{ + Elf32_Word n_namesz; /* Length of the note's name. */ + Elf32_Word n_descsz; /* Length of the note's descriptor. */ + Elf32_Word n_type; /* Type of the note. */ +} Elf32_Nhdr; + +typedef struct +{ + Elf64_Word n_namesz; /* Length of the note's name. */ + Elf64_Word n_descsz; /* Length of the note's descriptor. */ + Elf64_Word n_type; /* Type of the note. */ +} Elf64_Nhdr; + +/* Known names of notes. */ + +/* Solaris entries in the note section have this name. */ +#define ELF_NOTE_SOLARIS "SUNW Solaris" + +/* Note entries for GNU systems have this name. */ +#define ELF_NOTE_GNU "GNU" + + +/* Defined types of notes for Solaris. */ + +/* Value of descriptor (one word) is desired pagesize for the binary. */ +#define ELF_NOTE_PAGESIZE_HINT 1 + + +/* Defined note types for GNU systems. */ + +/* ABI information. The descriptor consists of words: + word 0: OS descriptor + word 1: major version of the ABI + word 2: minor version of the ABI + word 3: subminor version of the ABI +*/ +#define ELF_NOTE_ABI 1 + +/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI + note section entry. */ +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 +#define ELF_NOTE_OS_FREEBSD 3 + + +/* Move records. */ +typedef struct +{ + Elf32_Xword m_value; /* Symbol value. */ + Elf32_Word m_info; /* Size and index. */ + Elf32_Word m_poffset; /* Symbol offset. */ + Elf32_Half m_repeat; /* Repeat count. */ + Elf32_Half m_stride; /* Stride info. */ +} Elf32_Move; + +typedef struct +{ + Elf64_Xword m_value; /* Symbol value. */ + Elf64_Xword m_info; /* Size and index. */ + Elf64_Xword m_poffset; /* Symbol offset. */ + Elf64_Half m_repeat; /* Repeat count. */ + Elf64_Half m_stride; /* Stride info. */ +} Elf64_Move; + +/* Macro to construct move records. */ +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char) (info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM (info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) + + +/* Motorola 68k specific definitions. */ + +/* Values for Elf32_Ehdr.e_flags. */ +#define EF_CPU32 0x00810000 + +/* m68k relocs. */ + +#define R_68K_NONE 0 /* No reloc */ +#define R_68K_32 1 /* Direct 32 bit */ +#define R_68K_16 2 /* Direct 16 bit */ +#define R_68K_8 3 /* Direct 8 bit */ +#define R_68K_PC32 4 /* PC relative 32 bit */ +#define R_68K_PC16 5 /* PC relative 16 bit */ +#define R_68K_PC8 6 /* PC relative 8 bit */ +#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ +#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ +#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ +#define R_68K_GOT32O 10 /* 32 bit GOT offset */ +#define R_68K_GOT16O 11 /* 16 bit GOT offset */ +#define R_68K_GOT8O 12 /* 8 bit GOT offset */ +#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ +#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ +#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ +#define R_68K_PLT32O 16 /* 32 bit PLT offset */ +#define R_68K_PLT16O 17 /* 16 bit PLT offset */ +#define R_68K_PLT8O 18 /* 8 bit PLT offset */ +#define R_68K_COPY 19 /* Copy symbol at runtime */ +#define R_68K_GLOB_DAT 20 /* Create GOT entry */ +#define R_68K_JMP_SLOT 21 /* Create PLT entry */ +#define R_68K_RELATIVE 22 /* Adjust by program base */ +/* Keep this the last entry. */ +#define R_68K_NUM 23 + +/* Intel 80386 specific definitions. */ + +/* i386 relocs. */ + +#define R_386_NONE 0 /* No reloc */ +#define R_386_32 1 /* Direct 32 bit */ +#define R_386_PC32 2 /* PC relative 32 bit */ +#define R_386_GOT32 3 /* 32 bit GOT entry */ +#define R_386_PLT32 4 /* 32 bit PLT address */ +#define R_386_COPY 5 /* Copy symbol at runtime */ +#define R_386_GLOB_DAT 6 /* Create GOT entry */ +#define R_386_JMP_SLOT 7 /* Create PLT entry */ +#define R_386_RELATIVE 8 /* Adjust by program base */ +#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ +#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ +#define R_386_32PLT 11 +#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ +#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS + block offset */ +#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block + offset */ +#define R_386_TLS_LE 17 /* Offset relative to static TLS + block */ +#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of + general dynamic thread local data */ +#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of + local dynamic thread local data + in LE code */ +#define R_386_16 20 +#define R_386_PC16 21 +#define R_386_8 22 +#define R_386_PC8 23 +#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic + thread local data */ +#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ +#define R_386_TLS_GD_CALL 26 /* Relocation for call to + __tls_get_addr() */ +#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ +#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic + thread local data in LE code */ +#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ +#define R_386_TLS_LDM_CALL 30 /* Relocation for call to + __tls_get_addr() in LDM code */ +#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ +#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ +#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS + block offset */ +#define R_386_TLS_LE_32 34 /* Negated offset relative to static + TLS block */ +#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ +#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ +#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ +/* Keep this the last entry. */ +#define R_386_NUM 38 + +/* SUN SPARC specific definitions. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ + +/* Values for Elf64_Ehdr.e_flags. */ + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_LEDATA 0x800000 /* little endian data */ +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ +#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ +#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ +#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ + +/* SPARC relocs. */ + +#define R_SPARC_NONE 0 /* No reloc */ +#define R_SPARC_8 1 /* Direct 8 bit */ +#define R_SPARC_16 2 /* Direct 16 bit */ +#define R_SPARC_32 3 /* Direct 32 bit */ +#define R_SPARC_DISP8 4 /* PC relative 8 bit */ +#define R_SPARC_DISP16 5 /* PC relative 16 bit */ +#define R_SPARC_DISP32 6 /* PC relative 32 bit */ +#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ +#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ +#define R_SPARC_HI22 9 /* High 22 bit */ +#define R_SPARC_22 10 /* Direct 22 bit */ +#define R_SPARC_13 11 /* Direct 13 bit */ +#define R_SPARC_LO10 12 /* Truncated 10 bit */ +#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ +#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ +#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ +#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ +#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ +#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ +#define R_SPARC_COPY 19 /* Copy symbol at runtime */ +#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ +#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ +#define R_SPARC_RELATIVE 22 /* Adjust by program base */ +#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ + +/* Additional Sparc64 relocs. */ + +#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ +#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ +#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ +#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ +#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ +#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ +#define R_SPARC_10 30 /* Direct 10 bit */ +#define R_SPARC_11 31 /* Direct 11 bit */ +#define R_SPARC_64 32 /* Direct 64 bit */ +#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ +#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ +#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ +#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ +#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ +#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ +#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ +#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ +#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ +#define R_SPARC_7 43 /* Direct 7 bit */ +#define R_SPARC_5 44 /* Direct 5 bit */ +#define R_SPARC_6 45 /* Direct 6 bit */ +#define R_SPARC_DISP64 46 /* PC relative 64 bit */ +#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ +#define R_SPARC_HIX22 48 /* High 22 bit complemented */ +#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ +#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ +#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ +#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ +#define R_SPARC_REGISTER 53 /* Global register usage */ +#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ +#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ +#define R_SPARC_TLS_GD_HI22 56 +#define R_SPARC_TLS_GD_LO10 57 +#define R_SPARC_TLS_GD_ADD 58 +#define R_SPARC_TLS_GD_CALL 59 +#define R_SPARC_TLS_LDM_HI22 60 +#define R_SPARC_TLS_LDM_LO10 61 +#define R_SPARC_TLS_LDM_ADD 62 +#define R_SPARC_TLS_LDM_CALL 63 +#define R_SPARC_TLS_LDO_HIX22 64 +#define R_SPARC_TLS_LDO_LOX10 65 +#define R_SPARC_TLS_LDO_ADD 66 +#define R_SPARC_TLS_IE_HI22 67 +#define R_SPARC_TLS_IE_LO10 68 +#define R_SPARC_TLS_IE_LD 69 +#define R_SPARC_TLS_IE_LDX 70 +#define R_SPARC_TLS_IE_ADD 71 +#define R_SPARC_TLS_LE_HIX22 72 +#define R_SPARC_TLS_LE_LOX10 73 +#define R_SPARC_TLS_DTPMOD32 74 +#define R_SPARC_TLS_DTPMOD64 75 +#define R_SPARC_TLS_DTPOFF32 76 +#define R_SPARC_TLS_DTPOFF64 77 +#define R_SPARC_TLS_TPOFF32 78 +#define R_SPARC_TLS_TPOFF64 79 +/* Keep this the last entry. */ +#define R_SPARC_NUM 80 + +/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + +/* Bits present in AT_HWCAP, primarily for Sparc32. */ + +#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ +#define HWCAP_SPARC_STBAR 2 +#define HWCAP_SPARC_SWAP 4 +#define HWCAP_SPARC_MULDIV 8 +#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ +#define HWCAP_SPARC_ULTRA3 32 + +/* MIPS R3000 specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ +#define EF_MIPS_PIC 2 /* Contains PIC code */ +#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ +#define EF_MIPS_XGOT 8 +#define EF_MIPS_64BIT_WHIRL 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ + +/* Legal values for MIPS architecture level. */ + +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ + +/* The following are non-official names and should not be used. */ + +#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ +#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ + +/* Special section indices. */ + +#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ +#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ +#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ +#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ +#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ +#define SHT_MIPS_MSYM 0x70000001 +#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ +#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ +#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ +#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ +#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ +#define SHT_MIPS_PACKAGE 0x70000007 +#define SHT_MIPS_PACKSYM 0x70000008 +#define SHT_MIPS_RELD 0x70000009 +#define SHT_MIPS_IFACE 0x7000000b +#define SHT_MIPS_CONTENT 0x7000000c +#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ +#define SHT_MIPS_SHDR 0x70000010 +#define SHT_MIPS_FDESC 0x70000011 +#define SHT_MIPS_EXTSYM 0x70000012 +#define SHT_MIPS_DENSE 0x70000013 +#define SHT_MIPS_PDESC 0x70000014 +#define SHT_MIPS_LOCSYM 0x70000015 +#define SHT_MIPS_AUXSYM 0x70000016 +#define SHT_MIPS_OPTSYM 0x70000017 +#define SHT_MIPS_LOCSTR 0x70000018 +#define SHT_MIPS_LINE 0x70000019 +#define SHT_MIPS_RFDESC 0x7000001a +#define SHT_MIPS_DELTASYM 0x7000001b +#define SHT_MIPS_DELTAINST 0x7000001c +#define SHT_MIPS_DELTACLASS 0x7000001d +#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ +#define SHT_MIPS_DELTADECL 0x7000001f +#define SHT_MIPS_SYMBOL_LIB 0x70000020 +#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ +#define SHT_MIPS_TRANSLATE 0x70000022 +#define SHT_MIPS_PIXIE 0x70000023 +#define SHT_MIPS_XLATE 0x70000024 +#define SHT_MIPS_XLATE_DEBUG 0x70000025 +#define SHT_MIPS_WHIRL 0x70000026 +#define SHT_MIPS_EH_REGION 0x70000027 +#define SHT_MIPS_XLATE_OLD 0x70000028 +#define SHT_MIPS_PDR_EXCEPTION 0x70000029 + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ +#define SHF_MIPS_MERGE 0x20000000 +#define SHF_MIPS_ADDR 0x40000000 +#define SHF_MIPS_STRINGS 0x80000000 +#define SHF_MIPS_NOSTRIP 0x08000000 +#define SHF_MIPS_LOCAL 0x04000000 +#define SHF_MIPS_NAMES 0x02000000 +#define SHF_MIPS_NODUPE 0x01000000 + + +/* Symbol tables. */ + +/* MIPS specific values for `st_other'. */ +#define STO_MIPS_DEFAULT 0x0 +#define STO_MIPS_INTERNAL 0x1 +#define STO_MIPS_HIDDEN 0x2 +#define STO_MIPS_PROTECTED 0x3 +#define STO_MIPS_SC_ALIGN_UNUSED 0xff + +/* MIPS specific values for `st_info'. */ +#define STB_MIPS_SPLIT_COMMON 13 + +/* Entries found in sections of type SHT_MIPS_GPTAB. */ + +typedef union +{ + struct + { + Elf32_Word gt_current_g_value; /* -G value used for compilation */ + Elf32_Word gt_unused; /* Not used */ + } gt_header; /* First entry in section */ + struct + { + Elf32_Word gt_g_value; /* If this value were used for -G */ + Elf32_Word gt_bytes; /* This many bytes would be used */ + } gt_entry; /* Subsequent entries in section */ +} Elf32_gptab; + +/* Entry found in sections of type SHT_MIPS_REGINFO. */ + +typedef struct +{ + Elf32_Word ri_gprmask; /* General registers used */ + Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ + Elf32_Sword ri_gp_value; /* $gp register value */ +} Elf32_RegInfo; + +/* Entries found in sections of type SHT_MIPS_OPTIONS. */ + +typedef struct +{ + unsigned char kind; /* Determines interpretation of the + variable part of descriptor. */ + unsigned char size; /* Size of descriptor, including header. */ + Elf32_Section section; /* Section header index of section affected, + 0 for global options. */ + Elf32_Word info; /* Kind-specific information. */ +} Elf_Options; + +/* Values for `kind' field in Elf_Options. */ + +#define ODK_NULL 0 /* Undefined. */ +#define ODK_REGINFO 1 /* Register usage information. */ +#define ODK_EXCEPTIONS 2 /* Exception processing options. */ +#define ODK_PAD 3 /* Section padding options. */ +#define ODK_HWPATCH 4 /* Hardware workarounds performed */ +#define ODK_FILL 5 /* record the fill value used by the linker. */ +#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ +#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ +#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ + +/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ + +#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ +#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ +#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ +#define OEX_SMM 0x20000 /* Force sequential memory mode? */ +#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ +#define OEX_PRECISEFP OEX_FPDBUG +#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ + +#define OEX_FPU_INVAL 0x10 +#define OEX_FPU_DIV0 0x08 +#define OEX_FPU_OFLO 0x04 +#define OEX_FPU_UFLO 0x02 +#define OEX_FPU_INEX 0x01 + +/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ + +#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ +#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ +#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ +#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ + +#define OPAD_PREFIX 0x1 +#define OPAD_POSTFIX 0x2 +#define OPAD_SYMBOL 0x4 + +/* Entry found in `.options' section. */ + +typedef struct +{ + Elf32_Word hwp_flags1; /* Extra flags. */ + Elf32_Word hwp_flags2; /* Extra flags. */ +} Elf_Options_Hw; + +/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ + +#define OHWA0_R4KEOP_CHECKED 0x00000001 +#define OHWA1_R4KEOP_CLEAN 0x00000002 + +/* MIPS relocs. */ + +#define R_MIPS_NONE 0 /* No reloc */ +#define R_MIPS_16 1 /* Direct 16 bit */ +#define R_MIPS_32 2 /* Direct 32 bit */ +#define R_MIPS_REL32 3 /* PC relative 32 bit */ +#define R_MIPS_26 4 /* Direct 26 bit shifted */ +#define R_MIPS_HI16 5 /* High 16 bit */ +#define R_MIPS_LO16 6 /* Low 16 bit */ +#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ +#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ +#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ +#define R_MIPS_PC16 10 /* PC relative 16 bit */ +#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ +#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ + +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 +#define R_MIPS_GOT_HI16 22 +#define R_MIPS_GOT_LO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 +#define R_MIPS_CALL_HI16 30 +#define R_MIPS_CALL_LO16 31 +#define R_MIPS_SCN_DISP 32 +#define R_MIPS_REL16 33 +#define R_MIPS_ADD_IMMEDIATE 34 +#define R_MIPS_PJUMP 35 +#define R_MIPS_RELGOT 36 +#define R_MIPS_JALR 37 +/* Keep this the last entry. */ +#define R_MIPS_NUM 38 + +/* Legal values for p_type field of Elf32_Phdr. */ + +#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ +#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ +#define PT_MIPS_OPTIONS 0x70000002 + +/* Special program header types. */ + +#define PF_MIPS_LOCAL 0x10000000 + +/* Legal values for d_tag field of Elf32_Dyn. */ + +#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ +#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ +#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ +#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ +#define DT_MIPS_FLAGS 0x70000005 /* Flags */ +#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ +#define DT_MIPS_MSYM 0x70000007 +#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ +#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ +#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ +#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ +#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ +#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ +#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ +#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ +#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ +#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ +#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ +#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in + DT_MIPS_DELTA_CLASS. */ +#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ +#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in + DT_MIPS_DELTA_INSTANCE. */ +#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ +#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in + DT_MIPS_DELTA_RELOC. */ +#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta + relocations refer to. */ +#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in + DT_MIPS_DELTA_SYM. */ +#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the + class declaration. */ +#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in + DT_MIPS_DELTA_CLASSSYM. */ +#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ +#define DT_MIPS_PIXIE_INIT 0x70000023 +#define DT_MIPS_SYMBOL_LIB 0x70000024 +#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 +#define DT_MIPS_LOCAL_GOTIDX 0x70000026 +#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 +#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 +#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ +#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ +#define DT_MIPS_DYNSTR_ALIGN 0x7000002b +#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ +#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve + function stored in GOT. */ +#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added + by rld on dlopen() calls. */ +#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ +#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ +#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ +#define DT_MIPS_NUM 0x32 + +/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ + +#define RHF_NONE 0 /* No flags */ +#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ +#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ +#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ +#define RHF_NO_MOVE (1 << 3) +#define RHF_SGI_ONLY (1 << 4) +#define RHF_GUARANTEE_INIT (1 << 5) +#define RHF_DELTA_C_PLUS_PLUS (1 << 6) +#define RHF_GUARANTEE_START_INIT (1 << 7) +#define RHF_PIXIE (1 << 8) +#define RHF_DEFAULT_DELAY_LOAD (1 << 9) +#define RHF_REQUICKSTART (1 << 10) +#define RHF_REQUICKSTARTED (1 << 11) +#define RHF_CORD (1 << 12) +#define RHF_NO_UNRES_UNDEF (1 << 13) +#define RHF_RLD_ORDER_SAFE (1 << 14) + +/* Entries found in sections of type SHT_MIPS_LIBLIST. */ + +typedef struct +{ + Elf32_Word l_name; /* Name (string table index) */ + Elf32_Word l_time_stamp; /* Timestamp */ + Elf32_Word l_checksum; /* Checksum */ + Elf32_Word l_version; /* Interface version */ + Elf32_Word l_flags; /* Flags */ +} Elf32_Lib; + +typedef struct +{ + Elf64_Word l_name; /* Name (string table index) */ + Elf64_Word l_time_stamp; /* Timestamp */ + Elf64_Word l_checksum; /* Checksum */ + Elf64_Word l_version; /* Interface version */ + Elf64_Word l_flags; /* Flags */ +} Elf64_Lib; + + +/* Legal values for l_flags. */ + +#define LL_NONE 0 +#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ +#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ +#define LL_REQUIRE_MINOR (1 << 2) +#define LL_EXPORTS (1 << 3) +#define LL_DELAY_LOAD (1 << 4) +#define LL_DELTA (1 << 5) + +/* Entries found in sections of type SHT_MIPS_CONFLICT. */ + +typedef Elf32_Addr Elf32_Conflict; + + +/* HPPA specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ +#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ +#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ +#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch + prediction. */ +#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ +#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ + +/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ + +#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ +#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ +#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ + +/* Additional section indeces. */ + +#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared + symbols in ANSI C. */ +#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ +#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ +#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ +#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ +#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + +/* HPPA relocs. */ + +#define R_PARISC_NONE 0 /* No reloc. */ +#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ +#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ +#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ +#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ +#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ +#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ +#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ +#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ +#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ +#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ +#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ +#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ +#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ +#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ +#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ +#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ +#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ +#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ +#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ +#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ +#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ +#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ +#define R_PARISC_FPTR64 64 /* 64 bits function address. */ +#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ +#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ +#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ +#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ +#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ +#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ +#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ +#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ +#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ +#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ +#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ +#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ +#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ +#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ +#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ +#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ +#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ +#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ +#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ +#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 /* Copy relocation. */ +#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ +#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ +#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ +#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ +#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ +#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ +#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ +#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ +#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_HIRESERVE 255 + +/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ + +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) + +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 + +/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ + +#define PF_PARISC_SBP 0x08000000 + +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 + + +/* Alpha specific definitions. */ + +/* Legal values for e_flags field of Elf64_Ehdr. */ + +#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ +#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ + +/* Legal values for sh_type field of Elf64_Shdr. */ + +/* These two are primerily concerned with ECOFF debugging info. */ +#define SHT_ALPHA_DEBUG 0x70000001 +#define SHT_ALPHA_REGINFO 0x70000002 + +/* Legal values for sh_flags field of Elf64_Shdr. */ + +#define SHF_ALPHA_GPREL 0x10000000 + +/* Legal values for st_other field of Elf64_Sym. */ +#define STO_ALPHA_NOPV 0x80 /* No PV required. */ +#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ + +/* Alpha relocs. */ + +#define R_ALPHA_NONE 0 /* No reloc */ +#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ +#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ +#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ +#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ +#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ +#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ +#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ +#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ +#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ +#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ +#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ +#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ +#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ +#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ +#define R_ALPHA_TLS_GD_HI 28 +#define R_ALPHA_TLSGD 29 +#define R_ALPHA_TLS_LDM 30 +#define R_ALPHA_DTPMOD64 31 +#define R_ALPHA_GOTDTPREL 32 +#define R_ALPHA_DTPREL64 33 +#define R_ALPHA_DTPRELHI 34 +#define R_ALPHA_DTPRELLO 35 +#define R_ALPHA_DTPREL16 36 +#define R_ALPHA_GOTTPREL 37 +#define R_ALPHA_TPREL64 38 +#define R_ALPHA_TPRELHI 39 +#define R_ALPHA_TPRELLO 40 +#define R_ALPHA_TPREL16 41 +/* Keep this the last entry. */ +#define R_ALPHA_NUM 46 + +/* Magic values of the LITUSE relocation addend. */ +#define LITUSE_ALPHA_ADDR 0 +#define LITUSE_ALPHA_BASE 1 +#define LITUSE_ALPHA_BYTOFF 2 +#define LITUSE_ALPHA_JSR 3 +#define LITUSE_ALPHA_TLS_GD 4 +#define LITUSE_ALPHA_TLS_LDM 5 + + +/* PowerPC specific declarations */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 + +/* PowerPC relocations defined for the TLS access ABI. */ +#define R_PPC_TLS 67 /* none (sym+add)@tls */ +#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ +#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ +#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ +#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ +#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ +#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ +#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ +#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ +#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ + +/* Keep this the last entry. */ +#define R_PPC_NUM 95 + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ +#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + + +/* PowerPC64 relocations defined by the ABIs */ +#define R_PPC64_NONE R_PPC_NONE +#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ +#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ +#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ +#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ +#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ +#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ +#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ +#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN +#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN +#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ +#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ +#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN +#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN +#define R_PPC64_GOT16 R_PPC_GOT16 +#define R_PPC64_GOT16_LO R_PPC_GOT16_LO +#define R_PPC64_GOT16_HI R_PPC_GOT16_HI +#define R_PPC64_GOT16_HA R_PPC_GOT16_HA + +#define R_PPC64_COPY R_PPC_COPY +#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT +#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT +#define R_PPC64_RELATIVE R_PPC_RELATIVE + +#define R_PPC64_UADDR32 R_PPC_UADDR32 +#define R_PPC64_UADDR16 R_PPC_UADDR16 +#define R_PPC64_REL32 R_PPC_REL32 +#define R_PPC64_PLT32 R_PPC_PLT32 +#define R_PPC64_PLTREL32 R_PPC_PLTREL32 +#define R_PPC64_PLT16_LO R_PPC_PLT16_LO +#define R_PPC64_PLT16_HI R_PPC_PLT16_HI +#define R_PPC64_PLT16_HA R_PPC_PLT16_HA + +#define R_PPC64_SECTOFF R_PPC_SECTOFF +#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO +#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI +#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA +#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ +#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ +#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ +#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ +#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ +#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ +#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ +#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ +#define R_PPC64_PLT64 45 /* doubleword64 L + A */ +#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ +#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ +#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ +#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ +#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ +#define R_PPC64_TOC 51 /* doubleword64 .TOC */ +#define R_PPC64_PLTGOT16 52 /* half16* M + A */ +#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ +#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ +#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ + +#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ +#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ +#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ +#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ +#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ +#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ +#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ +#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ +#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ +#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ +#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ + +/* PowerPC64 relocations defined for the TLS access ABI. */ +#define R_PPC64_TLS 67 /* none (sym+add)@tls */ +#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ +#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ +#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ +#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ +#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ +#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ +#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ +#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ +#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ +#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ +#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ +#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ +#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ +#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ +#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ +#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ +#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ + +/* Keep this the last entry. */ +#define R_PPC64_NUM 107 + +/* PowerPC64 specific values for the Dyn d_tag field. */ +#define DT_PPC64_GLINK (DT_LOPROC + 0) +#define DT_PPC64_OPD (DT_LOPROC + 1) +#define DT_PPC64_OPDSZ (DT_LOPROC + 2) +#define DT_PPC64_NUM 3 + + +/* ARM specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_ARM_NEW_ABI 0x80 +#define EF_ARM_OLD_ABI 0x100 + +/* Other constants defined in the ARM ELF spec. version B-01. */ +/* NB. These conflict with values defined above. */ +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0XFF000000 + +#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 + +/* Additional symbol types for Thumb */ +#define STT_ARM_TFUNC 0xd + +/* ARM-specific values for sh_flags */ +#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ +#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined + in the input to a link step */ + +/* ARM-specific program header flags */ +#define PF_ARM_SB 0x10000000 /* Segment contains the location + addressed by the static base */ + +/* ARM relocs. */ +#define R_ARM_NONE 0 /* No reloc */ +#define R_ARM_PC24 1 /* PC relative 26 bit branch */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_REL32 3 /* PC relative 32 bit */ +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 /* Direct 16 bit */ +#define R_ARM_ABS12 6 /* Direct 12 bit */ +#define R_ARM_THM_ABS5 7 +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 +#define R_ARM_THM_PC8 11 +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 +#define R_ARM_THM_SWI8 14 +#define R_ARM_XPC25 15 +#define R_ARM_THM_XPC22 16 +#define R_ARM_COPY 20 /* Copy symbol at runtime */ +#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ +#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ +#define R_ARM_RELATIVE 23 /* Adjust by program base */ +#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ +#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT32 26 /* 32 bit GOT entry */ +#define R_ARM_PLT32 27 /* 32 bit PLT address */ +#define R_ARM_ALU_PCREL_7_0 32 +#define R_ARM_ALU_PCREL_15_8 33 +#define R_ARM_ALU_PCREL_23_15 34 +#define R_ARM_LDR_SBREL_11_0 35 +#define R_ARM_ALU_SBREL_19_12 36 +#define R_ARM_ALU_SBREL_27_20 37 +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ +#define R_ARM_THM_PC9 103 /* thumb conditional branch */ +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 +/* Keep this the last entry. */ +#define R_ARM_NUM 256 + +/* IA-64 specific declarations. */ + +/* Processor specific flags for the Ehdr e_flags field. */ +#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ +#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ +#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ +#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ +#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) +#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) +#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) + +/* Processor specific flags for the Phdr p_flags field. */ +#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ + +/* Processor specific flags for the Shdr sh_flags field. */ +#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ +#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Dyn d_tag field. */ +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 + +/* IA-64 relocations. */ +#define R_IA64_NONE 0x00 /* none */ +#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ +#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ +#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ +#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ +#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ +#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ +#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ +#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ +#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ +#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ +#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ +#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ +#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ +#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ +#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ +#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ +#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ +#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ +#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ +#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ +#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ +#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ +#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ +#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ +#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ +#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ +#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ +#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ +#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ +#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ +#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ +#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ +#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ +#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ +#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ +#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ +#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ +#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ +#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ +#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ +#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ +#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ +#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ +#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ +#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ +#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ +#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ +#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ +#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ +#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ +#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ +#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ +#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ +#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ +#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ +#define R_IA64_COPY 0x84 /* copy relocation */ +#define R_IA64_SUB 0x85 /* Addend and symbol difference */ +#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ +#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ +#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ +#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ +#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ +#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ +#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ +#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ +#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ +#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ +#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ +#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ +#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ +#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ +#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ +#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ + +/* SH specific declarations */ + +/* SH relocs. */ +#define R_SH_NONE 0 +#define R_SH_DIR32 1 +#define R_SH_REL32 2 +#define R_SH_DIR8WPN 3 +#define R_SH_IND12W 4 +#define R_SH_DIR8WPL 5 +#define R_SH_DIR8WPZ 6 +#define R_SH_DIR8BP 7 +#define R_SH_DIR8W 8 +#define R_SH_DIR8L 9 +#define R_SH_SWITCH16 25 +#define R_SH_SWITCH32 26 +#define R_SH_USES 27 +#define R_SH_COUNT 28 +#define R_SH_ALIGN 29 +#define R_SH_CODE 30 +#define R_SH_DATA 31 +#define R_SH_LABEL 32 +#define R_SH_SWITCH8 33 +#define R_SH_GNU_VTINHERIT 34 +#define R_SH_GNU_VTENTRY 35 +#define R_SH_TLS_GD_32 144 +#define R_SH_TLS_LD_32 145 +#define R_SH_TLS_LDO_32 146 +#define R_SH_TLS_IE_32 147 +#define R_SH_TLS_LE_32 148 +#define R_SH_TLS_DTPMOD32 149 +#define R_SH_TLS_DTPOFF32 150 +#define R_SH_TLS_TPOFF32 151 +#define R_SH_GOT32 160 +#define R_SH_PLT32 161 +#define R_SH_COPY 162 +#define R_SH_GLOB_DAT 163 +#define R_SH_JMP_SLOT 164 +#define R_SH_RELATIVE 165 +#define R_SH_GOTOFF 166 +#define R_SH_GOTPC 167 +/* Keep this the last entry. */ +#define R_SH_NUM 256 + +/* Additional s390 relocs */ + +#define R_390_NONE 0 /* No reloc. */ +#define R_390_8 1 /* Direct 8 bit. */ +#define R_390_12 2 /* Direct 12 bit. */ +#define R_390_16 3 /* Direct 16 bit. */ +#define R_390_32 4 /* Direct 32 bit. */ +#define R_390_PC32 5 /* PC relative 32 bit. */ +#define R_390_GOT12 6 /* 12 bit GOT offset. */ +#define R_390_GOT32 7 /* 32 bit GOT offset. */ +#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ +#define R_390_COPY 9 /* Copy symbol at runtime. */ +#define R_390_GLOB_DAT 10 /* Create GOT entry. */ +#define R_390_JMP_SLOT 11 /* Create PLT entry. */ +#define R_390_RELATIVE 12 /* Adjust by program base. */ +#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ +#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ +#define R_390_GOT16 15 /* 16 bit GOT offset. */ +#define R_390_PC16 16 /* PC relative 16 bit. */ +#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ +#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ +#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ +#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ +#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ +#define R_390_64 22 /* Direct 64 bit. */ +#define R_390_PC64 23 /* PC relative 64 bit. */ +#define R_390_GOT64 24 /* 64 bit GOT offset. */ +#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ +#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ +#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ +#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ +#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ +#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ +#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ +#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ +#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ +#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ +#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ +#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ +#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ +#define R_390_TLS_GDCALL 38 /* Tag for function call in general + dynamic TLS code. */ +#define R_390_TLS_LDCALL 39 /* Tag for function call in local + dynamic TLS code. */ +#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic + thread local data. */ +#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic + thread local data. */ +#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS + block. */ +#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS + block. */ +#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ +#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ +#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS + block. */ + +/* Keep this the last entry. */ +#define R_390_NUM 57 + +/* CRIS relocations. */ +#define R_CRIS_NONE 0 +#define R_CRIS_8 1 +#define R_CRIS_16 2 +#define R_CRIS_32 3 +#define R_CRIS_8_PCREL 4 +#define R_CRIS_16_PCREL 5 +#define R_CRIS_32_PCREL 6 +#define R_CRIS_GNU_VTINHERIT 7 +#define R_CRIS_GNU_VTENTRY 8 +#define R_CRIS_COPY 9 +#define R_CRIS_GLOB_DAT 10 +#define R_CRIS_JUMP_SLOT 11 +#define R_CRIS_RELATIVE 12 +#define R_CRIS_16_GOT 13 +#define R_CRIS_32_GOT 14 +#define R_CRIS_16_GOTPLT 15 +#define R_CRIS_32_GOTPLT 16 +#define R_CRIS_32_GOTREL 17 +#define R_CRIS_32_PLT_GOTREL 18 +#define R_CRIS_32_PLT_PCREL 19 + +#define R_CRIS_NUM 20 + +/* AMD x86-64 relocations. */ +#define R_X86_64_NONE 0 /* No reloc */ +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_COPY 5 /* Copy symbol at runtime */ +#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ +#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ +#define R_X86_64_RELATIVE 8 /* Adjust by program base */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative + offset to GOT */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_16 12 /* Direct 16 bit zero extended */ +#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ +#define R_X86_64_8 14 /* Direct 8 bit sign extended */ +#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ +#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ +#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ +#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ +#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset + to two GOT entries for GD symbol */ +#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset + to two GOT entries for LD symbol */ +#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ +#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset + to GOT entry for IE symbol */ +#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ + +#define R_X86_64_NUM 24 + +#endif /* elf.h */ diff --git a/common/libspumars/ppu/base/lib/host_mutex_psl1ght.c b/common/libspumars/ppu/base/lib/host_mutex_psl1ght.c new file mode 100644 index 00000000..dae7b1e2 --- /dev/null +++ b/common/libspumars/ppu/base/lib/host_mutex_psl1ght.c @@ -0,0 +1,39 @@ +#include <_ansi.h> +#include <_syslist.h> +#include +#include + +#include + +#include + +#include "context_internal.h" + +sys_mutex_t mars_shared_context_lock; + +static void __mars_shared_context_lock_init() __attribute__((constructor(105))); +static void __mars_shared_context_lock_init() +{ + sys_mutex_attr_t attr; + + sysMutexAttrInitialize(attr); + sysMutexCreate(&mars_shared_context_lock, &attr); +} + +int mars_host_mutex_lock(mars_host_mutex_t *mutex) +{ + int ret = sysMutexLock(*mutex, 0); + if(ret) + return MARS_ERROR_INTERNAL; + + return MARS_SUCCESS; +} + +int mars_host_mutex_unlock(mars_host_mutex_t *mutex) +{ + int ret = sysMutexUnlock(*mutex); + if(ret) + return MARS_ERROR_INTERNAL; + + return MARS_SUCCESS; +} diff --git a/common/libspumars/ppu/base/lib/mpu_cell.c b/common/libspumars/ppu/base/lib/mpu_cell.c new file mode 100644 index 00000000..5db45d0b --- /dev/null +++ b/common/libspumars/ppu/base/lib/mpu_cell.c @@ -0,0 +1,163 @@ +#include + +#include +#include +#include + +#include +#include + +#include "context_internal.h" +#include "workload_internal_types.h" +#include "kernel_internal_types.h" +#include "numa_internal.h" + +#define NUM_OF_SPU 6 + +extern const unsigned char mars_kernel_entry[]; + +/* This function must not be called */ +static int numa_mpu_max(void) +{ + return -1; +} + +static void mpu_mars_handler_entry(void *arg) +{ + int ret; + mars_mpu_context_t *mpu = (mars_mpu_context_t*)arg; + + for(;;) { + uint64_t ea; + sys_event_t event; + + ret = sysEventQueueReceive(mpu->spu_ppu_event_queue, &event, 0); + if(ret != 0) + sysThreadExit(-1); + + ea = ((uint64_t)(event.data_2&0x00ffffff)<<32)|event.data_3; + if(ea == MARS_HOST_SIGNAL_EXIT) + break; + + mars_ea_cond_signal(ea, 1); + } + sysThreadExit(0); +} + + +int mars_mpu_max(uint32_t *num) +{ + int mpu_max; + + if (mars_numa_enabled()) + mpu_max = numa_mpu_max(); + else + mpu_max = NUM_OF_SPU; + + if (mpu_max < 0) + return MARS_ERROR_INTERNAL; + + *num = mpu_max; + + return MARS_SUCCESS; +} + +int mars_mpu_create(mars_mpu_context_t *mpu, uint32_t num_mpus, uint32_t spu_prio, uint32_t ppu_prio) +{ + int ret; + sysSpuThreadGroupAttribute attr; + sys_event_queue_attr_t event_queue_attr = { SYS_EVENT_QUEUE_PRIO, SYS_EVENT_QUEUE_PPU, "" }; + + sysSpuThreadGroupAttributeInitialize(attr); + + mpu->spu_threads = (sys_spu_thread_t*)mars_malloc(sizeof(sys_spu_thread_t)*num_mpus); + if(!mpu->spu_threads) + return MARS_ERROR_MEMORY; + + ret = sysSpuImageImport(&mpu->spu_image,mars_kernel_entry,SPU_IMAGE_PROTECT); + if(ret) + goto error_kernel_image_import; + + ret = sysSpuThreadGroupCreate(&mpu->spu_thread_group, num_mpus, spu_prio, &attr); + if(ret) + goto error_thread_group_create; + + ret = sysEventQueueCreate(&mpu->spu_ppu_event_queue, &event_queue_attr, SYS_EVENT_QUEUE_KEY_LOCAL, 127); + if(ret) + goto error_event_queue_create; + + ret = sysThreadCreate(&mpu->ppu_handler_thread, mpu_mars_handler_entry, mpu, ppu_prio, 4096, THREAD_JOINABLE, "mpu_mars_handler"); + if(ret) + goto error_ppu_thread_create; + + return MARS_SUCCESS; + +error_ppu_thread_create: + sysEventQueueDestroy(mpu->spu_ppu_event_queue, 0); +error_event_queue_create: + sysSpuThreadGroupDestroy(mpu->spu_thread_group); +error_thread_group_create: + sysSpuImageClose(&mpu->spu_image); +error_kernel_image_import: + free(mpu->spu_threads); + return MARS_ERROR_INTERNAL; +} + +int mars_mpu_run(mars_mpu_context_t *mpu) +{ + return sysSpuThreadGroupStart(mpu->spu_thread_group); +} + +int mars_mpu_initialize(mars_mpu_context_t *mpu, uint64_t params_ea, uint32_t idx) +{ + int ret; + sysSpuThreadArgument arg; + sysSpuThreadAttribute attr; + + sysSpuThreadArgumentInitialize(arg); + sysSpuThreadAttributeInitialize(attr); + + arg.arg0 = params_ea; + ret = sysSpuThreadInitialize(&mpu->spu_threads[idx],mpu->spu_thread_group,idx,&mpu->spu_image,&attr,&arg); + if(ret) { + return MARS_ERROR_INTERNAL; + } + + ret = sysSpuThreadConnectEvent(mpu->spu_threads[idx],mpu->spu_ppu_event_queue,SPU_THREAD_EVENT_USER,MARS_KERNEL_SPU_EVENT_PORT); + if(ret) { + return MARS_ERROR_INTERNAL; + } + sysSpuPrintfAttachThread(mpu->spu_threads[idx]); + + return MARS_SUCCESS; +} + +int mars_mpu_wait_all(mars_mpu_context_t *mpu) +{ + int ret, i; + u64 ret_val; + u32 status, cause; + + for(i=0;i < mpu->num_spu;i++) { + sysSpuPrintfDetachThread(mpu->spu_threads[i]); + } + + ret = sysThreadJoin(mpu->ppu_handler_thread, &ret_val); + if(ret || ret_val) + return MARS_ERROR_INTERNAL; + + ret = sysSpuThreadGroupJoin(mpu->spu_thread_group, &cause, &status); + if(ret) + return MARS_ERROR_INTERNAL; + + ret = sysSpuThreadGroupDestroy(mpu->spu_thread_group); + if(ret) + return MARS_ERROR_INTERNAL; + + ret = sysSpuImageClose(&mpu->spu_image); + if(ret) + return MARS_ERROR_INTERNAL; + + free(mpu->spu_threads); + return MARS_SUCCESS; +} diff --git a/common/libspumars/ppu/base/lib/mutex_cell.c b/common/libspumars/ppu/base/lib/mutex_cell.c new file mode 100644 index 00000000..c4a0bd92 --- /dev/null +++ b/common/libspumars/ppu/base/lib/mutex_cell.c @@ -0,0 +1,159 @@ +#include +#include + +#include + +#include +#include +#include + +union mars_mutex_header { + struct mars_mutex_status status; + uint32_t bits; +}; + +static void init_status(struct mars_mutex_status *status) +{ + status->lock = MARS_MUTEX_UNLOCKED; + status->current_id = 0; + status->next_id = 0; +} + + +int mars_mutex_create(uint64_t *mutex_ea_ret) +{ + struct mars_mutex *mutex; + uint64_t mutex_ea; + + if (!mutex_ea_ret) + return MARS_ERROR_NULL; + + mutex_ea = mars_ea_memalign(MARS_MUTEX_ALIGN, MARS_MUTEX_SIZE); + if (!mutex_ea) + return MARS_ERROR_MEMORY; + + mutex = mars_ea_to_ptr(mutex_ea); + + init_status(&mutex->status); + __lwsync(); + + *mutex_ea_ret = mutex_ea; + + return MARS_SUCCESS; +} + +int mars_mutex_destroy(uint64_t mutex_ea) +{ + if (!mutex_ea) + return MARS_ERROR_NULL; + + mars_ea_free(mutex_ea); + + return MARS_SUCCESS; +} + +int mars_mutex_reset(uint64_t mutex_ea) +{ + struct mars_mutex *mutex = mars_ea_to_ptr(mutex_ea); + + if (!mutex_ea) + return MARS_ERROR_NULL; + + init_status(&mutex->status); + __lwsync(); + + return MARS_SUCCESS; +} + +int mars_mutex_lock(uint64_t mutex_ea) +{ + struct mars_mutex *mutex = mars_ea_to_ptr(mutex_ea); + union mars_mutex_header header; + uint8_t id; + int retry; + + if (!mutex_ea) + return MARS_ERROR_NULL; + + do { + header.bits = __lwarx(&mutex->status); + + /* get my id */ + id = header.status.next_id++; + if ((retry = !__stwcx(&mutex->status, header.bits))) + sysThreadYield(); //sched_yield(); + } while (retry); + + do { + header.bits = __lwarx(&mutex->status); + + if (header.status.lock == MARS_MUTEX_LOCKED || + header.status.current_id != id) { + /* wait until mutex is released */ + sysThreadYield(); //sched_yield(); + retry = 1; + } + else { + /* get lock */ + header.status.lock = MARS_MUTEX_LOCKED; + header.status.current_id++; + retry = !__stwcx(&mutex->status, header.bits); + } + } while (retry); + + __isync(); + + return MARS_SUCCESS; +} + +int mars_mutex_unlock(uint64_t mutex_ea) +{ + struct mars_mutex *mutex = mars_ea_to_ptr(mutex_ea); + union mars_mutex_header header; + + if (!mutex_ea) + return MARS_ERROR_NULL; + if (mutex->status.lock != MARS_MUTEX_LOCKED) + return MARS_ERROR_STATE; + + __lwsync(); + + do { + header.bits = __lwarx(&mutex->status); + header.status.lock = MARS_MUTEX_UNLOCKED; + } while (!__stwcx(&mutex->status, header.bits)); + + return MARS_SUCCESS; +} + +int mars_mutex_lock_get(uint64_t mutex_ea, struct mars_mutex *mutex) +{ +#ifdef MARS_ENABLE_DISCRETE_SHARED_MEMORY + int ret; + ret = mars_mutex_lock(mutex_ea); + if (ret != MARS_SUCCESS) + return ret; + mars_ea_get(mutex_ea, mutex, MARS_MUTEX_SIZE); + return MARS_SUCCESS; +#else /* !MARS_ENABLE_DISCRETE_SHARED_MEMORY */ + (void)mutex; /* ignored */ + return mars_mutex_lock(mutex_ea); +#endif /* !MARS_ENABLE_DISCRETE_SHARED_MEMORY */ +} + +int mars_mutex_unlock_put(uint64_t mutex_ea, struct mars_mutex *mutex) +{ +#ifdef MARS_ENABLE_DISCRETE_SHARED_MEMORY + int ret; + mars_ea_put( + mutex_ea + offsetof(struct mars_mutex, pad), + mutex->pad, sizeof(mutex->pad)); + ret = mars_mutex_unlock(mutex_ea); + if (ret == MARS_SUCCESS) + mutex->status.lock = MARS_MUTEX_UNLOCKED; + return ret; +#else /* !MARS_ENABLE_DISCRETE_SHARED_MEMORY */ + (void)mutex; /* ignored */ + return mars_mutex_unlock(mutex_ea); +#endif /* !MARS_ENABLE_DISCRETE_SHARED_MEMORY */ +} diff --git a/common/libspumars/ppu/base/lib/numa_internal.h b/common/libspumars/ppu/base/lib/numa_internal.h new file mode 100644 index 00000000..42bbec27 --- /dev/null +++ b/common/libspumars/ppu/base/lib/numa_internal.h @@ -0,0 +1,9 @@ +#ifndef __MARS_NUMA_INTERNAL_H__ +#define __MARS_NUMA_INTERNAL_H__ + +static inline int mars_numa_enabled(void) +{ + return 0; +} + +#endif diff --git a/common/libspumars/ppu/base/lib/workload_queue.c b/common/libspumars/ppu/base/lib/workload_queue.c new file mode 100644 index 00000000..647e93ed --- /dev/null +++ b/common/libspumars/ppu/base/lib/workload_queue.c @@ -0,0 +1,878 @@ +#include + +#include +#include +#include +#include + +#include "elf.h" +#include "context_internal.h" +#include "kernel_internal_types.h" +#include "workload_internal_types.h" + +static inline uint64_t get_workload_ea(uint64_t queue_ea, uint16_t workload_id) +{ + uint64_t context_ea = + mars_ea_get_uint64(queue_ea + + offsetof(struct mars_workload_queue_header, + context_ea)); + int context_index = + workload_id - (workload_id / MARS_WORKLOAD_PER_BLOCK) - 1; + + return context_ea + context_index * MARS_WORKLOAD_CONTEXT_SIZE; +} + +static inline uint64_t get_block_ea(uint64_t queue_ea, int block) +{ + return queue_ea + + offsetof(struct mars_workload_queue, block) + + MARS_WORKLOAD_QUEUE_BLOCK_SIZE * block; +} + +static inline uint64_t get_block_bits_ea(uint64_t block_ea, int index) +{ + return block_ea + + offsetof(struct mars_workload_queue_block, bits) + + sizeof(uint64_t) * index; +} + +static int change_bits(struct mars_context *mars, + uint16_t id, + uint64_t *workload_ea, + int (*check_bits)(uint64_t bits, uint64_t param), + uint64_t check_bits_param, + uint64_t (*set_bits)(uint64_t bits, uint64_t param), + uint64_t set_bits_param, + void (*callback)(struct mars_context *mars, uint16_t id)) +{ + int block; + int index; + uint64_t queue_ea; + uint64_t block_ea; + uint64_t bits_ea; + uint64_t bits; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + if (!mars->workload_queue_ea) + return MARS_ERROR_PARAMS; + if (id > MARS_WORKLOAD_ID_MAX || !(id % MARS_WORKLOAD_PER_BLOCK)) + return MARS_ERROR_PARAMS; + + queue_ea = mars->workload_queue_ea; + + /* calculate block/index from id */ + block = id / MARS_WORKLOAD_PER_BLOCK; + index = id % MARS_WORKLOAD_PER_BLOCK; + + /* prepare work area for queue block */ + block_ea = get_block_ea(queue_ea, block); + + mars_mutex_lock(block_ea); + + /* get bits from workload queue block */ + bits_ea = get_block_bits_ea(block_ea, index); + bits = mars_ea_get_uint64(bits_ea); + + /* check for valid state */ + if (!(*check_bits)(bits, check_bits_param)) { + mars_mutex_unlock(block_ea); + return MARS_ERROR_STATE; + } + + /* reset workload queue bits and set state to new state */ + bits = (*set_bits)(bits, set_bits_param); + + /* store new bits into queue block */ + mars_ea_put_uint64(bits_ea, bits); + + /* if callback requested call it */ + if (callback) + (*callback)(mars, id); + + mars_mutex_unlock(block_ea); + + /* if requested set workload context pointer to return */ + if (workload_ea) + *workload_ea = get_workload_ea(queue_ea, id); + + return MARS_SUCCESS; +} + +static int check_state_bits(uint64_t bits, uint64_t state) +{ + return (MARS_BITS_GET(&bits, WORKLOAD_STATE) == state); +} + +static int check_state_bits_not(uint64_t bits, uint64_t state) +{ + return (MARS_BITS_GET(&bits, WORKLOAD_STATE) != state); +} + +static uint64_t set_state_bits(uint64_t bits, uint64_t state) +{ + MARS_BITS_SET(&bits, WORKLOAD_STATE, state); + + return bits; +} + +static int change_state( + struct mars_context *mars, + uint16_t id, + uint64_t *workload_ea, + unsigned int old_state, + unsigned int new_state, + void (*callback)(struct mars_context *mars, uint16_t id)) +{ + return change_bits(mars, id, workload_ea, + check_state_bits, old_state, + set_state_bits, new_state, + callback); +} + +static void init_header(uint64_t queue_ea) +{ + int block; + uint16_t bits = 0; + struct mars_workload_queue *queue; + + /* prepare work area for queue header */ + queue = mars_ea_work_area_get(queue_ea, + MARS_WORKLOAD_QUEUE_HEADER_ALIGN, + MARS_WORKLOAD_QUEUE_HEADER_SIZE); + + /* initialize workload queue header */ + queue->header.flag = MARS_WORKLOAD_QUEUE_FLAG_NONE; + queue->header.queue_ea = queue_ea; + queue->header.context_ea = + queue_ea + offsetof(struct mars_workload_queue, context); + + /* create initial bit pattern of workload queue header */ + MARS_BITS_SET(&bits, BLOCK_PRIORITY, MARS_WORKLOAD_BLOCK_PRIORITY_MIN); + MARS_BITS_SET(&bits, BLOCK_COUNTER, MARS_WORKLOAD_BLOCK_COUNTER_MAX); + MARS_BITS_SET(&bits, BLOCK_READY, MARS_WORKLOAD_BLOCK_READY_OFF); + MARS_BITS_SET(&bits, BLOCK_WAITING, MARS_WORKLOAD_BLOCK_WAITING_OFF); + + for (block = 0; block < MARS_WORKLOAD_NUM_BLOCKS; block++) + queue->header.bits[block] = bits; + + /* update queue header on EA */ + mars_ea_put(queue_ea, queue, MARS_WORKLOAD_QUEUE_HEADER_SIZE); + + /* reset mutex portion of queue header */ + mars_mutex_reset(queue_ea); +} + +static void init_block(uint64_t block_ea, uint64_t initial_bits) +{ + int index; + struct mars_workload_queue_block *block = + mars_ea_work_area_get(block_ea, + MARS_WORKLOAD_QUEUE_BLOCK_ALIGN, + MARS_WORKLOAD_QUEUE_BLOCK_SIZE); + + for (index = 1; index < MARS_WORKLOAD_PER_BLOCK; index++) + block->bits[index] = initial_bits; + + /* update queue block on EA */ + mars_ea_put(block_ea, block, MARS_WORKLOAD_QUEUE_BLOCK_SIZE); + + /* reset mutex portion of queue block */ + mars_mutex_reset(block_ea); +} + +static void init_blocks(uint64_t queue_ea) +{ + int block; + uint64_t bits = 0; + + /* create initial bit pattern of workload queue blocks */ + MARS_BITS_SET(&bits, WORKLOAD_STATE, MARS_WORKLOAD_STATE_NONE); + + /* other bits are set by mars_workload_queue_schedule_begin properly */ + + /* initialize workload queue blocks */ + for (block = 0; block < MARS_WORKLOAD_NUM_BLOCKS; block++) { + uint64_t block_ea = get_block_ea(queue_ea, block); + + init_block(block_ea, bits); + } +} + +int mars_workload_queue_create(struct mars_context *mars) +{ + uint64_t queue_ea; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + if (mars->workload_queue_ea) + return MARS_ERROR_STATE; + + /* allocate workload instance */ + queue_ea = mars_ea_memalign(MARS_WORKLOAD_QUEUE_ALIGN, + MARS_WORKLOAD_QUEUE_SIZE); + if (!queue_ea) + return MARS_ERROR_MEMORY; + + /* initialize workload queue header */ + init_header(queue_ea); + + /* initialize workload queue blocks */ + init_blocks(queue_ea); + + /* sync EA */ + mars_ea_sync(); + + /* set the workload queue instance in the mars context */ + mars->workload_queue_ea = queue_ea; + + return MARS_SUCCESS; +} + +static int is_block_empty(uint64_t block_ea) +{ + int index; + struct mars_workload_queue_block *block = + mars_ea_work_area_get(block_ea, + MARS_WORKLOAD_QUEUE_BLOCK_ALIGN, + MARS_WORKLOAD_QUEUE_BLOCK_SIZE); + + /* get the workload queue block from shared memory */ + mars_ea_get(block_ea, block, sizeof(struct mars_workload_queue_block)); + + /* check status */ + for (index = 1; index < MARS_WORKLOAD_PER_BLOCK; index++) { + if (MARS_BITS_GET(&block->bits[index], WORKLOAD_STATE) != + MARS_WORKLOAD_STATE_NONE) + return MARS_ERROR_STATE; + } + + return MARS_SUCCESS; +} + +int mars_workload_queue_destroy(struct mars_context *mars) +{ + int block; + uint64_t queue_ea; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + if (!mars->workload_queue_ea) + return MARS_ERROR_PARAMS; + + queue_ea = mars->workload_queue_ea; + + /* check for any workloads left in workload queue */ + for (block = 0; block < MARS_WORKLOAD_NUM_BLOCKS; block++) { + uint64_t block_ea = get_block_ea(queue_ea, block); + int ret = is_block_empty(block_ea); + if (ret != MARS_SUCCESS) + return ret; + } + + /* free workload queue instance */ + mars_ea_free(queue_ea); + + /* set the workload queue to NULL for error checking */ + mars->workload_queue_ea = 0; + + return MARS_SUCCESS; +} + +int mars_workload_queue_exit(struct mars_context *mars) +{ + uint64_t queue_ea; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + if (!mars->workload_queue_ea) + return MARS_ERROR_PARAMS; + + queue_ea = mars->workload_queue_ea; + + mars_mutex_lock(queue_ea); + + mars_ea_put_uint32(queue_ea + + offsetof(struct mars_workload_queue_header, flag), + MARS_WORKLOAD_QUEUE_FLAG_EXIT); + + mars_mutex_unlock(queue_ea); + + return MARS_SUCCESS; +} + +int mars_workload_queue_query(struct mars_context *mars, + uint16_t id, + int query) +{ + int block; + int index; + uint64_t queue_ea; + uint64_t block_ea; + uint64_t bits_ea; + uint64_t bits; + + /* check function params */ + if (!mars) + return 0; + if (!mars->workload_queue_ea) + return 0; + if (id > MARS_WORKLOAD_ID_MAX || !(id % MARS_WORKLOAD_PER_BLOCK)) + return 0; + + queue_ea = mars->workload_queue_ea; + + /* calculate block/index from id */ + block = id / MARS_WORKLOAD_PER_BLOCK; + index = id % MARS_WORKLOAD_PER_BLOCK; + + /* prepare work area for queue block */ + block_ea = get_block_ea(queue_ea, block); + + mars_mutex_lock(block_ea); + + /* get bits from workload queue block */ + bits_ea = get_block_bits_ea(block_ea, index); + bits = mars_ea_get_uint64(bits_ea); + + mars_mutex_unlock(block_ea); + + switch (query) { + case MARS_WORKLOAD_QUERY_IS_INITIALIZED: + return (MARS_BITS_GET(&bits, WORKLOAD_STATE) != + MARS_WORKLOAD_STATE_NONE); + case MARS_WORKLOAD_QUERY_IS_READY: + return (MARS_BITS_GET(&bits, WORKLOAD_STATE) == + MARS_WORKLOAD_STATE_READY); + case MARS_WORKLOAD_QUERY_IS_WAITING: + return (MARS_BITS_GET(&bits, WORKLOAD_STATE) == + MARS_WORKLOAD_STATE_WAITING); + case MARS_WORKLOAD_QUERY_IS_RUNNING: + return (MARS_BITS_GET(&bits, WORKLOAD_STATE) == + MARS_WORKLOAD_STATE_RUNNING); + case MARS_WORKLOAD_QUERY_IS_FINISHED: + return (MARS_BITS_GET(&bits, WORKLOAD_STATE) == + MARS_WORKLOAD_STATE_FINISHED); + case MARS_WORKLOAD_QUERY_IS_SIGNAL_SET: + return (MARS_BITS_GET(&bits, WORKLOAD_SIGNAL) == + MARS_WORKLOAD_SIGNAL_ON); + } + + return 0; +} + +static int alloc_block(uint64_t block_ea) +{ + int ret = -1; + int index; + struct mars_workload_queue_block *block = + mars_ea_work_area_get(block_ea, + MARS_WORKLOAD_QUEUE_BLOCK_ALIGN, + MARS_WORKLOAD_QUEUE_BLOCK_SIZE); + + mars_mutex_lock(block_ea); + + /* get the workload queue block from shared memory */ + mars_ea_get(block_ea, block, MARS_WORKLOAD_QUEUE_BLOCK_SIZE); + + /* check status */ + for (index = 1; index < MARS_WORKLOAD_PER_BLOCK; index++) { + uint64_t bits = block->bits[index]; + if (MARS_BITS_GET(&bits, WORKLOAD_STATE) == + MARS_WORKLOAD_STATE_NONE) { + MARS_BITS_SET(&bits, WORKLOAD_STATE, + MARS_WORKLOAD_STATE_ADDING); + MARS_BITS_SET(&bits, WORKLOAD_KERNEL_ID, + MARS_KERNEL_ID_NONE); + mars_ea_put_uint64(get_block_bits_ea(block_ea, index), + bits); + ret = index; + break; + } + } + + mars_mutex_unlock(block_ea); + + return ret; +} + +static int add_workload_module(uint64_t workload_ea, + const void *workload_module_elf, + const char *workload_module_name) +{ + int ret, i; + int text_found = 0; + int data_found = 0; + Elf32_Ehdr *ehdr; + Elf32_Phdr *phdr; + struct mars_workload_module *workload_module; + + /* get work area for workload module */ + workload_module = mars_ea_work_area_get(workload_ea, + MARS_WORKLOAD_MODULE_ALIGN, + MARS_WORKLOAD_MODULE_SIZE); + + memset(workload_module, 0, MARS_WORKLOAD_MODULE_SIZE); + + /* process elf header information */ + ehdr = (Elf32_Ehdr *)workload_module_elf; + phdr = (Elf32_Phdr *)((void *)ehdr + ehdr->e_phoff); + + /* elf is not executable */ + if (ehdr->e_type != ET_EXEC) + return MARS_ERROR_FORMAT; + + for (i = 0; i < ehdr->e_phnum; i++) { + /* readonly text segment */ + if (phdr->p_type == PT_LOAD && + phdr->p_flags == PF_R + PF_X && + phdr->p_align == 0x80) { + /* make sure base addr is what we expect */ + if (text_found || + phdr->p_vaddr != MARS_WORKLOAD_MODULE_BASE_ADDR || + phdr->p_memsz != phdr->p_filesz) { + ret = MARS_ERROR_FORMAT; + goto error; + } + + /* initialize the workload module text info */ + workload_module->text_ea = mars_ea_map((void *)ehdr + + phdr->p_offset, + phdr->p_filesz); + if (!workload_module->text_ea) { + ret = MARS_ERROR_MEMORY; + goto error; + } + + workload_module->text_vaddr = phdr->p_vaddr; + workload_module->text_size = phdr->p_filesz; + + /* make sure we only find 1 text segment */ + text_found = 1; + /* read-write data segment */ + } else if (phdr->p_type == PT_LOAD && + phdr->p_flags == PF_R + PF_W && + phdr->p_align == 0x80) { + if (data_found) { + ret = MARS_ERROR_FORMAT; + goto error; + } + + workload_module->data_ea = mars_ea_map((void *)ehdr + + phdr->p_offset, + phdr->p_filesz); + if (!workload_module->data_ea) { + ret = MARS_ERROR_MEMORY; + goto error; + } + + workload_module->data_vaddr = phdr->p_vaddr; + workload_module->data_size = phdr->p_filesz; + workload_module->bss_size = phdr->p_memsz - + phdr->p_filesz; + + /* make sure we only find 1 data segment */ + data_found = 1; + } + + /* increment program header */ + phdr = (void *)phdr + ehdr->e_phentsize; + } + + /* make sure text and data segment is found */ + if (!text_found || !data_found) { + ret = MARS_ERROR_FORMAT; + goto error; + } + + /* set the entry point of execution */ + workload_module->entry = ehdr->e_entry; + if (workload_module_name) + strcpy((char *)workload_module->name, workload_module_name); + else + memset((char *)workload_module->name, 0, + MARS_WORKLOAD_MODULE_NAME_LEN_MAX + 1); + + /* update workload module on EA */ + mars_ea_put(workload_ea, workload_module, MARS_WORKLOAD_MODULE_SIZE); + mars_ea_sync(); + + return MARS_SUCCESS; + +error: + if (text_found) + mars_ea_unmap(workload_module->text_ea, + workload_module->text_size); + if (data_found) + mars_ea_unmap(workload_module->data_ea, + workload_module->data_size); + + return ret; +} + +static void remove_workload_module(struct mars_context *mars, uint16_t id) +{ + struct mars_workload_module *workload_module; + + /* prepare work area for workload module */ + workload_module = mars_ea_work_area_get( + get_workload_ea(mars->workload_queue_ea, id), + MARS_WORKLOAD_MODULE_ALIGN, + MARS_WORKLOAD_MODULE_SIZE); + + /* get workload module from ea */ + mars_ea_get(mars->workload_queue_ea, workload_module, + MARS_WORKLOAD_MODULE_SIZE); + + mars_ea_unmap(workload_module->text_ea, workload_module->text_size); + mars_ea_unmap(workload_module->data_ea, workload_module->data_size); +} + +int mars_workload_queue_add_begin(struct mars_context *mars, + uint16_t *id, + uint64_t *workload_ea, + const void *workload_module_elf, + const char *workload_module_name) +{ + int ret; + int block; + int index = 0; + uint64_t queue_ea; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + if (!mars->workload_queue_ea) + return MARS_ERROR_PARAMS; + if (!id) + return MARS_ERROR_NULL; + if (!workload_module_elf) + return MARS_ERROR_NULL; + if (workload_module_name && + strlen(workload_module_name) > MARS_WORKLOAD_MODULE_NAME_LEN_MAX) + return MARS_ERROR_PARAMS; + + queue_ea = mars->workload_queue_ea; + + /* find free workload queue entry */ + for (block = 0; block < MARS_WORKLOAD_NUM_BLOCKS; block++) { + uint64_t block_ea = get_block_ea(queue_ea, block); + + index = alloc_block(block_ea); + if (index >= 0) + break; + } + + /* no more free workload queue entry */ + if (block >= MARS_WORKLOAD_NUM_BLOCKS) + return MARS_ERROR_LIMIT; + + /* calculate and return workload id */ + *id = block * MARS_WORKLOAD_PER_BLOCK + index; + + /* add the workload module in workload context */ + ret = add_workload_module(get_workload_ea(queue_ea, *id), + workload_module_elf, workload_module_name); + if (ret != MARS_SUCCESS) { + change_state(mars, *id, NULL, + MARS_WORKLOAD_STATE_ADDING, + MARS_WORKLOAD_STATE_NONE, + NULL); + return ret; + } + + /* if requested set workload context pointer to return */ + if (workload_ea) + *workload_ea = get_workload_ea(queue_ea, *id); + + return MARS_SUCCESS; +} + +int mars_workload_queue_add_end(struct mars_context *mars, + uint16_t id, + int cancel) +{ + return change_state(mars, id, NULL, + MARS_WORKLOAD_STATE_ADDING, + cancel ? MARS_WORKLOAD_STATE_NONE : + MARS_WORKLOAD_STATE_FINISHED, + cancel ? remove_workload_module : NULL); +} + +int mars_workload_queue_remove_begin(struct mars_context *mars, + uint16_t id, + uint64_t *workload_ea) +{ + return change_state(mars, id, workload_ea, + MARS_WORKLOAD_STATE_FINISHED, + MARS_WORKLOAD_STATE_REMOVING, + NULL); +} + +int mars_workload_queue_remove_end(struct mars_context *mars, + uint16_t id, + int cancel) +{ + return change_state(mars, id, NULL, + MARS_WORKLOAD_STATE_REMOVING, + cancel ? MARS_WORKLOAD_STATE_FINISHED : + MARS_WORKLOAD_STATE_NONE, + cancel ? NULL : remove_workload_module); +} + +static uint64_t set_schedule_bits(uint64_t bits, uint64_t priority) +{ + /* set the info bits inside queue block for this workload */ + MARS_BITS_SET(&bits, WORKLOAD_STATE, MARS_WORKLOAD_STATE_SCHEDULING); + MARS_BITS_SET(&bits, WORKLOAD_PRIORITY, priority); + MARS_BITS_SET(&bits, WORKLOAD_COUNTER, MARS_WORKLOAD_COUNTER_MIN); + MARS_BITS_SET(&bits, WORKLOAD_SIGNAL, MARS_WORKLOAD_SIGNAL_OFF); + MARS_BITS_SET(&bits, WORKLOAD_WAIT_ID, MARS_WORKLOAD_ID_NONE); + + return bits; +} + +int mars_workload_queue_schedule_begin(struct mars_context *mars, + uint16_t id, uint8_t priority, + uint64_t *workload_ea) +{ + return change_bits(mars, id, workload_ea, + check_state_bits, MARS_WORKLOAD_STATE_FINISHED, + set_schedule_bits, priority, + NULL); +} + +static void update_header_bits(struct mars_context *mars, uint16_t id) +{ + int block = id / MARS_WORKLOAD_PER_BLOCK;; + int index; + uint64_t queue_ea; + uint64_t block_ea; + uint64_t header_bits_ea; + uint16_t header_bits; + uint64_t header_access_ea; + uint32_t header_access; + uint8_t block_ready = MARS_WORKLOAD_BLOCK_READY_OFF; + uint8_t block_waiting = MARS_WORKLOAD_BLOCK_WAITING_OFF; + uint8_t block_priority = MARS_WORKLOAD_BLOCK_PRIORITY_MIN; + + queue_ea = mars->workload_queue_ea; + + block_ea = get_block_ea(queue_ea, block); + + /* search through currently locked queue block workload bits */ + for (index = 1; index < MARS_WORKLOAD_PER_BLOCK; index++) { + uint64_t bits_ea = get_block_bits_ea(block_ea, index); + uint64_t bits = mars_ea_get_uint64(bits_ea); + uint8_t state = MARS_BITS_GET(&bits, WORKLOAD_STATE); + + /* workload state is ready so check priority */ + if (state == MARS_WORKLOAD_STATE_READY) { + uint8_t priority = MARS_BITS_GET(&bits, + WORKLOAD_PRIORITY); + + /* set block priority if higher then current */ + if (priority > block_priority) + block_priority = priority; + + /* set block ready bit in header bits for block */ + block_ready = MARS_WORKLOAD_BLOCK_READY_ON; + } else if (state == MARS_WORKLOAD_STATE_WAITING) { + /* set block waiting bit in header bits for block */ + block_waiting = MARS_WORKLOAD_BLOCK_WAITING_ON; + } + } + + /* lock the queue header */ + mars_mutex_lock(queue_ea); + + /* set the info bits inside queue header for this queue block */ + header_bits_ea = queue_ea + + offsetof(struct mars_workload_queue_header, bits) + + sizeof(uint16_t) * block; + header_bits = mars_ea_get_uint16(header_bits_ea); + + MARS_BITS_SET(&header_bits, BLOCK_READY, block_ready); + MARS_BITS_SET(&header_bits, BLOCK_WAITING, block_waiting); + MARS_BITS_SET(&header_bits, BLOCK_PRIORITY, block_priority); + + mars_ea_put_uint16(header_bits_ea, header_bits); + + /* increment the header access value */ + header_access_ea = queue_ea + + offsetof(struct mars_workload_queue_header, access); + header_access = mars_ea_get_uint32(header_access_ea); + + header_access++; + + mars_ea_put_uint32(header_access_ea, header_access); + + /* unlock the queue header */ + mars_mutex_unlock(queue_ea); +} + +int mars_workload_queue_schedule_end(struct mars_context *mars, + uint16_t id, + int cancel) +{ + return change_state(mars, id, NULL, + MARS_WORKLOAD_STATE_SCHEDULING, + cancel ? MARS_WORKLOAD_STATE_FINISHED: + MARS_WORKLOAD_STATE_READY, + cancel ? NULL : update_header_bits); +} + +static int unscheduling_state_bits(uint64_t bits, uint64_t param) +{ + (void)param; + + /* check for valid state */ + switch (MARS_BITS_GET(&bits, WORKLOAD_STATE)) { + case MARS_WORKLOAD_STATE_READY: + case MARS_WORKLOAD_STATE_RUNNING: + case MARS_WORKLOAD_STATE_WAITING: + return 1; + default: + return 0; + } +} + +int mars_workload_queue_unschedule_begin(struct mars_context *mars, + uint16_t id, + uint64_t *workload_ea) +{ + return change_bits(mars, id, workload_ea, + unscheduling_state_bits, 0, + set_state_bits, MARS_WORKLOAD_STATE_UNSCHEDULING, + NULL); +} + +int mars_workload_queue_unschedule_end(struct mars_context *mars, + uint16_t id) +{ + int ret; + int block; + int index; + uint64_t queue_ea; + uint64_t block_ea; + uint64_t bits_ea; + + ret = change_state(mars, id, NULL, + MARS_WORKLOAD_STATE_UNSCHEDULING, + MARS_WORKLOAD_STATE_FINISHED, + update_header_bits); + if (ret != MARS_SUCCESS) + return ret; + + queue_ea = mars->workload_queue_ea; + + block = id / MARS_WORKLOAD_PER_BLOCK; + index = id % MARS_WORKLOAD_PER_BLOCK; + + block_ea = get_block_ea(queue_ea, block); + bits_ea = get_block_bits_ea(block_ea, index); + + /* signal any threads possibly waiting for workload to finish */ + mars_ea_cond_signal(bits_ea, 1); + + return MARS_SUCCESS; +} + +static int is_workload_finished(uint32_t upper, void *param) +{ + (void)param; + + /* this function assumes 'WORKLOAD_STATE' is stored in upper 32 bits */ + uint64_t bits = (uint64_t)upper << 32; + + switch (MARS_BITS_GET(&bits, WORKLOAD_STATE)) { + case MARS_WORKLOAD_STATE_FINISHED: + return MARS_SUCCESS; + case MARS_WORKLOAD_STATE_NONE: + return MARS_ERROR_STATE; + default: + return -1; + } +} + +static int workload_queue_wait(struct mars_context *mars, + uint16_t id, + int try, + uint64_t *workload_ea) +{ + int ret; + int block; + int index; + uint64_t queue_ea; + uint64_t block_ea; + uint64_t bits_ea; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + if (!mars->workload_queue_ea) + return MARS_ERROR_PARAMS; + if (id > MARS_WORKLOAD_ID_MAX || !(id % MARS_WORKLOAD_PER_BLOCK)) + return MARS_ERROR_PARAMS; + + queue_ea = mars->workload_queue_ea; + + /* calculate block/index from id */ + block = id / MARS_WORKLOAD_PER_BLOCK; + index = id % MARS_WORKLOAD_PER_BLOCK; + + /* prepare work area for queue block */ + block_ea = get_block_ea(queue_ea, block); + bits_ea = get_block_bits_ea(block_ea, index); + + if (try) { + ret = is_workload_finished(mars_ea_get_uint32(bits_ea), NULL); + if (ret < 0) + ret = MARS_ERROR_BUSY; + } else { + ret = mars_ea_cond_wait(bits_ea, is_workload_finished, NULL); + } + + if (ret != MARS_SUCCESS) + return ret; + + /* if requested set workload context pointer to return */ + if (workload_ea) + *workload_ea = get_workload_ea(queue_ea, id); + + return MARS_SUCCESS; +} + +int mars_workload_queue_wait(struct mars_context *mars, + uint16_t id, + uint64_t *workload_ea) +{ + return workload_queue_wait(mars, id, 0, workload_ea); +} + +int mars_workload_queue_try_wait(struct mars_context *mars, + uint16_t id, + uint64_t *workload_ea) +{ + return workload_queue_wait(mars, id, 1, workload_ea); +} + +static uint64_t set_signal_bits(uint64_t bits, uint64_t signal) +{ + MARS_BITS_SET(&bits, WORKLOAD_SIGNAL, signal); + + return bits; +} + +int mars_workload_queue_signal_send(struct mars_context *mars, + uint16_t id) +{ + return change_bits(mars, id, NULL, + check_state_bits_not, MARS_WORKLOAD_STATE_NONE, + set_signal_bits, MARS_WORKLOAD_SIGNAL_ON, + update_header_bits); +} diff --git a/common/libspumars/ppu/ppu/mars_kernel.c b/common/libspumars/ppu/ppu/mars_kernel.c new file mode 100644 index 00000000..c9a87e6d --- /dev/null +++ b/common/libspumars/ppu/ppu/mars_kernel.c @@ -0,0 +1,724 @@ +__attribute__((aligned(128))) const unsigned char mars_kernel_entry[] = { +0x7f,0x45,0x4c,0x46,0x01,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x02,0x00,0x17,0x00,0x00,0x00,0x01,0x00,0x00,0x1e,0x68,0x00,0x00,0x00,0x34, +0x00,0x00,0x21,0x40,0x00,0x00,0x00,0x00,0x00,0x34,0x00,0x20,0x00,0x02,0x00,0x28, +0x00,0x0d,0x00,0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0xe0,0x00,0x00,0x26,0x80,0x00,0x00,0x00,0x07, +0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x04,0x00,0x00,0x20,0x80,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, +0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x7b,0x00,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81, +0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x33,0x03,0xba,0x80, +0x32,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x40,0x82,0x00,0x02,0x35,0x90,0x00,0x00, +0x00,0x20,0x00,0x00,0x12,0x00,0x08,0x2b,0x24,0x00,0x40,0x80,0x24,0xfc,0x80,0x81, +0x1c,0xc8,0x00,0x81,0x23,0x84,0xab,0x83,0x21,0xa0,0x00,0x82,0x42,0x12,0x00,0x03, +0x40,0x80,0x00,0x8a,0x00,0x20,0x00,0x00,0x04,0x00,0x01,0x89,0x3e,0xc0,0x01,0x86, +0x40,0x80,0x40,0x05,0x40,0x80,0x00,0x04,0x35,0x90,0x00,0x00,0x40,0x80,0x68,0x08, +0x40,0x80,0x5a,0x07,0x33,0x84,0xa5,0x8b,0x21,0xa0,0x08,0x09,0x3f,0xbf,0x05,0x8c, +0x3f,0xe1,0x06,0x0d,0x21,0xa0,0x08,0x8d,0x3f,0xe1,0x05,0x8e,0x21,0xa0,0x09,0x0e, +0x21,0xa0,0x09,0x85,0x21,0xa0,0x0a,0x04,0x21,0xa0,0x0a,0x88,0x01,0xa0,0x0d,0x82, +0x33,0x84,0x6a,0x0f,0xb2,0x03,0xc5,0x06,0x23,0x84,0x69,0x10,0x00,0x60,0x00,0x00, +0x33,0x84,0x9e,0x11,0x21,0xa0,0x08,0x09,0x3f,0xbf,0x08,0x92,0x3f,0xe1,0x09,0x13, +0x21,0xa0,0x08,0x93,0x3f,0xe1,0x08,0x94,0x21,0xa0,0x09,0x14,0x21,0xa0,0x09,0x85, +0x21,0xa0,0x0a,0x04,0x21,0xa0,0x0a,0x87,0x01,0xa0,0x0d,0x95,0x14,0x00,0x4a,0x96, +0x21,0x7f,0xf2,0x96,0x40,0x82,0x00,0x17,0x40,0x80,0x00,0x1b,0x04,0x00,0x0b,0x98, +0x42,0x12,0x00,0x19,0x40,0x80,0x40,0x1a,0x04,0x00,0x0d,0x9c,0x40,0x80,0x68,0x1d, +0x12,0x00,0x00,0x12,0x21,0xa0,0x01,0x18,0x33,0x84,0x93,0x1e,0x21,0xa0,0x08,0x19, +0x3f,0xbf,0x0f,0x1f,0x3f,0xe1,0x0f,0xa0,0x21,0xa0,0x08,0xa0,0x3f,0xe1,0x0f,0x21, +0x21,0xa0,0x09,0x21,0x21,0xa0,0x09,0x9a,0x21,0xa0,0x0a,0x1c,0x21,0xa0,0x0a,0x9d, +0x01,0xa0,0x0d,0x82,0x33,0x84,0x57,0xa2,0x14,0x00,0x91,0x23,0x00,0x20,0x00,0x00, +0x21,0x00,0x01,0xa3,0x01,0xa0,0x00,0x02,0x32,0x7f,0xf7,0x00,0x12,0x03,0x1e,0x8a, +0x21,0xa0,0x03,0x9b,0x21,0xa0,0x01,0x17,0x42,0x10,0x80,0x24,0x24,0x03,0x40,0xa4, +0x40,0x80,0x0f,0x86,0x33,0x84,0x53,0x84,0x40,0x80,0x40,0x05,0x42,0x11,0xc0,0x03, +0x40,0x20,0x00,0x7f,0x33,0x03,0x19,0x80,0x40,0x80,0x0f,0x83,0x33,0x03,0x2e,0x80, +0x42,0x11,0xcc,0x26,0x33,0x84,0x40,0xa5,0x3b,0x89,0x92,0xa7,0x35,0x90,0x00,0x00, +0x14,0x00,0x53,0xa8,0x21,0x00,0xac,0xa8,0x40,0x80,0x00,0x4e,0x40,0x80,0x00,0x4f, +0x40,0x80,0x00,0x09,0x40,0xff,0xff,0x8a,0x40,0x80,0x00,0x05,0x18,0x01,0x42,0xa9, +0x42,0x11,0xc0,0x2a,0x40,0x80,0x0e,0x2d,0x35,0x90,0x00,0x00,0x18,0x0a,0x94,0xab, +0x42,0x7f,0xff,0xb2,0x00,0x20,0x00,0x00,0x1c,0x0a,0x95,0xaf,0x38,0x8b,0x55,0xae, +0x0f,0x38,0x45,0x2c,0x3b,0x8b,0xd7,0x30,0x0f,0xbe,0x18,0x31,0x18,0x2c,0x98,0x33, +0x14,0x00,0x98,0x34,0x00,0x20,0x00,0x00,0x14,0x00,0x58,0x49,0x35,0x90,0x00,0x00, +0x40,0x20,0x00,0x7f,0x3f,0xbf,0x19,0xb5,0x5a,0x02,0x58,0xb6,0x00,0x20,0x00,0x00, +0x22,0x00,0x14,0x34,0x00,0x20,0x00,0x00,0x7a,0x02,0x58,0xb7,0x36,0x80,0x1b,0x38, +0x3f,0x3f,0x9a,0xba,0x36,0x80,0x1b,0xb9,0x0c,0x00,0x1c,0x3b,0x3f,0xe1,0x1d,0x3e, +0x08,0x2b,0x1d,0xbd,0x0c,0x00,0x1c,0xbc,0x56,0xc0,0x1e,0xbf,0x14,0x0f,0xdf,0x40, +0x23,0x00,0x2a,0xbf,0x59,0x13,0xe0,0x41,0x55,0xc0,0x20,0xc2,0x0c,0x00,0x21,0x43, +0x18,0x2f,0x21,0xc4,0x56,0xc0,0x22,0x45,0x7d,0x00,0x22,0xc6,0x00,0x20,0x00,0x00, +0x88,0x13,0xe0,0x46,0x36,0x80,0x23,0x47,0x89,0x02,0x82,0xc7,0x12,0x00,0xbd,0x89, +0x40,0x80,0x01,0x03,0x24,0x01,0xc0,0xc9,0x04,0x00,0x02,0x84,0x24,0x00,0x80,0x85, +0x24,0x01,0x40,0xce,0x24,0x01,0x80,0x89,0x24,0x00,0xc0,0xc0,0x24,0x01,0x00,0xc8, +0x33,0x00,0xb9,0x00,0x34,0x00,0xc0,0x8b,0x34,0x01,0x80,0x89,0x34,0x01,0x40,0xce, +0x34,0x00,0x80,0x85,0x34,0x01,0xc0,0xc9,0x34,0x01,0x00,0x8a,0x04,0x00,0x05,0xcf, +0x56,0xc0,0x24,0xca,0x22,0x00,0x0b,0x4a,0x40,0x20,0x00,0x7f,0x12,0x02,0x4e,0x89, +0x40,0x80,0x00,0x04,0x24,0x01,0x00,0x8a,0x04,0x00,0x02,0x83,0x24,0x00,0x80,0x85, +0x40,0x20,0x00,0x7f,0x24,0x01,0x40,0xce,0x24,0x01,0x80,0x89,0x24,0x01,0xc0,0xcf, +0x33,0x02,0x4a,0x00,0x12,0x7f,0xdb,0x0c,0x09,0x20,0xc1,0xcb,0x34,0x01,0x40,0xcd, +0x40,0x20,0x00,0x7f,0x34,0x01,0xc0,0xcf,0x0f,0x38,0x65,0xcc,0x34,0x01,0x80,0x89, +0x34,0x00,0x80,0x85,0x34,0x01,0x00,0x8a,0x08,0x33,0x26,0xce,0x1c,0x00,0x42,0x85, +0x7c,0x0c,0x82,0x82,0x20,0x7f,0xd5,0x02,0x7c,0xff,0xc5,0x03,0x20,0x00,0x10,0x83, +0x40,0x20,0x00,0x7f,0x21,0x7f,0xc9,0xce,0x40,0x82,0x00,0x27,0x12,0x01,0x1f,0x95, +0x04,0x00,0x13,0xa8,0x21,0xa0,0x00,0xa8,0x33,0x84,0x1b,0x05,0x42,0x13,0x00,0x29, +0x21,0xa0,0x08,0x29,0x3f,0xbf,0x02,0xaa,0x3f,0xe1,0x15,0x2b,0x21,0xa0,0x08,0xab, +0x3f,0xe1,0x02,0xac,0x21,0xa0,0x09,0x2c,0x40,0x80,0x40,0x2d,0x21,0xa0,0x09,0xad, +0x21,0xa0,0x0a,0x4e,0x40,0x80,0x68,0x2e,0x21,0xa0,0x0a,0xae,0x01,0xa0,0x0d,0x82, +0x40,0x80,0x40,0x05,0x42,0x13,0x00,0x04,0x42,0x11,0xc0,0x03,0x24,0x01,0xc0,0xa7, +0x33,0x01,0x15,0x00,0x34,0x01,0xc0,0xaf,0x20,0x00,0x6d,0x83,0x21,0xa0,0x01,0x2f, +0x32,0x7f,0xbc,0x00,0x04,0x00,0x18,0x89,0x04,0x00,0x02,0xc8,0x32,0x7f,0xda,0x00, +0x40,0x80,0x00,0x84,0x24,0x01,0x00,0x8a,0x04,0x00,0x05,0x03,0x33,0x02,0x30,0x80, +0x40,0x20,0x00,0x7f,0x12,0x00,0x76,0x89,0x4c,0xff,0xc1,0x86,0x34,0x01,0x00,0x84, +0x20,0x7f,0xb6,0x06,0x0f,0xe1,0x02,0x08,0x19,0x00,0xc4,0x03,0x04,0x00,0x01,0x87, +0x3f,0xe0,0x83,0x8c,0x23,0x84,0x39,0x8c,0x33,0x00,0x72,0x00,0x12,0x02,0xce,0x8a, +0x04,0x00,0x01,0x8d,0x34,0x03,0x40,0x8e,0x04,0x00,0x01,0x84,0x00,0x20,0x00,0x00, +0x40,0x80,0x80,0x05,0x23,0x84,0x33,0x8d,0x40,0x80,0x0f,0x86,0x42,0x10,0x80,0x03, +0x23,0x84,0x24,0x0e,0x33,0x02,0xc9,0x80,0x40,0x80,0x0f,0x83,0x33,0x02,0xde,0x80, +0x40,0x80,0x20,0x05,0x33,0x84,0x21,0x84,0x42,0x12,0x98,0x03,0x33,0x01,0x01,0x80, +0x7c,0x00,0x01,0x8f,0x0c,0x00,0x07,0x90,0x3f,0xe0,0xc8,0x11,0x23,0x84,0x1c,0x91, +0x40,0x20,0x00,0x7f,0x20,0x00,0x09,0x83,0x40,0x80,0x0c,0x17,0x33,0x84,0x1c,0x92, +0x40,0x80,0x0f,0x86,0x12,0x02,0xc1,0x8e,0x1c,0x06,0x09,0x19,0x34,0x00,0x09,0x13, +0x34,0x00,0x49,0x14,0x34,0x00,0x89,0x15,0x34,0x00,0xc9,0x16,0x34,0x00,0x09,0x04, +0x23,0x84,0x1a,0x13,0x23,0x84,0x1b,0x94,0x23,0x84,0x1d,0x15,0x23,0x84,0x1e,0x96, +0x38,0x85,0xc9,0x18,0x34,0x00,0x49,0x03,0x3b,0x86,0x4c,0x05,0x33,0x02,0xba,0x80, +0x40,0x80,0x0e,0x1b,0x12,0x02,0xb9,0x90,0x40,0x80,0x0a,0x1c,0x33,0x84,0x12,0x9a, +0x40,0x80,0x04,0x20,0x40,0x80,0x0f,0x86,0x1c,0x07,0x0d,0x1f,0x38,0x86,0xcd,0x1d, +0x1c,0x02,0x0d,0x22,0x38,0x87,0x0d,0x1e,0x1c,0x05,0x0d,0x23,0x38,0x88,0x0d,0x21, +0x24,0x00,0x80,0x9b,0x24,0x01,0xc0,0x9c,0x3b,0x87,0xce,0x85,0x3b,0x88,0xcf,0x03, +0x3b,0x88,0x90,0x84,0x33,0x02,0xb1,0x80,0x33,0x84,0x0b,0x24,0x34,0x01,0xc0,0xa5, +0x34,0x00,0x80,0xa7,0x12,0x00,0x42,0x8e,0x1c,0x05,0x12,0x2a,0x34,0x00,0x92,0x05, +0x1c,0x07,0x12,0x2b,0x38,0x89,0x52,0x26,0x40,0x80,0x00,0x25,0x38,0x89,0xd2,0x28, +0x04,0x00,0x02,0xa9,0x3b,0x8a,0x93,0x03,0x3b,0x8a,0xd4,0x2d,0x04,0x00,0x01,0xac, +0x18,0x0b,0x56,0x26,0x18,0x0a,0x53,0x24,0x58,0x09,0x92,0x2e,0x21,0x00,0x3b,0xae, +0x40,0x80,0x0f,0x83,0x33,0x02,0xbd,0x80,0x00,0x40,0x00,0x00,0x42,0x11,0x80,0x2f, +0x24,0x00,0x80,0xaf,0x33,0x03,0x2c,0x80,0x42,0x7f,0xff,0xb0,0x33,0x84,0x0e,0xb3, +0x42,0x11,0xc4,0x37,0x35,0x90,0x00,0x00,0x40,0x20,0x00,0x7f,0x33,0x83,0xc9,0xb6, +0x42,0x11,0x80,0x04,0x32,0x80,0x80,0xb1,0x33,0x83,0x33,0x3b,0x12,0x02,0xbc,0x9f, +0x40,0x20,0x00,0x7f,0x33,0x83,0x31,0xc8,0x16,0xe0,0x18,0xb2,0x3f,0x83,0x99,0xb4, +0x3b,0x8d,0xdb,0x38,0x34,0x00,0x80,0xc4,0x35,0x90,0x00,0x00,0x0f,0xbf,0x1a,0x35, +0x14,0x03,0xda,0x3a,0x18,0x4c,0x9c,0x39,0x40,0x20,0x00,0x7f,0x24,0x02,0x80,0xba, +0x18,0x2c,0x1a,0xbd,0xb7,0x8e,0x5c,0xbb,0x34,0x02,0x80,0xbe,0x24,0x02,0x40,0xbd, +0x34,0x02,0x40,0xc0,0x68,0x0c,0x9c,0x3c,0x18,0x2c,0x1f,0x3f,0x0f,0x61,0xe0,0x41, +0x0f,0x60,0xdf,0xc2,0x3f,0xbf,0x20,0xc3,0x18,0x11,0x21,0x45,0x24,0x00,0xc0,0xc2, +0x24,0x02,0xc0,0xc5,0x54,0xc0,0x21,0xc6,0x18,0x51,0x9e,0x47,0xb0,0x71,0xe3,0xc8, +0x68,0x11,0x9e,0x03,0x24,0x02,0x00,0x83,0x33,0x02,0xad,0x00,0x34,0x00,0xc0,0x8b, +0x34,0x00,0x80,0xc9,0x34,0x02,0xc0,0xcb,0x12,0x00,0x15,0x09,0x38,0x92,0x45,0xca, +0x3b,0x92,0xe5,0x4c,0x3f,0xbc,0x66,0x4d,0x3f,0xe3,0x26,0xce,0x14,0xe2,0x27,0x4f, +0x56,0xc0,0x27,0x89,0x24,0x03,0x00,0x89,0x40,0x20,0x00,0x7f,0x23,0x00,0x10,0x89, +0x40,0x80,0x0f,0x86,0x33,0x83,0xf1,0x84,0x40,0x80,0x80,0x05,0x42,0x10,0x80,0x03, +0x40,0x20,0x00,0x7f,0x33,0x02,0x92,0x80,0x40,0x80,0x0f,0x83,0x33,0x02,0x9c,0x80, +0x42,0x11,0x80,0x0a,0x33,0x83,0xeb,0x86,0x40,0x80,0x00,0x83,0x34,0x00,0xc0,0x82, +0x34,0x00,0x80,0x87,0x34,0x02,0xc0,0x90,0x12,0x00,0x44,0x11,0x32,0xbf,0xbf,0x92, +0x3f,0x83,0x43,0x0e,0x3a,0xe2,0x81,0x0d,0x38,0x81,0xc1,0x0c,0x38,0x81,0xc1,0x08, +0x14,0x3f,0xc7,0x0f,0x34,0x02,0x40,0x84,0x3f,0xbf,0x07,0x94,0x3b,0x84,0x06,0x11, +0x3f,0x82,0x0a,0x15,0x18,0x24,0x88,0x93,0x3f,0xe3,0xca,0x96,0x08,0x25,0x89,0x97, +0xb3,0x02,0x0b,0x8d,0x28,0x81,0xc1,0x18,0x40,0x20,0x00,0x7f,0x33,0x00,0x3b,0x80, +0x42,0x11,0x80,0x04,0x34,0x02,0x00,0x83,0x33,0x02,0xc9,0x00,0x12,0x7f,0x5c,0x89, +0x40,0x20,0x00,0x7f,0x34,0x03,0x00,0x84,0x40,0x20,0x00,0x7f,0x23,0x7f,0x5a,0x84, +0x33,0x83,0xdc,0x19,0x3f,0x83,0x4c,0x9a,0x4e,0xff,0xcd,0x1b,0x56,0xc0,0x0d,0x9c, +0x23,0x7f,0x58,0x1c,0x34,0x02,0x80,0x9d,0x12,0x00,0xa6,0x09,0x34,0x02,0x00,0xa1, +0x33,0x83,0x04,0x23,0x14,0x03,0xce,0x9e,0x0f,0x60,0xcf,0x1f,0x3f,0xbf,0x0f,0xa0, +0x18,0x48,0x50,0x22,0xb0,0x68,0x91,0x23,0x68,0x08,0x50,0x03,0x33,0x00,0xa1,0x80, +0x40,0x20,0x00,0x7f,0x32,0x7f,0x51,0x80,0x1c,0x04,0x13,0x26,0x24,0xff,0xd3,0x25, +0x32,0x7f,0xc3,0x00,0x01,0xa0,0x00,0x02,0x04,0x00,0x17,0xb0,0x21,0xa0,0x01,0x30, +0x40,0x20,0x00,0x7f,0x32,0x7f,0x91,0x00,0x40,0x80,0x00,0x03,0x33,0x00,0x9b,0x80, +0x1c,0x38,0x00,0x81,0x00,0x20,0x00,0x00,0x40,0x80,0x00,0x03,0x34,0x00,0x40,0x80, +0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x42,0x12,0x00,0x02,0x40,0x80,0x04,0x03, +0x1c,0x02,0x01,0x04,0x38,0x80,0xc1,0x05,0x3b,0x81,0x02,0x83,0x35,0x00,0x00,0x00, +0x33,0x83,0x9c,0x02,0x3f,0x83,0x81,0x03,0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00, +0x33,0x83,0xca,0x02,0x3f,0x83,0x81,0x03,0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00, +0x42,0x10,0x80,0x03,0x35,0x00,0x00,0x00,0x0f,0xbf,0x01,0x82,0x33,0x83,0x84,0x84, +0x42,0x7f,0xff,0x85,0x33,0x82,0xed,0x8d,0x35,0x80,0x00,0x0b,0x18,0x21,0x41,0x83, +0x18,0x21,0x41,0x06,0x08,0x00,0xc3,0x07,0x1c,0xff,0xc3,0x88,0x0f,0x62,0x04,0x09, +0x3f,0xbf,0x04,0x8a,0x54,0xc0,0x05,0x0b,0x18,0x41,0x05,0x8c,0xb0,0x63,0x06,0x0d, +0x68,0x01,0x05,0x83,0x35,0x00,0x00,0x00,0x3f,0xbe,0x41,0x83,0x78,0x01,0x01,0x82, +0x36,0x00,0x01,0x04,0x4c,0x02,0xc2,0x05,0x0c,0x00,0x02,0x83,0x35,0x00,0x00,0x00, +0x3f,0xbe,0x41,0x83,0x78,0x01,0x01,0x82,0x36,0x00,0x01,0x04,0x4c,0x02,0xc2,0x05, +0x1c,0x00,0x42,0x83,0x35,0x00,0x00,0x00,0x3f,0x82,0x02,0x04,0x32,0xbf,0xbf,0x82, +0x3f,0xe3,0xc2,0x05,0x18,0x20,0x81,0x83,0x08,0x21,0x41,0x83,0x35,0x00,0x00,0x00, +0x3f,0x82,0x02,0x04,0x32,0xf9,0xf9,0x82,0x3f,0xe2,0x82,0x05,0x18,0x20,0x81,0x83, +0x08,0x21,0x41,0x83,0x35,0x00,0x00,0x00,0x3f,0x82,0x02,0x05,0x32,0x9f,0x9f,0x82, +0x32,0xc0,0x40,0x04,0x33,0x82,0xdb,0x8a,0x40,0x20,0x00,0x7f,0x3f,0xe3,0x82,0x86, +0x18,0x20,0x81,0x83,0x33,0x82,0xdb,0x8c,0x16,0x01,0x02,0x07,0x35,0x80,0x00,0x05, +0x08,0x21,0xc1,0x88,0x08,0x21,0x84,0x09,0x18,0x22,0x84,0x8b,0x08,0x23,0x05,0x83, +0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x3f,0xbe,0x41,0x83,0x3f,0xe1,0x01,0x82, +0x14,0x1c,0x01,0x03,0x35,0x00,0x00,0x00,0x14,0x00,0x41,0x8b,0x24,0x00,0x40,0x80, +0x04,0x00,0x01,0x8a,0x35,0x90,0x00,0x00,0x24,0xfe,0x00,0x81,0x04,0x00,0x02,0x06, +0x1c,0xe0,0x00,0x81,0x40,0x80,0x00,0x05,0x40,0x80,0x00,0x09,0x00,0x20,0x00,0x00, +0x40,0x80,0x00,0x07,0x20,0x00,0x0f,0x0b,0x42,0x11,0x80,0x08,0x40,0x80,0x00,0x05, +0x1c,0x1e,0x04,0x0c,0x35,0x90,0x00,0x00,0x40,0x80,0x00,0x09,0x40,0x80,0x00,0x07, +0x40,0x80,0x04,0x0d,0x40,0x80,0x00,0x8e,0x40,0x20,0x00,0x7f,0x38,0x83,0x44,0x03, +0x1c,0x02,0x04,0x08,0x12,0x7f,0xfe,0x91,0x3b,0x82,0x01,0x82,0x3f,0xbe,0x41,0x04, +0x3f,0xbe,0x81,0x0f,0x3f,0xe1,0x02,0x10,0x3f,0xe1,0x07,0x91,0x14,0x04,0x08,0x12, +0x14,0x08,0x08,0x13,0x56,0xc0,0x09,0x14,0x5a,0x04,0x42,0x95,0x00,0x20,0x00,0x00, +0x56,0xc0,0x09,0x96,0x00,0x20,0x00,0x00,0x22,0x00,0x27,0x14,0x80,0xa1,0x48,0x95, +0x40,0x80,0x00,0x87,0x78,0x02,0x06,0x18,0x20,0x7f,0xf6,0x18,0x12,0x02,0x46,0x89, +0x42,0x11,0xc0,0x04,0x33,0x83,0x61,0x83,0x24,0x01,0x00,0x85,0x24,0x00,0x80,0x86, +0x24,0x01,0x80,0x87,0x24,0x01,0x40,0x89,0x24,0x00,0xc0,0x8a,0x24,0x01,0xc0,0x8b, +0x33,0x02,0x42,0x00,0x34,0x01,0xc0,0x9e,0x34,0x01,0x00,0x99,0x34,0x00,0x80,0x9a, +0x35,0x90,0x00,0x00,0x34,0x01,0x80,0x9b,0x34,0x01,0x40,0x9c,0x34,0x00,0xc0,0x9d, +0x40,0x20,0x00,0x7f,0x20,0x00,0x1d,0x9e,0x18,0x06,0x8d,0x3e,0x33,0x82,0xb8,0x8b, +0x42,0x11,0xc0,0x3f,0x32,0xfe,0xfe,0x84,0x40,0x80,0x0e,0x33,0x18,0x0f,0x9f,0xb2, +0x35,0x90,0x00,0x00,0x14,0x3f,0xcd,0xc5,0x1c,0x0a,0x99,0x43,0x38,0x8c,0xd9,0x41, +0x42,0x7f,0xff,0xc0,0x3f,0xbf,0x22,0xc7,0x14,0x3f,0xcc,0xc8,0x38,0x8c,0xd9,0x42, +0x14,0x3f,0xce,0x46,0x3e,0xa3,0x19,0x44,0x3f,0xbf,0x24,0x4b,0x3f,0x82,0x23,0xcc, +0x3b,0x90,0xe0,0xc9,0x3f,0x82,0x25,0x8a,0x3f,0xe2,0x26,0x4d,0x18,0x30,0x24,0xca, +0x3f,0xbf,0x25,0x4f,0x3f,0x60,0x66,0xce,0x3f,0xe2,0x45,0x06,0x18,0x22,0xe7,0x85, +0x08,0x33,0x82,0x8c,0x3f,0xe1,0x06,0x07,0x14,0xff,0x83,0x8d,0x08,0x31,0x86,0x8e, +0x18,0x30,0x07,0x03,0x3f,0xbf,0x01,0x82,0x18,0x21,0x01,0x0f,0x08,0x21,0x87,0x90, +0x3f,0xe1,0x08,0x11,0xb7,0xb0,0x88,0xc4,0x40,0x20,0x00,0x7f,0x28,0x8c,0xd9,0x3d, +0x42,0x11,0xc0,0x12,0x33,0x83,0x34,0x15,0x40,0x80,0x02,0x18,0x12,0x02,0x5c,0x8c, +0x1c,0x01,0x09,0x13,0x33,0x83,0x32,0x09,0x1c,0x20,0x00,0x81,0x3e,0xc1,0x09,0x14, +0x04,0x00,0x09,0x04,0x33,0x83,0x41,0x83,0x3b,0x84,0xca,0x96,0x1c,0x00,0x4b,0x17, +0xb1,0x02,0x4b,0x94,0x28,0x86,0x09,0x08,0x34,0x00,0x40,0x80,0x32,0x02,0x56,0x80, +0x7d,0x00,0x0b,0x17,0x00,0x20,0x00,0x00,0x81,0x22,0x47,0x17,0x32,0x7f,0xd9,0x00, +0x20,0x7f,0xf6,0x1d,0x18,0x06,0x8d,0x1f,0x42,0x11,0xc0,0x20,0x40,0x80,0x0e,0x22, +0x18,0x07,0xd0,0x21,0x42,0x7f,0xff,0xa7,0x1c,0x0a,0x90,0xa4,0x38,0x88,0x90,0xa3, +0x14,0x00,0x8e,0xa5,0x40,0x80,0x00,0x2e,0x3b,0x89,0x11,0xa6,0x18,0x29,0xd3,0x28, +0x3f,0xbf,0x14,0x29,0x20,0x00,0x04,0xa5,0x3f,0x3f,0x94,0xaa,0x3f,0xe1,0x15,0x2b, +0x14,0x0f,0xd5,0xae,0x7e,0x0f,0xd7,0x2c,0x56,0xc0,0x16,0x2d,0x23,0x00,0x01,0xad, +0x1d,0x00,0x57,0x2e,0x00,0x20,0x00,0x00,0x14,0x3f,0xd7,0x2f,0x33,0x82,0x92,0xb4, +0x42,0x11,0xc0,0x31,0x12,0x7f,0xe8,0x8d,0x40,0x80,0x0e,0x33,0x3f,0xbf,0x17,0xb0, +0x18,0x07,0xd8,0xb2,0x38,0x8c,0xd9,0x37,0x18,0x2d,0x14,0xb5,0x3f,0x82,0x18,0x38, +0x3e,0xa3,0x19,0x36,0x3f,0xe2,0x1c,0x39,0x3f,0x60,0x9c,0xba,0x08,0x2e,0x9a,0xbb, +0x3f,0xe1,0x1d,0xbc,0xb7,0xad,0xde,0x36,0x32,0x7f,0xe2,0x00,0x00,0x20,0x00,0x00, +0x12,0x7f,0xb1,0x0c,0x3f,0x82,0x02,0x05,0x40,0x80,0x00,0x04,0x33,0x82,0x82,0x82, +0x24,0x00,0x40,0x80,0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81,0x3f,0xe3,0x02,0x86, +0x18,0x20,0x81,0x83,0x08,0x21,0x81,0x87,0x40,0x80,0x00,0x03,0x24,0x00,0x80,0x87, +0x33,0x7f,0xab,0x00,0x34,0x00,0x80,0x83,0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80, +0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x04,0x00,0x01,0x82,0x24,0x00,0x40,0x80, +0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81,0x24,0x00,0x80,0x82,0x33,0x7f,0x87,0x80, +0x40,0x80,0x80,0x05,0x04,0x00,0x01,0x84,0x40,0x80,0x0f,0x86,0x00,0x20,0x00,0x00, +0x42,0x11,0x00,0x03,0x33,0x01,0xec,0x80,0x40,0x80,0x0f,0x83,0x33,0x01,0xf6,0x80, +0x40,0x20,0x00,0x7f,0x12,0x7f,0xa0,0x89,0x42,0x7f,0xff,0x84,0x34,0x00,0x80,0x83, +0x1c,0x0c,0x00,0x81,0x40,0x20,0x00,0x7f,0x0f,0xbf,0x01,0x85,0x34,0x00,0x40,0x80, +0x40,0x80,0x00,0x83,0x18,0x21,0x42,0x04,0x32,0x7f,0x9c,0x00,0x00,0x20,0x00,0x00, +0x04,0x00,0x01,0x82,0x24,0x00,0x40,0x80,0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81, +0x24,0x00,0x80,0x82,0x33,0x7f,0x7a,0x80,0x40,0x80,0x80,0x05,0x04,0x00,0x01,0x84, +0x40,0x80,0x0f,0x86,0x00,0x20,0x00,0x00,0x42,0x11,0x00,0x03,0x33,0x01,0xd4,0x80, +0x40,0x80,0x0f,0x83,0x33,0x01,0xe9,0x80,0x40,0x20,0x00,0x7f,0x12,0x7f,0x93,0x89, +0x42,0x7f,0xff,0x84,0x34,0x00,0x80,0x83,0x1c,0x0c,0x00,0x81,0x40,0x20,0x00,0x7f, +0x0f,0xbf,0x01,0x85,0x34,0x00,0x40,0x80,0x40,0x80,0x00,0x83,0x18,0x21,0x42,0x04, +0x32,0x7f,0x8f,0x00,0x00,0x20,0x00,0x00,0x04,0x00,0x01,0x85,0x40,0x80,0x1f,0x83, +0x3f,0xbf,0x02,0x84,0x3f,0xe1,0x02,0x85,0x3f,0xe1,0x02,0x04,0x32,0x02,0x42,0x80, +0x35,0x80,0x00,0x09,0x42,0x12,0x00,0x04,0x40,0x80,0x02,0x02,0x1c,0x01,0x02,0x03, +0x40,0x20,0x00,0x7f,0x38,0x80,0x82,0x05,0x3b,0x80,0xc2,0x86,0x01,0xa0,0x04,0x07, +0x08,0x01,0x83,0x83,0x35,0x00,0x00,0x00,0x14,0x03,0xc2,0x02,0x12,0x00,0x04,0x98, +0x41,0x81,0x81,0x86,0x33,0x82,0x5f,0x8a,0x40,0x80,0x08,0x08,0x34,0x00,0x02,0x0b, +0xb0,0xe0,0x81,0x06,0x34,0x00,0x01,0x8c,0x33,0x82,0x5d,0x0e,0x18,0x01,0xc5,0x0f, +0x1c,0xfc,0x02,0x85,0x38,0x82,0x01,0x90,0x38,0x82,0x02,0x11,0x4c,0x00,0x02,0x8d, +0x1c,0x04,0x04,0x08,0xb1,0x24,0x06,0x0e,0x04,0x00,0x08,0x0c,0xb2,0x44,0x45,0x8f, +0x04,0x00,0x08,0x8b,0x7a,0x04,0x84,0x93,0x09,0x24,0xc9,0x94,0x36,0x40,0x0a,0x15, +0x7c,0x00,0x0a,0x96,0x18,0x23,0x4b,0x17,0x40,0x20,0x00,0x7f,0x21,0x7f,0xf8,0x97, +0x0c,0x00,0x02,0x83,0x35,0x80,0x00,0x12,0x4c,0xff,0xc2,0x85,0x42,0x81,0x01,0x99, +0x58,0x21,0x41,0x84,0x5a,0x04,0x84,0x98,0x5a,0x02,0x49,0x1b,0xb3,0x41,0x02,0x19, +0x42,0x7f,0xff,0x9c,0x36,0x40,0x0c,0x1d,0x36,0x40,0x0d,0x9e,0x0b,0x66,0x8e,0x1f, +0x58,0x07,0x8e,0xa0,0x18,0x27,0x0f,0xa1,0x0f,0x60,0x50,0x22,0x18,0x28,0x4a,0xa3, +0x7c,0x00,0x11,0xa4,0x0c,0xff,0xd1,0x25,0x58,0x29,0x12,0x83,0x35,0x00,0x00,0x00, +0x40,0x20,0x00,0x7f,0x12,0x7f,0x50,0x89,0x40,0x20,0x00,0x7f,0x33,0x83,0x16,0x84, +0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x3f,0x83,0x82,0x02, +0x79,0x00,0xc1,0x05,0x23,0x00,0x07,0x85,0x33,0x7f,0x4c,0x00,0x40,0x80,0x0f,0x86, +0x04,0x00,0x01,0x84,0x40,0x80,0x80,0x05,0x42,0x10,0x00,0x03,0x33,0x01,0xa6,0x80, +0x40,0x80,0x0f,0x83,0x33,0x01,0xbb,0x80,0x42,0x10,0x00,0x03,0x00,0x20,0x00,0x00, +0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x40,0x20,0x00,0x7f,0x35,0x00,0x00,0x00, +0x42,0x10,0x80,0x03,0x32,0x7f,0xfd,0x80,0x42,0x10,0x80,0x05,0x12,0x00,0x06,0x93, +0x33,0x82,0x84,0x02,0x3e,0xe0,0x02,0x86,0x24,0x00,0x40,0x80,0x24,0xff,0x40,0x81, +0x1c,0xf4,0x00,0x81,0xb0,0x60,0x81,0x86,0x23,0x82,0x81,0x03,0x20,0x00,0x06,0x04, +0x42,0x10,0x80,0x0a,0x40,0x80,0x00,0x0c,0x1c,0x10,0x05,0x0b,0x1c,0x0c,0x05,0x87, +0x18,0x02,0x86,0x08,0x38,0x83,0x02,0x09,0x1c,0x04,0x06,0x0c,0x18,0x03,0x05,0x8d, +0x58,0x03,0x43,0x8e,0x24,0x01,0x04,0x09,0x21,0x7f,0xfd,0x0e,0x12,0x01,0xb2,0x89, +0x42,0x12,0x0c,0x0f,0x33,0x82,0xce,0x90,0x42,0x13,0x00,0x04,0x40,0x20,0x00,0x7f, +0x40,0x20,0x00,0x7f,0x3b,0x83,0xc8,0x11,0x04,0x00,0x08,0x83,0x24,0x00,0x80,0x91, +0x33,0x01,0xae,0x00,0x12,0x00,0x09,0x8a,0x42,0x13,0x00,0x04,0x33,0x83,0x07,0x93, +0x40,0x20,0x00,0x7f,0x34,0x00,0x80,0x96,0x1c,0x02,0x02,0x12,0x40,0x20,0x00,0x7f, +0x3b,0x84,0x89,0x94,0x7c,0x0d,0x8a,0x15,0x40,0x20,0x00,0x7f,0x20,0x00,0x04,0x95, +0x04,0x00,0x0b,0x03,0x33,0x01,0xdb,0x80,0x40,0x80,0x03,0x03,0x00,0x20,0x00,0x00, +0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80,0x40,0x20,0x00,0x7f,0x35,0x00,0x00,0x00, +0x40,0x80,0x01,0x1c,0x33,0x82,0xfe,0x1d,0x40,0x80,0x02,0x21,0x3e,0xc1,0x02,0x17, +0x40,0x80,0x0a,0x19,0x33,0x82,0xef,0x9f,0x1c,0x00,0x4a,0x1b,0x3e,0xc2,0x02,0x18, +0x40,0x80,0x04,0x2c,0x12,0x00,0x0b,0x93,0x3e,0xc0,0x02,0x1a,0xb3,0xc7,0x4e,0x17, +0x3f,0x83,0x8f,0xa0,0x28,0x88,0x42,0x1e,0x33,0x82,0xf9,0x22,0x18,0x08,0x91,0x23, +0x1c,0x00,0x51,0x24,0x18,0x08,0xc2,0x25,0x7c,0x0d,0x92,0x26,0x38,0x86,0x52,0xa7, +0x3e,0xa1,0x12,0xa8,0xb5,0x29,0xd0,0x28,0x28,0x86,0x52,0xa9,0x33,0x82,0xf3,0xaa, +0xb5,0x6a,0x8d,0x98,0x28,0x8b,0x02,0x2b,0x33,0x82,0xf3,0x04,0xb5,0xc1,0x12,0x1a, +0x20,0x00,0x02,0x26,0x40,0x80,0x00,0x2d,0x40,0x20,0x00,0x7f,0xb5,0xc1,0x16,0x9a, +0x42,0x13,0x00,0x04,0x23,0x82,0xef,0xae,0x04,0x00,0x0b,0x03,0x33,0x01,0xc6,0x80, +0x42,0x12,0x0c,0x32,0x33,0x82,0xae,0xb1,0x12,0x7f,0xa8,0x09,0x32,0x80,0x80,0xaf, +0x40,0x20,0x00,0x7f,0x33,0x82,0x05,0xb5,0x16,0x01,0x17,0xb0,0x3b,0x8c,0x98,0xb3, +0x18,0x4c,0x19,0xb4,0xb0,0x6d,0x1a,0x35,0x68,0x0c,0x19,0x83,0x33,0x7f,0xa3,0x80, +0x40,0x80,0x00,0x03,0x32,0x7f,0xe5,0x80,0x40,0x20,0x00,0x7f,0x21,0x00,0x02,0x83, +0x42,0x10,0xbc,0x06,0x33,0x82,0x52,0x83,0x3b,0x81,0x81,0x83,0x35,0x00,0x00,0x00, +0x40,0x20,0x00,0x7f,0x12,0x7f,0xfd,0x89,0x40,0x20,0x00,0x7f,0x33,0x82,0x48,0x82, +0x40,0x20,0x00,0x7f,0x24,0x00,0x01,0x82,0x33,0x82,0x49,0x04,0x24,0x00,0x41,0x84, +0x33,0x82,0x4a,0x05,0x24,0x00,0x81,0x85,0x32,0x7f,0xf9,0x00,0x00,0x20,0x00,0x00, +0x40,0x81,0x8f,0x82,0x24,0x00,0x40,0x80,0x04,0x00,0x02,0x09,0x35,0x90,0x00,0x00, +0x24,0xfd,0x80,0x81,0x1c,0xd8,0x00,0x81,0x59,0x00,0x81,0x84,0x24,0x01,0x40,0x83, +0x24,0x02,0x00,0x86,0x24,0x02,0x40,0x87,0x24,0x01,0xc0,0x88,0x23,0x00,0x28,0x84, +0x14,0x03,0xc1,0x8b,0x00,0x20,0x00,0x00,0x22,0x00,0x27,0x0b,0x35,0x90,0x00,0x00, +0x0f,0xbf,0x01,0x83,0x33,0x82,0x84,0x87,0x42,0x7f,0xff,0x8c,0x32,0x80,0x80,0x88, +0x42,0x11,0xc4,0x06,0x33,0x81,0xed,0x8a,0x18,0x23,0x05,0x92,0x12,0x01,0x76,0x93, +0x18,0x23,0x01,0x8d,0x24,0x00,0xc0,0x85,0x16,0xe0,0x04,0x0e,0x24,0x01,0x00,0x89, +0x0f,0x61,0xc6,0x90,0x3b,0x81,0x83,0x8f,0x0f,0x60,0xc9,0x14,0x42,0x11,0x80,0x04, +0x18,0x43,0x87,0x91,0x3f,0xbf,0x08,0x15,0x24,0x00,0x80,0x94,0xb2,0x64,0x48,0x8a, +0x54,0xc0,0x0a,0x96,0x68,0x03,0x87,0x93,0x18,0x44,0xcb,0x17,0xb0,0x65,0xcb,0x8a, +0x68,0x04,0xcb,0x03,0x24,0x01,0x80,0x83,0x33,0x01,0x6d,0x00,0x34,0x01,0x00,0x98, +0x35,0x80,0x0c,0x09,0x34,0x00,0x80,0x9c,0x34,0x00,0xc0,0x85,0x20,0x00,0x08,0x98, +0x42,0x11,0x80,0x19,0x04,0x00,0x02,0x84,0x18,0x07,0x0c,0x9b,0x38,0x87,0x0c,0x9a, +0x3b,0x86,0xcd,0x03,0x35,0x20,0x0c,0x00,0x34,0x00,0x80,0x9c,0x21,0x00,0x04,0x83, +0x42,0x11,0x80,0x04,0x34,0x01,0x80,0x83,0x33,0x01,0x99,0x00,0x40,0x80,0x03,0x83, +0x1c,0x28,0x00,0x81,0x34,0x00,0x40,0x80,0x40,0x20,0x00,0x7f,0x35,0x00,0x00,0x00, +0x42,0x11,0x80,0x1d,0x34,0x02,0x40,0x84,0x40,0x20,0x00,0x7f,0x34,0x02,0x00,0xa0, +0x18,0x07,0x0e,0x9f,0x38,0x87,0x0e,0x9e,0x24,0x00,0x80,0x9c,0x24,0x00,0xc0,0x9d, +0x3b,0x87,0xcf,0x03,0x35,0x20,0x10,0x00,0x42,0x11,0x80,0x24,0x34,0x00,0x80,0xa1, +0x34,0x00,0xc0,0xa2,0x3a,0xe9,0x10,0xa5,0x38,0x88,0x51,0x23,0xb4,0xc8,0xc1,0xa5, +0x28,0x88,0x51,0x26,0x34,0x01,0xc0,0xa7,0x20,0x00,0x02,0x27,0x34,0x01,0x40,0x83, +0x40,0x20,0x00,0x7f,0x35,0x20,0x13,0x80,0x42,0x11,0x80,0x04,0x34,0x01,0x80,0x83, +0x40,0x20,0x00,0x7f,0x33,0x01,0x89,0x80,0x40,0x80,0x00,0x03,0x32,0x7f,0xf0,0x80, +0x40,0x80,0x01,0x03,0x32,0x7f,0xef,0x80,0x04,0x00,0x02,0x02,0x12,0x7f,0xd0,0x8b, +0x42,0x05,0xc0,0x08,0x32,0x80,0x80,0x87,0x42,0x03,0xec,0x06,0x24,0x00,0x40,0x80, +0x40,0x80,0x00,0x05,0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81,0x16,0x02,0x03,0x87, +0x42,0x04,0x24,0x04,0x24,0x00,0x80,0x82,0x33,0x7f,0xcb,0x00,0x34,0x00,0x80,0x88, +0x21,0x00,0x04,0x03,0x20,0x00,0x03,0x88,0x42,0x11,0x00,0x04,0x34,0x00,0x04,0x05, +0x3e,0xc0,0x04,0x06,0xb1,0x21,0x42,0x06,0x40,0x20,0x00,0x7f,0x24,0x00,0x04,0x09, +0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00, +0x04,0x00,0x02,0x82,0x12,0x7f,0xc3,0x8d,0x14,0x3f,0xc2,0x04,0x32,0x80,0x80,0x85, +0x42,0x05,0xc0,0x08,0x24,0x00,0x40,0x80,0x42,0x04,0x04,0x06,0x24,0xff,0x40,0x81, +0x1c,0xf4,0x00,0x81,0x3f,0xbf,0x02,0x07,0x16,0xe0,0x02,0x85,0x00,0x20,0x00,0x00, +0x42,0x03,0xd4,0x04,0x24,0x00,0x80,0x82,0x33,0x7f,0xbd,0x00,0x34,0x00,0x80,0x87, +0x21,0x00,0x04,0x03,0x20,0x00,0x03,0x87,0x42,0x11,0x00,0x09,0x34,0x00,0x03,0x88, +0x3e,0xc0,0x03,0x86,0xb1,0x42,0x04,0x86,0x40,0x20,0x00,0x7f,0x24,0x00,0x03,0x8a, +0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00, +0x40,0x20,0x00,0x7f,0x12,0x7f,0xb5,0x89,0x40,0x80,0x00,0x08,0x33,0x82,0x84,0x82, +0x40,0x80,0x00,0x07,0x42,0x05,0x68,0x06,0x40,0x80,0x00,0x05,0x40,0x80,0x00,0x04, +0x40,0x20,0x00,0x7f,0x3f,0x83,0x81,0x03,0x32,0x7f,0xb1,0x00,0x00,0x20,0x00,0x00, +0x40,0x20,0x00,0x7f,0x12,0x7f,0xaf,0x89,0x40,0x80,0x00,0x08,0x32,0x80,0x80,0x87, +0x42,0x05,0x68,0x06,0x40,0x80,0x00,0x05,0x42,0x03,0xe0,0x04,0x15,0x40,0x43,0x87, +0x40,0x20,0x00,0x7f,0x40,0x20,0x00,0x7f,0x32,0x7f,0xab,0x00,0x00,0x20,0x00,0x00, +0x40,0x20,0x00,0x7f,0x12,0x7f,0xa9,0x89,0x40,0x80,0x00,0x08,0x33,0x82,0x78,0x82, +0x42,0x03,0xf8,0x06,0x32,0x81,0x81,0x87,0x40,0x80,0x00,0x05,0x40,0x80,0x00,0x04, +0x40,0x20,0x00,0x7f,0x3f,0x83,0x81,0x03,0x32,0x7f,0xa5,0x00,0x00,0x20,0x00,0x00, +0x42,0x7f,0xff,0x87,0x12,0x7f,0xa3,0x8b,0x40,0x80,0x00,0x08,0x00,0x20,0x00,0x00, +0x18,0x21,0xc1,0x82,0x33,0x82,0x71,0x83,0x42,0x03,0xf8,0x06,0x00,0x20,0x00,0x00, +0x40,0x80,0x00,0x05,0x3f,0xbf,0x01,0x07,0x42,0x03,0xe0,0x04,0x3f,0x83,0x81,0x83, +0x32,0x7f,0x9e,0x00,0x00,0x20,0x00,0x00,0x04,0x00,0x01,0x82,0x12,0x7f,0x9c,0x8d, +0x42,0x05,0x8c,0x08,0x32,0x80,0x80,0x87,0x42,0x03,0xec,0x06,0x32,0x80,0x80,0x85, +0x42,0x03,0xd4,0x04,0x24,0x00,0x40,0x80,0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81, +0x16,0xe0,0x03,0x87,0x00,0x20,0x00,0x00,0x16,0x02,0x02,0x85,0x24,0x00,0x80,0x82, +0x33,0x7f,0x96,0x00,0x34,0x00,0x80,0x8a,0x04,0x00,0x01,0x89,0x21,0x00,0x0f,0x83, +0x0f,0xbf,0x05,0x04,0x33,0x82,0x20,0x8d,0x42,0x7f,0xff,0x83,0x32,0x80,0x80,0x86, +0x42,0x11,0xc4,0x0e,0x33,0x81,0x89,0x88,0x14,0x03,0xc5,0x12,0x12,0x7f,0x29,0x95, +0x18,0x20,0xc2,0x0b,0x24,0x00,0x80,0x89,0x16,0xe0,0x03,0x0c,0x00,0x20,0x00,0x00, +0x0f,0x61,0xc5,0x90,0x3b,0x83,0x86,0x8f,0x18,0x20,0xc9,0x14,0x0f,0x60,0xca,0x16, +0x18,0x43,0x07,0x91,0x3f,0xbf,0x08,0x15,0xb2,0x64,0x48,0x88,0x3f,0xbf,0x0b,0x19, +0x54,0xc0,0x0a,0x97,0x68,0x03,0x07,0x93,0x18,0x45,0xc9,0x98,0xb0,0x86,0x0c,0x08, +0x68,0x05,0xc9,0x84,0x18,0x46,0x42,0x1b,0xb0,0x66,0xcd,0x88,0x68,0x06,0x42,0x03, +0x33,0x7f,0x1f,0x00,0x34,0x00,0x80,0x89,0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80, +0x04,0x00,0x04,0x83,0x35,0x00,0x00,0x00,0x7c,0x00,0x02,0x04,0x12,0x7f,0x82,0x8a, +0x40,0x80,0x40,0x07,0x32,0x80,0x80,0x85,0x40,0x80,0x08,0x02,0x42,0x05,0x8c,0x08, +0x81,0x20,0x83,0x84,0x42,0x03,0xec,0x06,0x16,0x01,0x02,0x85,0x3f,0xbf,0x04,0x87, +0x42,0x03,0xd4,0x04,0x32,0x7f,0x7d,0x80,0x40,0x81,0x8f,0x82,0x12,0x00,0x26,0x8a, +0x04,0x00,0x01,0x87,0x24,0x00,0x40,0x80,0x59,0x00,0x81,0x83,0x24,0xfe,0x40,0x81, +0x04,0x00,0x02,0x09,0x1c,0xe4,0x00,0x81,0x23,0x00,0x23,0x03,0x14,0x03,0xc3,0x8d, +0x40,0x20,0x00,0x7f,0x22,0x00,0x21,0x8d,0x0f,0xbf,0x03,0x85,0x33,0x82,0x03,0x88, +0x42,0x7f,0xff,0x8b,0x32,0x80,0x80,0x8a,0x42,0x11,0xc4,0x0e,0x33,0x81,0x6c,0x8c, +0x40,0x20,0x00,0x7f,0x12,0x00,0xf5,0x96,0x18,0x22,0xc2,0x84,0x24,0x00,0xc0,0x87, +0x16,0xe0,0x05,0x06,0x24,0x00,0x80,0x89,0x0f,0x61,0xc2,0x10,0x3b,0x83,0x84,0x0f, +0x42,0x11,0x80,0x04,0x24,0x01,0x00,0x8d,0x40,0x20,0x00,0x7f,0x24,0x01,0x40,0x8b, +0x18,0x41,0x87,0x91,0x3f,0xbf,0x08,0x13,0xb2,0x44,0x48,0x8c,0x54,0xc0,0x09,0x94, +0x68,0x01,0x87,0x92,0x18,0x45,0x09,0x15,0xb2,0xc5,0x4a,0x8c,0x68,0x05,0x09,0x16, +0x04,0x00,0x0b,0x03,0x24,0x01,0x80,0x96,0x40,0x20,0x00,0x7f,0x33,0x00,0xea,0x80, +0x42,0x11,0x80,0x1a,0x34,0x01,0x00,0x97,0x40,0x20,0x00,0x7f,0x34,0x01,0x40,0x98, +0x04,0x00,0x0d,0x04,0x12,0x01,0x1b,0x89,0x34,0x01,0x80,0x83,0x18,0x26,0x0b,0x99, +0x0f,0x60,0xcc,0x9b,0x00,0x20,0x00,0x00,0x18,0x06,0x8d,0x9d,0x38,0x86,0x8d,0x9c, +0x3b,0x87,0x4e,0x1e,0x24,0x01,0x00,0x9e,0x33,0x01,0x17,0x00,0x34,0x01,0x00,0x83, +0x34,0x00,0xc0,0x87,0x34,0x00,0x80,0x89,0x04,0x00,0x01,0x9f,0x0f,0x60,0x84,0xa0, +0x42,0x0a,0x26,0x21,0x5c,0x01,0xc4,0xa2,0x18,0x08,0x10,0xa4,0x38,0x88,0x10,0xa3, +0x3b,0x89,0x11,0xa5,0x21,0x00,0x1c,0xa2,0x35,0x00,0x12,0x80,0x00,0x00,0x14,0x78, +0x00,0x00,0x14,0x94,0x00,0x00,0x14,0xcc,0x00,0x00,0x14,0xd8,0x00,0x00,0x14,0xe8, +0x00,0x00,0x14,0xf8,0x00,0x00,0x15,0x08,0x00,0x00,0x15,0x18,0x00,0x20,0x00,0x00, +0x40,0x80,0x00,0x1f,0x32,0x7f,0xf7,0x00,0x33,0x82,0x13,0x37,0x3f,0x83,0x5b,0xb8, +0x14,0x3f,0xdc,0x03,0x00,0x20,0x00,0x00,0x1c,0x1c,0x00,0x81,0x34,0x00,0x40,0x80, +0x35,0x00,0x00,0x00,0x33,0x82,0x21,0xaf,0x3f,0x83,0x97,0xb0,0x79,0x0c,0x03,0xb1, +0x22,0x00,0x11,0x31,0x12,0x7f,0xfc,0x89,0x40,0x20,0x00,0x7f,0x33,0x82,0x18,0xb2, +0x40,0x20,0x00,0x7f,0x3f,0x83,0x59,0x33,0x7e,0x00,0x19,0xb4,0x09,0x2d,0x1a,0x35, +0x36,0x80,0x1a,0xb6,0x0c,0x00,0x1b,0x03,0x32,0x7f,0xf8,0x00,0x3f,0xbe,0x4f,0xae, +0x3f,0xe1,0x17,0x03,0x32,0x7f,0xf6,0x80,0x3f,0xbe,0x4f,0xac,0x3f,0xe1,0x16,0x2d, +0x14,0x04,0x16,0x83,0x32,0x7f,0xf4,0x80,0x3f,0xbe,0x4f,0xaa,0x3f,0xe1,0x15,0x2b, +0x14,0x08,0x15,0x83,0x32,0x7f,0xf2,0x80,0x3f,0xbe,0x4f,0xa8,0x3f,0xe1,0x14,0x29, +0x14,0x10,0x14,0x83,0x32,0x7f,0xf0,0x80,0x3f,0xbe,0x4f,0xa6,0x3f,0xe1,0x13,0x27, +0x14,0x20,0x13,0x83,0x32,0x7f,0xee,0x80,0x3f,0xbf,0x0f,0xb9,0x3f,0xe1,0x1c,0xba, +0x14,0x00,0x5d,0x03,0x32,0x7f,0xec,0x80,0x40,0x80,0x00,0x03,0x32,0x7f,0xeb,0x80, +0x42,0x11,0xc4,0x08,0x33,0x81,0xca,0x85,0x04,0x00,0x01,0x8a,0x35,0x90,0x00,0x00, +0x40,0x20,0x00,0x7f,0x32,0x80,0x80,0x86,0x0f,0x61,0xc1,0x83,0x33,0x81,0x32,0x87, +0x40,0x80,0x00,0x0c,0x12,0x00,0xbb,0x9b,0x40,0x80,0x00,0x0d,0x24,0x00,0x40,0x80, +0x16,0xe0,0x03,0x0b,0x24,0xfd,0x40,0x81,0x1c,0xd4,0x00,0x81,0x35,0x90,0x00,0x00, +0x40,0x20,0x00,0x7f,0x3b,0x82,0x02,0x8e,0x40,0xff,0xff,0x91,0x3f,0xbf,0x01,0x89, +0x40,0x80,0x00,0x92,0x24,0x02,0x80,0x84,0x42,0x11,0x80,0x04,0x24,0x00,0xc0,0x8a, +0x18,0x42,0xc7,0x02,0x24,0x00,0x80,0x91,0x54,0xc0,0x04,0x90,0x24,0x01,0x00,0x92, +0xb1,0xe0,0x81,0x07,0x24,0x01,0x40,0x8c,0x24,0x01,0x80,0x8d,0x68,0x02,0xc7,0x0f, +0x18,0x44,0x07,0x93,0xb0,0x64,0xc9,0x87,0x68,0x04,0x07,0x83,0x24,0x02,0x40,0x83, +0x33,0x00,0xae,0x00,0x34,0x01,0x80,0xc2,0x34,0x01,0x40,0xc1,0x34,0x01,0x00,0x8b, +0x35,0x90,0x00,0x00,0x34,0x00,0xc0,0x8a,0x40,0x20,0x00,0x7f,0x34,0x00,0x80,0x89, +0x0f,0x60,0xc5,0xc0,0x34,0x02,0x80,0x99,0x42,0x11,0x80,0x04,0x35,0x90,0x00,0x00, +0x18,0x10,0x02,0x15,0x38,0x90,0x02,0x14,0x3b,0x85,0x4a,0x16,0x3f,0xbe,0x4b,0x17, +0x3f,0xe1,0x0b,0x98,0x35,0x90,0x00,0x00,0x20,0x00,0x3f,0x19,0x14,0x04,0x0c,0x0c, +0x56,0xc0,0x06,0x0f,0x00,0x20,0x00,0x00,0x22,0x00,0x18,0x8f,0x00,0x20,0x00,0x00, +0x0f,0x38,0x44,0x8d,0x3f,0xbe,0x8b,0x10,0x40,0xbf,0xff,0x95,0x3f,0x3f,0xcb,0x11, +0x3f,0xe1,0x08,0x12,0x3f,0xbf,0x08,0x93,0x5a,0x10,0x89,0x03,0x3f,0xe1,0x09,0x84, +0x36,0x80,0x01,0x94,0x18,0x25,0x42,0x17,0x0c,0x00,0x0a,0x18,0x08,0x23,0x4c,0x19, +0x56,0xc0,0x0c,0x9a,0x00,0x20,0x00,0x00,0x23,0x00,0x33,0x1a,0x7a,0x10,0x89,0x1b, +0x59,0x10,0x4b,0x9c,0x36,0x80,0x0d,0x9d,0x55,0xc0,0x0e,0x1e,0x0c,0x00,0x0f,0x1f, +0x0c,0x00,0x0e,0xa0,0x18,0x28,0x0f,0xa1,0x56,0xc0,0x10,0xa2,0x7d,0x00,0x11,0x23, +0x88,0x30,0x4b,0xa3,0x36,0x80,0x11,0xa4,0x81,0x22,0x45,0xa4,0x40,0xbf,0xff,0xa5, +0x79,0x09,0x4b,0xa6,0x23,0x00,0x09,0x26,0x42,0x7f,0xff,0xa8,0x12,0x7f,0xe8,0x92, +0x42,0x11,0x80,0x27,0x33,0x81,0x13,0xae,0x18,0x2a,0x0b,0xa9,0x38,0x90,0x13,0xaa, +0x1c,0x00,0x54,0xab,0x3a,0xf0,0x13,0xac,0x3f,0xbf,0x15,0xad,0x18,0x2b,0x8b,0x16, +0x54,0xc0,0x16,0xaf,0x3f,0x82,0x17,0xb0,0x3f,0xe3,0x18,0x31,0x3f,0x60,0x58,0xb2, +0x08,0x25,0x99,0x33,0xb6,0x8a,0x99,0xac,0x28,0x90,0x13,0xb4,0x1c,0x00,0x45,0x8b, +0x7c,0x04,0x05,0xb5,0x20,0x7f,0xdf,0xb5,0x7c,0xff,0xc4,0xb6,0x21,0x00,0x1c,0xb6, +0x34,0x02,0x80,0xb7,0x20,0x00,0x18,0xb7,0x0f,0x60,0xc4,0xb8,0x12,0x00,0x52,0x91, +0x42,0x11,0x80,0x39,0x33,0x81,0xa3,0xc1,0x42,0x7f,0xff,0xbb,0x33,0x81,0xd2,0xbe, +0x0f,0x61,0x05,0x3a,0x00,0x20,0x00,0x00,0x18,0x0e,0x5c,0x3d,0x38,0x8e,0x5c,0x3c, +0x18,0x02,0x5d,0x40,0x3f,0x83,0xa0,0xc2,0x3f,0x83,0x9f,0x3f,0x3b,0x8f,0x5e,0x46, +0x18,0x2e,0xe1,0x44,0x18,0x2e,0xdf,0xc3,0x3f,0xbf,0x22,0x47,0x78,0x10,0x21,0xc5, +0x20,0x00,0x4a,0x45,0x32,0x81,0x81,0xc9,0x18,0x32,0x63,0x4a,0x78,0x11,0xe5,0x4b, +0x36,0x00,0x25,0xcc,0x4c,0x02,0xe6,0x4d,0x0c,0x00,0x26,0xc8,0x00,0x20,0x00,0x00, +0x42,0x11,0x80,0x4e,0x12,0x7e,0x1e,0x91,0x04,0x00,0x05,0x04,0x32,0xc0,0x40,0x4f, +0x40,0x80,0x02,0x03,0x33,0x80,0xff,0x87,0x38,0x8e,0x27,0x06,0x3a,0xf3,0x9c,0x08, +0x16,0x10,0x27,0x85,0x3f,0xe0,0xe4,0x02,0x08,0x21,0x63,0x8c,0x18,0x21,0xe3,0x0e, +0x23,0x81,0xbc,0x02,0x08,0x23,0x07,0x0f,0xb1,0xa1,0x87,0x88,0x28,0x8e,0x27,0x0d, +0x24,0x00,0x80,0x89,0x24,0x00,0xc0,0x8a,0x33,0x7e,0x16,0x00,0x34,0x00,0xc0,0x8a, +0x40,0x20,0x00,0x7f,0x34,0x00,0x80,0x89,0x04,0x00,0x05,0x04,0x24,0x00,0x80,0x89, +0x40,0x80,0x00,0x83,0x33,0x7e,0x12,0x80,0x40,0x20,0x00,0x7f,0x34,0x00,0x80,0x89, +0x42,0x11,0x80,0x04,0x34,0x02,0x40,0x83,0x24,0x00,0x80,0x89,0x33,0x00,0x9e,0x80, +0x34,0x00,0x80,0x83,0x1c,0x2c,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, +0x04,0x00,0x0b,0xc1,0x04,0x00,0x09,0x42,0x04,0x00,0x05,0x89,0x32,0x7f,0xd2,0x00, +0x14,0x08,0x0c,0x1a,0x56,0xc0,0x0d,0x1b,0x22,0x7f,0xda,0x9b,0x3f,0xbf,0x8b,0x1c, +0x3f,0xe1,0x0e,0x1d,0x7d,0xff,0xce,0x9e,0x23,0x00,0x24,0x1e,0x0f,0xbf,0x0e,0xae, +0x42,0x7f,0xff,0xaf,0x14,0x03,0xce,0xbf,0x42,0x11,0x80,0x3e,0x18,0x2b,0xd7,0x30, +0x78,0x0c,0x05,0x31,0x21,0x00,0x14,0x31,0x42,0x11,0xc4,0x37,0x33,0x81,0x69,0xb6, +0x0f,0x61,0xd8,0x32,0x32,0x80,0x80,0xb3,0x40,0x80,0x0f,0x86,0x33,0x80,0xd2,0xbb, +0x40,0x80,0x40,0x05,0x12,0x00,0x3f,0x94,0x42,0x13,0x00,0x03,0x24,0x02,0x00,0xbf, +0x16,0xe0,0x19,0xb4,0x3f,0xbf,0x19,0x35,0x3b,0x8d,0xdb,0x38,0x24,0x01,0xc0,0xc0, +0x40,0x20,0x00,0x7f,0x24,0x00,0x80,0x89,0x54,0xc0,0x1a,0xba,0x24,0x00,0xc0,0x8a, +0x18,0x4d,0x1c,0x39,0x24,0x01,0x00,0x8b,0x24,0x01,0x40,0xc1,0xb7,0x8e,0x5c,0xbb, +0x24,0x01,0x80,0xc2,0x68,0x0d,0x1c,0x3c,0x18,0x4e,0x9e,0x3d,0xb0,0x8f,0x5e,0xbb, +0x68,0x0e,0x9e,0x04,0x33,0x00,0x35,0x80,0x40,0x80,0x0f,0x83,0x33,0x00,0x4a,0x80, +0x42,0x13,0x00,0x3e,0x12,0x7f,0xc5,0x10,0x34,0x02,0x00,0xbf,0x34,0x01,0xc0,0xc0, +0x34,0x00,0x80,0x89,0x34,0x00,0xc0,0x8a,0x34,0x01,0x00,0x8b,0x34,0x01,0x40,0xc1, +0x34,0x01,0x80,0xc2,0x0f,0x60,0xdf,0xc3,0x18,0x0f,0xa1,0xc5,0x38,0x8f,0xa1,0xc4, +0x3b,0x91,0x62,0x46,0x3f,0xbc,0x63,0x47,0x3f,0xe3,0x23,0xc8,0x4e,0xff,0xe4,0x49, +0x56,0xc0,0x24,0xca,0x23,0x7f,0xbd,0x4a,0x42,0x11,0x80,0x4b,0x12,0x7f,0xbc,0x0e, +0x04,0x00,0x05,0x89,0x32,0xbf,0xbf,0x86,0x18,0x10,0x25,0xce,0x38,0x90,0x25,0xcc, +0x33,0x80,0xc8,0x05,0x38,0x90,0x25,0xcd,0x3a,0xf0,0x25,0xcf,0x3b,0x93,0xa6,0x08, +0x18,0x21,0x84,0x07,0x08,0x21,0x43,0x8e,0xb0,0x53,0x47,0x4f,0x28,0x90,0x25,0x82, +0x40,0x20,0x00,0x7f,0x32,0x7f,0xb5,0x00,0x40,0x80,0x00,0x22,0x32,0x88,0x08,0x1f, +0x12,0x7f,0xb3,0x93,0x15,0x40,0x4f,0xa0,0x18,0x28,0x0b,0x21,0x78,0x08,0x90,0xa3, +0x36,0x00,0x11,0xa4,0x4c,0x02,0xd2,0x25,0x40,0x20,0x00,0x7f,0x21,0x7f,0xb0,0x25, +0x04,0x00,0x05,0x89,0x33,0x80,0xbf,0xa9,0x32,0xc0,0x40,0x27,0x38,0x90,0x02,0x26, +0x3a,0xf0,0x02,0x28,0x16,0x04,0x13,0xab,0x18,0x2a,0x4b,0x2a,0x08,0x2a,0xd5,0x2c, +0xb5,0xa9,0x96,0x28,0x28,0x90,0x02,0x2d,0x40,0x20,0x00,0x7f,0x32,0x7f,0xaa,0x00, +0x40,0x80,0x00,0x48,0x32,0x7f,0xb9,0x80,0x40,0x80,0x12,0x02,0x33,0x81,0x70,0x83, +0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x00,0x20,0x00,0x00, +0x1c,0x09,0x01,0x84,0x38,0x80,0x81,0x85,0x42,0x0f,0xc0,0x03,0x3b,0x81,0x02,0x86, +0x35,0x20,0x03,0x00,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, +0x3f,0xe0,0xc1,0x83,0x23,0x81,0x75,0x83,0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00, +0x40,0xa0,0x00,0x09,0x33,0x80,0x9f,0x8a,0x04,0x00,0x01,0x88,0x00,0x20,0x00,0x00, +0x58,0x02,0x42,0x82,0x21,0x00,0x01,0x85,0x40,0x80,0x00,0x03,0x35,0x00,0x00,0x00, +0x81,0x62,0x42,0x82,0x12,0x7f,0xfc,0x92,0x21,0xa0,0x08,0x08,0x3f,0xbf,0x02,0x0c, +0x3f,0xe1,0x06,0x0d,0x21,0xa0,0x08,0x8d,0x3f,0xe1,0x02,0x0e,0x21,0xa0,0x09,0x0e, +0x04,0x00,0x05,0x8f,0x21,0xa0,0x09,0x8f,0x21,0xa0,0x0a,0x06,0x21,0xa0,0x0a,0x87, +0x18,0x02,0xc1,0x83,0x3f,0xbf,0x05,0x90,0x08,0x01,0x45,0x85,0x18,0x41,0x08,0x11, +0xb2,0x44,0x48,0x8a,0x68,0x01,0x08,0x12,0x04,0x00,0x09,0x04,0x32,0x7f,0xf3,0x80, +0x5c,0x07,0xc3,0x07,0x12,0x7f,0xf1,0x8f,0x40,0x20,0x00,0x7f,0x21,0x00,0x07,0x07, +0x40,0x80,0x00,0x0e,0x3f,0xbf,0x01,0x89,0x32,0x80,0x80,0x8a,0x08,0x21,0x04,0x8b, +0x16,0x03,0xc5,0x0c,0x18,0x23,0x05,0x8d,0x78,0x03,0x86,0x8f,0x36,0x00,0x07,0x90, +0x4c,0x02,0xc8,0x11,0x00,0x20,0x00,0x00,0x20,0x00,0x03,0x11,0x40,0x80,0x20,0x07, +0x32,0x7f,0xea,0x00,0x40,0x80,0x01,0x03,0x40,0x20,0x00,0x7f,0x35,0x00,0x00,0x00, +0x40,0x80,0x02,0x83,0x32,0x7f,0xfe,0x80,0x5c,0x07,0xc3,0x07,0x12,0x7f,0xe6,0x8f, +0x40,0x20,0x00,0x7f,0x21,0x00,0x07,0x07,0x40,0x80,0x00,0x0e,0x3f,0xbf,0x01,0x89, +0x32,0x80,0x80,0x8a,0x08,0x21,0x04,0x8b,0x16,0x03,0xc5,0x0c,0x18,0x23,0x05,0x8d, +0x78,0x03,0x86,0x8f,0x36,0x00,0x07,0x90,0x4c,0x02,0xc8,0x11,0x00,0x20,0x00,0x00, +0x20,0x00,0x03,0x11,0x40,0x80,0x10,0x07,0x32,0x7f,0xdf,0x00,0x40,0x80,0x01,0x03, +0x40,0x20,0x00,0x7f,0x35,0x00,0x00,0x00,0x40,0x80,0x02,0x83,0x32,0x7f,0xfe,0x80, +0x5c,0x07,0xc1,0x82,0x21,0x00,0x04,0x82,0x40,0x80,0x00,0x84,0x0b,0x60,0xc2,0x03, +0x21,0xa0,0x0b,0x03,0x40,0x80,0x01,0x05,0x21,0xa0,0x0b,0x85,0x01,0xa0,0x0c,0x02, +0x40,0x80,0x00,0x03,0x35,0x00,0x00,0x00,0x40,0x80,0x01,0x03,0x32,0x7f,0xff,0x00, +0x40,0x80,0x00,0x07,0x12,0x00,0x31,0x89,0x7c,0x00,0x02,0x05,0x78,0x01,0xc1,0x86, +0x0c,0x00,0x02,0x89,0x36,0x00,0x03,0x02,0x4c,0x02,0xc1,0x08,0x0c,0x00,0x04,0x0a, +0x08,0x22,0x84,0x8b,0x56,0xc0,0x05,0x8c,0x23,0x00,0x2d,0x0c,0x32,0x80,0x80,0x8d, +0x16,0x1f,0xc6,0x8e,0x18,0x23,0x81,0x8f,0x78,0x01,0xc7,0x90,0x36,0x00,0x08,0x11, +0x4c,0x02,0xc8,0x92,0x20,0x00,0x2a,0x92,0x14,0x1f,0xc2,0x25,0x21,0x00,0x29,0xa5, +0x40,0x82,0x00,0x07,0x21,0xa0,0x00,0x87,0x40,0x80,0x00,0x37,0x40,0x80,0x01,0x99, +0x04,0x00,0x02,0x18,0x3f,0xbf,0x01,0x9b,0x1c,0x04,0x02,0x1a,0x3e,0x80,0xc2,0x13, +0x04,0x00,0x03,0x9c,0x3e,0x80,0x02,0x16,0x40,0x80,0x01,0x1d,0x3e,0x80,0x82,0x17, +0x1c,0x03,0xc2,0x14,0x3f,0xe1,0x0d,0x9e,0x40,0x80,0x00,0x95,0x3f,0xe1,0x01,0x83, +0x04,0x00,0x1b,0x9f,0x40,0x80,0x40,0x20,0x40,0x80,0x68,0x21,0x40,0x80,0x5a,0x22, +0x12,0x00,0x00,0x17,0x21,0xa0,0x08,0x18,0x21,0xa0,0x08,0x9e,0x21,0xa0,0x09,0x03, +0x21,0xa0,0x09,0xa0,0x21,0xa0,0x0a,0x1f,0x21,0xa0,0x0a,0xa1,0x01,0xa0,0x0d,0x85, +0x20,0x00,0x14,0xb7,0x34,0x00,0x02,0x28,0x3f,0x83,0x54,0x29,0x7e,0x00,0x54,0xaa, +0x56,0xc0,0x15,0x2b,0x00,0x20,0x00,0x00,0x23,0x00,0x03,0xab,0x38,0x87,0x42,0x2c, +0x3b,0x85,0x16,0x2d,0x7a,0x09,0x56,0xae,0x56,0xc0,0x17,0x2f,0x00,0x20,0x00,0x00, +0x23,0x00,0x02,0x2f,0x01,0xa0,0x00,0x05,0x21,0xa0,0x01,0x1c,0x32,0x7f,0xf4,0x80, +0x1d,0x00,0x52,0xb1,0x34,0x00,0x02,0x30,0x04,0x00,0x16,0xa5,0xb6,0x4c,0x0a,0x96, +0x24,0x00,0x02,0x32,0x38,0x87,0x42,0x33,0xb6,0x8c,0xd8,0x97,0x28,0x87,0x42,0x34, +0x00,0x60,0x00,0x00,0x21,0xa0,0x08,0x18,0x21,0xa0,0x08,0x9e,0x21,0xa0,0x09,0x03, +0x21,0xa0,0x09,0xa0,0x21,0xa0,0x0a,0x1f,0x21,0xa0,0x0a,0xa2,0x01,0xa0,0x0d,0xb5, +0x14,0x00,0x5a,0xb6,0x21,0x7f,0xeb,0xb6,0x1c,0x00,0x5b,0xb7,0x7c,0x00,0x9b,0xb8, +0x20,0x7f,0xea,0x38,0x40,0x82,0x00,0x04,0x21,0xa0,0x01,0x04,0x40,0x80,0x00,0x03, +0x35,0x00,0x00,0x00,0x12,0x7f,0xf7,0x8a,0x40,0x20,0x00,0x7f,0x38,0x86,0x42,0x23, +0x40,0x20,0x00,0x7f,0x38,0x86,0x42,0x24,0x3b,0x86,0x91,0xa5,0x1d,0x00,0x52,0xa6, +0xb4,0xe9,0x13,0x13,0x28,0x86,0x42,0x27,0x40,0x20,0x00,0x7f,0x32,0x7f,0xf2,0x80, +0x40,0x80,0x00,0x83,0x32,0x7f,0xf9,0x80,0x40,0x80,0x02,0x83,0x32,0x7f,0xf8,0x80, +0x40,0x80,0x00,0x07,0x12,0x00,0x22,0x89,0x7c,0x00,0x02,0x06,0x78,0x01,0xc1,0x85, +0x0c,0x00,0x03,0x09,0x36,0x00,0x02,0x82,0x4c,0x02,0xc1,0x08,0x0c,0x00,0x04,0x0a, +0x08,0x22,0x45,0x0b,0x56,0xc0,0x05,0x8c,0x23,0x00,0x1e,0x0c,0x32,0x80,0x80,0x8d, +0x16,0x1f,0xc6,0x8e,0x18,0x23,0x81,0x8f,0x78,0x01,0xc7,0x90,0x36,0x00,0x08,0x11, +0x4c,0x02,0xc8,0x92,0x20,0x00,0x1b,0x92,0x14,0x1f,0xc2,0x13,0x21,0x00,0x1a,0x93, +0x34,0x00,0x02,0x14,0x3f,0x83,0x4a,0x15,0x7e,0x00,0x4a,0x96,0x56,0xc0,0x0b,0x17, +0x22,0x00,0x19,0x17,0x12,0x00,0x0a,0x29,0x21,0xa0,0x08,0x13,0x21,0xa0,0x09,0x13, +0x21,0xa0,0x09,0x93,0x21,0xa0,0x0a,0x13,0x40,0x80,0x66,0x18,0x21,0xa0,0x0a,0x98, +0x40,0x82,0x00,0x19,0x21,0xa0,0x00,0x99,0x40,0x80,0x00,0x1b,0x42,0x12,0x40,0x1c, +0x04,0x00,0x02,0x1a,0x3f,0xbf,0x01,0x9d,0x40,0x80,0x40,0x1e,0x3e,0xc0,0x02,0x1f, +0x40,0x80,0x68,0x20,0x3e,0x80,0x02,0x21,0x40,0x80,0x5a,0x22,0x3f,0xe1,0x01,0xa3, +0x3f,0xe1,0x0e,0xa4,0x21,0xa0,0x08,0x1c,0x21,0xa0,0x08,0xa4,0x21,0xa0,0x09,0x23, +0x21,0xa0,0x09,0x9e,0x21,0xa0,0x0a,0x13,0x21,0xa0,0x0a,0xa0,0x01,0xa0,0x0d,0x83, +0x33,0x80,0xdc,0x03,0x34,0x00,0x02,0x25,0xb4,0xc9,0x41,0x9f,0xb4,0xe9,0x8d,0xa1, +0x24,0x00,0x02,0x27,0x00,0x60,0x00,0x00,0x21,0xa0,0x08,0x1a,0x21,0xa0,0x08,0xa4, +0x21,0xa0,0x09,0x23,0x21,0xa0,0x09,0x9e,0x21,0xa0,0x0a,0x13,0x21,0xa0,0x0a,0xa2, +0x01,0xa0,0x0d,0xa8,0x14,0x00,0x54,0x03,0x21,0x7f,0xf5,0x83,0x40,0x82,0x00,0x04, +0x21,0xa0,0x01,0x04,0x35,0x00,0x00,0x00,0x40,0x80,0x00,0x83,0x32,0x7f,0xff,0x00, +0x40,0x80,0x02,0x83,0x32,0x7f,0xfe,0x00,0x40,0x80,0x03,0x83,0x32,0x7f,0xfd,0x00, +0x21,0xa0,0x0e,0x03,0x00,0x00,0x01,0x02,0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00, +0x0f,0x66,0x01,0x82,0x35,0x80,0x00,0x0c,0x5e,0x0f,0xc1,0x83,0x32,0xbb,0xbb,0x86, +0x56,0xc0,0x01,0x87,0x18,0x21,0x82,0x04,0x08,0x21,0x01,0x08,0x23,0x00,0x03,0x87, +0x01,0xe0,0x0e,0x89,0x21,0x00,0x04,0x89,0x21,0xa0,0x0e,0x05,0x21,0xa0,0x0f,0x08, +0x01,0xa0,0x0e,0x83,0x35,0x00,0x00,0x00,0x41,0x40,0x00,0x8a,0x00,0x20,0x00,0x00, +0x04,0x00,0x85,0x03,0x35,0x00,0x00,0x00,0x41,0x40,0x00,0x85,0x00,0x20,0x00,0x00, +0x04,0x02,0x82,0x83,0x35,0x00,0x00,0x00,0x40,0x80,0x00,0x00,0x42,0x14,0xf8,0x01, +0x24,0x00,0x00,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x33,0x7c,0x31,0x00, +0x33,0x7c,0x37,0x00,0x32,0x7c,0x33,0x80,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81, +0x1c,0xf8,0x00,0x81,0x20,0x84,0xa0,0x01,0x33,0x7f,0x60,0x00,0x1c,0x08,0x00,0x81, +0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81, +0x1c,0xf8,0x00,0x81,0x33,0x7f,0x63,0x80,0x30,0x84,0xa0,0x01,0x1c,0x08,0x00,0x81, +0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81, +0x1c,0xf8,0x00,0x81,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, +0x04,0x05,0x06,0x07,0x80,0x80,0x80,0x80,0x0c,0x0d,0x0e,0x0f,0x80,0x80,0x80,0x80, +0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0xff,0xfe,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, +0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x10,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0xff,0x00,0x01,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x0c,0x00,0x00,0x00,0x07,0x28,0x00,0x00,0x07,0x40,0x00,0x00,0x07,0x50, +0x00,0x00,0x07,0x60,0x00,0x00,0x0c,0xe0,0x00,0x00,0x1e,0xa8,0x00,0x00,0x13,0x38, +0x00,0x00,0x12,0x00,0x00,0x00,0x11,0xd0,0x00,0x00,0x11,0xa0,0x00,0x00,0x11,0x70, +0x00,0x00,0x11,0x00,0x00,0x00,0x13,0x08,0x00,0x00,0x10,0x98,0x00,0x00,0x12,0x38, +0x00,0x00,0x0b,0xe8,0x00,0x00,0x0d,0x48,0x00,0x00,0x0e,0xd8,0x00,0x00,0x1b,0x30, +0x00,0x00,0x1c,0xd0,0x00,0x00,0x1a,0x50,0x00,0x00,0x1a,0xa8,0x00,0x00,0x1b,0x00, +0x47,0x43,0x43,0x3a,0x20,0x28,0x47,0x4e,0x55,0x29,0x20,0x37,0x2e,0x32,0x2e,0x30, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x01,0x53,0x50,0x55,0x4e, +0x41,0x4d,0x45,0x00,0x2f,0x68,0x6f,0x6d,0x65,0x2f,0x6d,0x69,0x6b,0x65,0x2f,0x70, +0x72,0x69,0x76,0x61,0x74,0x65,0x2f,0x70,0x73,0x6c,0x31,0x67,0x68,0x74,0x2f,0x63, +0x6f,0x6d,0x6d,0x6f,0x6e,0x2f,0x6c,0x69,0x62,0x73,0x70,0x75,0x6d,0x61,0x72,0x73, +0x2f,0x73,0x70,0x75,0x2f,0x6c,0x69,0x62,0x2f,0x6d,0x61,0x72,0x73,0x5f,0x6b,0x65, +0x72,0x6e,0x65,0x6c,0x2e,0x65,0x6c,0x66,0x00,0x00,0x00,0x00,0x00,0x2e,0x73,0x79, +0x6d,0x74,0x61,0x62,0x00,0x2e,0x73,0x74,0x72,0x74,0x61,0x62,0x00,0x2e,0x73,0x68, +0x73,0x74,0x72,0x74,0x61,0x62,0x00,0x2e,0x69,0x6e,0x74,0x65,0x72,0x72,0x75,0x70, +0x74,0x00,0x2e,0x69,0x6e,0x69,0x74,0x00,0x2e,0x74,0x65,0x78,0x74,0x00,0x2e,0x66, +0x69,0x6e,0x69,0x00,0x2e,0x72,0x6f,0x64,0x61,0x74,0x61,0x00,0x2e,0x64,0x61,0x74, +0x61,0x00,0x2e,0x62,0x73,0x73,0x00,0x2e,0x63,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x00, +0x2e,0x6e,0x6f,0x74,0x65,0x2e,0x73,0x70,0x75,0x5f,0x6e,0x61,0x6d,0x65,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x01, +0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x04, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x04, +0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x01, +0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0xa0,0x00,0x00,0x1e,0xa8, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x1e,0xc8, +0x00,0x00,0x1f,0x48,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x01, +0x00,0x00,0x00,0x12,0x00,0x00,0x1e,0xe0,0x00,0x00,0x1f,0x60,0x00,0x00,0x00,0xa0, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10, +0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x1f,0x80, +0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x00,0x00,0x00,0x08, +0x00,0x00,0x00,0x03,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x60,0x00,0x00,0x06,0x80, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x4b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00, +0x00,0x00,0x20,0x60,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x54,0x00,0x00,0x00,0x07, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x80,0x00,0x00,0x00,0x5c, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x20,0xdc,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x48,0x00,0x00,0x05,0x60, +0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x10, +0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x28,0xa8,0x00,0x00,0x04,0x76,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, +0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20, +0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x1e,0xc8, +0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x1e,0xe0, +0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x80, +0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00, +0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x09,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x00,0x0a,0x00,0x00,0x07,0x28, +0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x1e,0x00,0x00,0x24,0x00, +0x00,0x00,0x00,0x80,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x2c,0x00,0x00,0x07,0x40, +0x00,0x00,0x00,0x0c,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x3a,0x00,0x00,0x07,0x50, +0x00,0x00,0x00,0x0c,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x4a,0x00,0x00,0x25,0xa0, +0x00,0x00,0x00,0x02,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x56,0x00,0x00,0x07,0x60, +0x00,0x00,0x00,0x08,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x63,0x00,0x00,0x21,0x00, +0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x6c,0x00,0x00,0x07,0x68, +0x00,0x00,0x00,0x40,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x7c,0x00,0x00,0x23,0x80, +0x00,0x00,0x00,0x80,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x89,0x00,0x00,0x07,0xa8, +0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x9a,0x00,0x00,0x07,0xc0, +0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xaf,0x00,0x00,0x07,0xd8, +0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xbe,0x00,0x00,0x07,0xf0, +0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xcf,0x00,0x00,0x08,0x08, +0x00,0x00,0x00,0x3c,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xe1,0x00,0x00,0x08,0x48, +0x00,0x00,0x00,0x10,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xf9,0x00,0x00,0x08,0x58, +0x00,0x00,0x02,0x74,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x0c,0x00,0x00,0x23,0x00, +0x00,0x00,0x00,0x80,0x01,0x00,0x00,0x07,0x00,0x00,0x01,0x18,0x00,0x00,0x0a,0xd0, +0x00,0x00,0x00,0x44,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x28,0x00,0x00,0x0b,0x18, +0x00,0x00,0x00,0x64,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x35,0x00,0x00,0x22,0x00, +0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x07,0x00,0x00,0x01,0x42,0x00,0x00,0x0b,0x80, +0x00,0x00,0x00,0x64,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x51,0x00,0x00,0x0b,0xe8, +0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x62,0x00,0x00,0x0c,0x00, +0x00,0x00,0x00,0x28,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x6c,0x00,0x00,0x0c,0x28, +0x00,0x00,0x00,0xb8,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x7a,0x00,0x00,0x0c,0xe0, +0x00,0x00,0x00,0x68,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x8d,0x00,0x00,0x20,0x00, +0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x07,0x00,0x00,0x01,0x9f,0x00,0x00,0x0d,0x48, +0x00,0x00,0x01,0x90,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xb1,0x00,0x00,0x0e,0xd8, +0x00,0x00,0x00,0x44,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xc5,0x00,0x00,0x0f,0x20, +0x00,0x00,0x01,0x78,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xd1,0x00,0x00,0x10,0x98, +0x00,0x00,0x00,0x64,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xeb,0x00,0x00,0x11,0x00, +0x00,0x00,0x00,0x6c,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x03,0x00,0x00,0x11,0x70, +0x00,0x00,0x00,0x2c,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x19,0x00,0x00,0x11,0xa0, +0x00,0x00,0x00,0x2c,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x2d,0x00,0x00,0x11,0xd0, +0x00,0x00,0x00,0x2c,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x41,0x00,0x00,0x12,0x00, +0x00,0x00,0x00,0x34,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x53,0x00,0x00,0x12,0x38, +0x00,0x00,0x00,0xd0,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x6b,0x00,0x00,0x13,0x08, +0x00,0x00,0x00,0x30,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x81,0x00,0x00,0x13,0x38, +0x00,0x00,0x01,0xf8,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x90,0x00,0x00,0x25,0x10, +0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x07,0x00,0x00,0x02,0xaa,0x00,0x00,0x25,0x70, +0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x07,0x00,0x00,0x02,0xbd,0x00,0x00,0x15,0x30, +0x00,0x00,0x04,0x68,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0xca,0x00,0x00,0x25,0x20, +0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x07,0x00,0x00,0x02,0xda,0x00,0x00,0x1f,0x80, +0x00,0x00,0x00,0x60,0x01,0x00,0x00,0x06,0x00,0x00,0x02,0xea,0x00,0x00,0x25,0x80, +0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x07,0x00,0x00,0x02,0xf9,0x00,0x00,0x25,0xb0, +0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x07,0x00,0x00,0x03,0x0a,0x00,0x00,0x25,0x90, +0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x07,0x00,0x00,0x03,0x16,0x00,0x00,0x25,0x30, +0x00,0x00,0x00,0x40,0x01,0x00,0x00,0x07,0x00,0x00,0x03,0x2d,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x03,0x33,0x00,0x00,0x19,0xe0, +0x00,0x00,0x00,0x70,0x02,0x00,0x00,0x03,0x00,0x00,0x03,0x44,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x03,0x4c,0x00,0x00,0x24,0x80, +0x00,0x00,0x00,0x80,0x01,0x00,0x00,0x07,0x00,0x00,0x03,0x59,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x03,0x6b,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x03,0x83,0x00,0x00,0x19,0xd0, +0x00,0x00,0x00,0x0c,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0x93,0x00,0x00,0x1c,0xd0, +0x00,0x00,0x01,0x30,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xa4,0x00,0x00,0x26,0x00, +0x00,0x00,0x00,0x80,0x11,0x00,0x00,0x07,0x00,0x00,0x03,0xb2,0x00,0x00,0x1e,0x10, +0x00,0x00,0x00,0x58,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xc8,0x00,0x00,0x19,0x98, +0x00,0x00,0x00,0x38,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xd7,0x00,0x00,0x1e,0xa8, +0x00,0x00,0x00,0x20,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xe5,0x00,0x00,0x00,0x04, +0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x02,0x00,0x00,0x03,0xeb,0x00,0x00,0x1b,0x00, +0x00,0x00,0x00,0x30,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xf4,0x00,0x00,0x1a,0x50, +0x00,0x00,0x00,0x58,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xfc,0x00,0x00,0x1e,0x68, +0x00,0x00,0x00,0x20,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x03,0x00,0x00,0x1e,0x88, +0x00,0x00,0x00,0x20,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x10,0x00,0x00,0x1f,0xe0, +0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x04,0x1c,0x00,0x00,0x00,0x38, +0x00,0x00,0x06,0xec,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x21,0x00,0x00,0x1e,0xc8, +0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x04,0x00,0x00,0x04,0x27,0x00,0x00,0x1b,0x30, +0x00,0x00,0x01,0xa0,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x36,0x00,0x00,0x29,0xf0, +0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x04,0x3e,0x00,0x00,0x1f,0xe0, +0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x04,0x45,0x00,0x00,0x25,0x00, +0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x07,0x00,0x00,0x04,0x54,0x00,0x00,0x26,0x80, +0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x04,0x59,0x00,0x00,0x00,0x20, +0x00,0x00,0x00,0x14,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x5e,0x00,0x00,0x1e,0x00, +0x00,0x00,0x00,0x0c,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x6e,0x00,0x00,0x1a,0xa8, +0x00,0x00,0x00,0x58,0x12,0x00,0x00,0x03,0x00,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x2e, +0x63,0x00,0x67,0x65,0x74,0x5f,0x6d,0x61,0x72,0x73,0x5f,0x63,0x6f,0x6e,0x74,0x65, +0x78,0x74,0x5f,0x65,0x61,0x00,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x70,0x61,0x72, +0x61,0x6d,0x73,0x00,0x67,0x65,0x74,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x69, +0x64,0x00,0x67,0x65,0x74,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x69, +0x64,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x69,0x64,0x00,0x67,0x65, +0x74,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x00,0x77,0x6f,0x72,0x6b,0x6c, +0x6f,0x61,0x64,0x00,0x67,0x65,0x74,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64, +0x5f,0x65,0x61,0x00,0x71,0x75,0x65,0x75,0x65,0x5f,0x68,0x65,0x61,0x64,0x65,0x72, +0x00,0x63,0x68,0x65,0x63,0x6b,0x5f,0x73,0x74,0x61,0x74,0x65,0x5f,0x62,0x69,0x74, +0x73,0x00,0x63,0x68,0x65,0x63,0x6b,0x5f,0x73,0x74,0x61,0x74,0x65,0x5f,0x62,0x69, +0x74,0x73,0x5f,0x6e,0x6f,0x74,0x00,0x73,0x65,0x74,0x5f,0x73,0x74,0x61,0x74,0x65, +0x5f,0x62,0x69,0x74,0x73,0x00,0x73,0x65,0x74,0x5f,0x77,0x61,0x69,0x74,0x5f,0x69, +0x64,0x5f,0x62,0x69,0x74,0x73,0x00,0x73,0x65,0x74,0x5f,0x73,0x63,0x68,0x65,0x64, +0x75,0x6c,0x65,0x5f,0x62,0x69,0x74,0x73,0x00,0x75,0x6e,0x73,0x63,0x68,0x65,0x64, +0x75,0x6c,0x69,0x6e,0x67,0x5f,0x73,0x74,0x61,0x74,0x65,0x5f,0x62,0x69,0x74,0x73, +0x00,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x68,0x65,0x61,0x64,0x65,0x72,0x5f,0x62, +0x69,0x74,0x73,0x00,0x71,0x75,0x65,0x75,0x65,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x00, +0x73,0x65,0x74,0x5f,0x73,0x69,0x67,0x6e,0x61,0x6c,0x5f,0x62,0x69,0x74,0x73,0x00, +0x65,0x6e,0x64,0x5f,0x63,0x61,0x6c,0x6c,0x62,0x61,0x63,0x6b,0x00,0x77,0x6f,0x72, +0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x62,0x75,0x66,0x00,0x62,0x65,0x67,0x69,0x6e,0x5f, +0x63,0x61,0x6c,0x6c,0x62,0x61,0x63,0x6b,0x00,0x68,0x6f,0x73,0x74,0x5f,0x73,0x69, +0x67,0x6e,0x61,0x6c,0x5f,0x73,0x65,0x6e,0x64,0x00,0x67,0x65,0x74,0x5f,0x74,0x69, +0x63,0x6b,0x73,0x00,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x6d,0x65,0x6d,0x63,0x6d, +0x70,0x00,0x67,0x65,0x74,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x62, +0x79,0x5f,0x69,0x64,0x00,0x72,0x65,0x74,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61, +0x64,0x2e,0x32,0x38,0x39,0x37,0x00,0x68,0x6f,0x73,0x74,0x5f,0x63,0x61,0x6c,0x6c, +0x62,0x61,0x63,0x6b,0x5f,0x73,0x65,0x74,0x00,0x68,0x6f,0x73,0x74,0x5f,0x63,0x61, +0x6c,0x6c,0x62,0x61,0x63,0x6b,0x5f,0x72,0x65,0x73,0x65,0x74,0x00,0x63,0x68,0x61, +0x6e,0x67,0x65,0x5f,0x62,0x69,0x74,0x73,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61, +0x64,0x5f,0x75,0x6e,0x73,0x63,0x68,0x65,0x64,0x75,0x6c,0x65,0x5f,0x62,0x65,0x67, +0x69,0x6e,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x73,0x63,0x68,0x65, +0x64,0x75,0x6c,0x65,0x5f,0x62,0x65,0x67,0x69,0x6e,0x00,0x77,0x6f,0x72,0x6b,0x6c, +0x6f,0x61,0x64,0x5f,0x73,0x69,0x67,0x6e,0x61,0x6c,0x5f,0x72,0x65,0x73,0x65,0x74, +0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x73,0x69,0x67,0x6e,0x61,0x6c, +0x5f,0x73,0x65,0x74,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x77,0x61, +0x69,0x74,0x5f,0x72,0x65,0x73,0x65,0x74,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61, +0x64,0x5f,0x77,0x61,0x69,0x74,0x5f,0x73,0x65,0x74,0x00,0x77,0x6f,0x72,0x6b,0x6c, +0x6f,0x61,0x64,0x5f,0x75,0x6e,0x73,0x63,0x68,0x65,0x64,0x75,0x6c,0x65,0x5f,0x65, +0x6e,0x64,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x73,0x63,0x68,0x65, +0x64,0x75,0x6c,0x65,0x5f,0x65,0x6e,0x64,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61, +0x64,0x5f,0x71,0x75,0x65,0x72,0x79,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64, +0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x69,0x73,0x5f,0x63,0x61,0x63,0x68,0x65, +0x64,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x69,0x73,0x5f,0x63,0x61, +0x63,0x68,0x65,0x64,0x00,0x73,0x65,0x61,0x72,0x63,0x68,0x5f,0x62,0x6c,0x6f,0x63, +0x6b,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x6d,0x6f,0x64,0x75,0x6c, +0x65,0x00,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x79,0x73,0x63,0x61,0x6c,0x6c, +0x73,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x73,0x74,0x61,0x74,0x65, +0x00,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5f,0x65, +0x61,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x65,0x61,0x00,0x63,0x61, +0x63,0x68,0x65,0x64,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x6d,0x6f, +0x64,0x75,0x6c,0x65,0x00,0x64,0x6d,0x61,0x2e,0x63,0x00,0x64,0x6d,0x61,0x5f,0x6c, +0x61,0x72,0x67,0x65,0x2e,0x70,0x61,0x72,0x74,0x2e,0x30,0x00,0x6d,0x75,0x74,0x65, +0x78,0x2e,0x63,0x00,0x6d,0x75,0x74,0x65,0x78,0x5f,0x62,0x75,0x66,0x66,0x65,0x72, +0x00,0x73,0x70,0x75,0x5f,0x74,0x68,0x72,0x65,0x61,0x64,0x5f,0x65,0x78,0x69,0x74, +0x2e,0x63,0x00,0x73,0x70,0x75,0x5f,0x74,0x68,0x72,0x65,0x61,0x64,0x5f,0x73,0x65, +0x6e,0x64,0x5f,0x65,0x76,0x65,0x6e,0x74,0x2e,0x63,0x00,0x5f,0x5f,0x77,0x6f,0x72, +0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x65,0x78,0x69,0x74,0x00,0x6d,0x75,0x74,0x65,0x78, +0x5f,0x75,0x6e,0x6c,0x6f,0x63,0x6b,0x5f,0x70,0x75,0x74,0x00,0x6b,0x65,0x72,0x6e, +0x65,0x6c,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x00,0x73,0x70,0x75,0x5f,0x74,0x68, +0x72,0x65,0x61,0x64,0x5f,0x73,0x65,0x6e,0x64,0x5f,0x65,0x76,0x65,0x6e,0x74,0x00, +0x5f,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x72,0x75,0x6e,0x00,0x77, +0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x65,0x78,0x69,0x74,0x00,0x5f,0x69,0x6e, +0x69,0x74,0x00,0x64,0x6d,0x61,0x5f,0x77,0x61,0x69,0x74,0x00,0x64,0x6d,0x61,0x5f, +0x67,0x65,0x74,0x00,0x5f,0x73,0x74,0x61,0x72,0x74,0x00,0x77,0x6f,0x72,0x6b,0x6c, +0x6f,0x61,0x64,0x5f,0x72,0x75,0x6e,0x00,0x5f,0x5f,0x62,0x73,0x73,0x5f,0x73,0x74, +0x61,0x72,0x74,0x00,0x6d,0x61,0x69,0x6e,0x00,0x5f,0x66,0x69,0x6e,0x69,0x00,0x6d, +0x75,0x74,0x65,0x78,0x5f,0x6c,0x6f,0x63,0x6b,0x5f,0x67,0x65,0x74,0x00,0x5f,0x5f, +0x73,0x74,0x61,0x63,0x6b,0x00,0x5f,0x65,0x64,0x61,0x74,0x61,0x00,0x5f,0x5f,0x6b, +0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x63,0x6b,0x00,0x5f,0x65,0x6e,0x64, +0x00,0x65,0x78,0x69,0x74,0x00,0x73,0x70,0x75,0x5f,0x74,0x68,0x72,0x65,0x61,0x64, +0x5f,0x65,0x78,0x69,0x74,0x00,0x64,0x6d,0x61,0x5f,0x70,0x75,0x74,0x00, +}; diff --git a/common/libspumars/ppu/ppu/mars_task_module.c b/common/libspumars/ppu/ppu/mars_task_module.c new file mode 100644 index 00000000..60cec3a7 --- /dev/null +++ b/common/libspumars/ppu/ppu/mars_task_module.c @@ -0,0 +1,475 @@ +__attribute__((aligned(128))) const unsigned char mars_task_module_entry[] = { +0x7f,0x45,0x4c,0x46,0x01,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x02,0x00,0x17,0x00,0x00,0x00,0x01,0x00,0x00,0x33,0xe0,0x00,0x00,0x00,0x34, +0x00,0x00,0x0d,0xd4,0x00,0x00,0x00,0x00,0x00,0x34,0x00,0x20,0x00,0x03,0x00,0x28, +0x00,0x0f,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2a,0x00, +0x00,0x00,0x2a,0x00,0x00,0x00,0x0b,0x30,0x00,0x00,0x0b,0x30,0x00,0x00,0x00,0x05, +0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x01,0x00,0x00,0x0c,0x80,0x00,0x00,0x35,0x80, +0x00,0x00,0x35,0x80,0x00,0x00,0x00,0x60,0x00,0x00,0x03,0xf0,0x00,0x00,0x00,0x06, +0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x04,0x00,0x00,0x0d,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, +0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x7b,0x00,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81, +0x33,0x00,0x54,0x00,0x33,0x01,0x2c,0x80,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80, +0x35,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x20,0x86,0xc0,0x01, +0x33,0x00,0xf1,0x00,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, +0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x30,0x86,0xc0,0x01, +0x32,0x01,0x46,0x80,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81, +0x20,0x86,0xc4,0x04,0x20,0x86,0xc2,0x01,0x30,0x86,0xc0,0x01,0x33,0x00,0xca,0x80, +0x33,0x00,0x08,0x00,0x30,0x86,0xc4,0x02,0x20,0x01,0x40,0x02,0x32,0x01,0x3e,0x00, +0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x20,0x86,0xc4,0x03, +0x33,0x00,0x06,0x80,0x30,0x86,0xc4,0x03,0x33,0x00,0xd5,0x00,0x30,0x86,0xc2,0x01, +0x00,0x40,0x00,0x00,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, +0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x40,0x80,0x00,0x4f, +0x32,0x00,0x04,0x80,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81, +0x42,0x1b,0x28,0x03,0x40,0x80,0x00,0x04,0x33,0x00,0xb5,0x00,0x40,0x80,0x08,0x4f, +0x3f,0xe3,0xe7,0xcf,0x42,0x1b,0x18,0x4a,0x42,0x1b,0x28,0x4b,0x33,0x80,0x08,0xcd, +0x24,0x00,0x25,0x4d,0x33,0x80,0x09,0xce,0x08,0x33,0xe7,0x4e,0x40,0x80,0x00,0xcc, +0x3f,0xe3,0x26,0x4c,0x40,0x80,0x17,0xcd,0x00,0x40,0x00,0x00,0x35,0x20,0x25,0x00, +0x21,0x00,0x02,0x4f,0x42,0x1b,0x28,0x03,0x40,0x80,0x00,0x84,0x33,0x00,0xac,0x80, +0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x24,0x00,0x65,0x4e,0x18,0x13,0x27,0x4e,0x1c,0xff,0xe6,0xcd,0x00,0x40,0x00,0x00, +0x24,0x00,0x25,0xd0,0x1c,0x04,0x25,0xcb,0x25,0x00,0x00,0x4d,0x32,0x7f,0xfc,0x80, +0x42,0x1a,0xf0,0x03,0x24,0x00,0x40,0x80,0x42,0x1a,0xf0,0x02,0x24,0xff,0x80,0x81, +0x78,0x00,0xc1,0x04,0x1c,0xf8,0x00,0x81,0x21,0x00,0x02,0x04,0x42,0x00,0x00,0x05, +0x20,0x00,0x01,0x05,0x35,0x20,0x02,0x80,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80, +0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x42,0x1a,0xf0,0x02,0x24,0x00,0x40,0x80, +0x42,0x1a,0xf0,0x04,0x24,0xff,0x80,0x81,0x08,0x01,0x01,0x03,0x1c,0xf8,0x00,0x81, +0x0f,0x5f,0x81,0x85,0x0f,0x38,0x42,0x86,0x18,0x01,0x43,0x07,0x0f,0x5f,0xc3,0x84, +0x20,0x00,0x04,0x04,0x42,0x00,0x00,0x08,0x20,0x00,0x03,0x08,0x00,0x20,0x00,0x00, +0x42,0x1a,0xf0,0x03,0x00,0x20,0x00,0x00,0x35,0x20,0x04,0x00,0x00,0x20,0x00,0x00, +0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00, +0x33,0x81,0x3e,0x02,0x24,0x00,0x40,0x80,0x24,0xff,0xc0,0xd0,0x24,0xff,0x80,0xd1, +0x24,0xff,0x00,0x81,0x1c,0xf0,0x00,0x81,0x3f,0x83,0x41,0x03,0x56,0xc0,0x01,0x84, +0x40,0x20,0x00,0x7f,0x23,0x00,0x0d,0x84,0x42,0x1a,0xc6,0x50,0x33,0x81,0x3a,0x8f, +0x42,0x1a,0xc4,0x05,0x42,0x1a,0xc4,0x51,0x08,0x14,0x02,0x86,0x0f,0x5f,0x83,0x07, +0x1c,0xff,0xc3,0xd0,0x58,0x03,0xe8,0x08,0x20,0x00,0x07,0x08,0x1c,0x00,0x47,0x89, +0x0f,0x60,0x84,0x8b,0x04,0x00,0x04,0x8a,0x40,0x20,0x00,0x7f,0x23,0x81,0x34,0x8a, +0x18,0x14,0x45,0x8d,0x38,0x94,0x45,0x8c,0x40,0x20,0x00,0x7f,0x3b,0x83,0x46,0x0e, +0x35,0x20,0x07,0x00,0x33,0x81,0x31,0x8f,0x58,0x03,0xe8,0x10,0x21,0x7f,0xfa,0x10, +0x33,0x7f,0xde,0x00,0x40,0x80,0x00,0x91,0x3f,0xe0,0xc8,0x92,0x23,0x81,0x2c,0x92, +0x1c,0x10,0x00,0x81,0x34,0x00,0x40,0x80,0x34,0xff,0xc0,0xd0,0x34,0xff,0x80,0xd1, +0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81, +0x1c,0xf8,0x00,0x81,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, +0x32,0x7f,0xdd,0x00,0x00,0x20,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81, +0x1c,0xf8,0x00,0x81,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, +0x40,0x20,0x00,0x7f,0x20,0x00,0x01,0x86,0x40,0x80,0x0f,0x86,0x32,0x00,0xfe,0x80, +0x40,0x80,0x0f,0x86,0x32,0x00,0xfc,0x80,0x32,0x00,0xe7,0x80,0x00,0x20,0x00,0x00, +0x40,0x80,0x00,0x04,0x32,0x7f,0xae,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81, +0x1c,0xf8,0x00,0x81,0x33,0x00,0xe3,0x00,0x40,0x80,0x03,0x84,0x33,0x00,0xe5,0x00, +0x40,0x20,0x00,0x7f,0x20,0x00,0x02,0x83,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80, +0x40,0x20,0x00,0x7f,0x32,0x00,0xe6,0x00,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80, +0x40,0x80,0x04,0x83,0x35,0x00,0x00,0x00,0x40,0x80,0x00,0x84,0x24,0x00,0x40,0x80, +0x40,0x20,0x00,0x7f,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x33,0x7f,0xa3,0x00, +0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x32,0x00,0xdf,0x80,0x00,0x20,0x00,0x00, +0x32,0x00,0xdd,0x80,0x00,0x20,0x00,0x00,0x32,0x00,0xe7,0x00,0x00,0x20,0x00,0x00, +0x40,0x20,0x00,0x7f,0x12,0x00,0xdf,0x0a,0x04,0x00,0x02,0x05,0x24,0x00,0x40,0x80, +0x04,0x00,0x01,0x86,0x24,0xfe,0xc0,0x81,0x1c,0xec,0x00,0x81,0x00,0x20,0x00,0x00, +0x1c,0x10,0x00,0x84,0x24,0x00,0x80,0x85,0x24,0x00,0xc0,0x86,0x33,0x00,0xda,0x00, +0x34,0x00,0x80,0x88,0x34,0x00,0xc0,0x89,0x21,0x00,0x06,0x03,0x12,0x00,0xd9,0x0a, +0x40,0x80,0x56,0x04,0x34,0x01,0x00,0x82,0x04,0x00,0x04,0x83,0x40,0x20,0x00,0x7f, +0x38,0x81,0x01,0x0a,0x3e,0xc3,0x01,0x07,0xb1,0x62,0x84,0x07,0x28,0x81,0x01,0x0b, +0x40,0x20,0x00,0x7f,0x33,0x00,0xd4,0x00,0x1c,0x14,0x00,0x81,0x34,0x00,0x40,0x80, +0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x40,0x80,0x04,0x04,0x1c,0x05,0x81,0x82, +0x38,0x81,0x01,0x83,0x3b,0x80,0x81,0x83,0x32,0x00,0xc6,0x80,0x00,0x20,0x00,0x00, +0x32,0x00,0xc2,0x80,0x00,0x20,0x00,0x00,0x32,0x00,0xbf,0x80,0x00,0x20,0x00,0x00, +0x24,0x00,0x40,0x80,0x24,0xff,0x00,0x81,0x1c,0xf0,0x00,0x81,0x24,0x00,0x80,0x86, +0x24,0x00,0xc0,0x85,0x33,0x00,0xd0,0x80,0x34,0x00,0x80,0x86,0x21,0x00,0x05,0x83, +0x40,0x80,0x00,0x84,0x00,0x20,0x00,0x00,0x04,0x00,0x03,0x03,0x33,0x7f,0x85,0x00, +0x33,0x00,0xc2,0x80,0x34,0x00,0xc0,0x83,0x1c,0x10,0x00,0x81,0x34,0x00,0x40,0x80, +0x40,0x20,0x00,0x7f,0x32,0x00,0xcb,0x80,0x1c,0x10,0x00,0x81,0x34,0x00,0x40,0x80, +0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x40,0x20,0x00,0x7f,0x12,0x00,0xb9,0x09, +0x04,0x00,0x01,0x82,0x24,0x00,0x40,0x80,0x40,0x80,0x01,0x04,0x24,0xff,0x40,0x81, +0x1c,0xf4,0x00,0x81,0x40,0x20,0x00,0x7f,0x40,0x20,0x00,0x7f,0x24,0x00,0x80,0x82, +0x33,0x00,0xb4,0x80,0x34,0x00,0x80,0x85,0x20,0x00,0x04,0x83,0x40,0x80,0x03,0x04, +0x04,0x00,0x02,0x83,0x33,0x00,0xb2,0x00,0x40,0x20,0x00,0x7f,0x20,0x00,0x04,0x83, +0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80,0x32,0x00,0xb1,0x80,0x40,0x80,0x03,0x83, +0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80,0x40,0x20,0x00,0x7f,0x35,0x00,0x00,0x00, +0x40,0x80,0x04,0x83,0x32,0x7f,0xfd,0x80,0x04,0x00,0x02,0x02,0x24,0x00,0x40,0x80, +0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81,0x24,0x00,0x80,0x82,0x33,0x00,0xaa,0x00, +0x34,0x00,0x80,0x85,0x21,0x00,0x04,0x83,0x40,0x80,0x00,0x84,0x00,0x20,0x00,0x00, +0x04,0x00,0x02,0x83,0x33,0x7f,0x6c,0x00,0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80, +0x40,0x20,0x00,0x7f,0x32,0x00,0xa6,0x00,0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80, +0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x04,0x00,0x02,0x82,0x12,0x00,0xa6,0x0a, +0x04,0x00,0x02,0x06,0x24,0x00,0x40,0x80,0x04,0x00,0x01,0x87,0x24,0xfe,0xc0,0x81, +0x1c,0xec,0x00,0x81,0x04,0x00,0x01,0x04,0x1c,0x10,0x00,0x85,0x24,0x00,0x80,0x86, +0x24,0x00,0xc0,0x87,0x33,0x00,0xa1,0x00,0x34,0x00,0x80,0x89,0x34,0x00,0xc0,0x8a, +0x40,0x20,0x00,0x7f,0x21,0x00,0x07,0x83,0x40,0x80,0x54,0x04,0x34,0x01,0x00,0x8b, +0x40,0x80,0x00,0x03,0x38,0x81,0x05,0x85,0x3e,0xe2,0x05,0x88,0xb1,0x81,0x41,0x88, +0x28,0x81,0x05,0x8c,0x21,0x00,0x05,0x09,0x40,0x80,0x00,0x04,0x00,0x20,0x00,0x00, +0x04,0x00,0x05,0x03,0x00,0x20,0x00,0x00,0x33,0x00,0x99,0x80,0x00,0x20,0x00,0x00, +0x1c,0x14,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x34,0x00,0x04,0x8d, +0x24,0x03,0x85,0x8d,0x32,0x7f,0xfa,0x80,0x42,0x1c,0xa8,0x0f,0x12,0x7f,0xa2,0x9c, +0x40,0x80,0x4a,0x06,0x33,0x80,0xa9,0x88,0x04,0x00,0x01,0x87,0x34,0x00,0x07,0x82, +0x04,0x00,0x02,0x0d,0x33,0x80,0xa9,0x8b,0x04,0x00,0x02,0x8e,0x24,0x00,0x40,0x80, +0x24,0xfe,0x00,0x81,0x1c,0xe0,0x00,0x81,0x1c,0x25,0x01,0x03,0x34,0x02,0xc1,0x09, +0x40,0x20,0x00,0x7f,0x38,0x81,0x81,0x0c,0x04,0x00,0x02,0x86,0x24,0x01,0xc0,0x87, +0x04,0x00,0x03,0x85,0x24,0x00,0xc0,0x88,0x24,0x00,0x80,0x8b,0x24,0x01,0x80,0x8d, +0x18,0x42,0x04,0x8a,0x24,0x01,0x40,0x8e,0x3b,0x80,0xc6,0x03,0xb2,0x02,0x85,0x0b, +0x24,0x01,0x00,0x8f,0x04,0x00,0x08,0x04,0x68,0x02,0x04,0x84,0x33,0x7f,0x94,0x80, +0x41,0x00,0x02,0x18,0x34,0x01,0x00,0x84,0x34,0x00,0xc0,0x92,0x34,0x00,0x80,0x96, +0x34,0x01,0xc0,0x94,0x12,0x7f,0x91,0x8d,0x34,0x01,0x40,0x86,0x34,0x00,0x02,0x05, +0x3f,0xbf,0x0a,0x15,0x34,0x02,0xc2,0x91,0x34,0x01,0x80,0x85,0x18,0x44,0x88,0x93, +0x08,0x06,0x02,0x83,0xb2,0xe4,0xc9,0x96,0x68,0x04,0x88,0x97,0x18,0x45,0x4b,0x99, +0xb0,0x86,0x4c,0x96,0x68,0x05,0x4b,0x84,0x33,0x7f,0x8b,0x00,0x1c,0x20,0x00,0x81, +0x40,0x80,0x0f,0x83,0x34,0x00,0x40,0x80,0x32,0x00,0x8a,0x00,0x00,0x20,0x00,0x00, +0x40,0x20,0x00,0x7f,0x12,0x7f,0x87,0x89,0x04,0x00,0x02,0x06,0x33,0x81,0x16,0x82, +0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x34,0x02,0xc1,0x05, +0x04,0x00,0x02,0x84,0x40,0x81,0x78,0x05,0x33,0x7f,0x83,0x00,0x1c,0x08,0x00,0x81, +0x40,0x80,0x0f,0x83,0x34,0x00,0x40,0x80,0x32,0x00,0x82,0x00,0x00,0x20,0x00,0x00, +0x40,0x80,0x4a,0x05,0x33,0x81,0x0f,0x82,0x40,0x80,0x54,0x0b,0x33,0x80,0xa6,0x84, +0x40,0x80,0x5c,0x0a,0x12,0x7f,0xda,0x99,0x40,0x80,0x5e,0x09,0x41,0x00,0x02,0x11, +0x1c,0x25,0x01,0x06,0x38,0x81,0x41,0x0e,0x04,0x1f,0xc8,0x92,0x38,0x82,0xc1,0x07, +0x40,0x80,0x00,0x85,0x38,0x82,0x81,0x08,0x08,0x04,0x82,0x13,0x3e,0xc2,0x01,0x0c, +0x3e,0xc3,0x01,0x0d,0x14,0xe0,0x09,0x95,0x3b,0x81,0x87,0x0f,0xb2,0x81,0xc2,0x0c, +0x04,0x00,0x0a,0x84,0x0c,0x1f,0xc7,0x90,0x28,0x82,0xc1,0x14,0x18,0x04,0x01,0x83, +0x14,0xe0,0x01,0x83,0xb2,0xc2,0x01,0x8c,0x28,0x82,0x81,0x16,0x38,0x82,0x41,0x17, +0xb3,0x05,0xca,0x8d,0x28,0x82,0x41,0x18,0x32,0x7f,0xce,0x00,0x00,0x20,0x00,0x00, +0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x21,0x00,0x06,0x83, +0x40,0x80,0x5e,0x03,0x12,0x7f,0xca,0x8a,0x40,0x80,0x5c,0x05,0x33,0x80,0xfc,0x82, +0x1c,0x2f,0x01,0x06,0x38,0x80,0xc1,0x04,0x1c,0x2e,0x01,0x08,0x38,0x81,0x41,0x07, +0x40,0x80,0x00,0x05,0x3b,0x81,0x82,0x04,0x3b,0x82,0x03,0x83,0x33,0x7f,0xc5,0x80, +0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x40,0x20,0x00,0x7f,0x35,0x80,0x00,0x09, +0x40,0x80,0x54,0x0b,0x33,0x80,0xf5,0x89,0x40,0x20,0x00,0x7f,0x40,0x20,0x00,0x7f, +0x1c,0x2a,0x04,0x8a,0x38,0x82,0xc4,0x8c,0x3b,0x82,0x86,0x0d,0x23,0x80,0x8a,0x8d, +0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x00,0x81, +0x1c,0xf0,0x00,0x81,0x33,0x00,0x4a,0x00,0x23,0x80,0xef,0x03,0x33,0x00,0x48,0x00, +0x40,0x80,0x00,0x04,0x33,0x00,0x4a,0x00,0x40,0x20,0x00,0x7f,0x21,0x00,0x26,0x83, +0x40,0x9f,0xf8,0x0d,0x33,0x80,0xeb,0x8a,0x40,0x80,0x4c,0x11,0x30,0x87,0xfe,0x0e, +0x40,0x80,0x0f,0x86,0x3e,0xe0,0x06,0x8f,0x40,0xa0,0x00,0x03,0x12,0x00,0x57,0x88, +0x1c,0x26,0x05,0x0c,0x34,0x02,0x05,0x0b,0xb2,0x03,0x85,0x8f,0x20,0x87,0xfe,0x10, +0x38,0x84,0x45,0x12,0x34,0x02,0x05,0x04,0x3b,0x83,0x09,0x05,0x33,0x00,0x53,0x80, +0x40,0x80,0x00,0x03,0x00,0x20,0x00,0x00,0x40,0x80,0x54,0x15,0x33,0x80,0xe2,0x93, +0x1c,0x2a,0x09,0x94,0x38,0x85,0x49,0x96,0x3b,0x85,0x0b,0x17,0x21,0x00,0x23,0x97, +0x40,0x80,0x4e,0x18,0x12,0x00,0x4e,0x90,0x40,0x80,0x4a,0x19,0x00,0x20,0x00,0x00, +0x40,0x80,0x44,0x1c,0x38,0x86,0x09,0x9a,0x1c,0x27,0x09,0x9b,0x38,0x86,0x49,0x83, +0x1c,0x22,0x09,0x9e,0x38,0x87,0x09,0x9d,0x1c,0x25,0x09,0x9f,0x24,0x00,0xc0,0x98, +0x40,0x80,0x0f,0x86,0x24,0x00,0x80,0x99,0x3b,0x86,0xcd,0x05,0x3b,0x87,0xc1,0x83, +0x3b,0x87,0x8e,0x84,0x33,0x00,0x46,0x80,0x40,0x80,0x00,0x31,0x33,0x80,0xd6,0xa0, +0x34,0x00,0x80,0xa1,0x34,0x00,0xc0,0xa3,0x40,0x20,0x00,0x7f,0x12,0x00,0x15,0x8e, +0x1c,0x25,0x10,0x26,0x34,0x02,0x90,0x05,0x1c,0x27,0x10,0x27,0x38,0x88,0x50,0x22, +0x38,0x88,0xd0,0x24,0x04,0x00,0x02,0xa5,0x3b,0x89,0x91,0x03,0x3b,0x89,0xd2,0x29, +0x04,0x00,0x01,0xa8,0x18,0x0a,0x54,0x32,0x18,0x09,0x59,0x30,0x58,0x0c,0x98,0x2a, +0x40,0x20,0x00,0x7f,0x21,0x00,0x0e,0xaa,0x40,0x80,0x0f,0x83,0x33,0x00,0x3d,0x80, +0x00,0x40,0x00,0x00,0x33,0x80,0xcb,0xab,0x40,0x80,0x52,0x2d,0x42,0x1a,0xc8,0x04, +0x1c,0x29,0x15,0xac,0x38,0x8b,0x55,0xae,0x1c,0x38,0x15,0x83,0x3b,0x8b,0x17,0x2f, +0x35,0x20,0x17,0x80,0x1c,0x10,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, +0x33,0x00,0x1f,0x80,0x40,0x80,0x00,0x84,0x33,0x00,0x21,0x80,0x12,0x7f,0xd8,0x89, +0x40,0x20,0x00,0x7f,0x21,0x7f,0xe0,0x83,0x33,0x80,0xc3,0x05,0x30,0x87,0xfe,0x04, +0x34,0x02,0x02,0x82,0x78,0x00,0x82,0x07,0x36,0x00,0x03,0x88,0x4c,0x02,0xc4,0x09, +0x20,0x7f,0xd4,0x09,0x32,0x7f,0xdc,0x80,0x1c,0x04,0x19,0x32,0x24,0xff,0xd9,0x31, +0x40,0x20,0x00,0x7f,0x32,0x7f,0xef,0x00,0x1c,0x10,0x00,0x81,0x34,0x00,0x40,0x80, +0x32,0x7e,0xe4,0x00,0x00,0x20,0x00,0x00,0x33,0x80,0x41,0x02,0x24,0xff,0xc0,0xd0, +0x42,0x1a,0xc0,0x50,0x24,0x00,0x40,0x80,0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81, +0x3b,0x94,0x01,0x05,0x7c,0xff,0xc2,0x83,0x40,0x20,0x00,0x7f,0x21,0x00,0x03,0x83, +0x1c,0xff,0x28,0x50,0x35,0x20,0x02,0x80,0x34,0x00,0x28,0x04,0x3b,0x94,0x02,0x05, +0x7c,0xff,0xc2,0x86,0x20,0x7f,0xfd,0x86,0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80, +0x34,0xff,0xc0,0xd0,0x35,0x00,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81, +0x1c,0xf8,0x00,0x81,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, +0x42,0x1f,0xf0,0x01,0x40,0x80,0x00,0x00,0x24,0x00,0x00,0x80,0x24,0xff,0x80,0x81, +0x1c,0xf8,0x00,0x81,0x23,0x80,0xad,0x83,0x33,0x7e,0xc1,0x80,0x33,0x7e,0xc6,0x80, +0x32,0x00,0x12,0x80,0x40,0x80,0x00,0x02,0x32,0x00,0x1b,0x00,0x40,0x80,0x02,0x02, +0x32,0x00,0x1a,0x00,0x40,0x80,0x04,0x02,0x32,0x00,0x19,0x00,0x40,0x80,0x06,0x02, +0x32,0x00,0x18,0x00,0x40,0x80,0x08,0x02,0x32,0x00,0x17,0x00,0x40,0x80,0x0a,0x02, +0x32,0x00,0x16,0x00,0x40,0x80,0x0e,0x02,0x32,0x00,0x15,0x00,0x40,0x80,0x10,0x02, +0x32,0x00,0x14,0x00,0x40,0x80,0x12,0x02,0x32,0x00,0x13,0x00,0x40,0x80,0x14,0x02, +0x32,0x00,0x12,0x00,0x40,0x80,0x16,0x02,0x32,0x00,0x11,0x00,0x40,0x80,0x18,0x02, +0x32,0x00,0x10,0x00,0x40,0x80,0x1a,0x02,0x32,0x00,0x0f,0x00,0x40,0x80,0x1c,0x02, +0x32,0x00,0x0e,0x00,0x40,0x80,0x1e,0x02,0x32,0x00,0x0d,0x00,0x40,0x80,0x10,0x03, +0x40,0x80,0x0c,0x02,0x32,0x00,0x0b,0x80,0x40,0x80,0x08,0x03,0x40,0x80,0x0c,0x02, +0x32,0x00,0x0a,0x00,0x40,0x80,0x40,0x03,0x40,0x80,0x0c,0x02,0x32,0x00,0x08,0x80, +0x40,0x80,0x20,0x02,0x32,0x00,0x07,0x80,0x40,0x80,0x22,0x02,0x32,0x00,0x06,0x80, +0x40,0x80,0x24,0x02,0x32,0x00,0x05,0x80,0x40,0x80,0x26,0x02,0x32,0x00,0x04,0x80, +0x40,0x80,0x28,0x02,0x32,0x00,0x03,0x80,0x40,0x80,0x2a,0x02,0x32,0x00,0x02,0x80, +0x40,0x80,0x2c,0x02,0x32,0x00,0x01,0x80,0x40,0x80,0x2e,0x02,0x32,0x00,0x00,0x80, +0x33,0x80,0x90,0x4c,0x38,0x80,0xa6,0x4d,0x18,0x00,0xa6,0x4e,0x3b,0x93,0xa6,0xcf, +0x35,0x00,0x27,0x80,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81, +0x33,0x7e,0xde,0x00,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x02,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x04,0x05,0x06,0x07,0x80,0x80,0x80,0x80,0x0c,0x0d,0x0e,0x0f,0x80,0x80,0x80,0x80, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, +0x00,0x00,0x2e,0x08,0x00,0x00,0x2e,0x00,0x00,0x00,0x2c,0xe8,0x00,0x00,0x2d,0xe8, +0x00,0x00,0x2a,0x50,0x00,0x00,0x2c,0xf0,0x00,0x00,0x2f,0x28,0x00,0x00,0x2d,0x70, +0x00,0x00,0x2e,0xd8,0x00,0x00,0x2e,0x68,0x00,0x00,0x2d,0x68,0x00,0x00,0x2d,0x60, +0x00,0x00,0x2d,0x38,0x00,0x00,0x2c,0xf8,0x00,0x00,0x2e,0x10,0x00,0x00,0x34,0xb8, +0x00,0x00,0x34,0xc0,0x00,0x00,0x34,0xc8,0x00,0x00,0x34,0xd0,0x00,0x00,0x34,0xd8, +0x47,0x43,0x43,0x3a,0x20,0x28,0x47,0x4e,0x55,0x29,0x20,0x37,0x2e,0x32,0x2e,0x30, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x4a,0x00,0x00,0x00,0x01,0x53,0x50,0x55,0x4e, +0x41,0x4d,0x45,0x00,0x2f,0x68,0x6f,0x6d,0x65,0x2f,0x6d,0x69,0x6b,0x65,0x2f,0x70, +0x72,0x69,0x76,0x61,0x74,0x65,0x2f,0x70,0x73,0x6c,0x31,0x67,0x68,0x74,0x2f,0x63, +0x6f,0x6d,0x6d,0x6f,0x6e,0x2f,0x6c,0x69,0x62,0x73,0x70,0x75,0x6d,0x61,0x72,0x73, +0x2f,0x73,0x70,0x75,0x2f,0x6c,0x69,0x62,0x2f,0x6d,0x61,0x72,0x73,0x5f,0x74,0x61, +0x73,0x6b,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x2e,0x65,0x6c,0x66,0x00,0x00,0x00, +0x00,0x2e,0x73,0x79,0x6d,0x74,0x61,0x62,0x00,0x2e,0x73,0x74,0x72,0x74,0x61,0x62, +0x00,0x2e,0x73,0x68,0x73,0x74,0x72,0x74,0x61,0x62,0x00,0x2e,0x69,0x6e,0x74,0x65, +0x72,0x72,0x75,0x70,0x74,0x00,0x2e,0x69,0x6e,0x69,0x74,0x00,0x2e,0x74,0x65,0x78, +0x74,0x00,0x2e,0x66,0x69,0x6e,0x69,0x00,0x2e,0x72,0x6f,0x64,0x61,0x74,0x61,0x00, +0x2e,0x63,0x74,0x6f,0x72,0x73,0x00,0x2e,0x64,0x74,0x6f,0x72,0x73,0x00,0x2e,0x64, +0x61,0x74,0x61,0x00,0x2e,0x62,0x73,0x73,0x00,0x2e,0x63,0x6f,0x6d,0x6d,0x65,0x6e, +0x74,0x00,0x2e,0x6e,0x6f,0x74,0x65,0x2e,0x73,0x70,0x75,0x5f,0x6e,0x61,0x6d,0x65, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1b, +0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x2a,0x00,0x00,0x00,0x01,0x00, +0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06, +0x00,0x00,0x2a,0x04,0x00,0x00,0x01,0x04,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2c, +0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x2a,0x30,0x00,0x00,0x01,0x30, +0x00,0x00,0x0a,0xc4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06, +0x00,0x00,0x34,0xf4,0x00,0x00,0x0b,0xf4,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38, +0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x12,0x00,0x00,0x35,0x10,0x00,0x00,0x0c,0x10, +0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10, +0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03, +0x00,0x00,0x35,0x80,0x00,0x00,0x0c,0x80,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47, +0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x35,0x88,0x00,0x00,0x0c,0x88, +0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03, +0x00,0x00,0x35,0x90,0x00,0x00,0x0c,0x90,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54, +0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x03,0x00,0x00,0x35,0xe0,0x00,0x00,0x0c,0xe0, +0x00,0x00,0x03,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x30, +0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0xe0,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x62, +0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x00, +0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x60,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, +0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x2c, +0x00,0x00,0x06,0xc0,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x04, +0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x16,0xec,0x00,0x00,0x06,0x95,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x2a,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x00, +0x00,0x00,0x2a,0x04,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x00, +0x00,0x00,0x2a,0x30,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x00,0x00, +0x00,0x00,0x34,0xf4,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x00,0x00, +0x00,0x00,0x35,0x10,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x05,0x00,0x00,0x00,0x00, +0x00,0x00,0x35,0x80,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x00, +0x00,0x00,0x35,0x88,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x00, +0x00,0x00,0x35,0x90,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x08,0x00,0x00,0x00,0x00, +0x00,0x00,0x35,0xe0,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x09,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x0a,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x0b,0x00,0x00,0x00,0x01, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x00,0x08, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x00,0x0f, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x00,0x1a, +0x00,0x00,0x35,0x80,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0x28, +0x00,0x00,0x35,0x88,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x36, +0x00,0x00,0x2b,0x60,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x4b, +0x00,0x00,0x2b,0x98,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x5e, +0x00,0x00,0x2b,0xf0,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x74, +0x00,0x00,0x35,0xe0,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x09,0x00,0x00,0x00,0x83, +0x00,0x00,0x35,0xf0,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x09,0x00,0x00,0x00,0x91, +0x00,0x00,0x2c,0x98,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xac, +0x00,0x00,0x2c,0xb0,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xb8, +0x00,0x00,0x2c,0xb8,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x0f, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x00,0xc9, +0x00,0x00,0x35,0x84,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0xd6, +0x00,0x00,0x33,0x78,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xec, +0x00,0x00,0x33,0xc8,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x07, +0x00,0x00,0x2a,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x18, +0x00,0x00,0x2a,0xd4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x2c, +0x00,0x00,0x2a,0xf4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x31, +0x00,0x00,0x2b,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x36, +0x00,0x00,0x2b,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x3b, +0x00,0x00,0x2b,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x40, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x01,0x4e, +0x00,0x00,0x2c,0xd0,0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x52, +0x00,0x00,0x2c,0xe8,0x00,0x00,0x00,0x04,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x5b, +0x00,0x00,0x2c,0xf0,0x00,0x00,0x00,0x08,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x66, +0x00,0x00,0x2c,0xf8,0x00,0x00,0x00,0x40,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x7b, +0x00,0x00,0x2d,0x38,0x00,0x00,0x00,0x24,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x8c, +0x00,0x00,0x2d,0x60,0x00,0x00,0x00,0x04,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x9d, +0x00,0x00,0x2d,0x68,0x00,0x00,0x00,0x04,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xae, +0x00,0x00,0x2d,0x70,0x00,0x00,0x00,0x74,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xbe, +0x00,0x00,0x2d,0xe8,0x00,0x00,0x00,0x14,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xcd, +0x00,0x00,0x2e,0x00,0x00,0x00,0x00,0x04,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xdb, +0x00,0x00,0x2e,0x08,0x00,0x00,0x00,0x04,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xe5, +0x00,0x00,0x2e,0x10,0x00,0x00,0x00,0x54,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xf4, +0x00,0x00,0x2e,0x68,0x00,0x00,0x00,0x70,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x02, +0x00,0x00,0x2e,0xd8,0x00,0x00,0x00,0x4c,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x0c, +0x00,0x00,0x2f,0x28,0x00,0x00,0x00,0x90,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x1a, +0x00,0x00,0x2f,0xb8,0x00,0x00,0x00,0xd4,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x26, +0x00,0x00,0x39,0x50,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x09,0x00,0x00,0x02,0x2b, +0x00,0x00,0x35,0x90,0x00,0x00,0x00,0x50,0x01,0x00,0x00,0x08,0x00,0x00,0x02,0x40, +0x00,0x00,0x34,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x02,0x54, +0x00,0x00,0x31,0xc8,0x00,0x00,0x01,0xac,0x12,0x00,0x00,0x03,0x00,0x00,0x02,0x62, +0x00,0x00,0x2a,0x30,0x00,0x00,0x00,0x20,0x12,0x00,0x00,0x03,0x00,0x00,0x02,0x73, +0x00,0x00,0x34,0x24,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x02,0x8c, +0x00,0x00,0x34,0x44,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x02,0xac, +0x00,0x00,0x31,0x50,0x00,0x00,0x00,0x74,0x12,0x00,0x00,0x03,0x00,0x00,0x02,0xbb, +0x00,0x00,0x34,0xb8,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x02,0xd6, +0x00,0x00,0x34,0x5c,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x02,0xfa, +0x00,0x00,0x34,0x3c,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0x18, +0x00,0x00,0x2a,0x90,0x00,0x00,0x00,0x30,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0x25, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x03,0x34, +0x00,0x00,0x35,0xe0,0x00,0x00,0x00,0x00,0x11,0x02,0x00,0x08,0x00,0x00,0x03,0x40, +0x00,0x00,0x36,0x20,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x09,0x00,0x00,0x03,0x4d, +0x00,0x00,0x35,0x8c,0x00,0x00,0x00,0x00,0x11,0x02,0x00,0x07,0x00,0x00,0x03,0x5a, +0x00,0x00,0x30,0xd0,0x00,0x00,0x00,0x7c,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0x66, +0x00,0x00,0x34,0x54,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0x88, +0x00,0x00,0x39,0x60,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x09,0x00,0x00,0x03,0x98, +0x00,0x00,0x34,0x34,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xb3, +0x00,0x00,0x2a,0x04,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x02,0x00,0x00,0x03,0xb9, +0x00,0x00,0x34,0x88,0x00,0x00,0x00,0x0c,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xd4, +0x00,0x00,0x30,0x90,0x00,0x00,0x00,0x3c,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xe4, +0x00,0x00,0x34,0x74,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x08, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x04,0x22, +0x00,0x00,0x34,0x14,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x3c, +0x00,0x00,0x34,0x04,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x52, +0x00,0x00,0x34,0x2c,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x71, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x04,0x8d, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x04,0x92, +0x00,0x00,0x34,0xa0,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0xaf, +0x00,0x00,0x34,0xc0,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0xcc, +0x00,0x00,0x34,0x0c,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0xec, +0x00,0x00,0x35,0xe0,0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x04,0xf8, +0x00,0x00,0x36,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x09,0x00,0x00,0x05,0x07, +0x00,0x00,0x33,0xe0,0x00,0x00,0x00,0x24,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0x19, +0x00,0x00,0x34,0xc8,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0x2d, +0x00,0x00,0x34,0x94,0x00,0x00,0x00,0x0c,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0x49, +0x00,0x00,0x34,0x1c,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0x65, +0x00,0x00,0x34,0xf4,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x04,0x00,0x00,0x05,0x6b, +0x00,0x00,0x2a,0x50,0x00,0x00,0x00,0x14,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0x75, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x05,0x7c, +0x00,0x00,0x34,0xb0,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0x9c, +0x00,0x00,0x34,0x6c,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0xc2, +0x00,0x00,0x34,0x4c,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0xe2, +0x00,0x00,0x34,0x64,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x06,0x04, +0x00,0x00,0x34,0x7c,0x00,0x00,0x00,0x0c,0x12,0x00,0x00,0x03,0x00,0x00,0x06,0x1e, +0x00,0x00,0x3f,0xe0,0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x06,0x26, +0x00,0x00,0x35,0xe0,0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x06,0x2d, +0x00,0x00,0x39,0x70,0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x06,0x32, +0x00,0x00,0x34,0xd8,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x06,0x47, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x06,0x4c, +0x00,0x00,0x2a,0x64,0x00,0x00,0x00,0x2c,0x12,0x00,0x00,0x03,0x00,0x00,0x06,0x56, +0x00,0x00,0x34,0xd0,0x00,0x00,0x00,0x10,0x12,0x00,0x00,0x03,0x00,0x00,0x06,0x6a, +0x00,0x00,0x34,0xa8,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x06,0x88, +0x00,0x00,0x36,0x10,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x09,0x00,0x63,0x72,0x74, +0x6e,0x2e,0x53,0x00,0x63,0x72,0x74,0x69,0x2e,0x53,0x00,0x63,0x72,0x74,0x73,0x74, +0x75,0x66,0x66,0x2e,0x63,0x00,0x5f,0x5f,0x43,0x54,0x4f,0x52,0x5f,0x4c,0x49,0x53, +0x54,0x5f,0x5f,0x00,0x5f,0x5f,0x44,0x54,0x4f,0x52,0x5f,0x4c,0x49,0x53,0x54,0x5f, +0x5f,0x00,0x64,0x65,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x5f,0x74,0x6d,0x5f, +0x63,0x6c,0x6f,0x6e,0x65,0x73,0x00,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x5f, +0x74,0x6d,0x5f,0x63,0x6c,0x6f,0x6e,0x65,0x73,0x00,0x5f,0x5f,0x64,0x6f,0x5f,0x67, +0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x74,0x6f,0x72,0x73,0x5f,0x61,0x75,0x78,0x00, +0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x65,0x64,0x2e,0x33,0x38,0x37,0x39,0x00,0x64, +0x74,0x6f,0x72,0x5f,0x69,0x64,0x78,0x2e,0x33,0x38,0x38,0x31,0x00,0x63,0x61,0x6c, +0x6c,0x5f,0x5f,0x5f,0x64,0x6f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x74, +0x6f,0x72,0x73,0x5f,0x61,0x75,0x78,0x00,0x66,0x72,0x61,0x6d,0x65,0x5f,0x64,0x75, +0x6d,0x6d,0x79,0x00,0x63,0x61,0x6c,0x6c,0x5f,0x66,0x72,0x61,0x6d,0x65,0x5f,0x64, +0x75,0x6d,0x6d,0x79,0x00,0x5f,0x5f,0x43,0x54,0x4f,0x52,0x5f,0x45,0x4e,0x44,0x5f, +0x5f,0x00,0x5f,0x5f,0x64,0x6f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x63,0x74, +0x6f,0x72,0x73,0x5f,0x61,0x75,0x78,0x00,0x63,0x61,0x6c,0x6c,0x5f,0x5f,0x5f,0x64, +0x6f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x63,0x74,0x6f,0x72,0x73,0x5f,0x61, +0x75,0x78,0x00,0x5f,0x5f,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x73,0x5f,0x73, +0x61,0x76,0x65,0x00,0x5f,0x5f,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x73,0x5f, +0x72,0x65,0x73,0x74,0x6f,0x72,0x65,0x00,0x62,0x6f,0x64,0x79,0x00,0x6c,0x6f,0x6f, +0x70,0x00,0x69,0x6e,0x73,0x74,0x00,0x64,0x6f,0x6e,0x65,0x00,0x74,0x61,0x73,0x6b, +0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x2e,0x63,0x00,0x64,0x6d,0x61,0x00,0x67,0x65, +0x74,0x5f,0x74,0x61,0x73,0x6b,0x00,0x74,0x61,0x73,0x6b,0x5f,0x79,0x69,0x65,0x6c, +0x64,0x00,0x74,0x61,0x73,0x6b,0x5f,0x73,0x69,0x67,0x6e,0x61,0x6c,0x5f,0x74,0x72, +0x79,0x5f,0x77,0x61,0x69,0x74,0x00,0x74,0x61,0x73,0x6b,0x5f,0x73,0x69,0x67,0x6e, +0x61,0x6c,0x5f,0x77,0x61,0x69,0x74,0x00,0x74,0x61,0x73,0x6b,0x5f,0x73,0x69,0x67, +0x6e,0x61,0x6c,0x5f,0x73,0x65,0x6e,0x64,0x00,0x74,0x61,0x73,0x6b,0x5f,0x73,0x69, +0x67,0x6e,0x61,0x6c,0x5f,0x68,0x6f,0x73,0x74,0x00,0x74,0x61,0x73,0x6b,0x5f,0x75, +0x6e,0x73,0x63,0x68,0x65,0x64,0x75,0x6c,0x65,0x00,0x67,0x65,0x74,0x5f,0x74,0x61, +0x73,0x6b,0x5f,0x62,0x79,0x5f,0x69,0x64,0x00,0x67,0x65,0x74,0x5f,0x6b,0x65,0x72, +0x6e,0x65,0x6c,0x5f,0x69,0x64,0x00,0x67,0x65,0x74,0x5f,0x74,0x69,0x63,0x6b,0x73, +0x00,0x74,0x61,0x73,0x6b,0x5f,0x63,0x61,0x6c,0x6c,0x5f,0x68,0x6f,0x73,0x74,0x00, +0x74,0x61,0x73,0x6b,0x5f,0x74,0x72,0x79,0x5f,0x77,0x61,0x69,0x74,0x00,0x74,0x61, +0x73,0x6b,0x5f,0x77,0x61,0x69,0x74,0x00,0x74,0x61,0x73,0x6b,0x5f,0x73,0x63,0x68, +0x65,0x64,0x75,0x6c,0x65,0x00,0x64,0x6d,0x61,0x5f,0x63,0x6f,0x6e,0x74,0x65,0x78, +0x74,0x00,0x74,0x61,0x73,0x6b,0x00,0x74,0x61,0x73,0x6b,0x5f,0x6d,0x6f,0x64,0x75, +0x6c,0x65,0x5f,0x73,0x79,0x73,0x63,0x61,0x6c,0x6c,0x73,0x00,0x63,0x61,0x6c,0x6c, +0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x79,0x73,0x63,0x61,0x6c,0x6c,0x00, +0x5f,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x6d,0x61,0x69,0x6e,0x00,0x6d,0x61, +0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x6d,0x61,0x69,0x6e,0x00,0x6d, +0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x67,0x65,0x74,0x5f,0x77, +0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64, +0x75,0x6c,0x65,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x77,0x61,0x69, +0x74,0x5f,0x72,0x65,0x73,0x65,0x74,0x00,0x5f,0x5f,0x74,0x61,0x73,0x6b,0x5f,0x72, +0x65,0x73,0x74,0x6f,0x72,0x65,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75, +0x6c,0x65,0x5f,0x6d,0x75,0x74,0x65,0x78,0x5f,0x6c,0x6f,0x63,0x6b,0x5f,0x67,0x65, +0x74,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x77,0x6f, +0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x73,0x63,0x68,0x65,0x64,0x75,0x6c,0x65,0x5f, +0x62,0x65,0x67,0x69,0x6e,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c, +0x65,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x77,0x61,0x69,0x74,0x5f, +0x73,0x65,0x74,0x00,0x74,0x61,0x73,0x6b,0x5f,0x72,0x65,0x73,0x74,0x6f,0x72,0x65, +0x00,0x6d,0x61,0x72,0x73,0x5f,0x74,0x61,0x73,0x6b,0x5f,0x6d,0x61,0x69,0x6e,0x00, +0x5f,0x5f,0x54,0x4d,0x43,0x5f,0x45,0x4e,0x44,0x5f,0x5f,0x00,0x5f,0x5f,0x77,0x6f, +0x72,0x6b,0x5f,0x73,0x74,0x61,0x63,0x6b,0x00,0x5f,0x5f,0x44,0x54,0x4f,0x52,0x5f, +0x45,0x4e,0x44,0x5f,0x5f,0x00,0x5f,0x5f,0x74,0x61,0x73,0x6b,0x5f,0x73,0x61,0x76, +0x65,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x77,0x6f, +0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x73,0x69,0x67,0x6e,0x61,0x6c,0x5f,0x72,0x65, +0x73,0x65,0x74,0x00,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x79,0x73,0x63,0x61, +0x6c,0x6c,0x73,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f, +0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x71,0x75,0x65,0x72,0x79,0x00,0x5f, +0x69,0x6e,0x69,0x74,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65, +0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x79,0x69,0x65,0x6c,0x64,0x00, +0x5f,0x5f,0x64,0x6d,0x61,0x5f,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x73,0x00, +0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x77,0x6f,0x72,0x6b, +0x6c,0x6f,0x61,0x64,0x5f,0x75,0x6e,0x73,0x63,0x68,0x65,0x64,0x75,0x6c,0x65,0x5f, +0x65,0x6e,0x64,0x00,0x5f,0x49,0x54,0x4d,0x5f,0x72,0x65,0x67,0x69,0x73,0x74,0x65, +0x72,0x54,0x4d,0x43,0x6c,0x6f,0x6e,0x65,0x54,0x61,0x62,0x6c,0x65,0x00,0x6d,0x61, +0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x67,0x65,0x74,0x5f,0x6b,0x65, +0x72,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64, +0x75,0x6c,0x65,0x5f,0x67,0x65,0x74,0x5f,0x74,0x69,0x63,0x6b,0x73,0x00,0x6d,0x61, +0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x67,0x65,0x74,0x5f,0x77,0x6f, +0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x62,0x79,0x5f,0x69,0x64,0x00,0x5f,0x49,0x54, +0x4d,0x5f,0x64,0x65,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x54,0x4d,0x43,0x6c, +0x6f,0x6e,0x65,0x54,0x61,0x62,0x6c,0x65,0x00,0x73,0x62,0x72,0x6b,0x00,0x6d,0x61, +0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x68,0x6f,0x73,0x74,0x5f,0x73, +0x69,0x67,0x6e,0x61,0x6c,0x5f,0x73,0x65,0x6e,0x64,0x00,0x6d,0x61,0x72,0x73,0x5f, +0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x6d,0x75,0x74,0x65,0x78,0x5f,0x75,0x6e,0x6c, +0x6f,0x63,0x6b,0x5f,0x70,0x75,0x74,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64, +0x75,0x6c,0x65,0x5f,0x67,0x65,0x74,0x5f,0x6d,0x61,0x72,0x73,0x5f,0x63,0x6f,0x6e, +0x74,0x65,0x78,0x74,0x5f,0x65,0x61,0x00,0x5f,0x5f,0x62,0x73,0x73,0x5f,0x73,0x74, +0x61,0x72,0x74,0x00,0x5f,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x73,0x74,0x61, +0x63,0x6b,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x65, +0x6e,0x74,0x72,0x79,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65, +0x5f,0x64,0x6d,0x61,0x5f,0x67,0x65,0x74,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f, +0x64,0x75,0x6c,0x65,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x66,0x69, +0x6e,0x69,0x73,0x68,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65, +0x5f,0x67,0x65,0x74,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x69,0x64, +0x00,0x5f,0x66,0x69,0x6e,0x69,0x00,0x74,0x61,0x73,0x6b,0x5f,0x65,0x78,0x69,0x74, +0x00,0x61,0x74,0x65,0x78,0x69,0x74,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64, +0x75,0x6c,0x65,0x5f,0x68,0x6f,0x73,0x74,0x5f,0x63,0x61,0x6c,0x6c,0x62,0x61,0x63, +0x6b,0x5f,0x72,0x65,0x73,0x65,0x74,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64, +0x75,0x6c,0x65,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x75,0x6e,0x73, +0x63,0x68,0x65,0x64,0x75,0x6c,0x65,0x5f,0x62,0x65,0x67,0x69,0x6e,0x00,0x6d,0x61, +0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f, +0x61,0x64,0x5f,0x73,0x69,0x67,0x6e,0x61,0x6c,0x5f,0x73,0x65,0x74,0x00,0x6d,0x61, +0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f, +0x61,0x64,0x5f,0x73,0x63,0x68,0x65,0x64,0x75,0x6c,0x65,0x5f,0x65,0x6e,0x64,0x00, +0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x77,0x6f,0x72,0x6b, +0x6c,0x6f,0x61,0x64,0x5f,0x77,0x61,0x69,0x74,0x00,0x5f,0x5f,0x73,0x74,0x61,0x63, +0x6b,0x00,0x5f,0x65,0x64,0x61,0x74,0x61,0x00,0x5f,0x65,0x6e,0x64,0x00,0x6d,0x61, +0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x64,0x6d,0x61,0x5f,0x77,0x61, +0x69,0x74,0x00,0x65,0x78,0x69,0x74,0x00,0x74,0x61,0x73,0x6b,0x5f,0x73,0x61,0x76, +0x65,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x64,0x6d, +0x61,0x5f,0x70,0x75,0x74,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c, +0x65,0x5f,0x68,0x6f,0x73,0x74,0x5f,0x63,0x61,0x6c,0x6c,0x62,0x61,0x63,0x6b,0x5f, +0x73,0x65,0x74,0x00,0x5f,0x5f,0x74,0x61,0x73,0x6b,0x5f,0x73,0x74,0x61,0x63,0x6b, +0x00, +}; diff --git a/common/libspumars/ppu/task/lib/elf.h b/common/libspumars/ppu/task/lib/elf.h new file mode 100644 index 00000000..b54dfc68 --- /dev/null +++ b/common/libspumars/ppu/task/lib/elf.h @@ -0,0 +1,2459 @@ +/* This file defines standard ELF types, structures, and macros. + Copyright (C) 1995-2003, 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef __ELF_H__ +#define __ELF_H__ + +//#include + +/* Standard ELF types. */ + +#include + +/* Type for a 16-bit quantity. */ +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +/* Types for signed and unsigned 32-bit quantities. */ +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; + +/* Types for signed and unsigned 64-bit quantities. */ +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* Type of addresses. */ +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; + +/* Type of file offsets. */ +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Off; + +/* Type for section indices, which are 16-bit quantities. */ +typedef uint16_t Elf32_Section; +typedef uint16_t Elf64_Section; + +/* Type for version symbol information. */ +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + + +/* The ELF file header. This appears at the start of every ELF file. */ + +#define EI_NIDENT (16) + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +/* Fields in the e_ident array. The EI_* macros are indices into the + array. The macros under each EI_* macro are the values the byte + may have. */ + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +/* Conglomeration of the identification bytes, for easy testing as a word. */ +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define ELFCLASSNUM 3 + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define ELFDATANUM 3 + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_SYSV 0 /* Alias. */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD. */ +#define ELFOSABI_LINUX 3 /* Linux. */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ +#define ELFOSABI_AIX 7 /* IBM AIX. */ +#define ELFOSABI_IRIX 8 /* SGI Irix. */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +/* Legal values for e_type (object file type). */ + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_NUM 5 /* Number of defined types */ +#define ET_LOOS 0xfe00 /* OS-specific range start */ +#define ET_HIOS 0xfeff /* OS-specific range end */ +#define ET_LOPROC 0xff00 /* Processor-specific range start */ +#define ET_HIPROC 0xffff /* Processor-specific range end */ + +/* Legal values for e_machine (architecture). */ + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* IBM System/370 */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC 64-bit */ +#define EM_S390 22 /* IBM S390 */ + +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ + +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_NUM 95 + +/* If it is necessary to assign new unofficial EM_* values, please + pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the + chances of collision with official or non-GNU unofficial values. */ + +#define EM_ALPHA 0x9026 + +/* Legal values for e_version (version). */ + +#define EV_NONE 0 /* Invalid ELF version */ +#define EV_CURRENT 1 /* Current version */ +#define EV_NUM 2 + +/* Section header. */ + +typedef struct +{ + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +typedef struct +{ + Elf64_Word sh_name; /* Section name (string tbl index) */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section flags */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Section size in bytes */ + Elf64_Word sh_link; /* Link to another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +/* Special section indices. */ + +#define SHN_UNDEF 0 /* Undefined section */ +#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ +#define SHN_LOPROC 0xff00 /* Start of processor-specific */ +#define SHN_BEFORE 0xff00 /* Order section before all others + (Solaris). */ +#define SHN_AFTER 0xff01 /* Order section after all others + (Solaris). */ +#define SHN_HIPROC 0xff1f /* End of processor-specific */ +#define SHN_LOOS 0xff20 /* Start of OS-specific */ +#define SHN_HIOS 0xff3f /* End of OS-specific */ +#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ +#define SHN_COMMON 0xfff2 /* Associated symbol is common */ +#define SHN_XINDEX 0xffff /* Index is in extra table. */ +#define SHN_HIRESERVE 0xffff /* End of reserved indices */ + +/* Legal values for sh_type (section type). */ + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* Number of defined types. */ +#define SHT_LOOS 0x60000000 /* Start OS-specific */ +#define SHT_GNU_HASH 0x6ffffff6 /* GNU style symbol hash table. */ +#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ +#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ +#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ +#define SHT_SUNW_move 0x6ffffffa +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ +#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ +#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ +#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ +#define SHT_HIOS 0x6fffffff /* End OS-specific type */ +#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ +#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ +#define SHT_LOUSER 0x80000000 /* Start of application-specific */ +#define SHT_HIUSER 0x8fffffff /* End of application-specific */ + +/* Legal values for sh_flags (section flags). */ + +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ +#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ +#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ +#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling + required */ +#define SHF_GROUP (1 << 9) /* Section is member of a group. */ +#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ +#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ +#define SHF_ORDERED (1 << 30) /* Special ordering requirement + (Solaris). */ +#define SHF_EXCLUDE (1 << 31) /* Section is excluded unless + referenced or allocated (Solaris).*/ + +/* Section group handling. */ +#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ + +/* Symbol table entry. */ + +typedef struct +{ + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +typedef struct +{ + Elf64_Word st_name; /* Symbol name (string tbl index) */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf64_Section st_shndx; /* Section index */ + Elf64_Addr st_value; /* Symbol value */ + Elf64_Xword st_size; /* Symbol size */ +} Elf64_Sym; + +/* The syminfo section if available contains additional information about + every dynamic symbol. */ + +typedef struct +{ + Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf32_Half si_flags; /* Per symbol flags */ +} Elf32_Syminfo; + +typedef struct +{ + Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf64_Half si_flags; /* Per symbol flags */ +} Elf64_Syminfo; + +/* Possible values for si_boundto. */ +#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ +#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ +#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ + +/* Possible bitmasks for si_flags. */ +#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ +#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ +#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ +#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy + loaded */ +/* Syminfo version values. */ +#define SYMINFO_NONE 0 +#define SYMINFO_CURRENT 1 +#define SYMINFO_NUM 2 + + +/* How to extract and insert information held in the st_info field. */ + +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) + +/* Legal values for ST_BIND subfield of st_info (symbol binding). */ + +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types. */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_NOTYPE 0 /* Symbol type is unspecified */ +#define STT_OBJECT 1 /* Symbol is a data object */ +#define STT_FUNC 2 /* Symbol is a code object */ +#define STT_SECTION 3 /* Symbol associated with a section */ +#define STT_FILE 4 /* Symbol's name is file name */ +#define STT_COMMON 5 /* Symbol is a common data object */ +#define STT_TLS 6 /* Symbol is thread-local data object*/ +#define STT_NUM 7 /* Number of defined types. */ +#define STT_LOOS 10 /* Start of OS-specific */ +#define STT_HIOS 12 /* End of OS-specific */ +#define STT_LOPROC 13 /* Start of processor-specific */ +#define STT_HIPROC 15 /* End of processor-specific */ + + +/* Symbol table indices are found in the hash buckets and chain table + of a symbol hash table section. This special index value indicates + the end of a chain, meaning no further symbols are found in that bucket. */ + +#define STN_UNDEF 0 /* End of a chain. */ + + +/* How to extract and insert information held in the st_other field. */ + +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) + +/* For ELF64 the definitions are the same. */ +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) + +/* Symbol visibility specification encoded in the st_other field. */ +#define STV_DEFAULT 0 /* Default symbol visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Sym unavailable in other modules */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation table entry without addend (in section of type SHT_REL). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +/* I have seen two different definitions of the Elf64_Rel and + Elf64_Rela structures, so we'll leave them out until Novell (or + whoever) gets their act together. */ +/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ +} Elf64_Rel; + +/* Relocation table entry with addend (in section of type SHT_RELA). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +} Elf32_Rela; + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ + Elf64_Sxword r_addend; /* Addend */ +} Elf64_Rela; + +/* How to extract and insert information held in the r_info field. */ + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val) & 0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) + +/* Program segment header. */ + +typedef struct +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +typedef struct +{ + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} Elf64_Phdr; + +/* Legal values for p_type (segment type). */ + +#define PT_NULL 0 /* Program header table entry unused */ +#define PT_LOAD 1 /* Loadable program segment */ +#define PT_DYNAMIC 2 /* Dynamic linking information */ +#define PT_INTERP 3 /* Program interpreter */ +#define PT_NOTE 4 /* Auxiliary information */ +#define PT_SHLIB 5 /* Reserved */ +#define PT_PHDR 6 /* Entry for header table itself */ +#define PT_TLS 7 /* Thread-local storage segment */ +#define PT_NUM 8 /* Number of defined types */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ +#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ +#define PT_LOSUNW 0x6ffffffa +#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ +#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ +#define PT_HISUNW 0x6fffffff +#define PT_HIOS 0x6fffffff /* End of OS-specific */ +#define PT_LOPROC 0x70000000 /* Start of processor-specific */ +#define PT_HIPROC 0x7fffffff /* End of processor-specific */ + +/* Legal values for p_flags (segment flags). */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ +#define PF_MASKOS 0x0ff00000 /* OS-specific */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Legal values for note segment descriptor types for core files. */ + +#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ +#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ +#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ +#define NT_PRXREG 4 /* Contains copy of prxregset struct */ +#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ +#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ +#define NT_AUXV 6 /* Contains copy of auxv array */ +#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ +#define NT_ASRS 8 /* Contains copy of asrset struct */ +#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ +#define NT_PSINFO 13 /* Contains copy of psinfo struct */ +#define NT_PRCRED 14 /* Contains copy of prcred struct */ +#define NT_UTSNAME 15 /* Contains copy of utsname struct */ +#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ +#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ +#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ + +/* Legal values for the note segment descriptor types for object files. */ + +#define NT_VERSION 1 /* Contains a version string. */ + + +/* Dynamic section entry. */ + +typedef struct +{ + Elf32_Sword d_tag; /* Dynamic entry type */ + union + { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +typedef struct +{ + Elf64_Sxword d_tag; /* Dynamic entry type */ + union + { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +} Elf64_Dyn; + +/* Legal values for d_tag (dynamic entry type). */ + +#define DT_NULL 0 /* Marks end of dynamic section */ +#define DT_NEEDED 1 /* Name of needed library */ +#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ +#define DT_PLTGOT 3 /* Processor defined value */ +#define DT_HASH 4 /* Address of symbol hash table */ +#define DT_STRTAB 5 /* Address of string table */ +#define DT_SYMTAB 6 /* Address of symbol table */ +#define DT_RELA 7 /* Address of Rela relocs */ +#define DT_RELASZ 8 /* Total size of Rela relocs */ +#define DT_RELAENT 9 /* Size of one Rela reloc */ +#define DT_STRSZ 10 /* Size of string table */ +#define DT_SYMENT 11 /* Size of one symbol table entry */ +#define DT_INIT 12 /* Address of init function */ +#define DT_FINI 13 /* Address of termination function */ +#define DT_SONAME 14 /* Name of shared object */ +#define DT_RPATH 15 /* Library search path (deprecated) */ +#define DT_SYMBOLIC 16 /* Start symbol search here */ +#define DT_REL 17 /* Address of Rel relocs */ +#define DT_RELSZ 18 /* Total size of Rel relocs */ +#define DT_RELENT 19 /* Size of one Rel reloc */ +#define DT_PLTREL 20 /* Type of reloc in PLT */ +#define DT_DEBUG 21 /* For debugging; unspecified */ +#define DT_TEXTREL 22 /* Reloc might modify .text */ +#define DT_JMPREL 23 /* Address of PLT relocs */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_NUM 34 /* Number used */ +#define DT_LOOS 0x6000000d /* Start of OS-specific */ +#define DT_HIOS 0x6ffff000 /* End of OS-specific */ +#define DT_LOPROC 0x70000000 /* Start of processor-specific */ +#define DT_HIPROC 0x7fffffff /* End of processor-specific */ +#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ + +/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the + Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's + approach. */ +#define DT_VALRNGLO 0x6ffffd00 +#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ +#define DT_CHECKSUM 0x6ffffdf8 +#define DT_PLTPADSZ 0x6ffffdf9 +#define DT_MOVEENT 0x6ffffdfa +#define DT_MOVESZ 0x6ffffdfb +#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ +#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting + the following DT_* entry. */ +#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ +#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ +#define DT_VALRNGHI 0x6ffffdff +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ +#define DT_VALNUM 12 + +/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the + Dyn.d_un.d_ptr field of the Elf*_Dyn structure. + + If any adjustment is made to the ELF object after it has been + built these entries will need to be adjusted. */ +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ +#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ +#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ +#define DT_CONFIG 0x6ffffefa /* Configuration information. */ +#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ +#define DT_AUDIT 0x6ffffefc /* Object auditing. */ +#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ +#define DT_MOVETAB 0x6ffffefe /* Move table. */ +#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ +#define DT_ADDRNUM 10 + +/* The versioning entry types. The next are defined as part of the + GNU extension. */ +#define DT_VERSYM 0x6ffffff0 + +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa + +/* These were chosen by Sun. */ +#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ +#define DT_VERDEF 0x6ffffffc /* Address of version definition + table */ +#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ +#define DT_VERNEED 0x6ffffffe /* Address of table with needed + versions */ +#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ +#define DT_VERSIONTAGNUM 16 + +/* Sun added these machine-independent extensions in the "processor-specific" + range. Be compatible. */ +#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ +#define DT_FILTER 0x7fffffff /* Shared object to get values from */ +#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) +#define DT_EXTRANUM 3 + +/* Values of `d_un.d_val' in the DT_FLAGS entry. */ +#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ +#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ + +/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 + entry in the dynamic section. */ +#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ +#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ +#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ +#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ +#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ +#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ +#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ +#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ +#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ +#define DF_1_TRANS 0x00000200 +#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ +#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ +#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ +#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ +#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ +#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ +#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ + +/* Flags for the feature selection in DT_FEATURE_1. */ +#define DTF_1_PARINIT 0x00000001 +#define DTF_1_CONFEXP 0x00000002 + +/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ +#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ +#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not + generally available. */ + +/* Version definition sections. */ + +typedef struct +{ + Elf32_Half vd_version; /* Version revision */ + Elf32_Half vd_flags; /* Version information */ + Elf32_Half vd_ndx; /* Version Index */ + Elf32_Half vd_cnt; /* Number of associated aux entries */ + Elf32_Word vd_hash; /* Version name hash value */ + Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf32_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf32_Verdef; + +typedef struct +{ + Elf64_Half vd_version; /* Version revision */ + Elf64_Half vd_flags; /* Version information */ + Elf64_Half vd_ndx; /* Version Index */ + Elf64_Half vd_cnt; /* Number of associated aux entries */ + Elf64_Word vd_hash; /* Version name hash value */ + Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf64_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf64_Verdef; + + +/* Legal values for vd_version (version revision). */ +#define VER_DEF_NONE 0 /* No version */ +#define VER_DEF_CURRENT 1 /* Current version */ +#define VER_DEF_NUM 2 /* Given version number */ + +/* Legal values for vd_flags (version information flags). */ +#define VER_FLG_BASE 0x1 /* Version definition of file itself */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + +/* Versym symbol index values. */ +#define VER_NDX_LOCAL 0 /* Symbol is local. */ +#define VER_NDX_GLOBAL 1 /* Symbol is global. */ +#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ +#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ + +/* Auxialiary version information. */ + +typedef struct +{ + Elf32_Word vda_name; /* Version or dependency names */ + Elf32_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf32_Verdaux; + +typedef struct +{ + Elf64_Word vda_name; /* Version or dependency names */ + Elf64_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf64_Verdaux; + + +/* Version dependency section. */ + +typedef struct +{ + Elf32_Half vn_version; /* Version of structure */ + Elf32_Half vn_cnt; /* Number of associated aux entries */ + Elf32_Word vn_file; /* Offset of filename for this + dependency */ + Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf32_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf32_Verneed; + +typedef struct +{ + Elf64_Half vn_version; /* Version of structure */ + Elf64_Half vn_cnt; /* Number of associated aux entries */ + Elf64_Word vn_file; /* Offset of filename for this + dependency */ + Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf64_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf64_Verneed; + + +/* Legal values for vn_version (version revision). */ +#define VER_NEED_NONE 0 /* No version */ +#define VER_NEED_CURRENT 1 /* Current version */ +#define VER_NEED_NUM 2 /* Given version number */ + +/* Auxiliary needed version information. */ + +typedef struct +{ + Elf32_Word vna_hash; /* Hash value of dependency name */ + Elf32_Half vna_flags; /* Dependency specific information */ + Elf32_Half vna_other; /* Unused */ + Elf32_Word vna_name; /* Dependency name string offset */ + Elf32_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf32_Vernaux; + +typedef struct +{ + Elf64_Word vna_hash; /* Hash value of dependency name */ + Elf64_Half vna_flags; /* Dependency specific information */ + Elf64_Half vna_other; /* Unused */ + Elf64_Word vna_name; /* Dependency name string offset */ + Elf64_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf64_Vernaux; + + +/* Legal values for vna_flags. */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + + +/* Auxiliary vector. */ + +/* This vector is normally only used by the program interpreter. The + usual definition in an ABI supplement uses the name auxv_t. The + vector is not usually defined in a standard file, but it + can't hurt. We rename it to avoid conflicts. The sizes of these + types are an arrangement between the exec server and the program + interpreter, so we don't fully specify them here. */ + +typedef struct +{ + int a_type; /* Entry type */ + union + { + long int a_val; /* Integer value */ + void *a_ptr; /* Pointer value */ + void (*a_fcn) (void); /* Function pointer value */ + } a_un; +} Elf32_auxv_t; + +typedef struct +{ + long int a_type; /* Entry type */ + union + { + long int a_val; /* Integer value */ + void *a_ptr; /* Pointer value */ + void (*a_fcn) (void); /* Function pointer value */ + } a_un; +} Elf64_auxv_t; + +/* Legal values for a_type (entry type). */ + +#define AT_NULL 0 /* End of vector */ +#define AT_IGNORE 1 /* Entry should be ignored */ +#define AT_EXECFD 2 /* File descriptor of program */ +#define AT_PHDR 3 /* Program headers for program */ +#define AT_PHENT 4 /* Size of program header entry */ +#define AT_PHNUM 5 /* Number of program headers */ +#define AT_PAGESZ 6 /* System page size */ +#define AT_BASE 7 /* Base address of interpreter */ +#define AT_FLAGS 8 /* Flags */ +#define AT_ENTRY 9 /* Entry point of program */ +#define AT_NOTELF 10 /* Program is not ELF */ +#define AT_UID 11 /* Real uid */ +#define AT_EUID 12 /* Effective uid */ +#define AT_GID 13 /* Real gid */ +#define AT_EGID 14 /* Effective gid */ +#define AT_CLKTCK 17 /* Frequency of times() */ + +/* Some more special a_type values describing the hardware. */ +#define AT_PLATFORM 15 /* String identifying platform. */ +#define AT_HWCAP 16 /* Machine dependent hints about + processor capabilities. */ + +/* This entry gives some information about the FPU initialization + performed by the kernel. */ +#define AT_FPUCW 18 /* Used FPU control word. */ + +/* Cache block sizes. */ +#define AT_DCACHEBSIZE 19 /* Data cache block size. */ +#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ +#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ + +/* A special ignored value for PPC, used by the kernel to control the + interpretation of the AUXV. Must be > 16. */ +#define AT_IGNOREPPC 22 /* Entry should be ignored. */ + +#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ + +/* Pointer to the global system page used for system calls and other + nice things. */ +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 + + +/* Note section contents. Each entry in the note section begins with + a header of a fixed form. */ + +typedef struct +{ + Elf32_Word n_namesz; /* Length of the note's name. */ + Elf32_Word n_descsz; /* Length of the note's descriptor. */ + Elf32_Word n_type; /* Type of the note. */ +} Elf32_Nhdr; + +typedef struct +{ + Elf64_Word n_namesz; /* Length of the note's name. */ + Elf64_Word n_descsz; /* Length of the note's descriptor. */ + Elf64_Word n_type; /* Type of the note. */ +} Elf64_Nhdr; + +/* Known names of notes. */ + +/* Solaris entries in the note section have this name. */ +#define ELF_NOTE_SOLARIS "SUNW Solaris" + +/* Note entries for GNU systems have this name. */ +#define ELF_NOTE_GNU "GNU" + + +/* Defined types of notes for Solaris. */ + +/* Value of descriptor (one word) is desired pagesize for the binary. */ +#define ELF_NOTE_PAGESIZE_HINT 1 + + +/* Defined note types for GNU systems. */ + +/* ABI information. The descriptor consists of words: + word 0: OS descriptor + word 1: major version of the ABI + word 2: minor version of the ABI + word 3: subminor version of the ABI +*/ +#define ELF_NOTE_ABI 1 + +/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI + note section entry. */ +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 +#define ELF_NOTE_OS_FREEBSD 3 + + +/* Move records. */ +typedef struct +{ + Elf32_Xword m_value; /* Symbol value. */ + Elf32_Word m_info; /* Size and index. */ + Elf32_Word m_poffset; /* Symbol offset. */ + Elf32_Half m_repeat; /* Repeat count. */ + Elf32_Half m_stride; /* Stride info. */ +} Elf32_Move; + +typedef struct +{ + Elf64_Xword m_value; /* Symbol value. */ + Elf64_Xword m_info; /* Size and index. */ + Elf64_Xword m_poffset; /* Symbol offset. */ + Elf64_Half m_repeat; /* Repeat count. */ + Elf64_Half m_stride; /* Stride info. */ +} Elf64_Move; + +/* Macro to construct move records. */ +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char) (info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM (info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) + + +/* Motorola 68k specific definitions. */ + +/* Values for Elf32_Ehdr.e_flags. */ +#define EF_CPU32 0x00810000 + +/* m68k relocs. */ + +#define R_68K_NONE 0 /* No reloc */ +#define R_68K_32 1 /* Direct 32 bit */ +#define R_68K_16 2 /* Direct 16 bit */ +#define R_68K_8 3 /* Direct 8 bit */ +#define R_68K_PC32 4 /* PC relative 32 bit */ +#define R_68K_PC16 5 /* PC relative 16 bit */ +#define R_68K_PC8 6 /* PC relative 8 bit */ +#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ +#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ +#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ +#define R_68K_GOT32O 10 /* 32 bit GOT offset */ +#define R_68K_GOT16O 11 /* 16 bit GOT offset */ +#define R_68K_GOT8O 12 /* 8 bit GOT offset */ +#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ +#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ +#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ +#define R_68K_PLT32O 16 /* 32 bit PLT offset */ +#define R_68K_PLT16O 17 /* 16 bit PLT offset */ +#define R_68K_PLT8O 18 /* 8 bit PLT offset */ +#define R_68K_COPY 19 /* Copy symbol at runtime */ +#define R_68K_GLOB_DAT 20 /* Create GOT entry */ +#define R_68K_JMP_SLOT 21 /* Create PLT entry */ +#define R_68K_RELATIVE 22 /* Adjust by program base */ +/* Keep this the last entry. */ +#define R_68K_NUM 23 + +/* Intel 80386 specific definitions. */ + +/* i386 relocs. */ + +#define R_386_NONE 0 /* No reloc */ +#define R_386_32 1 /* Direct 32 bit */ +#define R_386_PC32 2 /* PC relative 32 bit */ +#define R_386_GOT32 3 /* 32 bit GOT entry */ +#define R_386_PLT32 4 /* 32 bit PLT address */ +#define R_386_COPY 5 /* Copy symbol at runtime */ +#define R_386_GLOB_DAT 6 /* Create GOT entry */ +#define R_386_JMP_SLOT 7 /* Create PLT entry */ +#define R_386_RELATIVE 8 /* Adjust by program base */ +#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ +#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ +#define R_386_32PLT 11 +#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ +#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS + block offset */ +#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block + offset */ +#define R_386_TLS_LE 17 /* Offset relative to static TLS + block */ +#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of + general dynamic thread local data */ +#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of + local dynamic thread local data + in LE code */ +#define R_386_16 20 +#define R_386_PC16 21 +#define R_386_8 22 +#define R_386_PC8 23 +#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic + thread local data */ +#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ +#define R_386_TLS_GD_CALL 26 /* Relocation for call to + __tls_get_addr() */ +#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ +#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic + thread local data in LE code */ +#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ +#define R_386_TLS_LDM_CALL 30 /* Relocation for call to + __tls_get_addr() in LDM code */ +#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ +#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ +#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS + block offset */ +#define R_386_TLS_LE_32 34 /* Negated offset relative to static + TLS block */ +#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ +#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ +#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ +/* Keep this the last entry. */ +#define R_386_NUM 38 + +/* SUN SPARC specific definitions. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ + +/* Values for Elf64_Ehdr.e_flags. */ + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_LEDATA 0x800000 /* little endian data */ +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ +#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ +#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ +#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ + +/* SPARC relocs. */ + +#define R_SPARC_NONE 0 /* No reloc */ +#define R_SPARC_8 1 /* Direct 8 bit */ +#define R_SPARC_16 2 /* Direct 16 bit */ +#define R_SPARC_32 3 /* Direct 32 bit */ +#define R_SPARC_DISP8 4 /* PC relative 8 bit */ +#define R_SPARC_DISP16 5 /* PC relative 16 bit */ +#define R_SPARC_DISP32 6 /* PC relative 32 bit */ +#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ +#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ +#define R_SPARC_HI22 9 /* High 22 bit */ +#define R_SPARC_22 10 /* Direct 22 bit */ +#define R_SPARC_13 11 /* Direct 13 bit */ +#define R_SPARC_LO10 12 /* Truncated 10 bit */ +#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ +#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ +#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ +#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ +#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ +#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ +#define R_SPARC_COPY 19 /* Copy symbol at runtime */ +#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ +#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ +#define R_SPARC_RELATIVE 22 /* Adjust by program base */ +#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ + +/* Additional Sparc64 relocs. */ + +#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ +#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ +#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ +#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ +#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ +#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ +#define R_SPARC_10 30 /* Direct 10 bit */ +#define R_SPARC_11 31 /* Direct 11 bit */ +#define R_SPARC_64 32 /* Direct 64 bit */ +#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ +#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ +#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ +#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ +#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ +#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ +#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ +#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ +#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ +#define R_SPARC_7 43 /* Direct 7 bit */ +#define R_SPARC_5 44 /* Direct 5 bit */ +#define R_SPARC_6 45 /* Direct 6 bit */ +#define R_SPARC_DISP64 46 /* PC relative 64 bit */ +#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ +#define R_SPARC_HIX22 48 /* High 22 bit complemented */ +#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ +#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ +#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ +#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ +#define R_SPARC_REGISTER 53 /* Global register usage */ +#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ +#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ +#define R_SPARC_TLS_GD_HI22 56 +#define R_SPARC_TLS_GD_LO10 57 +#define R_SPARC_TLS_GD_ADD 58 +#define R_SPARC_TLS_GD_CALL 59 +#define R_SPARC_TLS_LDM_HI22 60 +#define R_SPARC_TLS_LDM_LO10 61 +#define R_SPARC_TLS_LDM_ADD 62 +#define R_SPARC_TLS_LDM_CALL 63 +#define R_SPARC_TLS_LDO_HIX22 64 +#define R_SPARC_TLS_LDO_LOX10 65 +#define R_SPARC_TLS_LDO_ADD 66 +#define R_SPARC_TLS_IE_HI22 67 +#define R_SPARC_TLS_IE_LO10 68 +#define R_SPARC_TLS_IE_LD 69 +#define R_SPARC_TLS_IE_LDX 70 +#define R_SPARC_TLS_IE_ADD 71 +#define R_SPARC_TLS_LE_HIX22 72 +#define R_SPARC_TLS_LE_LOX10 73 +#define R_SPARC_TLS_DTPMOD32 74 +#define R_SPARC_TLS_DTPMOD64 75 +#define R_SPARC_TLS_DTPOFF32 76 +#define R_SPARC_TLS_DTPOFF64 77 +#define R_SPARC_TLS_TPOFF32 78 +#define R_SPARC_TLS_TPOFF64 79 +/* Keep this the last entry. */ +#define R_SPARC_NUM 80 + +/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + +/* Bits present in AT_HWCAP, primarily for Sparc32. */ + +#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ +#define HWCAP_SPARC_STBAR 2 +#define HWCAP_SPARC_SWAP 4 +#define HWCAP_SPARC_MULDIV 8 +#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ +#define HWCAP_SPARC_ULTRA3 32 + +/* MIPS R3000 specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ +#define EF_MIPS_PIC 2 /* Contains PIC code */ +#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ +#define EF_MIPS_XGOT 8 +#define EF_MIPS_64BIT_WHIRL 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ + +/* Legal values for MIPS architecture level. */ + +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ + +/* The following are non-official names and should not be used. */ + +#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ +#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ + +/* Special section indices. */ + +#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ +#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ +#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ +#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ +#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ +#define SHT_MIPS_MSYM 0x70000001 +#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ +#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ +#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ +#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ +#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ +#define SHT_MIPS_PACKAGE 0x70000007 +#define SHT_MIPS_PACKSYM 0x70000008 +#define SHT_MIPS_RELD 0x70000009 +#define SHT_MIPS_IFACE 0x7000000b +#define SHT_MIPS_CONTENT 0x7000000c +#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ +#define SHT_MIPS_SHDR 0x70000010 +#define SHT_MIPS_FDESC 0x70000011 +#define SHT_MIPS_EXTSYM 0x70000012 +#define SHT_MIPS_DENSE 0x70000013 +#define SHT_MIPS_PDESC 0x70000014 +#define SHT_MIPS_LOCSYM 0x70000015 +#define SHT_MIPS_AUXSYM 0x70000016 +#define SHT_MIPS_OPTSYM 0x70000017 +#define SHT_MIPS_LOCSTR 0x70000018 +#define SHT_MIPS_LINE 0x70000019 +#define SHT_MIPS_RFDESC 0x7000001a +#define SHT_MIPS_DELTASYM 0x7000001b +#define SHT_MIPS_DELTAINST 0x7000001c +#define SHT_MIPS_DELTACLASS 0x7000001d +#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ +#define SHT_MIPS_DELTADECL 0x7000001f +#define SHT_MIPS_SYMBOL_LIB 0x70000020 +#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ +#define SHT_MIPS_TRANSLATE 0x70000022 +#define SHT_MIPS_PIXIE 0x70000023 +#define SHT_MIPS_XLATE 0x70000024 +#define SHT_MIPS_XLATE_DEBUG 0x70000025 +#define SHT_MIPS_WHIRL 0x70000026 +#define SHT_MIPS_EH_REGION 0x70000027 +#define SHT_MIPS_XLATE_OLD 0x70000028 +#define SHT_MIPS_PDR_EXCEPTION 0x70000029 + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ +#define SHF_MIPS_MERGE 0x20000000 +#define SHF_MIPS_ADDR 0x40000000 +#define SHF_MIPS_STRINGS 0x80000000 +#define SHF_MIPS_NOSTRIP 0x08000000 +#define SHF_MIPS_LOCAL 0x04000000 +#define SHF_MIPS_NAMES 0x02000000 +#define SHF_MIPS_NODUPE 0x01000000 + + +/* Symbol tables. */ + +/* MIPS specific values for `st_other'. */ +#define STO_MIPS_DEFAULT 0x0 +#define STO_MIPS_INTERNAL 0x1 +#define STO_MIPS_HIDDEN 0x2 +#define STO_MIPS_PROTECTED 0x3 +#define STO_MIPS_SC_ALIGN_UNUSED 0xff + +/* MIPS specific values for `st_info'. */ +#define STB_MIPS_SPLIT_COMMON 13 + +/* Entries found in sections of type SHT_MIPS_GPTAB. */ + +typedef union +{ + struct + { + Elf32_Word gt_current_g_value; /* -G value used for compilation */ + Elf32_Word gt_unused; /* Not used */ + } gt_header; /* First entry in section */ + struct + { + Elf32_Word gt_g_value; /* If this value were used for -G */ + Elf32_Word gt_bytes; /* This many bytes would be used */ + } gt_entry; /* Subsequent entries in section */ +} Elf32_gptab; + +/* Entry found in sections of type SHT_MIPS_REGINFO. */ + +typedef struct +{ + Elf32_Word ri_gprmask; /* General registers used */ + Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ + Elf32_Sword ri_gp_value; /* $gp register value */ +} Elf32_RegInfo; + +/* Entries found in sections of type SHT_MIPS_OPTIONS. */ + +typedef struct +{ + unsigned char kind; /* Determines interpretation of the + variable part of descriptor. */ + unsigned char size; /* Size of descriptor, including header. */ + Elf32_Section section; /* Section header index of section affected, + 0 for global options. */ + Elf32_Word info; /* Kind-specific information. */ +} Elf_Options; + +/* Values for `kind' field in Elf_Options. */ + +#define ODK_NULL 0 /* Undefined. */ +#define ODK_REGINFO 1 /* Register usage information. */ +#define ODK_EXCEPTIONS 2 /* Exception processing options. */ +#define ODK_PAD 3 /* Section padding options. */ +#define ODK_HWPATCH 4 /* Hardware workarounds performed */ +#define ODK_FILL 5 /* record the fill value used by the linker. */ +#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ +#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ +#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ + +/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ + +#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ +#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ +#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ +#define OEX_SMM 0x20000 /* Force sequential memory mode? */ +#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ +#define OEX_PRECISEFP OEX_FPDBUG +#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ + +#define OEX_FPU_INVAL 0x10 +#define OEX_FPU_DIV0 0x08 +#define OEX_FPU_OFLO 0x04 +#define OEX_FPU_UFLO 0x02 +#define OEX_FPU_INEX 0x01 + +/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ + +#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ +#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ +#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ +#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ + +#define OPAD_PREFIX 0x1 +#define OPAD_POSTFIX 0x2 +#define OPAD_SYMBOL 0x4 + +/* Entry found in `.options' section. */ + +typedef struct +{ + Elf32_Word hwp_flags1; /* Extra flags. */ + Elf32_Word hwp_flags2; /* Extra flags. */ +} Elf_Options_Hw; + +/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ + +#define OHWA0_R4KEOP_CHECKED 0x00000001 +#define OHWA1_R4KEOP_CLEAN 0x00000002 + +/* MIPS relocs. */ + +#define R_MIPS_NONE 0 /* No reloc */ +#define R_MIPS_16 1 /* Direct 16 bit */ +#define R_MIPS_32 2 /* Direct 32 bit */ +#define R_MIPS_REL32 3 /* PC relative 32 bit */ +#define R_MIPS_26 4 /* Direct 26 bit shifted */ +#define R_MIPS_HI16 5 /* High 16 bit */ +#define R_MIPS_LO16 6 /* Low 16 bit */ +#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ +#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ +#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ +#define R_MIPS_PC16 10 /* PC relative 16 bit */ +#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ +#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ + +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 +#define R_MIPS_GOT_HI16 22 +#define R_MIPS_GOT_LO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 +#define R_MIPS_CALL_HI16 30 +#define R_MIPS_CALL_LO16 31 +#define R_MIPS_SCN_DISP 32 +#define R_MIPS_REL16 33 +#define R_MIPS_ADD_IMMEDIATE 34 +#define R_MIPS_PJUMP 35 +#define R_MIPS_RELGOT 36 +#define R_MIPS_JALR 37 +/* Keep this the last entry. */ +#define R_MIPS_NUM 38 + +/* Legal values for p_type field of Elf32_Phdr. */ + +#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ +#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ +#define PT_MIPS_OPTIONS 0x70000002 + +/* Special program header types. */ + +#define PF_MIPS_LOCAL 0x10000000 + +/* Legal values for d_tag field of Elf32_Dyn. */ + +#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ +#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ +#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ +#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ +#define DT_MIPS_FLAGS 0x70000005 /* Flags */ +#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ +#define DT_MIPS_MSYM 0x70000007 +#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ +#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ +#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ +#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ +#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ +#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ +#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ +#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ +#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ +#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ +#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ +#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in + DT_MIPS_DELTA_CLASS. */ +#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ +#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in + DT_MIPS_DELTA_INSTANCE. */ +#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ +#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in + DT_MIPS_DELTA_RELOC. */ +#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta + relocations refer to. */ +#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in + DT_MIPS_DELTA_SYM. */ +#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the + class declaration. */ +#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in + DT_MIPS_DELTA_CLASSSYM. */ +#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ +#define DT_MIPS_PIXIE_INIT 0x70000023 +#define DT_MIPS_SYMBOL_LIB 0x70000024 +#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 +#define DT_MIPS_LOCAL_GOTIDX 0x70000026 +#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 +#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 +#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ +#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ +#define DT_MIPS_DYNSTR_ALIGN 0x7000002b +#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ +#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve + function stored in GOT. */ +#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added + by rld on dlopen() calls. */ +#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ +#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ +#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ +#define DT_MIPS_NUM 0x32 + +/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ + +#define RHF_NONE 0 /* No flags */ +#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ +#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ +#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ +#define RHF_NO_MOVE (1 << 3) +#define RHF_SGI_ONLY (1 << 4) +#define RHF_GUARANTEE_INIT (1 << 5) +#define RHF_DELTA_C_PLUS_PLUS (1 << 6) +#define RHF_GUARANTEE_START_INIT (1 << 7) +#define RHF_PIXIE (1 << 8) +#define RHF_DEFAULT_DELAY_LOAD (1 << 9) +#define RHF_REQUICKSTART (1 << 10) +#define RHF_REQUICKSTARTED (1 << 11) +#define RHF_CORD (1 << 12) +#define RHF_NO_UNRES_UNDEF (1 << 13) +#define RHF_RLD_ORDER_SAFE (1 << 14) + +/* Entries found in sections of type SHT_MIPS_LIBLIST. */ + +typedef struct +{ + Elf32_Word l_name; /* Name (string table index) */ + Elf32_Word l_time_stamp; /* Timestamp */ + Elf32_Word l_checksum; /* Checksum */ + Elf32_Word l_version; /* Interface version */ + Elf32_Word l_flags; /* Flags */ +} Elf32_Lib; + +typedef struct +{ + Elf64_Word l_name; /* Name (string table index) */ + Elf64_Word l_time_stamp; /* Timestamp */ + Elf64_Word l_checksum; /* Checksum */ + Elf64_Word l_version; /* Interface version */ + Elf64_Word l_flags; /* Flags */ +} Elf64_Lib; + + +/* Legal values for l_flags. */ + +#define LL_NONE 0 +#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ +#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ +#define LL_REQUIRE_MINOR (1 << 2) +#define LL_EXPORTS (1 << 3) +#define LL_DELAY_LOAD (1 << 4) +#define LL_DELTA (1 << 5) + +/* Entries found in sections of type SHT_MIPS_CONFLICT. */ + +typedef Elf32_Addr Elf32_Conflict; + + +/* HPPA specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ +#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ +#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ +#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch + prediction. */ +#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ +#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ + +/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ + +#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ +#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ +#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ + +/* Additional section indeces. */ + +#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared + symbols in ANSI C. */ +#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ +#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ +#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ +#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ +#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + +/* HPPA relocs. */ + +#define R_PARISC_NONE 0 /* No reloc. */ +#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ +#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ +#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ +#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ +#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ +#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ +#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ +#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ +#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ +#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ +#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ +#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ +#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ +#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ +#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ +#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ +#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ +#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ +#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ +#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ +#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ +#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ +#define R_PARISC_FPTR64 64 /* 64 bits function address. */ +#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ +#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ +#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ +#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ +#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ +#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ +#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ +#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ +#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ +#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ +#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ +#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ +#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ +#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ +#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ +#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ +#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ +#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ +#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ +#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 /* Copy relocation. */ +#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ +#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ +#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ +#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ +#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ +#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ +#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ +#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ +#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_HIRESERVE 255 + +/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ + +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) + +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 + +/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ + +#define PF_PARISC_SBP 0x08000000 + +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 + + +/* Alpha specific definitions. */ + +/* Legal values for e_flags field of Elf64_Ehdr. */ + +#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ +#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ + +/* Legal values for sh_type field of Elf64_Shdr. */ + +/* These two are primerily concerned with ECOFF debugging info. */ +#define SHT_ALPHA_DEBUG 0x70000001 +#define SHT_ALPHA_REGINFO 0x70000002 + +/* Legal values for sh_flags field of Elf64_Shdr. */ + +#define SHF_ALPHA_GPREL 0x10000000 + +/* Legal values for st_other field of Elf64_Sym. */ +#define STO_ALPHA_NOPV 0x80 /* No PV required. */ +#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ + +/* Alpha relocs. */ + +#define R_ALPHA_NONE 0 /* No reloc */ +#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ +#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ +#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ +#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ +#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ +#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ +#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ +#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ +#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ +#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ +#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ +#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ +#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ +#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ +#define R_ALPHA_TLS_GD_HI 28 +#define R_ALPHA_TLSGD 29 +#define R_ALPHA_TLS_LDM 30 +#define R_ALPHA_DTPMOD64 31 +#define R_ALPHA_GOTDTPREL 32 +#define R_ALPHA_DTPREL64 33 +#define R_ALPHA_DTPRELHI 34 +#define R_ALPHA_DTPRELLO 35 +#define R_ALPHA_DTPREL16 36 +#define R_ALPHA_GOTTPREL 37 +#define R_ALPHA_TPREL64 38 +#define R_ALPHA_TPRELHI 39 +#define R_ALPHA_TPRELLO 40 +#define R_ALPHA_TPREL16 41 +/* Keep this the last entry. */ +#define R_ALPHA_NUM 46 + +/* Magic values of the LITUSE relocation addend. */ +#define LITUSE_ALPHA_ADDR 0 +#define LITUSE_ALPHA_BASE 1 +#define LITUSE_ALPHA_BYTOFF 2 +#define LITUSE_ALPHA_JSR 3 +#define LITUSE_ALPHA_TLS_GD 4 +#define LITUSE_ALPHA_TLS_LDM 5 + + +/* PowerPC specific declarations */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 + +/* PowerPC relocations defined for the TLS access ABI. */ +#define R_PPC_TLS 67 /* none (sym+add)@tls */ +#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ +#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ +#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ +#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ +#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ +#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ +#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ +#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ +#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ + +/* Keep this the last entry. */ +#define R_PPC_NUM 95 + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ +#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + + +/* PowerPC64 relocations defined by the ABIs */ +#define R_PPC64_NONE R_PPC_NONE +#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ +#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ +#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ +#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ +#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ +#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ +#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ +#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN +#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN +#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ +#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ +#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN +#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN +#define R_PPC64_GOT16 R_PPC_GOT16 +#define R_PPC64_GOT16_LO R_PPC_GOT16_LO +#define R_PPC64_GOT16_HI R_PPC_GOT16_HI +#define R_PPC64_GOT16_HA R_PPC_GOT16_HA + +#define R_PPC64_COPY R_PPC_COPY +#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT +#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT +#define R_PPC64_RELATIVE R_PPC_RELATIVE + +#define R_PPC64_UADDR32 R_PPC_UADDR32 +#define R_PPC64_UADDR16 R_PPC_UADDR16 +#define R_PPC64_REL32 R_PPC_REL32 +#define R_PPC64_PLT32 R_PPC_PLT32 +#define R_PPC64_PLTREL32 R_PPC_PLTREL32 +#define R_PPC64_PLT16_LO R_PPC_PLT16_LO +#define R_PPC64_PLT16_HI R_PPC_PLT16_HI +#define R_PPC64_PLT16_HA R_PPC_PLT16_HA + +#define R_PPC64_SECTOFF R_PPC_SECTOFF +#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO +#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI +#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA +#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ +#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ +#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ +#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ +#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ +#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ +#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ +#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ +#define R_PPC64_PLT64 45 /* doubleword64 L + A */ +#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ +#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ +#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ +#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ +#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ +#define R_PPC64_TOC 51 /* doubleword64 .TOC */ +#define R_PPC64_PLTGOT16 52 /* half16* M + A */ +#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ +#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ +#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ + +#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ +#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ +#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ +#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ +#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ +#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ +#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ +#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ +#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ +#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ +#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ + +/* PowerPC64 relocations defined for the TLS access ABI. */ +#define R_PPC64_TLS 67 /* none (sym+add)@tls */ +#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ +#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ +#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ +#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ +#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ +#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ +#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ +#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ +#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ +#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ +#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ +#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ +#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ +#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ +#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ +#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ +#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ + +/* Keep this the last entry. */ +#define R_PPC64_NUM 107 + +/* PowerPC64 specific values for the Dyn d_tag field. */ +#define DT_PPC64_GLINK (DT_LOPROC + 0) +#define DT_PPC64_OPD (DT_LOPROC + 1) +#define DT_PPC64_OPDSZ (DT_LOPROC + 2) +#define DT_PPC64_NUM 3 + + +/* ARM specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_ARM_NEW_ABI 0x80 +#define EF_ARM_OLD_ABI 0x100 + +/* Other constants defined in the ARM ELF spec. version B-01. */ +/* NB. These conflict with values defined above. */ +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0XFF000000 + +#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 + +/* Additional symbol types for Thumb */ +#define STT_ARM_TFUNC 0xd + +/* ARM-specific values for sh_flags */ +#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ +#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined + in the input to a link step */ + +/* ARM-specific program header flags */ +#define PF_ARM_SB 0x10000000 /* Segment contains the location + addressed by the static base */ + +/* ARM relocs. */ +#define R_ARM_NONE 0 /* No reloc */ +#define R_ARM_PC24 1 /* PC relative 26 bit branch */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_REL32 3 /* PC relative 32 bit */ +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 /* Direct 16 bit */ +#define R_ARM_ABS12 6 /* Direct 12 bit */ +#define R_ARM_THM_ABS5 7 +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 +#define R_ARM_THM_PC8 11 +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 +#define R_ARM_THM_SWI8 14 +#define R_ARM_XPC25 15 +#define R_ARM_THM_XPC22 16 +#define R_ARM_COPY 20 /* Copy symbol at runtime */ +#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ +#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ +#define R_ARM_RELATIVE 23 /* Adjust by program base */ +#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ +#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT32 26 /* 32 bit GOT entry */ +#define R_ARM_PLT32 27 /* 32 bit PLT address */ +#define R_ARM_ALU_PCREL_7_0 32 +#define R_ARM_ALU_PCREL_15_8 33 +#define R_ARM_ALU_PCREL_23_15 34 +#define R_ARM_LDR_SBREL_11_0 35 +#define R_ARM_ALU_SBREL_19_12 36 +#define R_ARM_ALU_SBREL_27_20 37 +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ +#define R_ARM_THM_PC9 103 /* thumb conditional branch */ +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 +/* Keep this the last entry. */ +#define R_ARM_NUM 256 + +/* IA-64 specific declarations. */ + +/* Processor specific flags for the Ehdr e_flags field. */ +#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ +#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ +#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ +#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ +#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) +#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) +#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) + +/* Processor specific flags for the Phdr p_flags field. */ +#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ + +/* Processor specific flags for the Shdr sh_flags field. */ +#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ +#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Dyn d_tag field. */ +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 + +/* IA-64 relocations. */ +#define R_IA64_NONE 0x00 /* none */ +#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ +#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ +#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ +#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ +#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ +#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ +#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ +#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ +#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ +#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ +#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ +#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ +#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ +#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ +#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ +#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ +#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ +#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ +#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ +#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ +#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ +#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ +#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ +#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ +#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ +#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ +#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ +#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ +#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ +#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ +#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ +#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ +#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ +#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ +#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ +#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ +#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ +#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ +#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ +#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ +#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ +#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ +#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ +#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ +#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ +#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ +#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ +#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ +#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ +#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ +#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ +#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ +#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ +#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ +#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ +#define R_IA64_COPY 0x84 /* copy relocation */ +#define R_IA64_SUB 0x85 /* Addend and symbol difference */ +#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ +#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ +#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ +#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ +#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ +#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ +#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ +#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ +#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ +#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ +#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ +#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ +#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ +#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ +#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ +#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ + +/* SH specific declarations */ + +/* SH relocs. */ +#define R_SH_NONE 0 +#define R_SH_DIR32 1 +#define R_SH_REL32 2 +#define R_SH_DIR8WPN 3 +#define R_SH_IND12W 4 +#define R_SH_DIR8WPL 5 +#define R_SH_DIR8WPZ 6 +#define R_SH_DIR8BP 7 +#define R_SH_DIR8W 8 +#define R_SH_DIR8L 9 +#define R_SH_SWITCH16 25 +#define R_SH_SWITCH32 26 +#define R_SH_USES 27 +#define R_SH_COUNT 28 +#define R_SH_ALIGN 29 +#define R_SH_CODE 30 +#define R_SH_DATA 31 +#define R_SH_LABEL 32 +#define R_SH_SWITCH8 33 +#define R_SH_GNU_VTINHERIT 34 +#define R_SH_GNU_VTENTRY 35 +#define R_SH_TLS_GD_32 144 +#define R_SH_TLS_LD_32 145 +#define R_SH_TLS_LDO_32 146 +#define R_SH_TLS_IE_32 147 +#define R_SH_TLS_LE_32 148 +#define R_SH_TLS_DTPMOD32 149 +#define R_SH_TLS_DTPOFF32 150 +#define R_SH_TLS_TPOFF32 151 +#define R_SH_GOT32 160 +#define R_SH_PLT32 161 +#define R_SH_COPY 162 +#define R_SH_GLOB_DAT 163 +#define R_SH_JMP_SLOT 164 +#define R_SH_RELATIVE 165 +#define R_SH_GOTOFF 166 +#define R_SH_GOTPC 167 +/* Keep this the last entry. */ +#define R_SH_NUM 256 + +/* Additional s390 relocs */ + +#define R_390_NONE 0 /* No reloc. */ +#define R_390_8 1 /* Direct 8 bit. */ +#define R_390_12 2 /* Direct 12 bit. */ +#define R_390_16 3 /* Direct 16 bit. */ +#define R_390_32 4 /* Direct 32 bit. */ +#define R_390_PC32 5 /* PC relative 32 bit. */ +#define R_390_GOT12 6 /* 12 bit GOT offset. */ +#define R_390_GOT32 7 /* 32 bit GOT offset. */ +#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ +#define R_390_COPY 9 /* Copy symbol at runtime. */ +#define R_390_GLOB_DAT 10 /* Create GOT entry. */ +#define R_390_JMP_SLOT 11 /* Create PLT entry. */ +#define R_390_RELATIVE 12 /* Adjust by program base. */ +#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ +#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ +#define R_390_GOT16 15 /* 16 bit GOT offset. */ +#define R_390_PC16 16 /* PC relative 16 bit. */ +#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ +#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ +#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ +#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ +#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ +#define R_390_64 22 /* Direct 64 bit. */ +#define R_390_PC64 23 /* PC relative 64 bit. */ +#define R_390_GOT64 24 /* 64 bit GOT offset. */ +#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ +#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ +#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ +#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ +#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ +#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ +#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ +#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ +#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ +#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ +#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ +#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ +#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ +#define R_390_TLS_GDCALL 38 /* Tag for function call in general + dynamic TLS code. */ +#define R_390_TLS_LDCALL 39 /* Tag for function call in local + dynamic TLS code. */ +#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic + thread local data. */ +#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic + thread local data. */ +#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS + block. */ +#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS + block. */ +#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ +#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ +#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS + block. */ + +/* Keep this the last entry. */ +#define R_390_NUM 57 + +/* CRIS relocations. */ +#define R_CRIS_NONE 0 +#define R_CRIS_8 1 +#define R_CRIS_16 2 +#define R_CRIS_32 3 +#define R_CRIS_8_PCREL 4 +#define R_CRIS_16_PCREL 5 +#define R_CRIS_32_PCREL 6 +#define R_CRIS_GNU_VTINHERIT 7 +#define R_CRIS_GNU_VTENTRY 8 +#define R_CRIS_COPY 9 +#define R_CRIS_GLOB_DAT 10 +#define R_CRIS_JUMP_SLOT 11 +#define R_CRIS_RELATIVE 12 +#define R_CRIS_16_GOT 13 +#define R_CRIS_32_GOT 14 +#define R_CRIS_16_GOTPLT 15 +#define R_CRIS_32_GOTPLT 16 +#define R_CRIS_32_GOTREL 17 +#define R_CRIS_32_PLT_GOTREL 18 +#define R_CRIS_32_PLT_PCREL 19 + +#define R_CRIS_NUM 20 + +/* AMD x86-64 relocations. */ +#define R_X86_64_NONE 0 /* No reloc */ +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_COPY 5 /* Copy symbol at runtime */ +#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ +#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ +#define R_X86_64_RELATIVE 8 /* Adjust by program base */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative + offset to GOT */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_16 12 /* Direct 16 bit zero extended */ +#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ +#define R_X86_64_8 14 /* Direct 8 bit sign extended */ +#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ +#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ +#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ +#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ +#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset + to two GOT entries for GD symbol */ +#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset + to two GOT entries for LD symbol */ +#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ +#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset + to GOT entry for IE symbol */ +#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ + +#define R_X86_64_NUM 24 + +#endif /* elf.h */ diff --git a/common/libspumars/ppu/task/lib/task.c b/common/libspumars/ppu/task/lib/task.c new file mode 100644 index 00000000..efc84f73 --- /dev/null +++ b/common/libspumars/ppu/task/lib/task.c @@ -0,0 +1,391 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "mars/task.h" + +#include "elf.h" +#include "task_internal_types.h" + +extern const unsigned char mars_task_module_entry[]; + +static uint64_t task_exit_code_ea(uint64_t task_ea) +{ + return task_ea + offsetof(struct mars_task_context, exit_code); +} + +static int task_map_elf(struct mars_task_context *task, const void *elf_image) +{ + int ret, i; + int text_found = 0; + int data_found = 0; + Elf32_Ehdr *ehdr; + Elf32_Phdr *phdr; + + /* process elf header information */ + ehdr = (Elf32_Ehdr *)elf_image; + phdr = (Elf32_Phdr *)((void *)ehdr + ehdr->e_phoff); + + /* elf is not executable */ + if (ehdr->e_type != ET_EXEC) + return MARS_ERROR_FORMAT; + + /* iterate through program header segments */ + for (i = 0; i < ehdr->e_phnum; i++) { + /* readonly text segment */ + if (phdr->p_type == PT_LOAD && + phdr->p_flags == PF_R + PF_X && + phdr->p_align == 0x80) { + /* make sure base addr is what we expect */ + if (text_found || + phdr->p_vaddr != MARS_TASK_BASE_ADDR || + phdr->p_memsz != phdr->p_filesz) { + ret = MARS_ERROR_FORMAT; + goto error; + } + + /* initialize the task context text info */ + task->text_ea = mars_ea_map((void *)ehdr + + phdr->p_offset, + phdr->p_filesz); + if (!task->text_ea) { + ret = MARS_ERROR_MEMORY; + goto error; + } + + task->text_vaddr = phdr->p_vaddr; + task->text_size = phdr->p_filesz; + + /* make sure we only find 1 text segment */ + text_found = 1; + /* read-write data segment */ + } else if (phdr->p_type == PT_LOAD && + phdr->p_flags == PF_R + PF_W && + phdr->p_align == 0x80) { + if (data_found) { + ret = MARS_ERROR_FORMAT; + goto error; + } + + task->data_ea = mars_ea_map((void *)ehdr + + phdr->p_offset, + phdr->p_filesz); + if (!task->data_ea) { + ret = MARS_ERROR_MEMORY; + goto error; + } + + task->data_vaddr = phdr->p_vaddr; + task->data_size = phdr->p_filesz; + task->bss_size = phdr->p_memsz - phdr->p_filesz; + + /* make sure we only find 1 data segment */ + data_found = 1; + } + + /* increment program header */ + phdr = (void *)phdr + ehdr->e_phentsize; + } + + /* make sure text and data segment is found */ + if (!text_found || !data_found) { + ret = MARS_ERROR_FORMAT; + goto error; + } + + /* set the entry point of execution */ + task->entry = ehdr->e_entry; + + return MARS_SUCCESS; + +error: + if (text_found) + mars_ea_unmap(task->text_ea, task->text_size); + if (data_found) + mars_ea_unmap(task->data_ea, task->data_size); + + return ret; +} + +int mars_task_create(struct mars_context *mars, + struct mars_task_id *id_ret, + const char *name, const void *elf_image, + uint32_t context_save_size) +{ + int ret; + uint16_t workload_id; + uint64_t workload_ea; + struct mars_task_context *task; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + if (!id_ret) + return MARS_ERROR_NULL; + if (!elf_image) + return MARS_ERROR_NULL; + if (name && strlen(name) > MARS_TASK_NAME_LEN_MAX) + return MARS_ERROR_PARAMS; + if (context_save_size > MARS_TASK_CONTEXT_SAVE_SIZE_MAX) + return MARS_ERROR_PARAMS; + + /* invalidate id */ + id_ret->mars_context_ea = 0; + + /* begin process to add the task to the workload queue */ + ret = mars_workload_queue_add_begin(mars, &workload_id, &workload_ea, + mars_task_module_entry, + MARS_TASK_MODULE_NAME); + if (ret != MARS_SUCCESS) + return ret; + + /* prepare work area for task context */ + task = mars_ea_work_area_get(workload_ea, + MARS_TASK_CONTEXT_ALIGN, + MARS_TASK_CONTEXT_SIZE); + + /* map task ELF */ + ret = task_map_elf(task, elf_image); + if (ret != MARS_SUCCESS) { + mars_workload_queue_add_end(mars, workload_id, 1); + return ret; + } + + /* initialize task id */ + task->id.mars_context_ea = mars_ptr_to_ea(mars); + task->id.workload_id = workload_id; + if (name) + strcpy((char *)task->id.name, name); + else + task->id.name[0] = 0; + + /* initialize task exit code */ + task->exit_code = 0; + + /* no context save - run complete */ + if (context_save_size) { + /* allocate context save area */ + task->context_save_area_ea = + mars_ea_memalign(MARS_TASK_CONTEXT_SAVE_ALIGN, + context_save_size + + MARS_TASK_REGISTER_SAVE_AREA_SIZE); + if (!task->context_save_area_ea) { + mars_workload_queue_add_end(mars, workload_id, 1); + return MARS_ERROR_MEMORY; + } + } else + task->context_save_area_ea = 0; + + /* update task context on EA */ + mars_ea_put(workload_ea, task, MARS_TASK_CONTEXT_SIZE); + mars_ea_sync(); + + /* end process to add the task to the workload queue */ + ret = mars_workload_queue_add_end(mars, workload_id, 0); + if (ret != MARS_SUCCESS) { + mars_ea_free(task->context_save_area_ea); + mars_workload_queue_add_end(mars, workload_id, 1); + return ret; + } + + /* return id to caller */ + *id_ret = task->id; + + return MARS_SUCCESS; +} + +int mars_task_destroy(struct mars_task_id *id) +{ + int ret; + struct mars_context *mars; + struct mars_task_context *task; + uint64_t workload_ea; + + /* check function params */ + if (!id) + return MARS_ERROR_NULL; + if (!id->mars_context_ea) + return MARS_ERROR_PARAMS; + + /* get mars context pointer from task id */ + mars = mars_ea_to_ptr(id->mars_context_ea); + + /* begin process to remove the task from the workload queue */ + ret = mars_workload_queue_remove_begin(mars, id->workload_id, + &workload_ea); + if (ret != MARS_SUCCESS) + return ret; + + /* prepare work area for task context */ + task = mars_ea_work_area_get(workload_ea, + MARS_TASK_CONTEXT_ALIGN, + MARS_TASK_CONTEXT_SIZE); + + /* get task context from EA */ + mars_ea_get(workload_ea, task, MARS_TASK_CONTEXT_SIZE); + + /* free the allocated context save area if it has one */ + if (task->context_save_area_ea) + mars_ea_free(task->context_save_area_ea); + + /* unmap task ELF */ + mars_ea_unmap(task->text_ea, task->text_size); + mars_ea_unmap(task->data_ea, task->data_size); + + /* invalidate id */ + id->mars_context_ea = 0; + + /* end process to remove the task from the workload queue */ + return mars_workload_queue_remove_end(mars, id->workload_id, 0); +} + +int mars_task_schedule(const struct mars_task_id *id, + const struct mars_task_args *args, + uint8_t priority) +{ + int ret; + struct mars_context *mars; + struct mars_task_context *task; + uint64_t workload_ea; + + /* check function params */ + if (!id) + return MARS_ERROR_NULL; + if (!id->mars_context_ea) + return MARS_ERROR_PARAMS; + + /* get mars context pointer from task id */ + mars = mars_ea_to_ptr(id->mars_context_ea); + + /* begin process to schedule the workload in the workload queue */ + ret = mars_workload_queue_schedule_begin(mars, id->workload_id, + priority, &workload_ea); + if (ret != MARS_SUCCESS) + return ret; + + /* prepare work area for task context */ + task = mars_ea_work_area_get(workload_ea, + MARS_TASK_CONTEXT_ALIGN, + MARS_TASK_CONTEXT_SIZE); + + /* get task context from EA */ + mars_ea_get(workload_ea, task, MARS_TASK_CONTEXT_SIZE); + + /* initialize task specific context variables */ + task->stack = 0; + task->exit_code = 0; + if (args) + memcpy(&task->args, args, sizeof(struct mars_task_args)); + + /* update task context on EA */ + mars_ea_put(workload_ea, task, MARS_TASK_CONTEXT_SIZE); + mars_ea_sync(); + + /* end process to schedule the workload in the workload queue */ + return mars_workload_queue_schedule_end(mars, id->workload_id, 0); +} + +int mars_task_unschedule(const struct mars_task_id *id, int32_t exit_code) +{ + int ret; + struct mars_context *mars; + struct mars_task_context *task; + uint64_t workload_ea; + + /* check function params */ + if (!id) + return MARS_ERROR_NULL; + if (!id->mars_context_ea) + return MARS_ERROR_PARAMS; + + /* get mars context pointer from task id */ + mars = mars_ea_to_ptr(id->mars_context_ea); + + /* begin process to schedule the workload in the workload queue */ + ret = mars_workload_queue_unschedule_begin(mars, id->workload_id, + &workload_ea); + if (ret != MARS_SUCCESS) + return ret; + + /* prepare work area for task context */ + task = mars_ea_work_area_get(workload_ea, + MARS_TASK_CONTEXT_ALIGN, + MARS_TASK_CONTEXT_SIZE); + + /* get task context from EA */ + mars_ea_get(workload_ea, task, MARS_TASK_CONTEXT_SIZE); + + /* store exit code in task context */ + task->exit_code = exit_code; + + /* update task context on EA */ + mars_ea_put(workload_ea, task, MARS_TASK_CONTEXT_SIZE); + mars_ea_sync(); + + /* end process to unschedule the workload in the workload queue */ + return mars_workload_queue_unschedule_end(mars, id->workload_id); +} + +int mars_task_wait(const struct mars_task_id *id, int32_t *exit_code) +{ + int ret; + struct mars_context *mars; + uint64_t workload_ea; + + /* check function params */ + if (!id) + return MARS_ERROR_NULL; + if (!id->mars_context_ea) + return MARS_ERROR_PARAMS; + + /* get mars context pointer from task id */ + mars = mars_ea_to_ptr(id->mars_context_ea); + + /* blocking wait for workload completion */ + ret = mars_workload_queue_wait(mars, id->workload_id, &workload_ea); + if (ret != MARS_SUCCESS) + return ret; + + /* exit_code requested so return it to caller */ + if (exit_code) + *exit_code = mars_ea_get_uint32(task_exit_code_ea(workload_ea)); + + return MARS_SUCCESS; +} + +int mars_task_try_wait(const struct mars_task_id *id, int32_t *exit_code) +{ + int ret; + struct mars_context *mars; + uint64_t workload_ea; + + /* check function params */ + if (!id) + return MARS_ERROR_NULL; + if (!id->mars_context_ea) + return MARS_ERROR_PARAMS; + + /* get mars context pointer from task id */ + mars = mars_ea_to_ptr(id->mars_context_ea); + + /* non-blocking wait for workload completion */ + ret = mars_workload_queue_try_wait(mars, id->workload_id, &workload_ea); + if (ret != MARS_SUCCESS) + return ret; + + /* exit_code requested so return it to caller */ + if (exit_code) + *exit_code = mars_ea_get_uint32(task_exit_code_ea(workload_ea)); + + return MARS_SUCCESS; +} + +uint32_t mars_task_get_ticks(void) +{ + return mars_get_ticks(); +} diff --git a/common/libspumars/ppu/task/lib/task_barrier.c b/common/libspumars/ppu/task/lib/task_barrier.c new file mode 100644 index 00000000..97e703ba --- /dev/null +++ b/common/libspumars/ppu/task/lib/task_barrier.c @@ -0,0 +1,111 @@ +#include +#include +#include + +#include "mars/task_barrier.h" + +#include "task_barrier_internal_types.h" + +int mars_task_barrier_create(struct mars_context *mars, + uint64_t *barrier_ea_ret, + uint32_t total) +{ + struct mars_task_barrier *barrier; + uint64_t barrier_ea; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + if (!barrier_ea_ret) + return MARS_ERROR_NULL; + if (!total || total > MARS_TASK_BARRIER_WAIT_MAX) + return MARS_ERROR_PARAMS; + + /* allocate barrier instance */ + barrier_ea = mars_ea_memalign(MARS_TASK_BARRIER_ALIGN, + MARS_TASK_BARRIER_SIZE); + if (!barrier_ea) + return MARS_ERROR_MEMORY; + + /* prepare work area for initialization */ + barrier = mars_ea_work_area_get(barrier_ea, + MARS_TASK_BARRIER_ALIGN, + MARS_TASK_BARRIER_SIZE); + + /* initialize barrier instance on work area */ + barrier->mars_context_ea = mars_ptr_to_ea(mars); + barrier->total = total; + barrier->notified_count = 0; + barrier->waited_count = 0; + barrier->notify_wait_count = 0; + barrier->wait_count = 0; + + /* update barrier on EA */ + mars_ea_put(barrier_ea, barrier, MARS_TASK_BARRIER_SIZE); + mars_ea_sync(); + + mars_mutex_reset(barrier_ea); + + /* return barrier instance pointer */ + *barrier_ea_ret = barrier_ea; + + return MARS_SUCCESS; +} + +int mars_task_barrier_initialize(uint64_t barrier_ea, + uint32_t total) +{ + struct mars_task_barrier *barrier; + + /* check function params */ + if (!barrier_ea) + return MARS_ERROR_NULL; + if (!total || total > MARS_TASK_BARRIER_WAIT_MAX) + return MARS_ERROR_PARAMS; + + /* prepare work area */ + barrier = mars_ea_work_area_get(barrier_ea, + MARS_TASK_BARRIER_ALIGN, + MARS_TASK_BARRIER_SIZE); + + mars_ea_get(barrier_ea, barrier, MARS_TASK_BARRIER_SIZE); + + /* initialize barrier instance on work area */ + barrier->total = total; + barrier->notified_count = 0; + barrier->waited_count = 0; + barrier->notify_wait_count = 0; + barrier->wait_count = 0; + + /* update barrier on EA */ + mars_ea_put(barrier_ea, barrier, MARS_TASK_BARRIER_SIZE); + mars_ea_sync(); + + mars_mutex_reset(barrier_ea); + + return MARS_SUCCESS; +} + +int mars_task_barrier_destroy(uint64_t barrier_ea) +{ + struct mars_task_barrier *barrier; + + /* check function params */ + if (!barrier_ea) + return MARS_ERROR_NULL; + + /* prepare work area */ + barrier = mars_ea_work_area_get(barrier_ea, + MARS_TASK_BARRIER_ALIGN, + MARS_TASK_BARRIER_SIZE); + + mars_ea_get(barrier_ea, barrier, MARS_TASK_BARRIER_SIZE); + + /* make sure no tasks in wait list */ + if (barrier->wait_count) + return MARS_ERROR_STATE; + + mars_ea_free(barrier_ea); + + return MARS_SUCCESS; +} diff --git a/common/libspumars/ppu/task/lib/task_event_flag.c b/common/libspumars/ppu/task/lib/task_event_flag.c new file mode 100644 index 00000000..584b62c1 --- /dev/null +++ b/common/libspumars/ppu/task/lib/task_event_flag.c @@ -0,0 +1,314 @@ +#include + +#include +#include +#include +#include +#include + +#include "mars/task_event_flag.h" + +#include "task_event_flag_internal_types.h" + + +static inline uint64_t event_flag_bits_ea(uint64_t event_flag_ea) +{ + return event_flag_ea + + offsetof(struct mars_task_event_flag, bits); +} + +int mars_task_event_flag_create(struct mars_context *mars, + uint64_t *event_flag_ea_ret, + uint8_t direction, + uint8_t clear_mode) +{ + struct mars_task_event_flag *event_flag; + uint64_t event_flag_ea; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + if (!event_flag_ea_ret) + return MARS_ERROR_NULL; + if (direction != MARS_TASK_EVENT_FLAG_HOST_TO_MPU && + direction != MARS_TASK_EVENT_FLAG_MPU_TO_HOST && + direction != MARS_TASK_EVENT_FLAG_MPU_TO_MPU) + return MARS_ERROR_PARAMS; + if (clear_mode != MARS_TASK_EVENT_FLAG_CLEAR_AUTO && + clear_mode != MARS_TASK_EVENT_FLAG_CLEAR_MANUAL) + return MARS_ERROR_PARAMS; + + /* allocate event flag instance */ + event_flag_ea = mars_ea_memalign(MARS_TASK_EVENT_FLAG_ALIGN, + MARS_TASK_EVENT_FLAG_SIZE); + if (!event_flag_ea) + return MARS_ERROR_MEMORY; + + /* prepare work area for initialization */ + event_flag = mars_ea_work_area_get(event_flag_ea, + MARS_TASK_EVENT_FLAG_ALIGN, + MARS_TASK_EVENT_FLAG_SIZE); + + /* intialize event flag instance on work area */ + event_flag->mars_context_ea = mars_ptr_to_ea(mars); + event_flag->bits = 0; + event_flag->direction = direction; + event_flag->clear_mode = clear_mode; + event_flag->wait_count = 0; + + /* update event flag on EA */ + mars_ea_put(event_flag_ea, event_flag, MARS_TASK_EVENT_FLAG_SIZE); + mars_ea_sync(); + + mars_mutex_reset(event_flag_ea); + + /* return event flag instance pointer */ + *event_flag_ea_ret = event_flag_ea; + + return MARS_SUCCESS; +} + +int mars_task_event_flag_destroy(uint64_t event_flag_ea) +{ + struct mars_task_event_flag *event_flag; + + /* check function params */ + if (!event_flag_ea) + return MARS_ERROR_NULL; + + /* prepare work area */ + event_flag = mars_ea_work_area_get(event_flag_ea, + MARS_TASK_EVENT_FLAG_ALIGN, + MARS_TASK_EVENT_FLAG_SIZE); + + mars_ea_get(event_flag_ea, event_flag, MARS_TASK_EVENT_FLAG_SIZE); + + /* make sure no tasks in wait list */ + if (event_flag->wait_count) + return MARS_ERROR_STATE; + + mars_ea_free(event_flag_ea); + + return MARS_SUCCESS; +} + +int mars_task_event_flag_clear(uint64_t event_flag_ea, uint32_t bits) +{ + uint32_t new_bits; + uint64_t bits_ea; + + if (!event_flag_ea) + return MARS_ERROR_NULL; + + bits_ea = event_flag_bits_ea(event_flag_ea); + + mars_mutex_lock(event_flag_ea); + + /* clear the necessary bits */ + new_bits = mars_ea_get_uint32(bits_ea) & ~bits; + mars_ea_put_uint32(bits_ea, new_bits); + + mars_mutex_unlock(event_flag_ea); + + return MARS_SUCCESS; +} + +int mars_task_event_flag_set(uint64_t event_flag_ea, uint32_t bits) +{ + int ret; + int i; + struct mars_context *mars; + struct mars_task_event_flag *event_flag; + + /* check function params */ + if (!event_flag_ea) + return MARS_ERROR_NULL; + + /* prepare work area */ + event_flag = mars_ea_work_area_get(event_flag_ea, + MARS_TASK_EVENT_FLAG_ALIGN, + MARS_TASK_EVENT_FLAG_SIZE); + + /* get event flag from EA */ + mars_mutex_lock_get(event_flag_ea, (struct mars_mutex *)event_flag); + + /* check event flag status */ + ret = MARS_ERROR_NULL; + if (!event_flag->mars_context_ea) + goto end; + ret = MARS_ERROR_STATE; + if (event_flag->direction != MARS_TASK_EVENT_FLAG_HOST_TO_MPU) + goto end; + + /* get mars context pointer */ + mars = mars_ea_to_ptr(event_flag->mars_context_ea); + + /* set the necessary bits */ + event_flag->bits |= bits; + + /* save current set bits */ + bits = event_flag->bits; + + /* search through wait list for tasks to be signalled */ + for (i = 0; i < event_flag->wait_count; i++) { + /* check condition based on wait mode */ + switch (event_flag->wait_mask_mode[i]) { + case MARS_TASK_EVENT_FLAG_MASK_OR: + if ((bits & event_flag->wait_mask[i]) == 0) + continue; + break; + case MARS_TASK_EVENT_FLAG_MASK_AND: + if ((bits & event_flag->wait_mask[i]) != + event_flag->wait_mask[i]) + continue; + break; + } + + /* signal the task to go to ready state */ + ret = mars_workload_queue_signal_send(mars, + event_flag->wait_id[i]); + if (ret != MARS_SUCCESS) + goto end; + + /* flush id from wait list */ + event_flag->wait_count--; + memmove(&event_flag->wait_id[i], + &event_flag->wait_id[i + 1], + sizeof(uint16_t) * (event_flag->wait_count - i)); + memmove(&event_flag->wait_mask[i], + &event_flag->wait_mask[i + 1], + sizeof(uint32_t) * (event_flag->wait_count - i)); + memmove(&event_flag->wait_mask_mode[i], + &event_flag->wait_mask_mode[i + 1], + sizeof(uint8_t) * (event_flag->wait_count - i)); + i--; + } + + ret = MARS_SUCCESS; + +end: + mars_mutex_unlock_put(event_flag_ea, (struct mars_mutex *)event_flag); + + return ret; +} + +static int test_any_bits(uint32_t bits, void *param) +{ + const uint32_t *mask = (const uint32_t *)param; + + return (bits & *mask) ? MARS_SUCCESS : -1; +} + +static int test_all_bits(uint32_t bits, void *param) +{ + const uint32_t *mask = (const uint32_t *)param; + + return ((bits & *mask) == *mask) ? MARS_SUCCESS : -1; +} + +static int wait(uint64_t event_flag_ea, + uint32_t mask, uint8_t mask_mode, uint32_t *bits, int try) +{ + int ret; + struct mars_task_event_flag *event_flag; + uint64_t bits_ea; + + /* check function params */ + if (!event_flag_ea) + return MARS_ERROR_NULL; + + /* prepare work area */ + event_flag = mars_ea_work_area_get(event_flag_ea, + MARS_TASK_EVENT_FLAG_ALIGN, + MARS_TASK_EVENT_FLAG_SIZE); + + /* get event flag from EA */ + mars_mutex_lock_get(event_flag_ea, (struct mars_mutex *)event_flag); + + /* check function params */ + ret = MARS_ERROR_STATE; + if (event_flag->direction != MARS_TASK_EVENT_FLAG_MPU_TO_HOST) + goto end; + ret = MARS_ERROR_PARAMS; + if (mask_mode != MARS_TASK_EVENT_FLAG_MASK_OR && + mask_mode != MARS_TASK_EVENT_FLAG_MASK_AND) + goto end; + + /* check condition based on wait mode */ + bits_ea = event_flag_bits_ea(event_flag_ea); + switch (mask_mode) { + case MARS_TASK_EVENT_FLAG_MASK_OR: + while ((event_flag->bits & mask) == 0) { + /* get current bits status if return bits requested */ + if (bits) + *bits = event_flag->bits; + + mars_mutex_unlock_put(event_flag_ea, + (struct mars_mutex *)event_flag); + + /* only try so return busy */ + if (try) + return MARS_ERROR_BUSY; + + /* wait until condition is met */ + ret = mars_ea_cond_wait(bits_ea, test_any_bits, &mask); + if (ret) + return ret; + + mars_mutex_lock_get(event_flag_ea, + (struct mars_mutex *)event_flag); + } + break; + case MARS_TASK_EVENT_FLAG_MASK_AND: + while ((event_flag->bits & mask) != mask) { + /* get current bits status if return bits requested */ + if (bits) + *bits = event_flag->bits; + + mars_mutex_unlock_put(event_flag_ea, + (struct mars_mutex *)event_flag); + + /* only try so return busy */ + if (try) + return MARS_ERROR_BUSY; + + /* wait until condition is met */ + ret = mars_ea_cond_wait(bits_ea, test_all_bits, &mask); + if (ret) + return ret; + + mars_mutex_lock_get(event_flag_ea, + (struct mars_mutex *)event_flag); + } + break; + } + + /* return bits if requested */ + if (bits) + *bits = event_flag->bits; + + /* clear event if clear mode is auto */ + if (event_flag->clear_mode == MARS_TASK_EVENT_FLAG_CLEAR_AUTO) + event_flag->bits &= ~mask; + + ret = MARS_SUCCESS; + +end: + mars_mutex_unlock_put(event_flag_ea, (struct mars_mutex *)event_flag); + + return ret; +} + +int mars_task_event_flag_wait(uint64_t event_flag_ea, + uint32_t mask, uint8_t mask_mode, + uint32_t *bits) +{ + return wait(event_flag_ea, mask, mask_mode, bits, 0); +} + +int mars_task_event_flag_try_wait(uint64_t event_flag_ea, + uint32_t mask, uint8_t mask_mode, + uint32_t *bits) +{ + return wait(event_flag_ea, mask, mask_mode, bits, 1); +} diff --git a/common/libspumars/ppu/task/lib/task_queue.c b/common/libspumars/ppu/task/lib/task_queue.c new file mode 100644 index 00000000..ecbbd867 --- /dev/null +++ b/common/libspumars/ppu/task/lib/task_queue.c @@ -0,0 +1,372 @@ +#include +#include +#include +#include +#include + +#include "mars/task_queue.h" + +#include "task_queue_internal_types.h" + +static inline uint64_t queue_count_ea(uint64_t queue_ea) +{ + return queue_ea + offsetof(struct mars_task_queue, count); +} + +int mars_task_queue_create(struct mars_context *mars, + uint64_t *queue_ea_ret, + uint32_t size, + uint32_t depth, + uint8_t direction) +{ + struct mars_task_queue *queue; + uint64_t queue_ea; + uint64_t buffer_ea; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + if (!queue_ea_ret) + return MARS_ERROR_NULL; + if (size & MARS_TASK_QUEUE_ENTRY_SIZE_MASK) + return MARS_ERROR_PARAMS; + if (size > MARS_TASK_QUEUE_ENTRY_SIZE_MAX) + return MARS_ERROR_PARAMS; + if (direction != MARS_TASK_QUEUE_HOST_TO_MPU && + direction != MARS_TASK_QUEUE_MPU_TO_HOST && + direction != MARS_TASK_QUEUE_MPU_TO_MPU) + return MARS_ERROR_PARAMS; + + /* allocate queue instance */ + queue_ea = mars_ea_memalign(MARS_TASK_QUEUE_ALIGN, + MARS_TASK_QUEUE_SIZE); + if (!queue_ea) + return MARS_ERROR_MEMORY; + + /* prepare work area for initialization */ + queue = mars_ea_work_area_get(queue_ea, + MARS_TASK_QUEUE_ALIGN, + MARS_TASK_QUEUE_SIZE); + + /* allocate queue buffer instance */ + buffer_ea = mars_ea_memalign(MARS_TASK_QUEUE_BUFFER_ALIGN, + size * depth); + if (!buffer_ea) { + mars_ea_free(queue_ea); + return MARS_ERROR_MEMORY; + } + + /* initialize queue instance */ + queue->mars_context_ea = mars_ptr_to_ea(mars); + queue->buffer_ea = buffer_ea; + queue->push_ea = buffer_ea; + queue->pop_ea = buffer_ea; + queue->size = size; + queue->depth = depth; + queue->direction = direction; + queue->count = 0; + queue->push_wait_count = 0; + queue->pop_wait_count = 0; + queue->push_wait_head = 0; + queue->pop_wait_head = 0; + + /* update queue on EA */ + mars_ea_put(queue_ea, queue, MARS_TASK_QUEUE_SIZE); + mars_ea_sync(); + + mars_mutex_reset(queue_ea); + + /* return queue instance pointer */ + *queue_ea_ret = queue_ea; + + return MARS_SUCCESS; +} + +int mars_task_queue_destroy(uint64_t queue_ea) +{ + struct mars_task_queue *queue; + + /* check function params */ + if (!queue_ea) + return MARS_ERROR_NULL; + + /* prepare work area */ + queue = mars_ea_work_area_get(queue_ea, + MARS_TASK_QUEUE_ALIGN, + MARS_TASK_QUEUE_SIZE); + + mars_ea_get(queue_ea, queue, MARS_TASK_QUEUE_SIZE); + + /* make sure no tasks in wait list */ + if (queue->push_wait_count || queue->pop_wait_count) + return MARS_ERROR_STATE; + + /* free shared memory */ + mars_ea_free(queue->buffer_ea); + mars_ea_free(queue_ea); + + return MARS_SUCCESS; +} + +int mars_task_queue_count(uint64_t queue_ea, uint32_t *count) +{ + /* check function params */ + if (!queue_ea) + return MARS_ERROR_NULL; + if (!count) + return MARS_ERROR_NULL; + if (queue_ea & MARS_TASK_QUEUE_ALIGN_MASK) + return MARS_ERROR_ALIGN; + + /* get queue from EA */ + mars_mutex_lock(queue_ea); + + /* return current items in queue */ + *count = mars_ea_get_uint32(queue_count_ea(queue_ea)); + + mars_mutex_unlock(queue_ea); + + return MARS_SUCCESS; +} + +int mars_task_queue_clear(uint64_t queue_ea) +{ + int i; + struct mars_context *mars; + struct mars_task_queue *queue; + + /* check function params */ + if (!queue_ea) + return MARS_ERROR_NULL; + if (queue_ea & MARS_TASK_QUEUE_ALIGN_MASK) + return MARS_ERROR_ALIGN; + + /* prepare work area */ + queue = mars_ea_work_area_get(queue_ea, + MARS_TASK_QUEUE_ALIGN, + MARS_TASK_QUEUE_SIZE); + + mars_mutex_lock_get(queue_ea, (struct mars_mutex *)queue); + + /* get mars context pointer */ + mars = mars_ea_to_ptr(queue->mars_context_ea); + + /* signal all waiting tasks that queue is ready for push */ + for (i = 0; i < queue->push_wait_count; i++) + mars_workload_queue_signal_send(mars, queue->push_wait_id[0]); + + /* flush all ids from push wait list */ + queue->push_wait_count = 0; + + /* clear the queue count and push/pop ea */ + queue->count = 0; + queue->push_ea = queue->buffer_ea; + queue->pop_ea = queue->buffer_ea; + + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)queue); + + return MARS_SUCCESS; +} + +static void push_update(struct mars_task_queue *queue) +{ + struct mars_context *mars = mars_ea_to_ptr(queue->mars_context_ea); + + /* signal waiting task that queue is ready for pop */ + if (queue->pop_wait_count) { + /* signal waiting task */ + mars_workload_queue_signal_send(mars, + queue->pop_wait_id[queue->pop_wait_head]); + + /* flush id from pop wait list */ + queue->pop_wait_count--; + queue->pop_wait_head++; + queue->pop_wait_head %= MARS_TASK_QUEUE_WAIT_MAX; + } + + /* increment queue count */ + queue->count++; + + /* increment queue push ea */ + queue->push_ea += queue->size; + + /* wrap to front of queue if necessary */ + if (queue->push_ea == queue->buffer_ea + (queue->size * queue->depth)) + queue->push_ea = queue->buffer_ea; +} + +static int test_not_full(uint32_t count, void *param) +{ + const struct mars_task_queue *queue = + (const struct mars_task_queue *)param; + + return (count != queue->depth) ? MARS_SUCCESS : -1; +} + +static int push(uint64_t queue_ea, const void *data, int try) +{ + int ret; + struct mars_task_queue *queue; + + /* check function params */ + if (!queue_ea) + return MARS_ERROR_NULL; + if (queue_ea & MARS_TASK_QUEUE_ALIGN_MASK) + return MARS_ERROR_ALIGN; + + /* prepare work area */ + queue = mars_ea_work_area_get(queue_ea, + MARS_TASK_QUEUE_ALIGN, + MARS_TASK_QUEUE_SIZE); + + mars_mutex_lock_get(queue_ea, (struct mars_mutex *)queue); + + /* check queue status */ + ret = MARS_ERROR_STATE; + if (queue->direction != MARS_TASK_QUEUE_HOST_TO_MPU) + goto end; + + /* queue is full so wait */ + while (queue->count == queue->depth) { + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)queue); + + /* only try so return busy */ + if (try) + return MARS_ERROR_BUSY; + + ret = mars_ea_cond_wait(queue_count_ea(queue_ea), + test_not_full, queue); + if (ret) + return ret; + + mars_mutex_lock_get(queue_ea, (struct mars_mutex *)queue); + } + + /* copy data into queue */ + mars_ea_put(queue->push_ea, data, queue->size); + + /* update queue data */ + push_update(queue); + + ret = MARS_SUCCESS; +end: + mars_mutex_unlock_put(queue_ea, (struct mars_mutex*)queue); + + return ret; +} + +int mars_task_queue_push(uint64_t queue_ea, const void *data) +{ + return push(queue_ea, data, 0); +} + +int mars_task_queue_try_push(uint64_t queue_ea, const void *data) +{ + return push(queue_ea, data, 1); +} + +static void pop_update(struct mars_task_queue *queue) +{ + struct mars_context *mars = mars_ea_to_ptr(queue->mars_context_ea); + + /* signal waiting task that queue is ready for push */ + if (queue->push_wait_count) { + /* signal waiting task */ + mars_workload_queue_signal_send(mars, + queue->push_wait_id[queue->push_wait_head]); + + /* flush id from push wait list */ + queue->push_wait_count--; + queue->push_wait_head++; + queue->push_wait_head %= MARS_TASK_QUEUE_WAIT_MAX; + } + + /* decrement queue count */ + queue->count--; + + /* increment queue pop ea */ + queue->pop_ea += queue->size; + + /* wrap to front of queue if necessary */ + if (queue->pop_ea == queue->buffer_ea + (queue->size * queue->depth)) + queue->pop_ea = queue->buffer_ea; +} + +static int test_not_empty(uint32_t count, void *param) +{ + (void)param; + + return count ? MARS_SUCCESS : -1; +} + +static int pop(uint64_t queue_ea, void *data, int peek, int try) +{ + int ret; + struct mars_task_queue *queue; + + /* check function params */ + if (!queue_ea) + return MARS_ERROR_NULL; + if (queue_ea & MARS_TASK_QUEUE_ALIGN_MASK) + return MARS_ERROR_ALIGN; + + /* prepare work area */ + queue = mars_ea_work_area_get(queue_ea, + MARS_TASK_QUEUE_ALIGN, + MARS_TASK_QUEUE_SIZE); + + mars_mutex_lock_get(queue_ea, (struct mars_mutex *)queue); + + ret = MARS_ERROR_STATE; + if (queue->direction != MARS_TASK_QUEUE_MPU_TO_HOST) + goto end; + + /* queue is empty so wait */ + while (!queue->count) { + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)queue); + + /* only try so return busy */ + if (try) + return MARS_ERROR_BUSY; + + ret = mars_ea_cond_wait(queue_count_ea(queue_ea), + test_not_empty, queue); + if (ret) + return ret; + + mars_mutex_lock_get(queue_ea, (struct mars_mutex *)queue); + } + + /* copy data from queue */ + mars_ea_get(queue->pop_ea, data, queue->size); + + /* update queue data only if this is not a peek operation */ + if (!peek) + pop_update(queue); + + ret = MARS_SUCCESS; +end: + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)queue); + + return ret; +} + +int mars_task_queue_pop(uint64_t queue_ea, void *data) +{ + return pop(queue_ea, data, 0, 0); +} + +int mars_task_queue_try_pop(uint64_t queue_ea, void *data) +{ + return pop(queue_ea, data, 0, 1); +} + +int mars_task_queue_peek(uint64_t queue_ea, void *data) +{ + return pop(queue_ea, data, 1, 0); +} + +int mars_task_queue_try_peek(uint64_t queue_ea, void *data) +{ + return pop(queue_ea, data, 1, 1); +} diff --git a/common/libspumars/ppu/task/lib/task_semaphore.c b/common/libspumars/ppu/task/lib/task_semaphore.c new file mode 100644 index 00000000..c5200937 --- /dev/null +++ b/common/libspumars/ppu/task/lib/task_semaphore.c @@ -0,0 +1,73 @@ +#include +#include +#include + +#include "mars/task_semaphore.h" + +#include "task_semaphore_internal_types.h" + +int mars_task_semaphore_create(struct mars_context *mars, + uint64_t *semaphore_ea_ret, + int32_t count) +{ + struct mars_task_semaphore *semaphore; + uint64_t semaphore_ea; + + /* check function params */ + if (!mars) + return MARS_ERROR_NULL; + if (!semaphore_ea_ret) + return MARS_ERROR_NULL; + + /* allocate semaphore instance */ + semaphore_ea = mars_ea_memalign(MARS_TASK_SEMAPHORE_ALIGN, + MARS_TASK_SEMAPHORE_SIZE); + if (!semaphore_ea) + return MARS_ERROR_MEMORY; + + /* prepare work area for initialization */ + semaphore = mars_ea_work_area_get(semaphore_ea, + MARS_TASK_SEMAPHORE_ALIGN, + MARS_TASK_SEMAPHORE_SIZE); + + /* initialize semaphore instance */ + semaphore->mars_context_ea = mars_ptr_to_ea(mars); + semaphore->count = count; + semaphore->wait_count = 0; + semaphore->wait_head = 0; + + /* update semaphore on EA */ + mars_ea_put(semaphore_ea, semaphore, MARS_TASK_SEMAPHORE_SIZE); + mars_ea_sync(); + + mars_mutex_reset(semaphore_ea); + + /* return semaphore instance */ + *semaphore_ea_ret = semaphore_ea; + + return MARS_SUCCESS; +} + +int mars_task_semaphore_destroy(uint64_t semaphore_ea) +{ + struct mars_task_semaphore *semaphore; + + /* check function params */ + if (!semaphore_ea) + return MARS_ERROR_NULL; + + /* prepare work area */ + semaphore = mars_ea_work_area_get(semaphore_ea, + MARS_TASK_SEMAPHORE_ALIGN, + MARS_TASK_SEMAPHORE_SIZE); + + mars_ea_get(semaphore_ea, semaphore, MARS_TASK_SEMAPHORE_SIZE); + + /* make sure no tasks in wait list */ + if (semaphore->wait_count) + return MARS_ERROR_STATE; + + mars_ea_free(semaphore_ea); + + return MARS_SUCCESS; +} diff --git a/common/libspumars/ppu/task/lib/task_signal.c b/common/libspumars/ppu/task/lib/task_signal.c new file mode 100644 index 00000000..69bf2d08 --- /dev/null +++ b/common/libspumars/ppu/task/lib/task_signal.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +#include "mars/task_signal.h" + +int mars_task_signal_send(struct mars_task_id *id) +{ + int ret; + struct mars_context *mars; + + /* check function params */ + if (!id) + return MARS_ERROR_NULL; + + /* get mars context pointer */ + mars = mars_ea_to_ptr(id->mars_context_ea); + if (!mars) + return MARS_ERROR_PARAMS; + + /* send signal to workload */ + ret = mars_workload_queue_signal_send(mars, id->workload_id); + if (ret != MARS_SUCCESS) + return ret; + + return MARS_SUCCESS; +} diff --git a/common/libspumars/spu/Makefile b/common/libspumars/spu/Makefile new file mode 100644 index 00000000..b55884ca --- /dev/null +++ b/common/libspumars/spu/Makefile @@ -0,0 +1,136 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(PSL1GHT)),) +$(error "Please set PSL1GHT in your environment. export PSL1GHT=") +endif + +include $(PSL1GHT)/spu_rules + +BUILD := build + +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PLATFORM)),) +#--------------------------------------------------------------------------------- +export BASEDIR := $(CURDIR) +export DEPF := $(BASEDIR)/deps +export LIBF := $(BASEDIR)/lib + +export LD := $(CC) + +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + +export LIBDIR := $(LIBF)/$(PLATFORM) +export DEPSDIR := $(DEPF)/$(PLATFORM) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +LIBRARY := $(LIBDIR)/libspumars +KERNELMODULE := $(LIBF)/mars_kernel +TASKMODULE := $(LIBF)/mars_task_module + +#--------------------------------------------------------------------------------- +INCLUDES := -I$(BASEDIR) \ + -I$(BASEDIR)/../include/common \ + -I$(BASEDIR)/../include/spu \ + -I$(BASEDIR)/../base/common \ + -I$(BASEDIR)/../task/common \ + -I$(BASEDIR)/../../../spu/include + +LIBS := -lsputhread +LIBPATHS := -L$(BASEDIR)/../../../spu/libsputhread/lib/spu + +CFLAGS := -Wall -ffunction-sections -fdata-sections $(MACHDEP) -DLIBSPUMARS_INTERNAL $(INCLUDES) +ASFLAGS := $(MACHDEP) -D__ASSEMBLY__ $(INCLUDES) + +#--------------------------------------------------------------------------------- +VPATH := $(BASEDIR) \ + $(BASEDIR)/base/lib \ + $(BASEDIR)/base/kernel \ + $(BASEDIR)/task/lib \ + $(BASEDIR)/task/module + +#--------------------------------------------------------------------------------- +OBJS_LIB := module.o \ + task.o task_barrier.o task_event_flag.o \ + task_queue.o task_semaphore.o task_signal.o + +OBJS_KERNEL := kernel_crt.o switch.o kernel.o dma.o mutex.o + +OBJS_MODULE := task_switch.o task_module.o + +all: spu + +#--------------------------------------------------------------------------------- +spu: +#--------------------------------------------------------------------------------- + @[ -d $(LIBF)/spu ] || mkdir -p $(LIBF)/spu + @[ -d $(DEPF)/spu ] || mkdir -p $(DEPF)/spu + @[ -d spu ] || mkdir -p spu + @$(MAKE) PLATFORM=spu lib -C spu -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +install-header: +#--------------------------------------------------------------------------------- + @[ -d $(PSL1GHT)/spu/include ] || mkdir -p $(PSL1GHT)/spu/include + @cp -frv $(CURDIR)/../include/common/mars $(PSL1GHT)/spu/include + @cp -frv $(CURDIR)/../include/spu/mars $(PSL1GHT)/spu/include + +#--------------------------------------------------------------------------------- +install: all install-header +#--------------------------------------------------------------------------------- + @[ -d $(PSL1GHT)/spu/lib ] || mkdir -p $(PSL1GHT)/spu/lib + @cp -frv $(CURDIR)/lib/spu/*.a $(PSL1GHT)/spu/lib + +#--------------------------------------------------------------------------------- +$(LIBRARY).a: CFLAGS += -Os --param max-inline-insns-single=20 +$(LIBRARY).a: $(OBJS_LIB) + +$(KERNELMODULE).elf: CFLAGS += -Os -mfixed-range=80-127 -funroll-loops -fschedule-insns +$(KERNELMODULE).elf: $(OBJS_KERNEL) + @echo linking ... $(notdir $@) + $(VERB) $(LD) $^ -nostdlib \ + -Wl,--defsym=__stack=0x29f0 \ + -Wl,-Map -Wl,$(CURDIR)/$(notdir $@).map -Wl,--cref \ + -Wl,--gc-sections \ + -Wl,--sort-common \ + -Wl,--sort-section -Wl,alignment \ + -Wl,-N \ + $(LIBPATHS) $(LIBS) -o $@ + +$(TASKMODULE).elf: CFLAGS += -Os -mfixed-range=80-127 -funroll-loops -fschedule-insns +$(TASKMODULE).elf: $(OBJS_MODULE) + @echo linking ... $(notdir $@) + $(VERB) $(LD) $^ -nodefaultlibs \ + -Wl,--defsym=__stack=0x3fe0 -Wl,-Ttext-segment=0x2a00 \ + -Wl,--entry,mars_module_entry -Wl,-u,mars_module_entry \ + -Wl,-Map -Wl,$(CURDIR)/$(notdir $@).map -Wl,--cref \ + -Wl,--gc-sections \ + -Wl,--sort-common \ + -Wl,--sort-section -Wl,alignment \ + $(LIBPATHS) $(LIBS) -L$(LIBDIR) -lspumars -o $@ + +#--------------------------------------------------------------------------------- + +.PHONY: lib spu install + +#--------------------------------------------------------------------------------- +lib: $(LIBRARY).a $(KERNELMODULE).elf $(TASKMODULE).elf +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +clean: +#--------------------------------------------------------------------------------- + @echo clean ... + @rm -rf spu + @rm -rf $(DEPF) + @rm -rf $(LIBF) + +-include $(DEPSDIR)/*.d diff --git a/common/libspumars/spu/base/kernel/dma.c b/common/libspumars/spu/base/kernel/dma.c new file mode 100644 index 00000000..72e72b6e --- /dev/null +++ b/common/libspumars/spu/base/kernel/dma.c @@ -0,0 +1,59 @@ +#include +#include + +#include + +#include "kernel_internal_types.h" + +#define MARS_DMA_TAG_MAX 31 +#define MARS_DMA_SIZE_MAX 16384 +#define MARS_DMA_ALIGN_MASK 0xf + +static int dma_large(void *ls, uint64_t ea, uint32_t size, uint32_t tag, unsigned int dma_cmd) +{ + unsigned int cmd = MFC_CMD_WORD(0, 0, dma_cmd); + + if (tag > MARS_DMA_TAG_MAX) + return MARS_ERROR_PARAMS; + if (((uintptr_t)ls & MARS_DMA_ALIGN_MASK) || + ((uintptr_t)ea & MARS_DMA_ALIGN_MASK)) + return MARS_ERROR_ALIGN; + + while (size) { + unsigned int block_size; + + block_size = (size < MARS_DMA_SIZE_MAX) ? + size : MARS_DMA_SIZE_MAX; + + spu_mfcdma64((volatile void *)ls, mfc_ea2h(ea), mfc_ea2l(ea), + block_size, tag, cmd); + + ls += block_size; + ea += block_size; + size -= block_size; + } + + return MARS_SUCCESS; +} + +int dma_get(void *ls, uint64_t ea, uint32_t size, uint32_t tag) +{ + return dma_large(ls, ea, size, tag, MFC_GET_CMD); +} + +int dma_put(const void *ls, uint64_t ea, uint32_t size, uint32_t tag) +{ + return dma_large((void *)ls, ea, size, tag, MFC_PUT_CMD); +} + +int dma_wait(uint32_t tag) +{ + if (tag > MARS_DMA_TAG_MAX) + return MARS_ERROR_PARAMS; + + mfc_write_tag_mask(1 << tag); + mfc_write_tag_update_all(); + mfc_read_tag_status(); + + return MARS_SUCCESS; +} diff --git a/common/libspumars/spu/base/kernel/kernel.c b/common/libspumars/spu/base/kernel/kernel.c new file mode 100644 index 00000000..f4149a12 --- /dev/null +++ b/common/libspumars/spu/base/kernel/kernel.c @@ -0,0 +1,1065 @@ +#include +#include +#include + +#include +#include + +#include + +#include + +#include "callback_internal_types.h" +#include "kernel_internal_types.h" +#include "workload_internal_types.h" + +#define MARS_KERNEL_STATUS_EXIT 0x1 +#define MARS_KERNEL_STATUS_IDLE 0x2 +#define MARS_KERNEL_STATUS_BUSY 0x4 +#define MARS_KERNEL_STATUS_RETRY 0x8 + +#define MARS_KERNEL_UPDATE_HEADER_BITS_ACCESS 0x0 +#define MARS_KERNEL_UPDATE_HEADER_BITS_STATE 0x1 +#define MARS_KERNEL_UPDATE_HEADER_BITS_COUNTER 0x2 +#define MARS_KERNEL_UPDATE_HEADER_BITS_COUNTER_RESET 0x4 + +/* kernel */ +union mars_kernel_buffer kernel_buffer; +static struct mars_kernel_params kernel_params; +static uint64_t kernel_params_ea; + +/* workload queue */ +static struct mars_workload_queue_header queue_header; +static struct mars_workload_queue_block queue_block; + +/* workload */ +static struct mars_workload_context workload_buf; +static struct mars_workload_context workload; +static uint16_t workload_id; +static uint64_t workload_ea; +static uint8_t workload_state; +static uint8_t workload_is_cached; + +/* workload module */ +static struct mars_workload_module cached_workload_module; +static struct mars_workload_module *workload_module; +static uint8_t workload_module_is_cached; + +/* workload module entry */ +typedef void (*module_entry)(const struct mars_kernel_syscalls *kernel_syscalls); + +/* defined in switch.S */ +extern void workload_run(void); +extern void workload_exit(uint8_t state); + +/* called by switch.S */ +void __workload_run(void); +void __workload_exit(uint8_t state); + +static void kernel_memcpy(void *dst, const void *src, int size) +{ + __vector const int *vptr_src = (__vector const int *)src; + __vector int *vptr_dst = (__vector int *)dst; + __vector int *vptr_end = (__vector int *)(dst + size); + + while (__builtin_expect(vptr_dst < vptr_end, 1)) + *vptr_dst++ = *vptr_src++; +} + +static int kernel_memcmp(const void *s1, const void *s2, int size) +{ + unsigned int offset1, offset2; + __vector int n_v; + __vector unsigned char shuffle1, shuffle2; + __vector const unsigned char *vptr_1 = (__vector const unsigned char*)s1; + __vector const unsigned char *vptr_2 = (__vector const unsigned char*)s2; + __vector unsigned int neq_v, tmp1_v, shift_n_v, mask_v, gt_v, lt_v; + __vector unsigned char data1A, data1B, data1, data2A, data2B, data2; + + n_v = spu_promote(size, 0); + data1 = data2 = spu_splats((unsigned char)0); + + offset1 = (unsigned int)(vptr_1)&15; + offset2 = (unsigned int)(vptr_2)&15; + + shuffle1 = (__vector unsigned char)spu_add((__vector unsigned int)spu_splats((unsigned char)offset1), + ((__vector unsigned int) {0x00010203, 0x04050607, 0x08090A0B, 0x0C0D0E0F})); + shuffle2 = (__vector unsigned char)spu_add((__vector unsigned int)spu_splats((unsigned char)offset2), + ((__vector unsigned int) {0x00010203, 0x04050607, 0x08090A0B, 0x0C0D0E0F})); + + data1A = *vptr_1++; + data2A = *vptr_2++; + do { + data1B = *vptr_1++; + data2B = *vptr_2++; + + data1 = spu_shuffle(data1A, data1B, shuffle1); + data2 = spu_shuffle(data2A, data2B, shuffle2); + + data1A = data1B; + data2A = data2B; + + neq_v = spu_gather(spu_xor(spu_cmpeq(data1, data2), -1)); + n_v = spu_add(n_v, -16); + tmp1_v = spu_cmpeq(neq_v, 0); + tmp1_v = spu_and(tmp1_v, spu_cmpgt(n_v, 0)); + } while(spu_extract(tmp1_v, 0)); + + mask_v = spu_splats((unsigned int)0xFFFF); + shift_n_v = spu_andc((__vector unsigned int)spu_sub(0, n_v), spu_cmpgt(n_v, -1)); + mask_v = spu_and(spu_sl(mask_v, spu_extract(shift_n_v, 0)), mask_v); + + gt_v = spu_gather(spu_cmpgt(data1, data2)); + lt_v = spu_gather(spu_cmpgt(data2, data1)); + gt_v = spu_sub(-1, spu_sl(spu_cmpgt(gt_v, lt_v), 1)); + + mask_v = spu_cmpeq(spu_and(neq_v, mask_v), 0); + return (spu_extract(spu_andc(gt_v, mask_v), 0)); +} + +static uint32_t get_ticks() +{ + return kernel_params.kernel_ticks.offset - spu_read_decrementer(); +} + +static uint64_t get_mars_context_ea(void) +{ + return kernel_params.mars_context_ea; +} + +static uint16_t get_kernel_id(void) +{ + return kernel_params.kernel_id; +} + +static uint16_t get_workload_id(void) +{ + return workload_id; +} + +static struct mars_workload_context *get_workload(void) +{ + return &workload; +} + + +static uint64_t get_workload_ea(uint16_t id) +{ + int context_index = id - (id / MARS_WORKLOAD_PER_BLOCK) - 1; + + return queue_header.context_ea + + context_index * MARS_WORKLOAD_CONTEXT_SIZE; + +} + +static struct mars_workload_context *get_workload_by_id(uint16_t id) +{ + static struct mars_workload_context ret_workload; + + /* id is caller workload's id so return current workload */ + if (id == workload_id) + return &workload; + + /* get the workload context from workload queue */ + dma_get(&ret_workload, get_workload_ea(id), MARS_WORKLOAD_CONTEXT_SIZE, MARS_KERNEL_DMA_TAG); + dma_wait(MARS_KERNEL_DMA_TAG); + + return &ret_workload; +} + +static uint64_t get_block_ea(int block) +{ + return queue_header.queue_ea + + offsetof(struct mars_workload_queue, block) + + MARS_WORKLOAD_QUEUE_BLOCK_SIZE * block; +} + +static uint64_t get_block_bits(uint16_t id) +{ + int block; + int index; + uint64_t block_ea; + uint64_t block_bits; + + /* check function params */ + if (id > MARS_WORKLOAD_ID_MAX || !(id % MARS_WORKLOAD_PER_BLOCK)) + return 0; + + /* calculate block/index from id */ + block = id / MARS_WORKLOAD_PER_BLOCK; + index = id % MARS_WORKLOAD_PER_BLOCK; + + /* calculate block ea */ + block_ea = get_block_ea(block); + + /* lock the queue block */ + mutex_lock_get(block_ea, (struct mars_mutex *)&queue_block); + + block_bits = queue_block.bits[index]; + + /* unlock the queue block */ + mutex_unlock_put(block_ea, (struct mars_mutex *)&queue_block); + + return block_bits; +} + +static void update_header_bits(int mode, int block) +{ + int index; + uint16_t *block_bits = &queue_header.bits[block]; + uint8_t block_ready = MARS_WORKLOAD_BLOCK_READY_OFF; + uint8_t block_waiting = MARS_WORKLOAD_BLOCK_WAITING_OFF; + uint8_t block_priority = MARS_WORKLOAD_BLOCK_PRIORITY_MIN; + uint8_t block_counter = MARS_WORKLOAD_BLOCK_COUNTER_MIN; + + if (mode & MARS_KERNEL_UPDATE_HEADER_BITS_STATE) { + /* search through currently locked queue block workload bits */ + for (index = 1; index < MARS_WORKLOAD_PER_BLOCK; index++) { + uint64_t *bits = &queue_block.bits[index]; + uint8_t state = MARS_BITS_GET(bits, WORKLOAD_STATE); + + /* workload state is ready so check priority */ + if (state & MARS_WORKLOAD_STATE_READY) { + uint8_t priority = + MARS_BITS_GET(bits, WORKLOAD_PRIORITY); + + /* set block priority if higher then current */ + if (priority > block_priority) + block_priority = priority; + + /* set block ready bit in header bits */ + block_ready = MARS_WORKLOAD_BLOCK_READY_ON; + } else if (state & MARS_WORKLOAD_STATE_WAITING) { + /* set block waiting bit in header bits */ + block_waiting = MARS_WORKLOAD_BLOCK_WAITING_ON; + } + } + } + + /* lock the queue header */ + mutex_lock_get(kernel_params.workload_queue_ea, + (struct mars_mutex *)&queue_header); + + /* update header bits state */ + if (mode & MARS_KERNEL_UPDATE_HEADER_BITS_STATE) { + /* set the info bits inside queue header for this queue block */ + MARS_BITS_SET(block_bits, BLOCK_READY, block_ready); + MARS_BITS_SET(block_bits, BLOCK_WAITING, block_waiting); + MARS_BITS_SET(block_bits, BLOCK_PRIORITY, block_priority); + /* update header bits counter */ + } else if (mode) { + /* reset is not specified so increment current block counter */ + if (mode & MARS_KERNEL_UPDATE_HEADER_BITS_COUNTER) { + block_counter = + MARS_BITS_GET(block_bits, BLOCK_COUNTER); + if (block_counter < MARS_WORKLOAD_BLOCK_COUNTER_MAX) + block_counter++; + } + + /* set the block counter bits */ + MARS_BITS_SET(block_bits, BLOCK_COUNTER, block_counter); + } + + /* increment the header access value */ + queue_header.access++; + + /* unlock the queue header */ + mutex_unlock_put(kernel_params.workload_queue_ea, + (struct mars_mutex *)&queue_header); +} + +static int change_bits(uint16_t id, + int (*check_bits)(uint64_t bits, uint64_t param), + uint64_t check_bits_param, + uint64_t (*set_bits)(uint64_t bits, uint64_t param), + uint64_t set_bits_param, + void (*callback)(uint16_t id)) +{ + int block; + int index; + uint64_t block_ea; + uint64_t bits; + + /* check function params */ + if (id > MARS_WORKLOAD_ID_MAX || !(id % MARS_WORKLOAD_PER_BLOCK)) + return MARS_ERROR_PARAMS; + + /* calculate block/index from id */ + block = id / MARS_WORKLOAD_PER_BLOCK; + index = id % MARS_WORKLOAD_PER_BLOCK; + + /* calculate block ea */ + block_ea = get_block_ea(block); + + /* lock the queue block */ + mutex_lock_get(block_ea, (struct mars_mutex *)&queue_block); + + /* check for valid state */ + if (check_bits && + !(*check_bits)(queue_block.bits[index], check_bits_param)) { + mutex_unlock_put(block_ea, (struct mars_mutex *)&queue_block); + return MARS_ERROR_STATE; + } + + /* reset workload queue bits and set state to new state */ + bits = (*set_bits)(queue_block.bits[index], set_bits_param); + + /* store new bits into queue block */ + queue_block.bits[index] = bits; + + /* if callback requested call it */ + if (callback) + (*callback)(id); + + /* unlock the queue block */ + mutex_unlock_put(block_ea, (struct mars_mutex *)&queue_block); + + return MARS_SUCCESS; +} + +static int check_state_bits(uint64_t bits, uint64_t state) +{ + return (MARS_BITS_GET(&bits, WORKLOAD_STATE) == state); +} + +static int check_state_bits_not(uint64_t bits, uint64_t state) +{ + return !check_state_bits(bits, state); +} + +static uint64_t set_state_bits(uint64_t bits, uint64_t state) +{ + MARS_BITS_SET(&bits, WORKLOAD_STATE, state); + + return bits; +} + +static int change_state(uint16_t id, + unsigned int old_state, + unsigned int new_state, + void (*callback)(uint16_t id)) +{ + return change_bits(id, + check_state_bits, old_state, + set_state_bits, new_state, + callback); +} + +static int workload_query(uint16_t id, int query) +{ + uint64_t bits = get_block_bits(id); + + switch (query) { + case MARS_WORKLOAD_QUERY_IS_MODULE_CACHED: + return workload_module_is_cached; + case MARS_WORKLOAD_QUERY_IS_CONTEXT_CACHED: + return (id == workload_id && workload_is_cached); + case MARS_WORKLOAD_QUERY_IS_INITIALIZED: + return (MARS_BITS_GET(&bits, WORKLOAD_STATE)); + case MARS_WORKLOAD_QUERY_IS_READY: + return (MARS_BITS_GET(&bits, WORKLOAD_STATE) & + MARS_WORKLOAD_STATE_READY); + case MARS_WORKLOAD_QUERY_IS_WAITING: + return (MARS_BITS_GET(&bits, WORKLOAD_STATE) & + MARS_WORKLOAD_STATE_WAITING); + case MARS_WORKLOAD_QUERY_IS_RUNNING: + return (MARS_BITS_GET(&bits, WORKLOAD_STATE) & + MARS_WORKLOAD_STATE_RUNNING); + case MARS_WORKLOAD_QUERY_IS_FINISHED: + return (MARS_BITS_GET(&bits, WORKLOAD_STATE) & + MARS_WORKLOAD_STATE_FINISHED); + case MARS_WORKLOAD_QUERY_IS_SIGNAL_SET: + return (MARS_BITS_GET(&bits, WORKLOAD_SIGNAL) & + MARS_WORKLOAD_SIGNAL_ON); + } + + return 0; +} + +static uint64_t set_wait_id_bits(uint64_t bits, uint64_t id) +{ + MARS_BITS_SET(&bits, WORKLOAD_WAIT_ID, id); + + return bits; +} + +static int workload_wait_set(uint16_t id) +{ + return change_bits(workload_id, + check_state_bits_not, MARS_WORKLOAD_STATE_NONE, + set_wait_id_bits, id, + NULL); +} + +static int workload_wait_reset(void) +{ + return change_bits(workload_id, + NULL, 0, + set_wait_id_bits, MARS_WORKLOAD_ID_NONE, + NULL); +} + +static uint64_t set_signal_bits(uint64_t bits, uint64_t signal) +{ + MARS_BITS_SET(&bits, WORKLOAD_SIGNAL, signal); + + /* update queue header bits access in case kernel is idle */ + update_header_bits(MARS_KERNEL_UPDATE_HEADER_BITS_ACCESS, 0); + + return bits; +} + +static int workload_signal_set(uint16_t id) +{ + return change_bits(id, + check_state_bits_not, MARS_WORKLOAD_STATE_NONE, + set_signal_bits, MARS_WORKLOAD_SIGNAL_ON, + NULL); +} + +static int workload_signal_reset(void) +{ + return change_bits(workload_id, + NULL, 0, + set_signal_bits, MARS_WORKLOAD_SIGNAL_OFF, + NULL); +} + +static void begin_callback(uint16_t id) +{ + /* get the workload context from workload queue */ + dma_get(&workload_buf, get_workload_ea(id), + MARS_WORKLOAD_CONTEXT_SIZE, MARS_KERNEL_DMA_TAG); + dma_wait(MARS_KERNEL_DMA_TAG); + + /* update queue header bits */ + update_header_bits(MARS_KERNEL_UPDATE_HEADER_BITS_STATE, + id / MARS_WORKLOAD_PER_BLOCK); +} + +static void end_callback(uint16_t id) +{ + /* put the workload context into workload queue */ + dma_put((void *)&workload_buf, get_workload_ea(id), + MARS_WORKLOAD_CONTEXT_SIZE, MARS_KERNEL_DMA_TAG); + dma_wait(MARS_KERNEL_DMA_TAG); + + /* update queue header bits */ + update_header_bits(MARS_KERNEL_UPDATE_HEADER_BITS_STATE, + id / MARS_WORKLOAD_PER_BLOCK); +} + +static uint64_t set_schedule_bits(uint64_t bits, uint64_t priority) +{ + /* set the info bits inside queue block for this workload */ + MARS_BITS_SET(&bits, WORKLOAD_STATE, MARS_WORKLOAD_STATE_SCHEDULING); + MARS_BITS_SET(&bits, WORKLOAD_PRIORITY, priority); + MARS_BITS_SET(&bits, WORKLOAD_COUNTER, MARS_WORKLOAD_COUNTER_MAX); + MARS_BITS_SET(&bits, WORKLOAD_SIGNAL, MARS_WORKLOAD_SIGNAL_OFF); + MARS_BITS_SET(&bits, WORKLOAD_WAIT_ID, MARS_WORKLOAD_ID_NONE); + + return bits; +} + +static int workload_schedule_begin(uint16_t id, uint8_t priority, + struct mars_workload_context **workload) +{ + int ret; + + /* change bits necessary to begin scheduling */ + ret = change_bits(id, + check_state_bits, MARS_WORKLOAD_STATE_FINISHED, + set_schedule_bits, priority, + begin_callback); + if (ret != MARS_SUCCESS) + return ret; + + /* if requested set workload context pointer to return */ + if (workload) + *workload = &workload_buf; + + return MARS_SUCCESS; +} + +static int workload_schedule_end(uint16_t id, int cancel) +{ + return change_state(id, + MARS_WORKLOAD_STATE_SCHEDULING, + cancel ? MARS_WORKLOAD_STATE_FINISHED : + MARS_WORKLOAD_STATE_READY, + end_callback); +} + +static int unscheduling_state_bits(uint64_t bits, uint64_t param) +{ + (void)param; + + uint8_t state = MARS_BITS_GET(&bits, WORKLOAD_STATE); + + return (state & (MARS_WORKLOAD_STATE_READY | + MARS_WORKLOAD_STATE_RUNNING | + MARS_WORKLOAD_STATE_WAITING)); +} + +static int workload_unschedule_begin(uint16_t id, + struct mars_workload_context **workload) +{ + int ret; + + /* change bits necessary to begin unscheduling */ + ret = change_bits(id, + unscheduling_state_bits, 0, + set_state_bits, MARS_WORKLOAD_STATE_UNSCHEDULING, + begin_callback); + if (ret != MARS_SUCCESS) + return ret; + + /* if requested set workload context pointer to return */ + if (workload) + *workload = &workload_buf; + + return MARS_SUCCESS; +} + +static void notify_host_bits(uint64_t block_ea, int index); + +static int workload_unschedule_end(uint16_t id) +{ + int ret; + int block; + int index; + uint64_t block_ea; + + ret = change_state(id, + MARS_WORKLOAD_STATE_UNSCHEDULING, + MARS_WORKLOAD_STATE_FINISHED, + end_callback); + if (ret != MARS_SUCCESS) + return ret; + + /* calculate block/index from id */ + block = id / MARS_WORKLOAD_PER_BLOCK; + index = id % MARS_WORKLOAD_PER_BLOCK; + + /* calculate block ea */ + block_ea = get_block_ea(block); + + /* notify host */ + notify_host_bits(block_ea, index); + + return MARS_SUCCESS; +} + +static void host_signal_send(uint64_t watch_point_ea) +{ + spu_thread_send_event(MARS_KERNEL_SPU_EVENT_PORT, (uint32_t)(watch_point_ea >> 32), (watch_point_ea & 0xffffffff)); +} + +static int host_callback_queue_push(void) +{ + uint64_t queue_ea = kernel_params.callback_queue_ea; + struct mars_callback_queue *queue = &kernel_buffer.callback_queue; + + /* lock the queue */ + mutex_lock_get(queue_ea, (struct mars_mutex *)queue); + + /* check if queue is full */ + if (queue->count == MARS_CALLBACK_QUEUE_MAX) { + mutex_unlock_put(queue_ea, (struct mars_mutex *)queue); + return MARS_ERROR_LIMIT; + } + + /* set the push flag to signal host of new item */ + queue->flag = MARS_CALLBACK_QUEUE_FLAG_PUSH; + + /* put workload id at tail of queue */ + queue->workload_id[queue->tail] = workload_id; + + /* increment count */ + queue->count++; + + /* increment tail */ + queue->tail++; + + /* wrap tail to front of queue if necessary */ + if (queue->tail == MARS_CALLBACK_QUEUE_MAX) + queue->tail = 0; + + /* unlock the queue */ + mutex_unlock_put(queue_ea, (struct mars_mutex *)queue); + + return MARS_SUCCESS; +} + +static int host_callback_set(uint64_t callback_ea, + const struct mars_callback_args *in) +{ + int ret; + struct mars_workload_callback *callback = + (struct mars_workload_callback *) + ((void *)&workload + MARS_WORKLOAD_MODULE_SIZE); + + /* set the callback function pointer into workload callback area */ + callback->callback_ea = callback_ea; + + /* input args specified so copy into workload data area */ + if (in) + kernel_memcpy(&callback->callback_args, in, + MARS_CALLBACK_ARGS_SIZE); + + /* push workload id to callback queue */ + ret = host_callback_queue_push(); + if (ret != MARS_SUCCESS) + return ret; + + /* notify host to process callback */ + host_signal_send(kernel_params.callback_queue_ea + + offsetof(struct mars_callback_queue, flag)); + + return MARS_SUCCESS; +} + +static int host_callback_reset(struct mars_callback_args *out) +{ + struct mars_workload_callback *callback = + (struct mars_workload_callback *) + ((void *)&workload + MARS_WORKLOAD_MODULE_SIZE); + + /* output requested so dma from workload data area */ + if (out) + kernel_memcpy(out, &callback->callback_args, + MARS_CALLBACK_ARGS_SIZE); + + return callback->callback_ret; +} + +static struct mars_kernel_syscalls kernel_syscalls = +{ + get_ticks, + get_mars_context_ea, + get_kernel_id, + get_workload_id, + get_workload, + get_workload_by_id, + workload_exit, + workload_query, + workload_wait_set, + workload_wait_reset, + workload_signal_set, + workload_signal_reset, + workload_schedule_begin, + workload_schedule_end, + workload_unschedule_begin, + workload_unschedule_end, + host_signal_send, + host_callback_set, + host_callback_reset, + mutex_lock_get, + mutex_unlock_put, + dma_get, + dma_put, + dma_wait +}; + +void __workload_run() +{ + /* call module entry function */ + ((module_entry)workload_module->entry)(&kernel_syscalls); +} + +void __workload_exit(uint8_t state) +{ + workload_state = state; +} + +static int search_block(int block, int ready) +{ + int i; + int index = -1; + uint8_t max_priority = 0; + uint16_t max_counter = 0; + uint64_t block_ea = get_block_ea(block); + + /* lock the queue block */ + mutex_lock_get(block_ea, (struct mars_mutex *)&queue_block); + + /* search through all workloads in block */ + for (i = 1; i < MARS_WORKLOAD_PER_BLOCK; i++) { + uint64_t *bits = &queue_block.bits[i]; + uint8_t state = MARS_BITS_GET(bits, WORKLOAD_STATE); + uint8_t priority = MARS_BITS_GET(bits, WORKLOAD_PRIORITY); + uint8_t signal = MARS_BITS_GET(bits, WORKLOAD_SIGNAL); + uint16_t wait_id = MARS_BITS_GET(bits, WORKLOAD_WAIT_ID); + uint16_t counter = MARS_BITS_GET(bits, WORKLOAD_COUNTER); + + /* found workload in ready state */ + if (ready && (state & MARS_WORKLOAD_STATE_READY)) { + /* compare priority and counter with previous ones */ + if (index < 0 || priority > max_priority || + (priority == max_priority && counter > max_counter)) { + index = i; + max_counter = counter; + max_priority = priority; + } + + /* increment wait counter without overflowing */ + if (counter < MARS_WORKLOAD_COUNTER_MAX) + MARS_BITS_SET(bits, WORKLOAD_COUNTER, + counter + 1); + /* found workload in waiting state */ + } else if (!ready && (state & MARS_WORKLOAD_STATE_WAITING)) { + /* waiting for workload to finish so check status */ + if (wait_id != MARS_WORKLOAD_ID_NONE) { + struct mars_workload_queue_block *wait_block; + uint8_t wait_state; + + int bl = wait_id / MARS_WORKLOAD_PER_BLOCK; + int id = wait_id % MARS_WORKLOAD_PER_BLOCK; + + /* check if workload id is in the same block */ + if (block != bl) { + /* set pointer to check buffer block */ + wait_block = + &kernel_buffer.workload_queue_block; + + /* fetch the necessary block */ + dma_get(wait_block, get_block_ea(bl), + MARS_WORKLOAD_QUEUE_BLOCK_SIZE, + MARS_KERNEL_DMA_TAG); + dma_wait(MARS_KERNEL_DMA_TAG); + } else { + /* set pointer to check current block */ + wait_block = &queue_block; + } + + /* get state of workload its waiting for */ + wait_state = + MARS_BITS_GET(&wait_block->bits[id], + WORKLOAD_STATE); + + /* check if workload is finished and reset */ + if (wait_state & MARS_WORKLOAD_STATE_FINISHED) { + MARS_BITS_SET(bits, WORKLOAD_WAIT_ID, + MARS_WORKLOAD_ID_NONE); + MARS_BITS_SET(bits, WORKLOAD_STATE, + MARS_WORKLOAD_STATE_READY); + + index = i; + } + /* waiting for signal so check signal bit and reset */ + } else if (signal & MARS_WORKLOAD_SIGNAL_ON) { + MARS_BITS_SET(bits, WORKLOAD_SIGNAL, + MARS_WORKLOAD_SIGNAL_OFF); + MARS_BITS_SET(bits, WORKLOAD_STATE, + MARS_WORKLOAD_STATE_READY); + + index = i; + } + } + } + + /* index is set so reserve the runnable workload */ + if (index >= 0) { + if (ready) { + /* update the current state of the workload */ + MARS_BITS_SET(&queue_block.bits[index], + WORKLOAD_STATE, + MARS_WORKLOAD_STATE_RUNNING); + + /* reset the counter for reserved workload */ + MARS_BITS_SET(&queue_block.bits[index], + WORKLOAD_COUNTER, + MARS_WORKLOAD_COUNTER_MIN); + + /* check if kernel ran this workload most recently */ + workload_is_cached = + (workload_id == + MARS_WORKLOAD_PER_BLOCK * block + index) && + (kernel_params.kernel_id == + MARS_BITS_GET(&queue_block.bits[index], + WORKLOAD_KERNEL_ID)); + + /* set the kernel id of this kernel into block bits */ + MARS_BITS_SET(&queue_block.bits[index], + WORKLOAD_KERNEL_ID, + kernel_params.kernel_id); + + /* reset block counter */ + update_header_bits( + MARS_KERNEL_UPDATE_HEADER_BITS_COUNTER_RESET, + block); + } + + /* update queue header bits */ + update_header_bits(MARS_KERNEL_UPDATE_HEADER_BITS_STATE, block); + } + + /* unlock the queue block */ + mutex_unlock_put(block_ea, (struct mars_mutex *)&queue_block); + + /* returns -1 if no runnable workload found */ + return index; +} + +static void notify_host_bits(uint64_t block_ea, int index) +{ + uint64_t bits_ea = + block_ea + + offsetof(struct mars_workload_queue_block, bits) + + sizeof(uint64_t) * index; + + host_signal_send(bits_ea); +} + +static int workload_reserve(void) +{ + int i; + int block = -1; + int index; + int retry = 0; + uint8_t max_block_priority = 0; + uint16_t max_block_counter = 0; + + /* get the workload queue header */ + dma_get(&queue_header, kernel_params.workload_queue_ea, + MARS_WORKLOAD_QUEUE_HEADER_SIZE, MARS_KERNEL_DMA_TAG); + dma_wait(MARS_KERNEL_DMA_TAG); + + /* return exit status if exit flag is set from host */ + if (queue_header.flag & MARS_WORKLOAD_QUEUE_FLAG_EXIT) + return MARS_KERNEL_STATUS_EXIT; + + /* search workload queue header for highest priority ready block that + * has waited the longest in ready state */ + for (i = 0; i < MARS_WORKLOAD_NUM_BLOCKS; i++) { + uint16_t *bits = &queue_header.bits[i]; + uint8_t block_ready = MARS_BITS_GET(bits, BLOCK_READY); + uint8_t block_waiting = MARS_BITS_GET(bits, BLOCK_WAITING); + uint8_t block_priority = MARS_BITS_GET(bits, BLOCK_PRIORITY); + uint16_t block_counter = MARS_BITS_GET(bits, BLOCK_COUNTER); + + /* block is ready so check scheduling conditions */ + if (block_ready) { + if (block < 0 || block_priority > max_block_priority || + (block_priority == max_block_priority && + block_counter > max_block_counter)) { + block = i; + max_block_priority = block_priority; + max_block_counter = block_counter; + } + + /* increment block counter */ + update_header_bits( + MARS_KERNEL_UPDATE_HEADER_BITS_COUNTER, i); + } + + /* block is waiting so check block */ + if (block_waiting) + retry |= (search_block(i, 0) >= 0); + } + + /* no runnable workload found */ + if (block < 0) + return retry ? + MARS_KERNEL_STATUS_RETRY : MARS_KERNEL_STATUS_IDLE; + + /* search block for workload index to run */ + index = search_block(block, 1); + if (index < 0) + return MARS_KERNEL_STATUS_RETRY; + + /* set global workload info based on workload_id */ + workload_id = MARS_WORKLOAD_PER_BLOCK * block + index; + workload_ea = get_workload_ea(workload_id); + workload_module = (struct mars_workload_module *)&workload; + + /* get the workload context code from workload queue */ + dma_get(&workload, workload_ea, + MARS_WORKLOAD_CONTEXT_SIZE, MARS_KERNEL_DMA_TAG); + dma_wait(MARS_KERNEL_DMA_TAG); + + return MARS_KERNEL_STATUS_BUSY; +} + +static void workload_release(void) +{ + int unscheduled; + int block = workload_id / MARS_WORKLOAD_PER_BLOCK; + int index = workload_id % MARS_WORKLOAD_PER_BLOCK; + uint64_t block_ea = get_block_ea(block); + uint8_t state; + + /* lock the queue block */ + mutex_lock_get(block_ea, (struct mars_mutex *)&queue_block); + + /* get current state */ + state = MARS_BITS_GET(&queue_block.bits[index], WORKLOAD_STATE); + + /* if state is unscheduling or finished, it (was/will be) aborted */ + unscheduled = state & (MARS_WORKLOAD_STATE_UNSCHEDULING | + MARS_WORKLOAD_STATE_FINISHED); + + if (!unscheduled) { + /* put the workload context into workload queue */ + dma_put(&workload, workload_ea, + MARS_WORKLOAD_CONTEXT_SIZE, MARS_KERNEL_DMA_TAG); + dma_wait(MARS_KERNEL_DMA_TAG); + + /* update current workload state in workload queue block */ + MARS_BITS_SET(&queue_block.bits[index], WORKLOAD_STATE, + workload_state); + + /* update queue header bits */ + update_header_bits(MARS_KERNEL_UPDATE_HEADER_BITS_STATE, block); + } + + /* unlock the queue block */ + mutex_unlock_put(block_ea, (struct mars_mutex *)&queue_block); + + /* workload state is finished so notify host */ + if (!unscheduled && (workload_state & MARS_WORKLOAD_STATE_FINISHED)) + notify_host_bits(block_ea, index); +} + +static void workload_module_load(void) +{ + __vector unsigned char *bss_ptr, *bss_end; + + workload_module_is_cached = + !(kernel_memcmp(&cached_workload_module, workload_module, + MARS_WORKLOAD_MODULE_SIZE)); + + /* only reload the readonly text segment if different from cached */ + if (!workload_module_is_cached) { + /* store the current cached workload module ea */ + kernel_memcpy(&cached_workload_module, workload_module, + MARS_WORKLOAD_MODULE_SIZE); + + /* load the text into mpu storage from host storage */ + dma_get((void *)workload_module->text_vaddr, + workload_module->text_ea, + workload_module->text_size, + MARS_KERNEL_DMA_TAG); + } + + /* load the read-write data segment */ + dma_get((void *)workload_module->data_vaddr, + workload_module->data_ea, + workload_module->data_size, + MARS_KERNEL_DMA_TAG); + + /* 0 the bss segment */ + bss_ptr = (__vector unsigned char *)(workload_module->data_vaddr + + workload_module->data_size); + bss_end = (__vector unsigned char *)((void *)bss_ptr + + workload_module->bss_size); + + while (__builtin_expect(bss_ptr < bss_end, 1)) + *bss_ptr++ = spu_splats((unsigned char)0); + + dma_wait(MARS_KERNEL_DMA_TAG); + + /* sync before executing loaded code */ + spu_sync(); +} + +static void idle_wait(void) +{ + struct mars_workload_queue_header *cur_queue_header = + &kernel_buffer.workload_queue_header; + + /* set event mask for the lost event */ + spu_write_event_mask(MFC_LLR_LOST_EVENT); + + /* get current atomic state of queue header */ + mfc_getllar(cur_queue_header, kernel_params.workload_queue_ea, 0, 0); + mfc_read_atomic_status(); + + /* check if queue header has been modified since we last fetched it */ + if (!kernel_memcmp(&queue_header, cur_queue_header, + MARS_WORKLOAD_QUEUE_HEADER_SIZE)) { + /* wait until queue header is modified */ + spu_read_event_status(); + spu_write_event_ack(MFC_LLR_LOST_EVENT); + } + + /* clear any remnant lost event */ + spu_write_event_ack(MFC_LLR_LOST_EVENT); +} + +static void get_params(void) +{ + /* set event mask for the lost event */ + spu_write_event_mask(MFC_LLR_LOST_EVENT); + + /* set sync begin flag so host knows to begin sync process */ + do { + mfc_getllar(&kernel_params, kernel_params_ea, 0, 0); + mfc_read_atomic_status(); + + /* set the flag */ + kernel_params.kernel_ticks.flag = + MARS_KERNEL_TICKS_FLAG_SYNC_BEGIN; + spu_dsync(); + + mfc_putllc(&kernel_params, kernel_params_ea, 0, 0); + } while (mfc_read_atomic_status() & MFC_PUTLLC_STATUS); + + /* get the tick offset from host and check if sync end flag is set */ + do { + spu_write_event_ack(MFC_LLR_LOST_EVENT); + + mfc_getllar(&kernel_params, kernel_params_ea, 0, 0); + mfc_read_atomic_status(); + + /* host set the sync end flag so finish */ + if (kernel_params.kernel_ticks.flag & + MARS_KERNEL_TICKS_FLAG_SYNC_END) + break; + + spu_read_event_status(); + } while (1); + + /* now, kernel parameters, including offset of tick counters, + * are stored in 'kernel_params'. + */ + + /* start decrementer */ + spu_write_decrementer(0); + + /* clear any remnant lost event */ + spu_write_event_ack(MFC_LLR_LOST_EVENT); +} + +int main(uint64_t params_ea) +{ + kernel_params_ea = params_ea; + + get_params(); + + do { + int status = workload_reserve(); + + if (status & MARS_KERNEL_STATUS_BUSY) { + workload_module_load(); + workload_run(); + workload_release(); + } else if (status & MARS_KERNEL_STATUS_IDLE) { + idle_wait(); + } else if (status & MARS_KERNEL_STATUS_EXIT) { + break; + } + } while (1); + + host_signal_send(MARS_HOST_SIGNAL_EXIT); + + return MARS_SUCCESS; +} + + +void exit(int rc) +{ + spu_thread_exit(rc); + for(;;) + ; +} diff --git a/common/libspumars/spu/base/kernel/kernel_crt.S b/common/libspumars/spu/base/kernel/kernel_crt.S new file mode 100644 index 00000000..ca7f3053 --- /dev/null +++ b/common/libspumars/spu/base/kernel/kernel_crt.S @@ -0,0 +1,46 @@ +.section .interrupt, "ax", @progbits + heq $LR, $LR, $LR + +.section .init + +.globl _init +.type _init, @function +_init: + stqd $LR, 16($SP) + stqd $SP, -32($SP) + ai $SP, $SP, -32 + + ai $SP, $SP, 32 + lqd $LR, 16($SP) + bi $LR + +.section .fini + +.globl _fini +.type _fini, @function +_fini: + stqd $LR, 16($SP) + stqd $SP, -32($SP) + ai $SP, $SP, -32 + + ai $SP, $SP, 32 + lqd $LR, 16($SP) + bi $LR + +.section .text + +.globl _start +.type _start, @function +_start: + il $LR, 0 + ila $SP, __stack + + stqd $LR, 0($SP) /* init back chain to NULL */ + stqd $SP, -32($SP) /* init stack frame */ + ai $SP, $SP, -32 /* push stack frame */ + + brsl $LR, _init + brsl $LR, main + br exit +.size _start, .-_start + \ No newline at end of file diff --git a/common/libspumars/spu/base/kernel/mutex.c b/common/libspumars/spu/base/kernel/mutex.c new file mode 100644 index 00000000..d118a20b --- /dev/null +++ b/common/libspumars/spu/base/kernel/mutex.c @@ -0,0 +1,105 @@ +#include +#include + +#include +#include + +#include "kernel_internal_types.h" + +#define MARS_MUTEX_STATE_NONE 0 +#define MARS_MUTEX_STATE_DONE 2 + +static struct mars_mutex mutex_buffer; + +int mutex_lock_get(uint64_t mutex_ea, struct mars_mutex *mutex) +{ + int state = MARS_MUTEX_STATE_NONE; + uint8_t id = 0; + + /* check function params */ + if (!mutex_ea) + return MARS_ERROR_NULL; + if (!mutex) + return MARS_ERROR_NULL; + if (mutex_ea & MARS_MUTEX_ALIGN_MASK) + return MARS_ERROR_ALIGN; + if ((uintptr_t)mutex & MARS_MUTEX_ALIGN_MASK) + return MARS_ERROR_ALIGN; + + /* set event mask for the lost event */ + spu_write_event_mask(MFC_LLR_LOST_EVENT); + + /* update waiting state */ + while (state != MARS_MUTEX_STATE_DONE) { + mfc_getllar(mutex, mutex_ea, 0, 0); + mfc_read_atomic_status(); + + if (state && + (mutex->status.lock == MARS_MUTEX_LOCKED || + mutex->status.current_id != id)) { + /* wait until mutex is released */ + spu_read_event_status(); + spu_write_event_ack(MFC_LLR_LOST_EVENT); + } + else { + if (state) { + /* get lock */ + mutex->status.lock = MARS_MUTEX_LOCKED; + mutex->status.current_id++; + } + else { + /* get my id */ + id = mutex->status.next_id++; + } + + spu_dsync(); + mfc_putllc(mutex, mutex_ea, 0, 0); + if (!(mfc_read_atomic_status() & MFC_PUTLLC_STATUS)) + state++; + } + } + + /* clear any remnant lost event */ + spu_write_event_ack(MFC_LLR_LOST_EVENT); + + return MARS_SUCCESS; +} + +int mutex_unlock_put(uint64_t mutex_ea, struct mars_mutex *mutex) +{ + int status; + + /* check function params */ + if (!mutex_ea) + return MARS_ERROR_NULL; + if (!mutex) + return MARS_ERROR_NULL; + if (mutex_ea & MARS_MUTEX_ALIGN_MASK) + return MARS_ERROR_ALIGN; + if ((uintptr_t)mutex & MARS_MUTEX_ALIGN_MASK) + return MARS_ERROR_ALIGN; + if (mutex->status.lock != MARS_MUTEX_LOCKED) + return MARS_ERROR_STATE; + + mfc_sync(0); + + /* set event mask for the lost event */ + spu_write_event_mask(MFC_LLR_LOST_EVENT); + + do { + mfc_getllar(&mutex_buffer, mutex_ea, 0, 0); + mfc_read_atomic_status(); + + mutex->status = mutex_buffer.status; + mutex->status.lock = MARS_MUTEX_UNLOCKED; + + spu_dsync(); + mfc_putllc(mutex, mutex_ea, 0, 0); + status = mfc_read_atomic_status() & MFC_PUTLLC_STATUS; + } while (status); + + /* clear any remnant lost event */ + spu_write_event_ack(MFC_LLR_LOST_EVENT); + + return MARS_SUCCESS; +} diff --git a/common/libspumars/spu/base/kernel/switch.S b/common/libspumars/spu/base/kernel/switch.S new file mode 100644 index 00000000..34becef5 --- /dev/null +++ b/common/libspumars/spu/base/kernel/switch.S @@ -0,0 +1,39 @@ +.section .bss + +.align 4 +.globl __kernel_stack +__kernel_stack: + .space 16 + + +.text + +.globl workload_run +.type workload_run, @function +workload_run: + stqd $LR, 16($SP) + stqd $SP, -32($SP) + ai $SP, $SP, -32 + + stqa $SP, __kernel_stack + brsl $LR, __workload_run + + ai $SP, $SP, 32 + lqd $LR, 16($SP) + bi $LR +.size workload_run, .-workload_run + +.globl workload_exit +.type workload_exit, @function +workload_exit: + stqd $LR, 16($SP) + stqd $SP, -32($SP) + ai $SP, $SP, -32 + + brsl $LR, __workload_exit + lqa $SP, __kernel_stack + + ai $SP, $SP, 32 + lqd $LR, 16($SP) + bi $LR +.size workload_exit, .-workload_exit diff --git a/common/libspumars/spu/base/lib/module.S b/common/libspumars/spu/base/lib/module.S new file mode 100644 index 00000000..cb941268 --- /dev/null +++ b/common/libspumars/spu/base/lib/module.S @@ -0,0 +1,307 @@ +/* NOTE: Order of defines must be the same as the member order of + struct mars_kernel_syscalls declaration in kernel_internal_types.h */ +#define get_ticks 0 +#define get_mars_context_ea 4 +#define get_kernel_id 8 +#define get_workload_id 12 +#define get_workload 16 +#define get_workload_by_id 20 +#define workload_exit 24 +#define workload_query 28 +#define workload_wait_set 32 +#define workload_wait_reset 36 +#define workload_signal_set 40 +#define workload_signal_reset 44 +#define workload_schedule_begin 48 +#define workload_schedule_end 52 +#define workload_unschedule_begin 56 +#define workload_unschedule_end 60 +#define host_signal_send 64 +#define host_callback_set 68 +#define host_callback_reset 72 +#define mutex_lock_get 76 +#define mutex_unlock_put 80 +#define dma_get 84 +#define dma_put 88 +#define dma_wait 92 + +/* NOTE: Value of defines must equal defines in workload_internal_types.h */ +#define WORKLOAD_EXIT_STATE_READY 0x10 /* MARS_WORKLOAD_STATE_READY */ +#define WORKLOAD_EXIT_STATE_WAITING 0x20 /* MARS_WORKLOAD_STATE_WAITING */ +#define WORKLOAD_EXIT_STATE_FINISHED 0x80 /* MARS_WORKLOAD_STATE_FINISHED */ + +.section .bss + +/* const struct mars_kernel_syscalls *kernel_syscalls */ +.align 4 +.globl kernel_syscalls +kernel_syscalls: +.space 16 + + +.section .text + +/* void mars_module_entry(const struct mars_kernel_syscalls *syscalls) */ +.global mars_module_entry +.type mars_module_entry, @function +mars_module_entry: + ila $SP, __stack /* switch to module stack */ + il $LR, 0 /* set link register to NULL */ + stqd $LR, 0($SP) /* init back chain to NULL */ + stqd $SP, -32($SP) /* init stack frame */ + ai $SP, $SP, -32 /* push stack frame */ + + stqr $3, kernel_syscalls /* save kernel syscalls ptr */ + brsl $LR, _init /* call _init() */ + brsl $LR, mars_module_main /* call mars_module_main() */ + br mars_module_workload_finish /* return to kernel */ +.size mars_module_entry, .-mars_module_entry + + +/* uint32_t mars_module_get_ticks(void) */ +.global mars_module_get_ticks +.type mars_module_get_ticks, @function +mars_module_get_ticks: + il $2, get_ticks + br call_kernel_syscall +.size mars_module_get_ticks, .-mars_module_get_ticks + + +/* uint64_t mars_module_get_mars_context_ea(void) */ +.global mars_module_get_mars_context_ea +.type mars_module_get_mars_context_ea, @function +mars_module_get_mars_context_ea: + il $2, get_mars_context_ea + br call_kernel_syscall +.size mars_module_get_mars_context_ea, .-mars_module_get_mars_context_ea + + +/* uint16_t mars_module_get_kernel_id(void) */ +.global mars_module_get_kernel_id +.type mars_module_get_kernel_id, @function +mars_module_get_kernel_id: + il $2, get_kernel_id + br call_kernel_syscall +.size mars_module_get_kernel_id, .-mars_module_get_kernel_id + +/* uint16_t mars_module_get_workload_id(void) */ +.global mars_module_get_workload_id +.type mars_module_get_workload_id, @function +mars_module_get_workload_id: + il $2, get_workload_id + br call_kernel_syscall +.size mars_module_get_workload_id, .-mars_module_get_workload_id + + +/* struct mars_workload_context *mars_module_get_workload(void) */ +.global mars_module_get_workload +.type mars_module_get_workload, @function +mars_module_get_workload: + il $2, get_workload + br call_kernel_syscall +.size mars_module_get_workload, .-mars_module_get_workload + + +/* struct mars_workload_context *mars_module_get_workload_by_id(uint16_t id) */ +.global mars_module_get_workload_by_id +.type mars_module_get_workload_by_id, @function +mars_module_get_workload_by_id: + il $2, get_workload_by_id + br call_kernel_syscall +.size mars_module_get_workload_by_id, .-mars_module_get_workload_by_id + + +/* int mars_module_workload_query(uint16_t id, int query) */ +.global mars_module_workload_query +.type mars_module_workload_query, @function +mars_module_workload_query: + il $2, workload_query + br call_kernel_syscall +.size mars_module_workload_query, .-mars_module_workload_query + + +/* int mars_module_workload_wait_set(uint16_t id) */ +.global mars_module_workload_wait_set +.type mars_module_workload_wait_set, @function +mars_module_workload_wait_set: + il $2, workload_wait_set + br call_kernel_syscall +.size mars_module_workload_wait_set, .-mars_module_workload_wait_set + +/* int mars_module_workload_wait_reset(void) */ +.global mars_module_workload_wait_reset +.type mars_module_workload_wait_reset, @function +mars_module_workload_wait_reset: + il $2, workload_wait_reset + br call_kernel_syscall +.size mars_module_workload_wait_reset, .-mars_module_workload_wait_reset + + +/* int mars_module_workload_signal_set(uint16_t id) */ +.global mars_module_workload_signal_set +.type mars_module_workload_signal_set, @function +mars_module_workload_signal_set: + il $2, workload_signal_set + br call_kernel_syscall +.size mars_module_workload_signal_set, .-mars_module_workload_signal_set + + +/* int mars_module_workload_signal_reset(void) */ +.global mars_module_workload_signal_reset +.type mars_module_workload_signal_reset, @function +mars_module_workload_signal_reset: + il $2, workload_signal_reset + br call_kernel_syscall +.size mars_module_workload_signal_reset, .-mars_module_workload_signal_reset + + +/* int mars_module_workload_schedule_begin(uint16_t id, uint8_t priority, + struct mars_workload_context **workload) */ +.global mars_module_workload_schedule_begin +.type mars_module_workload_schedule_begin, @function +mars_module_workload_schedule_begin: + il $2, workload_schedule_begin + br call_kernel_syscall +.size mars_module_workload_schedule_begin, .-mars_module_workload_schedule_begin + + +/* int mars_module_workload_schedule_end(uint16_t id, int cancel) */ +.global mars_module_workload_schedule_end +.type mars_module_workload_schedule_end, @function +mars_module_workload_schedule_end: + il $2, workload_schedule_end + br call_kernel_syscall +.size mars_module_workload_schedule_end, .-mars_module_workload_schedule_end + + +/* int mars_module_workload_unschedule_begin(uint16_t id, + struct mars_workload_context **workload) */ +.global mars_module_workload_unschedule_begin +.type mars_module_workload_unschedule_begin, @function +mars_module_workload_unschedule_begin: + il $2, workload_unschedule_begin + br call_kernel_syscall +.size mars_module_workload_unschedule_begin, .-mars_module_workload_unschedule_begin + + +/* int mars_module_workload_unschedule_end(uint16_t id) */ +.global mars_module_workload_unschedule_end +.type mars_module_workload_unschedule_end, @function +mars_module_workload_unschedule_end: + il $2, workload_unschedule_end + br call_kernel_syscall +.size mars_module_workload_unschedule_end, .-mars_module_workload_unschedule_end + + +/* void mars_module_workload_wait(void) */ +.global mars_module_workload_wait +.type mars_module_workload_wait, @function +mars_module_workload_wait: + il $3, WORKLOAD_EXIT_STATE_WAITING + il $2, workload_exit + br call_kernel_syscall +.size mars_module_workload_wait, .-mars_module_workload_wait + + +/* void mars_module_workload_yield(void) */ +.global mars_module_workload_yield +.type mars_module_workload_yield, @function +mars_module_workload_yield: + il $3, WORKLOAD_EXIT_STATE_READY + il $2, workload_exit + br call_kernel_syscall +.size mars_module_workload_yield, .-mars_module_workload_yield + + +/* void mars_module_workload_finish(void) */ +.global mars_module_workload_finish +.type mars_module_workload_finish, @function +mars_module_workload_finish: + il $3, WORKLOAD_EXIT_STATE_FINISHED + il $2, workload_exit + br call_kernel_syscall +.size mars_module_workload_finish, .-mars_module_workload_finish + +/* int mars_module_host_signal_send(uint64_t watch_point_ea) */ +.global mars_module_host_signal_send +.type mars_module_host_signal_send, @function +mars_module_host_signal_send: + il $2, host_signal_send + br call_kernel_syscall +.size mars_module_host_signal_send, .-mars_module_host_signal_send + + +/* int mars_module_host_callback_set(uint64_t callback_ea, + const struct mars_callback_args *in) */ +.global mars_module_host_callback_set +.type mars_module_host_callback_set, @function +mars_module_host_callback_set: + il $2, host_callback_set + br call_kernel_syscall +.size mars_module_host_callback_set, .-mars_module_host_callback_set + + +/* int mars_module_host_callback_reset(struct mars_callback_args *out) */ +.global mars_module_host_callback_reset +.type mars_module_host_callback_reset, @function +mars_module_host_callback_reset: + il $2, host_callback_reset + br call_kernel_syscall +.size mars_module_host_callback_reset, .-mars_module_host_callback_reset + + +/* int mars_module_mutex_lock_get(uint64_t mutex_ea, + struct mars_mutex *mutex) */ +.global mars_module_mutex_lock_get +.type mars_module_mutex_lock_get, @function +mars_module_mutex_lock_get: + il $2, mutex_lock_get + br call_kernel_syscall +.size mars_module_mutex_lock_get, .-mars_module_mutex_lock_get + + +/* int mars_module_mutex_unlock_put(uint64_t mutex_ea, + struct mars_mutex *mutex) */ +.global mars_module_mutex_unlock_put +.type mars_module_mutex_unlock_put, @function +mars_module_mutex_unlock_put: + il $2, mutex_unlock_put + br call_kernel_syscall +.size mars_module_mutex_unlock_put, .-mars_module_mutex_unlock_put + + +/* int mars_module_dma_get(void *ls, uint64_t ea, uint32_t size, + uint32_t tag) */ +.global mars_module_dma_get +.type mars_module_dma_get, @function +mars_module_dma_get: + il $2, dma_get + br call_kernel_syscall +.size mars_module_dma_get, .-mars_module_dma_get + + +/* int mars_module_dma_put(const void *ls, uint64_t ea, uint32_t size, + uint32_t tag) */ +.global mars_module_dma_put +.type mars_module_dma_put, @function +mars_module_dma_put: + il $2, dma_put + br call_kernel_syscall +.size mars_module_dma_put, .-mars_module_dma_get + + +/* int mars_module_dma_wait(uint32_t tag) */ +.global mars_module_dma_wait +.type mars_module_dma_wait, @function +mars_module_dma_wait: + il $2, dma_wait + br call_kernel_syscall +.size mars_module_dma_wait, .-mars_module_dma_wait + + +call_kernel_syscall: + lqr $76, kernel_syscalls + lqx $77, $76, $2 + a $78, $76, $2 + rotqby $79, $77, $78 + bi $79 diff --git a/common/libspumars/spu/task/lib/task.c b/common/libspumars/spu/task/lib/task.c new file mode 100644 index 00000000..1f4a7f47 --- /dev/null +++ b/common/libspumars/spu/task/lib/task.c @@ -0,0 +1,177 @@ +#include + +#include +#include +//#include + +#include "../module/task_module.h" + +const struct mars_task_module_syscalls *mars_task_module_syscalls; + +int main(const struct mars_task_args *args, + const struct mars_task_module_syscalls *module_syscalls) +{ + struct mars_task_context *task; + + /* save the module syscalls pointer */ + mars_task_module_syscalls = module_syscalls; + + /* get task context */ + task = mars_task_module_get_task(); + + /* call task main function and store return value in task context */ + task->exit_code = mars_task_main(args); + + /* exit */ + mars_task_module_exit(); + + return MARS_SUCCESS; +} + +void mars_task_exit(int32_t exit_code) +{ + struct mars_task_context *task; + + /* get task context */ + task = mars_task_module_get_task(); + + /* store exit code in task context */ + task->exit_code = exit_code; + + /* exit */ + mars_task_module_exit(); +} + +int mars_task_yield(void) +{ + struct mars_task_context *task; + + /* get task context */ + task = mars_task_module_get_task(); + + /* make sure task context has a context save area */ + if (!task->context_save_area_ea) + return MARS_ERROR_FORMAT; + + mars_task_module_yield(mars_task_module_get_heap()); + + return MARS_SUCCESS; +} + +int mars_task_schedule(const struct mars_task_id *id, + const struct mars_task_args *args, + uint8_t priority) +{ + /* check function params */ + if (!id) + return MARS_ERROR_NULL; + + return mars_task_module_schedule(id->workload_id, args, priority); +} + +int mars_task_unschedule(const struct mars_task_id *id, int32_t exit_code) +{ + /* check function params */ + if (!id) + return MARS_ERROR_NULL; + + return mars_task_module_unschedule(id->workload_id, exit_code); +} + +int mars_task_wait(const struct mars_task_id *id, int32_t *exit_code) +{ + struct mars_task_context *task; + + /* check function params */ + if (!id) + return MARS_ERROR_NULL; + + /* get task context */ + task = mars_task_module_get_task(); + + /* make sure task context has a context save area */ + if (!task->context_save_area_ea) + return MARS_ERROR_FORMAT; + + mars_task_module_wait(id->workload_id, mars_task_module_get_heap()); + + /* exit code requested so get it from the task context and return it */ + if (exit_code) { + task = (struct mars_task_context *) + mars_task_module_get_task_by_id(id); + if (!task) + return MARS_ERROR_INTERNAL; + + *exit_code = task->exit_code; + } + + return MARS_SUCCESS; +} + +int mars_task_try_wait(const struct mars_task_id *id, int32_t *exit_code) +{ + struct mars_task_context *task; + + /* check function params */ + if (!id) + return MARS_ERROR_NULL; + + mars_task_module_try_wait(id->workload_id); + + /* exit code requested so get it from the task context and return it */ + if (exit_code) { + task = (struct mars_task_context *) + mars_task_module_get_task_by_id(id); + if (!task) + return MARS_ERROR_INTERNAL; + + *exit_code = task->exit_code; + } + + return MARS_SUCCESS; +} + +int mars_task_call_host(uint64_t callback_ea, + const struct mars_callback_args *in, + struct mars_callback_args *out) +{ + struct mars_task_context *task; + + /* get task context */ + task = mars_task_module_get_task(); + + /* make sure task context has a context save area */ + if (!task->context_save_area_ea) + return MARS_ERROR_FORMAT; + + return mars_task_module_call_host(callback_ea, in, out, + mars_task_module_get_heap()); +} + +uint32_t mars_task_get_ticks(void) +{ + return mars_task_module_get_ticks(); +} + +uint16_t mars_task_get_kernel_id(void) +{ + return mars_task_module_get_kernel_id(); +} + +const struct mars_task_id *mars_task_get_id(void) +{ + struct mars_task_context *task; + + task = mars_task_module_get_task(); + + return &task->id; +} + +const char *mars_task_get_name(void) +{ + struct mars_task_context *task; + + task = mars_task_module_get_task(); + + return task->id.name[0] ? (const char *)task->id.name : NULL; +} diff --git a/common/libspumars/spu/task/lib/task_barrier.c b/common/libspumars/spu/task/lib/task_barrier.c new file mode 100644 index 00000000..87d7df98 --- /dev/null +++ b/common/libspumars/spu/task/lib/task_barrier.c @@ -0,0 +1,164 @@ +#include + +#include +#include + +#include "task_barrier_internal_types.h" +#include "task_internal_types.h" +#include "../module/task_module.h" + +static struct mars_task_barrier barrier; + +static int notify(uint64_t barrier_ea, int try) +{ + int i; + struct mars_task_context *task; + + /* check function params */ + if (!barrier_ea) + return MARS_ERROR_NULL; + if (barrier_ea & MARS_TASK_BARRIER_ALIGN_MASK) + return MARS_ERROR_ALIGN; + + /* get task context */ + task = mars_task_module_get_task(); + + /* make sure task context has a context save area */ + if (!task->context_save_area_ea && !try) + return MARS_ERROR_FORMAT; + + mars_mutex_lock_get(barrier_ea, (struct mars_mutex *)&barrier); + + /* previous barrier wait not complete so wait */ + if (barrier.notified_count == barrier.total) { + /* only try so return busy */ + if (try) { + mars_mutex_unlock_put(barrier_ea, + (struct mars_mutex *)&barrier); + return MARS_ERROR_BUSY; + } + + /* check if barrier notify wait limit reached */ + if (barrier.notify_wait_count == barrier.total) { + mars_mutex_unlock_put(barrier_ea, + (struct mars_mutex *)&barrier); + return MARS_ERROR_LIMIT; + } + + /* add id to wait list */ + barrier.notify_wait_id[barrier.notify_wait_count] = + task->id.workload_id; + barrier.notify_wait_count++; + + mars_mutex_unlock_put(barrier_ea, + (struct mars_mutex *)&barrier); + + /* wait for signal */ + mars_task_module_signal_wait(mars_task_module_get_heap()); + + mars_mutex_lock_get(barrier_ea, (struct mars_mutex *)&barrier); + } + + /* increment notified count */ + barrier.notified_count++; + + /* notified count reached total so release barrier */ + if (barrier.notified_count == barrier.total) { + /* signal all task ids in wait list */ + for (i = 0; i < barrier.wait_count; i++) + mars_task_module_signal_send(barrier.wait_id[i]); + barrier.wait_count = 0; + } + + mars_mutex_unlock_put(barrier_ea, (struct mars_mutex *)&barrier); + + return MARS_SUCCESS; +} + +int mars_task_barrier_notify(uint64_t barrier_ea) +{ + return notify(barrier_ea, 0); +} + +int mars_task_barrier_try_notify(uint64_t barrier_ea) +{ + return notify(barrier_ea, 1); +} + +static int wait(uint64_t barrier_ea, int try) +{ + int i; + struct mars_task_context *task; + + /* check function params */ + if (!barrier_ea) + return MARS_ERROR_NULL; + if (barrier_ea & MARS_TASK_BARRIER_ALIGN_MASK) + return MARS_ERROR_ALIGN; + + /* get task context */ + task = mars_task_module_get_task(); + + /* make sure task context has a context save area */ + if (!task->context_save_area_ea && !try) + return MARS_ERROR_FORMAT; + + mars_mutex_lock_get(barrier_ea, (struct mars_mutex *)&barrier); + + /* not all tasks notified barrier so need to wait */ + if (barrier.notified_count != barrier.total) { + /* only try so return busy */ + if (try) { + mars_mutex_unlock_put(barrier_ea, + (struct mars_mutex *)&barrier); + return MARS_ERROR_BUSY; + } + + /* check if barrier wait limit reached */ + if (barrier.wait_count == barrier.total) { + mars_mutex_unlock_put(barrier_ea, + (struct mars_mutex *)&barrier); + return MARS_ERROR_LIMIT; + } + + /* add id to wait list */ + barrier.wait_id[barrier.wait_count] = task->id.workload_id; + barrier.wait_count++; + + mars_mutex_unlock_put(barrier_ea, + (struct mars_mutex *)&barrier); + + /* wait for signal */ + mars_task_module_signal_wait(mars_task_module_get_heap()); + + mars_mutex_lock_get(barrier_ea, (struct mars_mutex *)&barrier); + } + + /* increment waited count */ + barrier.waited_count++; + + /* all tasks have called wait so reset barrier */ + if (barrier.waited_count == barrier.total) { + barrier.notified_count = 0; + barrier.waited_count = 0; + + /* signal all task ids in notify wait list */ + for (i = 0; i < barrier.notify_wait_count; i++) + mars_task_module_signal_send(barrier.notify_wait_id[i]); + barrier.notify_wait_count = 0; + } + + mars_mutex_unlock_put(barrier_ea, (struct mars_mutex *)&barrier); + + return MARS_SUCCESS; +} + +int mars_task_barrier_wait(uint64_t barrier_ea) +{ + return wait(barrier_ea, 0); +} + +int mars_task_barrier_try_wait(uint64_t barrier_ea) +{ + return wait(barrier_ea, 1); +} diff --git a/common/libspumars/spu/task/lib/task_event_flag.c b/common/libspumars/spu/task/lib/task_event_flag.c new file mode 100644 index 00000000..755b1562 --- /dev/null +++ b/common/libspumars/spu/task/lib/task_event_flag.c @@ -0,0 +1,207 @@ +#include +#include + +#include +#include +#include + +#include "task_event_flag_internal_types.h" +#include "task_internal_types.h" +#include "../module/task_module.h" + +static struct mars_task_event_flag event_flag; + +int mars_task_event_flag_clear(uint64_t event_flag_ea, uint32_t bits) +{ + /* check function params */ + if (!event_flag_ea) + return MARS_ERROR_NULL; + if (event_flag_ea & MARS_TASK_EVENT_FLAG_ALIGN_MASK) + return MARS_ERROR_ALIGN; + + mars_mutex_lock_get(event_flag_ea, (struct mars_mutex *)&event_flag); + + /* clear the necessary bits */ + event_flag.bits &= ~bits; + + mars_mutex_unlock_put(event_flag_ea, (struct mars_mutex *)&event_flag); + + return MARS_SUCCESS; +} + +int mars_task_event_flag_set(uint64_t event_flag_ea, uint32_t bits) +{ + int i; + + /* check function params */ + if (!event_flag_ea) + return MARS_ERROR_NULL; + if (event_flag_ea & MARS_TASK_EVENT_FLAG_ALIGN_MASK) + return MARS_ERROR_ALIGN; + + mars_mutex_lock_get(event_flag_ea, (struct mars_mutex *)&event_flag); + + /* check for valid direction */ + if (event_flag.direction != MARS_TASK_EVENT_FLAG_MPU_TO_HOST && + event_flag.direction != MARS_TASK_EVENT_FLAG_MPU_TO_MPU) { + mars_mutex_unlock_put(event_flag_ea, + (struct mars_mutex *)&event_flag); + return MARS_ERROR_STATE; + } + + /* set the necessary bits */ + event_flag.bits |= bits; + + /* save current set bits */ + bits = event_flag.bits; + + /* search through wait list for tasks to be signalled */ + for (i = 0; i < event_flag.wait_count; i++) { + /* check condition based on wait mode */ + switch (event_flag.wait_mask_mode[i]) { + case MARS_TASK_EVENT_FLAG_MASK_OR: + if ((bits & event_flag.wait_mask[i]) == 0) + continue; + break; + case MARS_TASK_EVENT_FLAG_MASK_AND: + if ((bits & event_flag.wait_mask[i]) + != event_flag.wait_mask[i]) + continue; + break; + } + + /* signal the waiting tasks */ + mars_task_module_signal_send(event_flag.wait_id[i]); + + /* flush id from wait list */ + event_flag.wait_count--; + memmove(&event_flag.wait_id[i], + &event_flag.wait_id[i + 1], + sizeof(uint16_t) * (event_flag.wait_count - i)); + memmove(&event_flag.wait_mask[i], + &event_flag.wait_mask[i + 1], + sizeof(uint32_t) * (event_flag.wait_count - i)); + memmove(&event_flag.wait_mask_mode[i], + &event_flag.wait_mask_mode[i + 1], + sizeof(uint8_t) * (event_flag.wait_count - i)); + i--; + } + + mars_mutex_unlock_put(event_flag_ea, (struct mars_mutex *)&event_flag); + + /* signal the waiting host */ + if (event_flag.direction != MARS_TASK_EVENT_FLAG_HOST_TO_MPU && + event_flag.direction != MARS_TASK_EVENT_FLAG_MPU_TO_MPU) + mars_task_module_signal_host(event_flag_ea + + offsetof(struct mars_task_event_flag, bits)); + + return MARS_SUCCESS; +} + +static int wait(uint64_t event_flag_ea,uint32_t mask, uint8_t mask_mode, + uint32_t *bits, int try) +{ + int wait = 0; + struct mars_task_context *task; + + /* check function params */ + if (!event_flag_ea) + return MARS_ERROR_NULL; + if (event_flag_ea & MARS_TASK_EVENT_FLAG_ALIGN_MASK) + return MARS_ERROR_ALIGN; + if (mask_mode != MARS_TASK_EVENT_FLAG_MASK_OR && + mask_mode != MARS_TASK_EVENT_FLAG_MASK_AND) + return MARS_ERROR_PARAMS; + + /* get task context */ + task = mars_task_module_get_task(); + + /* make sure task context has a context save area */ + if (!task->context_save_area_ea && !try) + return MARS_ERROR_FORMAT; + + mars_mutex_lock_get(event_flag_ea, (struct mars_mutex *)&event_flag); + + /* check for valid direction */ + if (event_flag.direction != MARS_TASK_EVENT_FLAG_HOST_TO_MPU && + event_flag.direction != MARS_TASK_EVENT_FLAG_MPU_TO_MPU) { + mars_mutex_unlock_put(event_flag_ea, + (struct mars_mutex *)&event_flag); + return MARS_ERROR_STATE; + } + + /* check condition based on wait mode */ + switch (mask_mode) { + case MARS_TASK_EVENT_FLAG_MASK_OR: + if ((event_flag.bits & mask) == 0) + wait = 1; + break; + case MARS_TASK_EVENT_FLAG_MASK_AND: + if ((event_flag.bits & mask) != mask) + wait = 1; + break; + } + + if (wait) { + /* only try so return busy */ + if (try) { + /* get current bits status if return bits requested */ + if (bits) + *bits = event_flag.bits; + + mars_mutex_unlock_put(event_flag_ea, + (struct mars_mutex *)&event_flag); + + return MARS_ERROR_BUSY; + } + + /* check if event flag wait limit reached */ + if (event_flag.wait_count == MARS_TASK_EVENT_FLAG_WAIT_MAX) { + mars_mutex_unlock_put(event_flag_ea, + (struct mars_mutex *)&event_flag); + return MARS_ERROR_LIMIT; + } + + /* add id to wait list */ + event_flag.wait_id[event_flag.wait_count] = + task->id.workload_id; + event_flag.wait_mask[event_flag.wait_count] = mask; + event_flag.wait_mask_mode[event_flag.wait_count] = mask_mode; + event_flag.wait_count++; + + mars_mutex_unlock_put(event_flag_ea, + (struct mars_mutex *)&event_flag); + + /* wait for signal */ + mars_task_module_signal_wait(mars_task_module_get_heap()); + + mars_mutex_lock_get(event_flag_ea, + (struct mars_mutex *)&event_flag); + } + + /* return bits if requested */ + if (bits) + *bits = event_flag.bits; + + /* clear event if clear mode is auto */ + if (event_flag.clear_mode == MARS_TASK_EVENT_FLAG_CLEAR_AUTO) + event_flag.bits &= ~mask; + + mars_mutex_unlock_put(event_flag_ea, (struct mars_mutex *)&event_flag); + + return MARS_SUCCESS; +} + +int mars_task_event_flag_wait(uint64_t event_flag_ea, + uint32_t mask, uint8_t mask_mode, + uint32_t *bits) +{ + return wait(event_flag_ea, mask, mask_mode, bits, 0); +} + +int mars_task_event_flag_try_wait(uint64_t event_flag_ea, + uint32_t mask, uint8_t mask_mode, + uint32_t *bits) +{ + return wait(event_flag_ea, mask, mask_mode, bits, 1); +} diff --git a/common/libspumars/spu/task/lib/task_queue.c b/common/libspumars/spu/task/lib/task_queue.c new file mode 100644 index 00000000..398ec3f2 --- /dev/null +++ b/common/libspumars/spu/task/lib/task_queue.c @@ -0,0 +1,455 @@ +#include + +#include +#include +#include + +#include "task_internal_types.h" +#include "task_queue_internal_types.h" +#include "../module/task_module.h" + +static struct mars_task_queue queue; + +int mars_task_queue_count(uint64_t queue_ea, uint32_t *count) +{ + /* check function params */ + if (!queue_ea) + return MARS_ERROR_NULL; + if (!count) + return MARS_ERROR_NULL; + if (queue_ea & MARS_TASK_QUEUE_ALIGN_MASK) + return MARS_ERROR_ALIGN; + + mars_mutex_lock_get(queue_ea, (struct mars_mutex *)&queue); + + /* return current items in queue */ + *count = queue.count; + + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)&queue); + + return MARS_SUCCESS; +} + +int mars_task_queue_clear(uint64_t queue_ea) +{ + int i; + int signal_host; + + /* check function params */ + if (!queue_ea) + return MARS_ERROR_NULL; + if (queue_ea & MARS_TASK_QUEUE_ALIGN_MASK) + return MARS_ERROR_ALIGN; + + mars_mutex_lock_get(queue_ea, (struct mars_mutex *)&queue); + + /* whether signal the waiting host */ + signal_host = + (queue.count == queue.depth && + queue.direction != MARS_TASK_QUEUE_MPU_TO_HOST && + queue.direction != MARS_TASK_QUEUE_MPU_TO_MPU); + + /* signal all waiting tasks that queue is ready for push */ + for (i = 0; i < queue.push_wait_count; i++) + mars_task_module_signal_send(queue.push_wait_id[i]); + + /* flush all ids from push wait list */ + queue.push_wait_count = 0; + + /* clear the queue count and push/pop ea */ + queue.count = 0; + queue.push_ea = queue.buffer_ea; + queue.pop_ea = queue.buffer_ea; + + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)&queue); + + /* signal the waiting host */ + if (signal_host) + mars_task_module_signal_host( + queue_ea + offsetof(struct mars_task_queue, count)); + + return MARS_SUCCESS; +} + +static int push_update(void) +{ + int signal_host; + + /* whether signal the waiting host */ + signal_host = + (queue.count == 0 && + queue.direction != MARS_TASK_QUEUE_HOST_TO_MPU && + queue.direction != MARS_TASK_QUEUE_MPU_TO_MPU); + + /* signal waiting task that queue is ready for pop */ + if (queue.pop_wait_count) { + /* signal waiting task */ + mars_task_module_signal_send( + queue.pop_wait_id[queue.pop_wait_head]); + + /* flush id from pop wait list */ + queue.pop_wait_count--; + queue.pop_wait_head++; + queue.pop_wait_head %= MARS_TASK_QUEUE_WAIT_MAX; + } + + /* increment queue count */ + queue.count++; + + /* increment queue push ea */ + queue.push_ea += queue.size; + + /* wrap to front of queue if necessary */ + if (queue.push_ea == queue.buffer_ea + (queue.size * queue.depth)) + queue.push_ea = queue.buffer_ea; + + return signal_host; +} + +static int push(uint64_t queue_ea, const void *data, + int try, int begin, uint32_t tag) +{ + struct mars_task_context *task; + int signal_host; + + /* check function params */ + if (!queue_ea) + return MARS_ERROR_NULL; + if (queue_ea & MARS_TASK_QUEUE_ALIGN_MASK) + return MARS_ERROR_ALIGN; + if ((uintptr_t)data & MARS_TASK_QUEUE_ENTRY_ALIGN_MASK) + return MARS_ERROR_ALIGN; + if (tag > MARS_TASK_MODULE_DMA_TAG_MAX) + return MARS_ERROR_PARAMS; + + /* get task context */ + task = mars_task_module_get_task(); + + /* make sure task context has a context save area */ + if (!task->context_save_area_ea && !try) + return MARS_ERROR_FORMAT; + + mars_mutex_lock_get(queue_ea, (struct mars_mutex *)&queue); + + /* check for valid direction */ + if (queue.direction != MARS_TASK_QUEUE_MPU_TO_HOST && + queue.direction != MARS_TASK_QUEUE_MPU_TO_MPU) { + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)&queue); + return MARS_ERROR_STATE; + } + + /* queue is full so wait */ + while (queue.count == queue.depth) { + uint8_t push_wait_tail; + + /* only try so return busy */ + if (try) { + mars_mutex_unlock_put(queue_ea, + (struct mars_mutex *)&queue); + return MARS_ERROR_BUSY; + } + + /* check if push wait list full */ + if (queue.push_wait_count == MARS_TASK_QUEUE_WAIT_MAX) { + mars_mutex_unlock_put(queue_ea, + (struct mars_mutex *)&queue); + return MARS_ERROR_LIMIT; + } + + /* add id to push wait list */ + push_wait_tail = (queue.push_wait_head + queue.push_wait_count) + % MARS_TASK_QUEUE_WAIT_MAX; + queue.push_wait_id[push_wait_tail] = task->id.workload_id; + queue.push_wait_count++; + + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)&queue); + + /* wait for signal */ + mars_task_module_signal_wait(mars_task_module_get_heap()); + + mars_mutex_lock_get(queue_ea, (struct mars_mutex *)&queue); + } + + /* begin data transfer into queue */ + mars_dma_put(data, queue.push_ea, queue.size, tag); + + /* only begin data transfer so return */ + if (begin) + return MARS_SUCCESS; + + /* wait for dma completion */ + mars_dma_wait(tag); + + /* update queue data */ + signal_host = push_update(); + + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)&queue); + + /* signal the waiting host */ + if (signal_host) + mars_task_module_signal_host( + queue_ea + offsetof(struct mars_task_queue, count)); + + return MARS_SUCCESS; +} + +int mars_task_queue_push(uint64_t queue_ea, const void *data) +{ + return push(queue_ea, data, 0, 0, MARS_TASK_MODULE_DMA_TAG); +} + +int mars_task_queue_push_begin(uint64_t queue_ea, const void *data, + uint32_t tag) +{ + return push(queue_ea, data, 0, 1, tag); +} + +int mars_task_queue_push_end(uint64_t queue_ea, uint32_t tag) +{ + int signal_host; + + /* check function params */ + if (!queue_ea) + return MARS_ERROR_NULL; + if (queue_ea & MARS_TASK_QUEUE_ALIGN_MASK) + return MARS_ERROR_ALIGN; + if (tag > MARS_TASK_MODULE_DMA_TAG_MAX) + return MARS_ERROR_PARAMS; + + /* wait for dma completion */ + mars_dma_wait(tag); + + /* update queue data */ + signal_host = push_update(); + + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)&queue); + + /* signal the waiting host */ + if (signal_host) + mars_task_module_signal_host( + queue_ea + offsetof(struct mars_task_queue, count)); + + return MARS_SUCCESS; +} + +int mars_task_queue_try_push(uint64_t queue_ea, const void *data) +{ + return push(queue_ea, data, 1, 0, MARS_TASK_MODULE_DMA_TAG); +} + +int mars_task_queue_try_push_begin(uint64_t queue_ea, const void *data, + uint32_t tag) +{ + return push(queue_ea, data, 1, 1, tag); +} + +static int pop_update(void) +{ + int signal_host; + + /* whether signal the waiting host */ + signal_host = + (queue.count == queue.depth && + queue.direction != MARS_TASK_QUEUE_MPU_TO_HOST && + queue.direction != MARS_TASK_QUEUE_MPU_TO_MPU); + + /* signal waiting task that queue is ready for push */ + if (queue.push_wait_count) { + /* signal waiting task */ + mars_task_module_signal_send( + queue.push_wait_id[queue.push_wait_head]); + + /* flush id from push wait list */ + queue.push_wait_count--; + queue.push_wait_head++; + queue.push_wait_head %= MARS_TASK_QUEUE_WAIT_MAX; + } + + /* decrement queue count */ + queue.count--; + + /* increment queue pop ea */ + queue.pop_ea += queue.size; + + /* wrap to front of queue if necessary */ + if (queue.pop_ea == queue.buffer_ea + (queue.size * queue.depth)) + queue.pop_ea = queue.buffer_ea; + + return signal_host; +} + +static int pop(uint64_t queue_ea, void *data, + int peek, int try, int begin, uint32_t tag) +{ + struct mars_task_context *task; + int signal_host; + + /* check function params */ + if (!queue_ea) + return MARS_ERROR_NULL; + if (queue_ea & MARS_TASK_QUEUE_ALIGN_MASK) + return MARS_ERROR_ALIGN; + if ((uintptr_t)data & MARS_TASK_QUEUE_ENTRY_ALIGN_MASK) + return MARS_ERROR_ALIGN; + if (tag > MARS_TASK_MODULE_DMA_TAG_MAX) + return MARS_ERROR_PARAMS; + + /* get task context */ + task = mars_task_module_get_task(); + + /* make sure task context has a context save area */ + if (!task->context_save_area_ea && !try) + return MARS_ERROR_FORMAT; + + mars_mutex_lock_get(queue_ea, (struct mars_mutex *)&queue); + + /* check for valid direction */ + if (queue.direction != MARS_TASK_QUEUE_HOST_TO_MPU && + queue.direction != MARS_TASK_QUEUE_MPU_TO_MPU) { + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)&queue); + return MARS_ERROR_STATE; + } + + /* queue is empty so wait */ + while (!queue.count) { + uint8_t pop_wait_tail; + + /* only try so return busy */ + if (try) { + mars_mutex_unlock_put(queue_ea, + (struct mars_mutex *)&queue); + return MARS_ERROR_BUSY; + } + + /* check if pop wait list full */ + if (queue.pop_wait_count == MARS_TASK_QUEUE_WAIT_MAX) { + mars_mutex_unlock_put(queue_ea, + (struct mars_mutex *)&queue); + return MARS_ERROR_LIMIT; + } + + /* add id to pop wait list */ + pop_wait_tail = (queue.pop_wait_head + queue.pop_wait_count) + % MARS_TASK_QUEUE_WAIT_MAX; + queue.pop_wait_id[pop_wait_tail] = task->id.workload_id; + queue.pop_wait_count++; + + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)&queue); + + /* wait for signal */ + mars_task_module_signal_wait(mars_task_module_get_heap()); + + mars_mutex_lock_get(queue_ea, (struct mars_mutex *)&queue); + } + + /* begin data transfer from queue */ + mars_dma_get(data, queue.pop_ea, queue.size, tag); + + /* only begin data transfer so return */ + if (begin) + return MARS_SUCCESS; + + /* wait for dma completion */ + mars_dma_wait(tag); + + /* update queue data only if this is not a peek operation */ + if (!peek) + signal_host = pop_update(); + else + signal_host = 0; + + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)&queue); + + /* signal the waiting host */ + if (signal_host) + mars_task_module_signal_host( + queue_ea + offsetof(struct mars_task_queue, count)); + + return MARS_SUCCESS; +} + +int mars_task_queue_pop(uint64_t queue_ea, void *data) +{ + return pop(queue_ea, data, 0, 0, 0, MARS_TASK_MODULE_DMA_TAG); +} + +int mars_task_queue_pop_begin(uint64_t queue_ea, void *data, uint32_t tag) +{ + return pop(queue_ea, data, 0, 0, 1, tag); +} + +int mars_task_queue_pop_end(uint64_t queue_ea, uint32_t tag) +{ + int signal_host; + + /* check function params */ + if (!queue_ea) + return MARS_ERROR_NULL; + if (queue_ea & MARS_TASK_QUEUE_ALIGN_MASK) + return MARS_ERROR_ALIGN; + if (tag > MARS_TASK_MODULE_DMA_TAG_MAX) + return MARS_ERROR_PARAMS; + + /* wait for dma completion */ + mars_dma_wait(tag); + + /* update queue data */ + signal_host = pop_update(); + + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)&queue); + + /* signal the waiting host */ + if (signal_host) + mars_task_module_signal_host( + queue_ea + offsetof(struct mars_task_queue, count)); + + return MARS_SUCCESS; +} + +int mars_task_queue_try_pop(uint64_t queue_ea, void *data) +{ + return pop(queue_ea, data, 0, 1, 0, MARS_TASK_MODULE_DMA_TAG); +} + +int mars_task_queue_try_pop_begin(uint64_t queue_ea, void *data, uint32_t tag) +{ + return pop(queue_ea, data, 0, 1, 1, tag); +} + +int mars_task_queue_peek(uint64_t queue_ea, void *data) +{ + return pop(queue_ea, data, 1, 0, 0, MARS_TASK_MODULE_DMA_TAG); +} + +int mars_task_queue_peek_begin(uint64_t queue_ea, void *data, uint32_t tag) +{ + return pop(queue_ea, data, 1, 0, 1, tag); +} + +int mars_task_queue_peek_end(uint64_t queue_ea, uint32_t tag) +{ + /* check function params */ + if (!queue_ea) + return MARS_ERROR_NULL; + if (queue_ea & MARS_TASK_QUEUE_ALIGN_MASK) + return MARS_ERROR_ALIGN; + if (tag > MARS_TASK_MODULE_DMA_TAG_MAX) + return MARS_ERROR_PARAMS; + + /* wait for dma completion */ + mars_dma_wait(tag); + + mars_mutex_unlock_put(queue_ea, (struct mars_mutex *)&queue); + + return MARS_SUCCESS; +} + +int mars_task_queue_try_peek(uint64_t queue_ea, void *data) +{ + return pop(queue_ea, data, 1, 1, 0, MARS_TASK_MODULE_DMA_TAG); +} + +int mars_task_queue_try_peek_begin(uint64_t queue_ea, void *data, uint32_t tag) +{ + return pop(queue_ea, data, 1, 1, 1, tag); +} diff --git a/common/libspumars/spu/task/lib/task_semaphore.c b/common/libspumars/spu/task/lib/task_semaphore.c new file mode 100644 index 00000000..35eed766 --- /dev/null +++ b/common/libspumars/spu/task/lib/task_semaphore.c @@ -0,0 +1,93 @@ +#include +#include +#include + +#include "task_internal_types.h" +#include "task_semaphore_internal_types.h" +#include "../module/task_module.h" + +static struct mars_task_semaphore semaphore; + +int mars_task_semaphore_acquire(uint64_t semaphore_ea) +{ + struct mars_task_context *task; + + /* check function params */ + if (!semaphore_ea) + return MARS_ERROR_NULL; + if (semaphore_ea & MARS_TASK_SEMAPHORE_ALIGN_MASK) + return MARS_ERROR_ALIGN; + + /* get task context */ + task = mars_task_module_get_task(); + + /* make sure task context has a context save area */ + if (!task->context_save_area_ea) + return MARS_ERROR_FORMAT; + + mars_mutex_lock_get(semaphore_ea, (struct mars_mutex *)&semaphore); + + /* check if semaphore wait limit reached */ + if (semaphore.wait_count == MARS_TASK_SEMAPHORE_WAIT_MAX) { + mars_mutex_unlock_put(semaphore_ea, + (struct mars_mutex *)&semaphore); + return MARS_ERROR_LIMIT; + } + + if (semaphore.count <= 0) { + uint8_t wait_tail = (semaphore.wait_head + semaphore.wait_count) + % MARS_TASK_SEMAPHORE_WAIT_MAX; + + /* add id to wait list */ + semaphore.wait_id[wait_tail] = task->id.workload_id; + semaphore.wait_count++; + + mars_mutex_unlock_put(semaphore_ea, + (struct mars_mutex *)&semaphore); + + /* wait for signal */ + mars_task_module_signal_wait(mars_task_module_get_heap()); + + return MARS_SUCCESS; + } + + /* decrement semaphore count */ + semaphore.count--; + + mars_mutex_unlock_put(semaphore_ea, (struct mars_mutex *)&semaphore); + + return MARS_SUCCESS; +} + +int mars_task_semaphore_release(uint64_t semaphore_ea) +{ + /* check function params */ + if (!semaphore_ea) + return MARS_ERROR_NULL; + if (semaphore_ea & MARS_TASK_SEMAPHORE_ALIGN_MASK) + return MARS_ERROR_ALIGN; + + mars_mutex_lock_get(semaphore_ea, (struct mars_mutex *)&semaphore); + + /* increment semaphore count */ + semaphore.count++; + + /* signal those that are waiting */ + if (semaphore.count > 0 && semaphore.wait_count) { + /* decrement semaphore count */ + semaphore.count--; + + /* signal waiting task */ + mars_task_module_signal_send( + semaphore.wait_id[semaphore.wait_head]); + + /* flush id from wait list */ + semaphore.wait_count--; + semaphore.wait_head++; + semaphore.wait_head %= MARS_TASK_SEMAPHORE_WAIT_MAX; + } + + mars_mutex_unlock_put(semaphore_ea, (struct mars_mutex *)&semaphore); + + return MARS_SUCCESS; +} diff --git a/common/libspumars/spu/task/lib/task_signal.c b/common/libspumars/spu/task/lib/task_signal.c new file mode 100644 index 00000000..3bdcf014 --- /dev/null +++ b/common/libspumars/spu/task/lib/task_signal.c @@ -0,0 +1,33 @@ +#include +#include +#include + +#include "../module/task_module.h" + +int mars_task_signal_send(struct mars_task_id *id) +{ + /* check function params */ + if (!id) + return MARS_ERROR_NULL; + + return mars_task_module_signal_send(id->workload_id); +} + +int mars_task_signal_wait(void) +{ + struct mars_task_context *task; + + /* get task context */ + task = mars_task_module_get_task(); + + /* make sure task context has a context save area */ + if (!task->context_save_area_ea) + return MARS_ERROR_FORMAT; + + return mars_task_module_signal_wait(mars_task_module_get_heap()); +} + +int mars_task_signal_try_wait(void) +{ + return mars_task_module_signal_try_wait(); +} diff --git a/common/libspumars/spu/task/module/task_module.c b/common/libspumars/spu/task/module/task_module.c new file mode 100644 index 00000000..9d77fe5d --- /dev/null +++ b/common/libspumars/spu/task/module/task_module.c @@ -0,0 +1,358 @@ +#include + +#include +#include + +#include "task_module.h" + +#define MARS_TASK_MODULE_DMA_SIZE_MAX 16384 +#define MARS_TASK_MODULE_DMA_SIZE_MASK 0x7f + +/* global task variables */ +static struct mars_task_context *task; + +/* called by task_switch.S */ +void __module_main(void); +void __dma_registers(void *ptr, int put); +void __task_save(void *task_heap); +void __task_restore(int task_cached); + +/* defined in task_switch.S */ +extern void *__task_stack; +extern void task_exit(void); +extern void task_save(void *task_heap, int wait); +extern void task_restore(int task_cached); + +/* task entry */ +typedef void (*mars_task_entry)(struct mars_task_args *args, + struct mars_task_module_syscalls *task_module_syscalls); + +static uint32_t get_ticks(void) +{ + return mars_module_get_ticks(); +} + +static uint16_t get_kernel_id(void) +{ + return mars_module_get_kernel_id(); +} + +static struct mars_task_context *get_task(void) +{ + return (struct mars_task_context *)mars_module_get_workload(); +} + +static struct mars_task_context * + get_task_by_id(const struct mars_task_id *task_id) +{ + return (struct mars_task_context *) + mars_module_get_workload_by_id(task_id->workload_id); +} + +static void dma_wait(void) +{ + mars_module_dma_wait(MARS_TASK_MODULE_DMA_TAG); +} + +static void dma(void *ls, uint64_t ea, int size, int put) +{ + if (put) + mars_module_dma_put(ls, ea, size, MARS_TASK_MODULE_DMA_TAG); + else + mars_module_dma_get(ls, ea, size, MARS_TASK_MODULE_DMA_TAG); +} + +/* + * Structure of Context Save Area + * High Address + * +------------------------------------+ + * | Local Storage (stack) | + * +------------------------------------+ + * | Local Storage (text + data + heap) | + * +------------------------------------+ + * | Non-volatile Registers | + * +------------------------------------+ + * Low Address +*/ +static void dma_context(uint32_t low_size, uint32_t high_size, int put) +{ + /* save or restore data segment and heap (low address) */ + dma((void *)task->data_vaddr, task->context_save_area_ea + + MARS_TASK_REGISTER_SAVE_AREA_SIZE, + low_size, put); + + /* save or restore stack (high address) */ + dma((void *)MARS_TASK_BASE_ADDR + MARS_TASK_CONTEXT_SAVE_SIZE_MAX - + high_size, task->context_save_area_ea + + MARS_TASK_REGISTER_SAVE_AREA_SIZE + low_size, + high_size, put); + + dma_wait(); +} + +void __dma_registers(void *ptr, int put) +{ + /* dma registers state to/from the context save area */ + dma(ptr, task->context_save_area_ea, MARS_TASK_REGISTER_SAVE_AREA_SIZE, + put); + + dma_wait(); +} + +void __task_save(void *task_heap) +{ + /* save workload stack pointer */ + task->stack = (uint32_t)__task_stack; + + /* save data segment and heap size (low address) */ + task->context_save_area_low_size = + ((uintptr_t)task_heap - task->data_vaddr + + MARS_TASK_MODULE_DMA_SIZE_MASK) & + ~MARS_TASK_MODULE_DMA_SIZE_MASK; + + /* save used stack size (high address) */ + task->context_save_area_high_size = + (MARS_TASK_BASE_ADDR + MARS_TASK_CONTEXT_SAVE_SIZE_MAX - + (uintptr_t)__task_stack + + MARS_TASK_MODULE_DMA_SIZE_MASK) & + ~MARS_TASK_MODULE_DMA_SIZE_MASK; + + /* save context MPU storage state */ + dma_context(task->context_save_area_low_size, + task->context_save_area_high_size, 1); +} + +void __task_restore(int task_cached) +{ + /* if task not cashed restore context MPU storage state */ + if (!task_cached) + dma_context(task->context_save_area_low_size, + task->context_save_area_high_size, 0); + + /* restore workload stack pointer */ + __task_stack = (void *)task->stack; +} + +static void task_yield(void *task_heap) +{ + task_save(task_heap, 0); +} + +static void task_arg_copy(struct mars_task_args* __restrict__ dst, const struct mars_task_args* __restrict__ src) +{ + __vector const int *v_src = (__vector const int*)src; + __vector int *v_dst = (__vector int*)dst; + __vector int *v_end = (__vector int*)((char*)src + sizeof(*src)); + while(__builtin_expect(v_src < v_end, 1)) + *v_dst++ = *v_src++; +} + +static int task_schedule(uint16_t workload_id, + const struct mars_task_args *args, + uint8_t priority) +{ + int ret; + struct mars_task_context *schedule_task; + struct mars_workload_context *schedule_workload; + + ret = mars_module_workload_schedule_begin(workload_id, priority, + &schedule_workload); + if (ret != MARS_SUCCESS) + return ret; + + /* cast workload context to task context */ + schedule_task = (struct mars_task_context *)schedule_workload; + + /* initialize task specific context variables */ + schedule_task->stack = 0; + schedule_task->exit_code = 0; + if (args) + task_arg_copy(&schedule_task->args, args); + + /* end process to schedule the workload in the workload queue */ + return mars_module_workload_schedule_end(workload_id, 0); +} + +static int task_unschedule(uint16_t workload_id, int32_t exit_code) +{ + int ret; + struct mars_task_context *unschedule_task; + struct mars_workload_context *unschedule_workload; + + ret = mars_module_workload_unschedule_begin(workload_id, + &unschedule_workload); + if (ret != MARS_SUCCESS) + return ret; + + /* cast workload context to task context */ + unschedule_task = (struct mars_task_context *)unschedule_workload; + + unschedule_task->exit_code = exit_code; + + /* end process to unschedule the workload in the workload queue */ + return mars_module_workload_unschedule_end(workload_id); +} + +static int task_wait(uint16_t workload_id, void *task_heap) +{ + int ret; + + ret = mars_module_workload_wait_set(workload_id); + if (ret != MARS_SUCCESS) + return ret; + + task_save(task_heap, 1); + + return mars_module_workload_wait_reset(); +} + +static int task_try_wait(uint16_t workload_id) +{ + /* make sure workload is initialized */ + if (!mars_module_workload_query(workload_id, + MARS_WORKLOAD_QUERY_IS_INITIALIZED)) + return MARS_ERROR_STATE; + + /* if workload not finished return busy */ + if (!mars_module_workload_query(workload_id, + MARS_WORKLOAD_QUERY_IS_FINISHED)) + return MARS_ERROR_BUSY; + + return mars_module_workload_wait_reset(); +} + +static void task_signal_host(uint64_t watch_point_ea) +{ + mars_module_host_signal_send(watch_point_ea); +} + +static int task_signal_send(uint16_t workload_id) +{ + return mars_module_workload_signal_set(workload_id); +} + +static int task_signal_wait(void *task_heap) +{ + task_save(task_heap, 1); + + return mars_module_workload_signal_reset(); +} + +static int task_signal_try_wait(void) +{ + /* if signal not yet received return busy */ + if (!mars_module_workload_query(mars_module_get_workload_id(), + MARS_WORKLOAD_QUERY_IS_SIGNAL_SET)) + return MARS_ERROR_BUSY; + + return mars_module_workload_signal_reset(); +} + +static int task_call_host(uint64_t callback_ea, + const struct mars_callback_args *in, + struct mars_callback_args *out, void *task_heap) +{ + int ret; + + ret = mars_module_host_callback_set(callback_ea, in); + if (ret != MARS_SUCCESS) + return ret; + + task_save(task_heap, 1); + + mars_module_workload_signal_reset(); + + return mars_module_host_callback_reset(out); +} + +static struct mars_task_module_syscalls task_module_syscalls = +{ + get_ticks, + get_kernel_id, + get_task, + get_task_by_id, + + task_exit, + task_yield, + task_schedule, + task_unschedule, + task_wait, + task_try_wait, + task_signal_host, + task_signal_send, + task_signal_wait, + task_signal_try_wait, + task_call_host, + + mars_module_mutex_lock_get, + mars_module_mutex_unlock_put, + + mars_module_dma_get, + mars_module_dma_put, + mars_module_dma_wait +}; + +static void task_run(void) +{ + __vector unsigned char *bss_ptr, *bss_end; + + /* load the read-write data segment */ + dma((void *)task->data_vaddr, task->data_ea, task->data_size, 0); + + /* 0 the bss section */ + bss_ptr = (__vector unsigned char *)(task->data_vaddr + + task->data_size); + bss_end = (__vector unsigned char *)((void *)bss_ptr + + task->bss_size); + + while (__builtin_expect(bss_ptr < bss_end, 1)) + *bss_ptr++ = spu_splats((unsigned char)0); + + dma_wait(); + + /* sync before executing loaded code */ + spu_sync(); + + /* call entry function */ + ((mars_task_entry)task->entry)(&task->args, &task_module_syscalls); +} + +void __module_main(void) +{ + int task_cached = 0; + int text_cached = 0; + uint64_t *cached_text_ea = (uint64_t *)(MARS_TASK_BASE_ADDR - 16); + + /* get task context */ + task = get_task(); + + /* check if this workload module was in the cache */ + if (mars_module_workload_query(mars_module_get_workload_id(), + MARS_WORKLOAD_QUERY_IS_MODULE_CACHED)) { + /* check if task context is cached in mpu storage */ + task_cached = mars_module_workload_query( + mars_module_get_workload_id(), + MARS_WORKLOAD_QUERY_IS_CONTEXT_CACHED); + + /* check if text is cached in mpu storage */ + text_cached = task_cached || (*cached_text_ea == task->text_ea); + } + + /* only reload the readonly text segment if different from cached */ + if (!text_cached) { + /* set info of current cached text */ + *cached_text_ea = task->text_ea; + + /* dma text segment from host storage to mpu storage */ + dma((void *)MARS_TASK_BASE_ADDR, + task->text_ea, task->text_size, 0); + } + + /* if stack pointer is uninitialized run fresh, otherwise resume */ + if (!task->stack) + task_run(); + else + task_restore(task_cached); + + /* we should never reach here */ +} diff --git a/common/libspumars/spu/task/module/task_module.h b/common/libspumars/spu/task/module/task_module.h new file mode 100644 index 00000000..a312b29a --- /dev/null +++ b/common/libspumars/spu/task/module/task_module.h @@ -0,0 +1,179 @@ +#ifndef __MARS_TASK_MODULE_H__ +#define __MARS_TASK_MODULE_H__ + +#include +#include + +#include "mars/callback_types.h" +#include "mars/mutex_types.h" +#include "mars/task_types.h" + +#include "task_internal_types.h" + +#define MARS_TASK_MODULE_DMA_TAG 31 +#define MARS_TASK_MODULE_DMA_TAG_MAX 31 + +/* mars task module syscalls */ +struct mars_task_module_syscalls { + uint32_t (*get_ticks)(void); + uint16_t (*get_kernel_id)(void); + struct mars_task_context * (*get_task)(void); + struct mars_task_context * (*get_task_by_id) + (const struct mars_task_id *task_id); + + void (*exit)(void); + void (*yield)(void *heap); + int (*schedule)(uint16_t workload_id, + const struct mars_task_args *args, + uint8_t priority); + int (*unschedule)(uint16_t workload_id, int32_t exit_code); + int (*wait)(uint16_t workload_id, void *heap); + int (*try_wait)(uint16_t workload_id); + void (*signal_host)(uint64_t watch_point_ea); + int (*signal_send)(uint16_t workload_id); + int (*signal_wait)(void *heap); + int (*signal_try_wait)(void); + int (*call_host)(uint64_t callback_ea, + const struct mars_callback_args *in, + struct mars_callback_args *out, void *heap); + + int (*mutex_lock_get)(uint64_t mutex_ea, + struct mars_mutex *mutex); + int (*mutex_unlock_put)(uint64_t mutex_ea, + struct mars_mutex *mutex); + + int (*dma_get)(void *ls, uint64_t ea, uint32_t size, uint32_t tag); + int (*dma_put)(const void *ls, uint64_t ea, uint32_t size, + uint32_t tag); + int (*dma_wait)(uint32_t tag); +}; + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct mars_task_module_syscalls *mars_task_module_syscalls; + +static inline void *mars_task_module_get_heap(void) +{ + return sbrk(0); +} + +static inline uint32_t mars_task_module_get_ticks(void) +{ + return (*mars_task_module_syscalls->get_ticks)(); +} + +static inline uint16_t mars_task_module_get_kernel_id(void) +{ + return (*mars_task_module_syscalls->get_kernel_id)(); +} + +static inline struct mars_task_context *mars_task_module_get_task(void) +{ + return (*mars_task_module_syscalls->get_task)(); +} + +static inline struct mars_task_context *mars_task_module_get_task_by_id + (const struct mars_task_id *task_id) +{ + return (*mars_task_module_syscalls->get_task_by_id)(task_id); +} + +static inline void mars_task_module_exit(void) +{ + (*mars_task_module_syscalls->exit)(); +} + +static inline void mars_task_module_yield(void *task_heap) +{ + (*mars_task_module_syscalls->yield)(task_heap); +} + +static inline int mars_task_module_schedule(uint16_t workload_id, + const struct mars_task_args *args, + uint8_t priority) +{ + return (*mars_task_module_syscalls->schedule)(workload_id, args, + priority); +} + +static inline int mars_task_module_unschedule(uint16_t workload_id, + int32_t exit_code) +{ + return (*mars_task_module_syscalls->unschedule)(workload_id, exit_code); +} + +static inline int mars_task_module_wait(uint16_t workload_id, void *task_heap) +{ + return (*mars_task_module_syscalls->wait)(workload_id, task_heap); +} + +static inline int mars_task_module_try_wait(uint16_t workload_id) +{ + return (*mars_task_module_syscalls->try_wait)(workload_id); +} + +static inline void mars_task_module_signal_host(uint64_t watch_point_ea) +{ + (*mars_task_module_syscalls->signal_host)(watch_point_ea); +} + +static inline int mars_task_module_signal_send(uint16_t workload_id) +{ + return (*mars_task_module_syscalls->signal_send)(workload_id); +} + +static inline int mars_task_module_signal_wait(void *task_heap) +{ + return (*mars_task_module_syscalls->signal_wait)(task_heap); +} + +static inline int mars_task_module_signal_try_wait(void) +{ + return (*mars_task_module_syscalls->signal_try_wait)(); +} + +static inline int mars_task_module_call_host(uint64_t callback_ea, + const struct mars_callback_args *in, + struct mars_callback_args *out, + void *task_heap) +{ + return (*mars_task_module_syscalls->call_host)(callback_ea, in, out, + task_heap); +} + +static inline int mars_mutex_lock_get(uint64_t mutex_ea, + struct mars_mutex *mutex) +{ + return (*mars_task_module_syscalls->mutex_lock_get)(mutex_ea, mutex); +} + +static inline int mars_mutex_unlock_put(uint64_t mutex_ea, + struct mars_mutex *mutex) +{ + return (*mars_task_module_syscalls->mutex_unlock_put)(mutex_ea, mutex); +} + +static inline int mars_dma_get(void *ls, uint64_t ea, uint32_t size, + uint32_t tag) +{ + return (*mars_task_module_syscalls->dma_get)(ls, ea, size, tag); +} + +static inline int mars_dma_put(const void *ls, uint64_t ea, uint32_t size, + uint32_t tag) +{ + return (*mars_task_module_syscalls->dma_put)(ls, ea, size, tag); +} + +static inline int mars_dma_wait(uint32_t tag) +{ + return (*mars_task_module_syscalls->dma_wait)(tag); +} + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/common/libspumars/spu/task/module/task_switch.S b/common/libspumars/spu/task/module/task_switch.S new file mode 100644 index 00000000..195431c0 --- /dev/null +++ b/common/libspumars/spu/task/module/task_switch.S @@ -0,0 +1,195 @@ +#define NUM_REGS 48 /* number of registers to be saved/restored */ + +.section .bss + +/* void *__module_stack */ +.align 4 +.globl __module_stack +__module_stack: +.space 16 + +/* void *__task_stack */ +.align 4 +.globl __task_stack +__task_stack: +.space 16 + +/* void *__work_stack*/ +.align 4 +.global __work_stack +__work_stack: +.space (NUM_REGS + 3) * 16 + + +.text + +/* void mars_module_main(void) */ +.global mars_module_main +.type mars_module_main, @function +mars_module_main: + stqd $LR, 16($SP) /* save link register */ + stqd $SP, -32($SP) /* save back chain */ + ai $SP, $SP, -32 /* push stack frame */ + + stqa $SP, __module_stack /* save module stack */ + brsl $LR, __module_main /* call module main body */ + + ai $SP, $SP, 32 /* pop stack frame */ + lqd $LR, 16($SP) /* restore link register */ + bi $LR /* return */ + +.size mars_module_main, .-mars_module_main + + +/* void task_exit(void) */ +.global task_exit +.type task_exit, @function +task_exit: + stqd $LR, 16($SP) /* save link register */ + stqd $SP, -32($SP) /* save back chain */ + ai $SP, $SP, -32 /* push stack frame */ + + lqa $SP, __module_stack /* restore module stack */ + br mars_module_workload_finish /* module finish (no return) */ + +.size task_exit, .-task_exit + + +/* void task_save(void *task_heap, int wait) */ +.global task_save +.type task_save, @function +task_save: + stqd $LR, 16($SP) /* save link register */ + stqd $SP, -32($SP) /* save back chain */ + ai $SP, $SP, -32 /* push stack frame */ + + stqa $4, __work_stack /* save func param */ + + stqa $SP, __task_stack /* save task stack */ + + lqa $SP, __module_stack /* restore module stack */ + + brsl $LR, __task_save /* call task save body */ + + brsl $LR, __registers_save /* save registers */ + + lqa $2, __work_stack /* restore func param */ + + brz $2, mars_module_workload_yield /* module yield (no return) */ + br mars_module_workload_wait /* module wait (no return) */ + +.size task_save, .-task_save + + +/* void task_restore(int task_cached) */ +.global task_restore +.type task_restore, @function +task_restore: + stqd $LR, 16($SP) /* save link register */ + stqd $SP, -32($SP) /* save back chain */ + ai $SP, $SP, -32 /* push stack frame */ + + stqa $3, __work_stack /* save func param */ + + brsl $LR, __registers_restore /* restore registers */ + + lqa $3, __work_stack /* restore func param */ + + brsl $LR, __task_restore /* call task restore body */ + + lqa $SP, __task_stack /* restore task stack */ + sync /* sync before execution */ + + ai $SP, $SP, 32 /* pop task_save stack frame */ + lqd $LR, 16($SP) /* restore link register */ + bi $LR /* return from task_save call */ + +.size task_restore, .-task_restore + + +/* + * Registers are saved/restored to task module work stack + * + * High Address +* +------------------------+ <--- TOP OF STACK (__work_stack + 48 + 768) + * | Non-volatile Registers | | + * | $127 | | + * | $126 | | + * | $125 | (size) + * | ... | NUM_REGS * 16 (48 * 16 = 768) + * | $82 | | + * | $81 | | + * | $80 | | + * +------------------------+ <--- REGS_PTR (__work_stack + 48) + * | Work Code Block | + * +------------------------+ <--- CODE_PTR + 16 (__work_stack + 32) + * | Loop Code Block | + * +------------------------+ <--- CODE_PTR (__work_stack + 16) + * | Save Func Param | + * +------------------------+ <--- __work_stack + * Low Address + */ + +#define CODE_PTR $74 /* where code will be loaded into stack */ +#define REGS_PTR $75 /* where regs will be placed into stack */ +#define REGS_INC $76 /* register increment value for instruction */ +#define TEMP $77 /* temporary register */ +#define INST $78 /* register to store volatile instruction */ +#define INST_MASK $79 /* mask instruction to store or load */ + +__registers_save: + stqd $LR, 16($SP) /* save link register */ + stqd $SP, -32($SP) /* save back chain pointer */ + ai $SP, $SP, -32 /* push stack frame */ + + il INST_MASK, 0 /* no mask - default is stqd */ + br body /* jump to the body */ + +__registers_restore: + stqd $LR, 16($SP) /* save link register */ + stqd $SP, -32($SP) /* save back chain pointer */ + ai $SP, $SP, -32 /* push stack frame */ + + ila $3, __work_stack + 48 /* ptr = __work_stack */ + il $4, 0 /* restore so put = 0 */ + brsl $LR, __dma_registers /* call __dma_registers(0) */ + + il INST_MASK, 16 /* set necessary bits */ + shlqbyi INST_MASK, INST_MASK, 15 /* shift to correct position */ + +body: + ila CODE_PTR, __work_stack + 16 /* set code load addr pointer */ + ila REGS_PTR, __work_stack + 48 /* set regs load addr pointer */ + lqr TEMP, loop /* load loop code to register */ + stqd TEMP, 0(CODE_PTR) /* store loop code to stack */ + lqr INST, inst /* load inst code to register */ + or INST, INST, INST_MASK /* mask instr to stqd or lqd */ + il REGS_INC, 1 /* set bit in reg incrementer */ + shlqbyi REGS_INC, REGS_INC, 12 /* shift to correct word slot */ + il TEMP, NUM_REGS - 1 /* initialize loop counter */ + sync /* wait till loop code stored */ + bisl $LR, CODE_PTR /* jump and return */ + + brnz INST_MASK, done /* restore called so done */ + + ila $3, __work_stack + 48 /* ptr = __work_stack */ + il $4, 1 /* regs save so put = 1 */ + brsl $LR, __dma_registers /* call __dma_registers(1) */ + +done: + ai $SP, $SP, 32 /* pop stack frame */ + lqd $LR, 16($SP) /* load saved link register */ + bi $LR /* done so return to caller */ + + .balignl 16, 0 /* align self modifying code */ +loop: + stqd INST, 16(CODE_PTR) /* store inst code to stack */ + a INST, INST, REGS_INC /* increment register number */ + ai TEMP, TEMP, -1 /* decrment loop counter */ + sync /* wait till inst code stored */ + +inst: + stqd $80, 0(REGS_PTR) /* store reg to stack */ + ai REGS_PTR, REGS_PTR, 16 /* increment regs pointer */ + biz TEMP, $LR /* if (TEMP == 0) done */ + br loop /* if (TEMP != 0) loop */ diff --git a/common/libspumars/task/common/task_barrier_internal_types.h b/common/libspumars/task/common/task_barrier_internal_types.h new file mode 100644 index 00000000..b973c66d --- /dev/null +++ b/common/libspumars/task/common/task_barrier_internal_types.h @@ -0,0 +1,22 @@ +#ifndef __MARS_TASK_BARRIER_INTERNAL_TYPES_H__ +#define __MARS_TASK_BARRIER_INTERNAL_TYPES_H__ + +#include + +#define MARS_TASK_BARRIER_SIZE 128 +#define MARS_TASK_BARRIER_ALIGN 128 +#define MARS_TASK_BARRIER_ALIGN_MASK 0x7f + +struct mars_task_barrier { + uint32_t lock; + uint32_t total; + uint32_t notified_count; + uint32_t waited_count; + uint16_t notify_wait_count; + uint16_t notify_wait_id[MARS_TASK_BARRIER_WAIT_MAX]; + uint16_t wait_count; + uint16_t wait_id[MARS_TASK_BARRIER_WAIT_MAX]; + uint64_t mars_context_ea; +} __attribute__((aligned(MARS_TASK_BARRIER_ALIGN))); + +#endif diff --git a/common/libspumars/task/common/task_event_flag_internal_types.h b/common/libspumars/task/common/task_event_flag_internal_types.h new file mode 100644 index 00000000..f5fae5e1 --- /dev/null +++ b/common/libspumars/task/common/task_event_flag_internal_types.h @@ -0,0 +1,22 @@ +#ifndef __MARS_TASK_EVENT_FLAG_INTERNAL_TYPES_H__ +#define __MARS_TASK_EVENT_FLAG_INTERNAL_TYPES_H__ + +#include + +#define MARS_TASK_EVENT_FLAG_SIZE 128 +#define MARS_TASK_EVENT_FLAG_ALIGN 128 +#define MARS_TASK_EVENT_FLAG_ALIGN_MASK 0x7f + +struct mars_task_event_flag { + uint32_t lock; + uint32_t bits; + uint8_t direction; + uint8_t clear_mode; + uint16_t wait_count; + uint16_t wait_id[MARS_TASK_EVENT_FLAG_WAIT_MAX + 1]; + uint32_t wait_mask[MARS_TASK_EVENT_FLAG_WAIT_MAX]; + uint8_t wait_mask_mode[MARS_TASK_EVENT_FLAG_WAIT_MAX + 1]; + uint64_t mars_context_ea; +} __attribute__((aligned(MARS_TASK_EVENT_FLAG_ALIGN))); + +#endif diff --git a/common/libspumars/task/common/task_internal_types.h b/common/libspumars/task/common/task_internal_types.h new file mode 100644 index 00000000..e6db0416 --- /dev/null +++ b/common/libspumars/task/common/task_internal_types.h @@ -0,0 +1,44 @@ +#ifndef __MARS_TASK_INTERNAL_TYPES_H__ +#define __MARS_TASK_INTERNAL_TYPES_H__ + +#include + +#include "mars/workload_types.h" + +#define MARS_TASK_MODULE_NAME "MARS TASK" + +#define MARS_TASK_CONTEXT_SIZE MARS_WORKLOAD_CONTEXT_SIZE +#define MARS_TASK_CONTEXT_ALIGN MARS_WORKLOAD_CONTEXT_ALIGN +#define MARS_TASK_CONTEXT_SAVE_ALIGN 128 + +#define MARS_TASK_REGISTER_SAVE_AREA_SIZE (16 * (127 - 80)) + +struct mars_task_context { + uint8_t workload_reserved[MARS_WORKLOAD_RESERVED_SIZE]; + + uint64_t text_ea; /* ea of text segment */ + uint64_t data_ea; /* ea of data segment */ + uint32_t text_vaddr; /* vaddr of text segment */ + uint32_t data_vaddr; /* vaddr of data segment */ + uint32_t text_size; /* size of text segment */ + uint32_t data_size; /* size of data segment */ + uint32_t bss_size; /* size of bss segment */ + uint32_t entry; /* entry address of exec */ + uint32_t stack; /* stack pointer of exec */ + uint32_t exit_code; /* exit code */ + uint64_t context_save_area_ea; /* context save area */ + uint32_t context_save_area_low_size; /* size of low save area */ + uint32_t context_save_area_high_size; /* size of high save area */ + struct mars_task_id id; /* task id */ + struct mars_task_args args; /* task args */ + + uint8_t pad[MARS_TASK_CONTEXT_SIZE - + (MARS_WORKLOAD_RESERVED_SIZE + + sizeof(uint64_t)*3 + + sizeof(uint32_t)*10 + + sizeof(struct mars_task_id) + + sizeof(struct mars_task_args)) + ]; +} __attribute__((aligned(MARS_TASK_CONTEXT_ALIGN))); + +#endif diff --git a/common/libspumars/task/common/task_queue_internal_types.h b/common/libspumars/task/common/task_queue_internal_types.h new file mode 100644 index 00000000..7f428bb0 --- /dev/null +++ b/common/libspumars/task/common/task_queue_internal_types.h @@ -0,0 +1,34 @@ +#ifndef __MARS_TASK_QUEUE_INTERNAL_TYPES_H__ +#define __MARS_TASK_QUEUE_INTERNAL_TYPES_H__ + +#include + +#define MARS_TASK_QUEUE_SIZE 128 +#define MARS_TASK_QUEUE_ALIGN 128 +#define MARS_TASK_QUEUE_ALIGN_MASK 0x7f +#define MARS_TASK_QUEUE_ENTRY_SIZE_MASK 0xf +#define MARS_TASK_QUEUE_ENTRY_ALIGN 16 +#define MARS_TASK_QUEUE_ENTRY_ALIGN_MASK 0xf +#define MARS_TASK_QUEUE_BUFFER_ALIGN 16 +#define MARS_TASK_QUEUE_BUFFER_ALIGN_MASK 0xf + +struct mars_task_queue { + uint32_t lock; + uint32_t size; + uint32_t depth; + uint32_t count; + uint64_t buffer_ea; + uint64_t push_ea; + uint64_t pop_ea; + uint8_t pad; + uint8_t direction; + uint8_t push_wait_head; + uint8_t pop_wait_head; + uint16_t push_wait_count; + uint16_t pop_wait_count; + uint16_t push_wait_id[MARS_TASK_QUEUE_WAIT_MAX]; + uint16_t pop_wait_id[MARS_TASK_QUEUE_WAIT_MAX]; + uint64_t mars_context_ea; +} __attribute__((aligned(MARS_TASK_QUEUE_ALIGN))); + +#endif diff --git a/common/libspumars/task/common/task_semaphore_internal_types.h b/common/libspumars/task/common/task_semaphore_internal_types.h new file mode 100644 index 00000000..07c0f88b --- /dev/null +++ b/common/libspumars/task/common/task_semaphore_internal_types.h @@ -0,0 +1,20 @@ +#ifndef __MARS_TASK_SEMAPHORE_INTERNAL_TYPES_H__ +#define __MARS_TASK_SEMAPHORE_INTERNAL_TYPES_H__ + +#include + +#define MARS_TASK_SEMAPHORE_SIZE 128 +#define MARS_TASK_SEMAPHORE_ALIGN 128 +#define MARS_TASK_SEMAPHORE_ALIGN_MASK 0x7f + +struct mars_task_semaphore { + uint32_t lock; + int32_t count; + uint16_t wait_count; + uint16_t wait_id[MARS_TASK_SEMAPHORE_WAIT_MAX]; + uint8_t wait_head; + uint8_t pad; + uint64_t mars_context_ea; +} __attribute__((aligned(MARS_TASK_SEMAPHORE_ALIGN))); + +#endif diff --git a/ppu/include/lv2/spu.h b/ppu/include/lv2/spu.h index 04357a63..d308297f 100644 --- a/ppu/include/lv2/spu.h +++ b/ppu/include/lv2/spu.h @@ -145,7 +145,7 @@ s32 sysSpuPrintfAttachThread(sys_spu_thread_t thread); \return zero if no error occured, nonzero otherwise. */ -s32 sysSpuPrintfDetach_Tread(sys_spu_thread_t thread); +s32 sysSpuPrintfDetachThread(sys_spu_thread_t thread); /*! \brief Terminate Spu printf service \param none diff --git a/ppu/include/sys/cond.h b/ppu/include/sys/cond.h index 8ebbe18b..26bf1d43 100644 --- a/ppu/include/sys/cond.h +++ b/ppu/include/sys/cond.h @@ -24,6 +24,14 @@ typedef struct sys_cond_attr char name[8]; /*!< Name. */ } sys_cond_attr_t; +#define sysCondAttrInitialize(x) \ + do { \ + x.attr_pshared = SYS_COND_ATTR_PSHARED; \ + x.key = 0; \ + x.flags = 0; \ + x.name[0] = '\0'; \ + } while(0) + /*! \brief Create a condition variable. \param cond Pointer to storage for the created condition variable identifier. \param mutex Pointer to the associated mutex. diff --git a/ppu/include/sys/mutex.h b/ppu/include/sys/mutex.h index d785abe6..f65d0954 100644 --- a/ppu/include/sys/mutex.h +++ b/ppu/include/sys/mutex.h @@ -21,7 +21,7 @@ #define SYS_MUTEX_ATTR_NOT_RECURSIVE 0x0020 /*! \brief Default sharing policy for mutex attributes. */ -#define SYS_MUTEX_ATTR_PSHARED 0x0200 +#define SYS_MUTEX_ATTR_NOT_PSHARED 0x0200 /*! \brief Mutex is adaptive. */ #define SYS_MUTEX_ATTR_ADAPTIVE 0x1000 @@ -69,6 +69,17 @@ typedef struct sys_mutex_attr char name[8]; } sys_mutex_attr_t; +#define sysMutexAttrInitialize(x) \ + do{ \ + x.attr_protocol = SYS_MUTEX_PROTOCOL_PRIO; \ + x.attr_recursive = SYS_MUTEX_ATTR_NOT_RECURSIVE; \ + x.attr_pshared = SYS_MUTEX_ATTR_NOT_PSHARED; \ + x.attr_adaptive = SYS_MUTEX_ATTR_NOT_ADAPTIVE; \ + x.key = 0; \ + x.flags = 0; \ + x.name[0] = '\0'; \ + }while(0) + /*! \brief Create a mutex. \param mutex Pointer to storage for the mutex id. \param attr Pointer to the mutex attributes. diff --git a/ppu/include/sys/spu.h b/ppu/include/sys/spu.h index ff38d771..137914ba 100644 --- a/ppu/include/sys/spu.h +++ b/ppu/include/sys/spu.h @@ -122,6 +122,20 @@ to the Cell Broadband Engine documentation. #define SPU_Sig_Notify_1 0x1400C //!< Signal notification 1 #define SPU_Sig_Notify_2 0x1C00C //!< Signal notification 2 +#define SPU_THREAD_GROUP_TYPE_NORMAL 0x0 +#define SPU_THREAD_GROUP_TYPE_SEQUENTIAL 0x1 +#define SPU_THREAD_GROUP_TYPE_SYSTEM 0x2 +#define SPU_THREAD_GROUP_TYPE_MEMORY_FROM_CONTAINER 0x4 + +#define SPU_THREAD_GROUP_JOIN_GROUP_EXIT 0x0001 +#define SPU_THREAD_GROUP_JOIN_ALL_THREADS_EXIT 0x0002 +#define SPU_THREAD_GROUP_JOIN_TERMINATED 0x0004 + +#define SPU_THREAD_GROUP_EVENT_RUN 0x1 +#define SPU_THREAD_GROUP_EVENT_RUN_KEY 0xFFFFFFFF53505500ULL +#define SPU_THREAD_GROUP_EVENT_EXCEPTION 0x2 +#define SPU_THREAD_GROUP_EVENT_EXCEPTION_KEY 0xFFFFFFFF53505503ULL + //! No thread attributes #define SPU_THREAD_ATTR_NONE 0x00 //! Enables interrupts. @@ -211,20 +225,83 @@ typedef struct _sys_spu_thread_arg */ typedef struct _sys_spu_thread_attr { - u32 nameAddress; //!< Effective address of the thread's name string. - u32 nameSize; //!< Size of the name string in bytes (including terminating null byte). - u32 attribute; //!< OR'ed list of SPU thread attribute flags (or \ref SPU_THREAD_ATTR_NONE) + const char *name ATTRIBUTE_PRXPTR; //!< Effective address of the thread's name string. + u32 nsize; //!< Size of the name string in bytes (including terminating null byte). + u32 option; //!< OR'ed list of SPU thread attribute flags (or \ref SPU_THREAD_ATTR_NONE) } sysSpuThreadAttribute; //! A structure containing SPU thread group attributes. typedef struct _sys_spu_thread_group_attr { - u32 nameSize; //!< Size of the name string in bytes (including terminating null byte). - u32 nameAddress; //!< Effective address of the thread group's name string. - u32 groupType; //!< Thread group type (\c 0 for normal thread groups). - u32 memContainer; //!< Memory container id (\c 0 for normal thread groups). + u32 nsize; //!< Size of the name string in bytes (including terminating null byte). + const char *name ATTRIBUTE_PRXPTR; //!< Effective address of the thread group's name string. + u32 type; //!< Thread group type (\c 0 for normal thread groups). + union { + sys_mem_container_t ct; //!< Memory container id (\c 0 for normal thread groups). + } option; } sysSpuThreadGroupAttribute; +#define sysSpuThreadAttributeInitialize(x) \ + do { \ + x.name = NULL; \ + x.nsize = 0; \ + x.option = SPU_THREAD_ATTR_NONE; \ + } while(0) + +#define sysSpuThreadAttributeName(x, s) \ + do { \ + x.name = s; \ + if(s == NULL) { \ + x.nsize = 0; \ + } else { \ + int n = 0; \ + for(; (n<127) && (s[n] != '\0'); n++) \ + ; \ + x.nsize = n + 1; \ + } \ + } while(0) + +#define sysSpuThreadAttributeOption(x, f) \ + do { \ + x.option = f; \ + } while(0) + +#define sysSpuThreadArgumentInitialize(x) \ + do { \ + x.arg0 = x.arg1 = x.arg2 = x.arg3 = 0; \ + } while(0) + +#define sysSpuThreadGroupAttributeInitialize(x) \ + do { \ + x.name = NULL; \ + x.nsize = 0; \ + x.type = SPU_THREAD_GROUP_TYPE_NORMAL; \ + } while(0) + +#define sysSpuThreadGroupAttributeName(x, s) \ + do { \ + x.name = s; \ + if(s == NULL) { \ + x.nsize = 0; \ + } else { \ + int n = 0; \ + for(; (n<127) && (s[n] != '\0'); n++) \ + ; \ + x.nsize = n + 1; \ + } \ + } while(0) + +#define sysSpuThreadGroupAttributeType(x, t) \ + do { \ + x.type = t; \ + } while(0) + +#define sysSpuThreadGroupAttributeMemoryContainer(x, ct) \ + do { \ + x.type |= SPU_THREAD_GROUP_TYPE_MEMORY_FROM_CONTAINER; \ + x.option.ct = ct; \ + } while(0) + /*! \brief Initialize the SPU management. \param spus Total number of needed SPUs (from 1 to 6). diff --git a/ppu/sprx/liblv2/exports.h b/ppu/sprx/liblv2/exports.h index 43801fcd..bd3570c5 100644 --- a/ppu/sprx/liblv2/exports.h +++ b/ppu/sprx/liblv2/exports.h @@ -142,7 +142,7 @@ EXPORT(sysSpuImageImport, 0xebe5f72f); EXPORT(sysSpuPrintfAttachGroup, 0xdd0c1e09); /* sysPrxForUser */ EXPORT(sysSpuPrintfAttachThread, 0x1ae10b92); /* sysPrxForUser */ EXPORT(sysSpuPrintfDetachGroup, 0x5fdfb2fe); /* sysPrxForUser */ -EXPORT(sysSpuPrintfDetach_Tread, 0xb3bbcf2a); /* sysPrxForUser */ +EXPORT(sysSpuPrintfDetachThread, 0xb3bbcf2a); /* sysPrxForUser */ EXPORT(sysSpuPrintfFinalize, 0xdd3b27ac); /* sysPrxForUser */ EXPORT(sysSpuPrintfInitialize, 0x45fe2fce); /* sysPrxForUser */ diff --git a/ppu_rules b/ppu_rules index c14c8e69..91dd077a 100644 --- a/ppu_rules +++ b/ppu_rules @@ -29,7 +29,7 @@ PS3LOADAPP := ps3load$(POSTFIX) RAW2H := raw2h$(POSTFIX) # fake SELF type4 / type8 tools -FSELF := fself.py +FSELF := fself FSELF_NPDRM := $(FSELF) -n # signed SELF type4 / type8 tools diff --git a/spu/Makefile b/spu/Makefile index f5faecd4..3a9e5d35 100644 --- a/spu/Makefile +++ b/spu/Makefile @@ -6,6 +6,8 @@ all: @$(MAKE) -C libsputhread --no-print-directory + @$(MAKE) -C libspudma --no-print-directory + @$(MAKE) -C libspuatomic --no-print-directory install-headers: @[ -d $(PSL1GHT)/spu ] || mkdir -p $(PSL1GHT)/spu @@ -13,8 +15,12 @@ install-headers: install: install-headers @$(MAKE) -C libsputhread install --no-print-directory + @$(MAKE) -C libspudma install --no-print-directory + @$(MAKE) -C libspuatomic install --no-print-directory clean: @$(MAKE) -C libsputhread clean --no-print-directory + @$(MAKE) -C libspudma clean --no-print-directory + @$(MAKE) -C libspuatomic clean --no-print-directory .PHONY: all clean install diff --git a/spu/include/dma/spu_dma.h b/spu/include/dma/spu_dma.h new file mode 100644 index 00000000..99d0b9ad --- /dev/null +++ b/spu/include/dma/spu_dma.h @@ -0,0 +1,391 @@ +#ifndef __SPU_DMA_H__ +#define __SPU_DMA_H__ + +#include +#include +#include + +#define MAX_DMA_BLOCK_SIZE 16384 + +#define spu_dma_ea2ls(ea, ls) (void*)((uintptr_t)(ls)+((uint32_t)(ea)&15)) + +#ifdef NO_SPU_DMA_ASSERT +#define spu_dma_assert(cond, ...) +#else +#ifndef SPU_DMA_ASSERT_VERBOSE +#define spu_dma_assert(cond, ...) spu_hcmpeq((cond), 0) +#else +#include +#define spu_dma_assert(cond, fmt, ...) \ +do { \ + if(!(cond)) { \ + spu_printf("%s:%u %s" fmt, __FILE__,__LINE__,"[spu_dma_assert]",##__VA_ARGS__); \ + __asm__ volatile("stopd $0,$0,$0\n"); \ + } \ +} while(0) +#endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------------------------- +// DMA assertion +//--------------------------------------------------------------------------------------------------------------------------------------------- +#define spu_dma_normal_assert(ls,ea,size,tag) \ + spu_dma_assert((((uintptr_t)(ls) & 0xf) == 0) & \ + (((uintptr_t)(ea) & 0xf) == 0) & \ + (((size) & 0xf) == 0) & \ + ((size) <= (16<<10)) & \ + ((tag) < 32), "ls=%#x,ea=%#llx,size=%#x,tag=%u\n", ls,ea,size,tag) + +#define spu_dma_small_assert(ls,ea,size,tag) \ + spu_dma_assert((((uintptr_t)(ls) & 0xf) == ((uintptr_t)(ea) & 0xf)) & \ + (((uintptr_t)(ls) & (size - 1)) == 0) & \ + (((size)==1)||((size)==2)||((size)==4)||((size)==8)) & \ + ((tag) < 32), "ls=%#x,ea=%#llx,size=%#x,tag=%u\n", ls,ea,size,tag) + +#define spu_dma_list_assert(ls,ea,la,lsize,tag) \ + spu_dma_assert((((uintptr_t)(ls) & 0xf) == 0) & \ + (((uintptr_t)(ea) & 0xf) == 0) & \ + (((uintptr_t)(la) & 7) == 0) & \ + (((lsize) & 7) == 0) & \ + ((lsize) <= (16<<10)) & \ + ((tag) < 32), "ls=%#x,ea=%#llx,la=%#x,lsize=%#x,tag=%u\n", ls,ea,(uintptr_t)(la),lsize,tag) + +#define spu_dma_atomic_assert(ls,ea) \ + spu_dma_assert((((uintptr_t)(ls) & 0x7f) == 0) & \ + (((uintptr_t)(ea) & 0x7f) == 0), "ls=%#x,ea=%#llx\n", ls,ea) + +#define spu_dma_putqlluc_assert(ls,ea,tag) \ + spu_dma_assert((((uintptr_t)(ls) & 0x7f) == 0) & \ + (((uintptr_t)(ea) & 0x7f) == 0) & \ + ((tag) < 32), "ls=%#x,ea=%#llx,tag=%u\n", ls,ea,tag) + +#define spu_dma_large_assert(ls,ea,tag) \ + spu_dma_assert((((uintptr_t)(ls) & 0xf) == 0) & \ + (((uintptr_t)(ea) & 0xf) == 0) & \ + ((tag) < 32), "ls=%#x,ea=%#llx,tag=%u\n", ls,ea,tag) + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef mfc_list_element_t spu_dma_list_element; + +//--------------------------------------------------------------------------------------------------------------------------------------------- +// DMA which transfer size is a multiple of 16Bytes +//--------------------------------------------------------------------------------------------------------------------------------------------- +__attribute__ ((__always_inline__)) +static inline void spu_dma_put(const void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_normal_assert(ls, ea, size, tag); + mfc_put((volatile void*)(uintptr_t)ls, ea, size, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_putb(const void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_normal_assert(ls, ea, size, tag); + mfc_putb((volatile void*)(uintptr_t)ls, ea, size, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_putf(const void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_normal_assert(ls, ea, size, tag); + mfc_putf((volatile void*)(uintptr_t)ls, ea, size, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_get(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_normal_assert(ls, ea, size, tag); + mfc_get(ls, ea, size, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_getb(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_normal_assert(ls, ea, size, tag); + mfc_getb(ls, ea, size, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_getf(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_normal_assert(ls, ea, size, tag); + mfc_getf(ls, ea, size, tag, tid, rid); +} + + +//--------------------------------------------------------------------------------------------------------------------------------------------- +// DMA which transfer size is within 16Bytes +//--------------------------------------------------------------------------------------------------------------------------------------------- +__attribute__ ((__always_inline__)) +static inline void spu_dma_small_put(const void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_small_assert(ls, ea, size, tag); + mfc_put((volatile void*)(uintptr_t)ls, ea, size, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_small_putb(const void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_small_assert(ls, ea, size, tag); + mfc_putb((volatile void*)(uintptr_t)ls, ea, size, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_small_putf(const void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_small_assert(ls, ea, size, tag); + mfc_putf((volatile void*)(uintptr_t)ls, ea, size, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_small_get(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_small_assert(ls, ea, size, tag); + mfc_get(ls, ea, size, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_small_getb(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_small_assert(ls, ea, size, tag); + mfc_getb(ls, ea, size, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_small_getf(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_small_assert(ls, ea, size, tag); + mfc_getf(ls, ea, size, tag, tid, rid); +} + + +//--------------------------------------------------------------------------------------------------------------------------------------------- +// List DMA +//--------------------------------------------------------------------------------------------------------------------------------------------- +__attribute__ ((__always_inline__)) +static inline void spu_dma_list_put(const void *ls, uint64_t ea, const spu_dma_list_element *list, uint32_t lsize, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_list_assert(ls, ea, list, lsize, tag); + mfc_putl((volatile void*)(uintptr_t)ls, ea, list, lsize, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_list_putb(const void *ls, uint64_t ea, const spu_dma_list_element *list, uint32_t lsize, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_list_assert(ls, ea, list, lsize, tag); + mfc_putlb((volatile void*)(uintptr_t)ls, ea, list, lsize, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_list_putf(const void *ls, uint64_t ea, const spu_dma_list_element *list, uint32_t lsize, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_list_assert(ls, ea, list, lsize, tag); + mfc_putlf((volatile void*)(uintptr_t)ls, ea, list, lsize, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_list_get(void *ls, uint64_t ea, const spu_dma_list_element *list, uint32_t lsize, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_list_assert(ls, ea, list, lsize, tag); + mfc_getl(ls, ea, list, lsize, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_list_getb(void *ls, uint64_t ea, const spu_dma_list_element *list, uint32_t lsize, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_list_assert(ls, ea, list, lsize, tag); + mfc_getlb(ls, ea, list, lsize, tag, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_list_getf(void *ls, uint64_t ea, const spu_dma_list_element *list, uint32_t lsize, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_list_assert(ls, ea, list, lsize, tag); + mfc_getlf(ls, ea, list, lsize, tag, tid, rid); +} + + +//--------------------------------------------------------------------------------------------------------------------------------------------- +// Atomic DMA +//--------------------------------------------------------------------------------------------------------------------------------------------- +__attribute__ ((__always_inline__)) +static inline void spu_dma_getllar(void *ls, uint64_t ea, uint32_t tid, uint32_t rid) +{ + spu_dma_atomic_assert(ls, ea); + mfc_getllar(ls, ea, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_putllc(const void *ls, uint64_t ea, uint32_t tid, uint32_t rid) +{ + spu_dma_atomic_assert(ls, ea); + mfc_putllc((volatile void*)(uintptr_t)ls, ea, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_putlluc(const void *ls, uint64_t ea, uint32_t tid, uint32_t rid) +{ + spu_dma_atomic_assert(ls, ea); + mfc_putlluc((volatile void*)(uintptr_t)ls, ea, tid, rid); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_putqlluc(const void *ls, uint64_t ea, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_putqlluc_assert(ls, ea, tag); + mfc_putqlluc((volatile void*)(uintptr_t)ls, ea, tag, tid, rid); +} + + +//--------------------------------------------------------------------------------------------------------------------------------------------- +// DMA utilities - data typed DMA +//--------------------------------------------------------------------------------------------------------------------------------------------- +void spu_dma_and_wait(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t cmd); + +static inline void spu_dma_put_uint8(uint8_t value, uint64_t ea, uint32_t tag, uint32_t tid, uint32_t rid) __attribute__ ((__always_inline__)); +static inline void spu_dma_put_uint16(uint16_t value, uint64_t ea, uint32_t tag, uint32_t tid, uint32_t rid) __attribute__ ((__always_inline__)); +static inline void spu_dma_put_uint32(uint32_t value, uint64_t ea, uint32_t tag, uint32_t tid, uint32_t rid) __attribute__ ((__always_inline__)); +static inline void spu_dma_put_uint64(uint64_t value, uint64_t ea, uint32_t tag, uint32_t tid, uint32_t rid) __attribute__ ((__always_inline__)); + +static inline uint8_t spu_dma_get_uint8(uint64_t ea, uint32_t tag, uint32_t tid, uint32_t rid) __attribute__ ((__always_inline__)); +static inline uint16_t spu_dma_get_uint16(uint64_t ea, uint32_t tag, uint32_t tid, uint32_t rid) __attribute__ ((__always_inline__)); +static inline uint32_t spu_dma_get_uint32(uint64_t ea, uint32_t tag, uint32_t tid, uint32_t rid) __attribute__ ((__always_inline__)); +static inline uint64_t spu_dma_get_uint64(uint64_t ea, uint32_t tag, uint32_t tid, uint32_t rid) __attribute__ ((__always_inline__)); + +#define spu_dma_put_uint_template(SIZE) \ +__attribute__ ((__always_inline__)) \ +static inline void \ +spu_dma_put_uint##SIZE(uint##SIZE##_t value, uint64_t ea, uint32_t tag, uint32_t tid, uint32_t rid) \ +{ \ + qword buf = (qword)spu_splats(value); \ + spu_dma_small_assert(ea, ea, sizeof(uint##SIZE##_t), tag); \ + spu_dma_and_wait(spu_dma_ea2ls(ea,&buf), ea, sizeof(uint##SIZE##_t), tag, MFC_CMD_WORD(tid,rid,MFC_PUT_CMD)); \ +} +spu_dma_put_uint_template(8) +spu_dma_put_uint_template(16) +spu_dma_put_uint_template(32) +spu_dma_put_uint_template(64) + +#define spu_dma_get_uint_template(SIZE) \ +__attribute__ ((__always_inline__)) \ +static inline uint##SIZE##_t \ +spu_dma_get_uint##SIZE(uint64_t ea, uint32_t tag, uint32_t tid, uint32_t rid) \ +{ \ + qword buf; \ + spu_dma_small_assert(ea, ea, sizeof(uint##SIZE##_t), tag); \ + spu_dma_and_wait(spu_dma_ea2ls(ea,&buf), ea, sizeof(uint##SIZE##_t), tag, MFC_CMD_WORD(tid,rid,MFC_GET_CMD)); \ + return *(uint##SIZE##_t*)((uintptr_t)&buf + ((uintptr_t)ea&15)); \ +} +spu_dma_get_uint_template(8) +spu_dma_get_uint_template(16) +spu_dma_get_uint_template(32) +spu_dma_get_uint_template(64) + + +//--------------------------------------------------------------------------------------------------------------------------------------------- +// DMA utilities - any size DMA +//--------------------------------------------------------------------------------------------------------------------------------------------- +void spu_dma_large_cmd(uintptr_t ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t cmd); + +__attribute__ ((__always_inline__)) +static inline void spu_dma_large_put(const void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_large_assert(ls, ea, tag); + spu_dma_large_cmd((uintptr_t)ls,ea,size,tag,MFC_CMD_WORD(tid,rid,MFC_PUT_CMD)); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_large_putb(const void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_large_assert(ls, ea, tag); + spu_dma_large_cmd((uintptr_t)ls,ea,size,tag,MFC_CMD_WORD(tid,rid,MFC_PUTB_CMD)); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_large_putf(const void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_large_assert(ls, ea, tag); + spu_dma_large_cmd((uintptr_t)ls,ea,size,tag,MFC_CMD_WORD(tid,rid,MFC_PUTF_CMD)); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_large_get(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_large_assert(ls, ea, tag); + spu_dma_large_cmd((uintptr_t)ls,ea,size,tag,MFC_CMD_WORD(tid,rid,MFC_GET_CMD)); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_large_getb(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_large_assert(ls, ea, tag); + spu_dma_large_cmd((uintptr_t)ls,ea,size,tag,MFC_CMD_WORD(tid,rid,MFC_GETB_CMD)); +} + +__attribute__ ((__always_inline__)) +static inline void spu_dma_large_getf(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + spu_dma_large_assert(ls, ea, tag); + spu_dma_large_cmd((uintptr_t)ls,ea,size,tag,MFC_CMD_WORD(tid,rid,MFC_GETF_CMD)); +} + + +//--------------------------------------------------------------------------------------------------------------------------------------------- +// DMA utilities - tag wait +//--------------------------------------------------------------------------------------------------------------------------------------------- +__attribute__ ((__always_inline__)) +static inline void spu_dma_cancel_tag_status_update() +{ + mfc_write_tag_update_immediate(); + do {} while(__builtin_expect(mfc_stat_tag_update() == 0,0)); + mfc_read_tag_status(); +} + +__attribute__ ((__always_inline__)) +static inline uint32_t spu_dma_cancel_and_wait_tag_status_any(uint32_t tagmask) +{ + spu_dma_cancel_tag_status_update(); + mfc_write_tag_mask(tagmask); + return mfc_read_tag_status_any(); +} + +__attribute__ ((__always_inline__)) +static inline uint32_t spu_dma_cancel_and_wait_tag_status_all(uint32_t tagmask) +{ + spu_dma_cancel_tag_status_update(); + mfc_write_tag_mask(tagmask); + return mfc_read_tag_status_all(); +} + +__attribute__ ((__always_inline__)) +static inline uint32_t spu_dma_wait_tag_status_immediate(uint32_t tagmask) +{ + mfc_write_tag_mask(tagmask); + return mfc_read_tag_status_immediate(); +} + +__attribute__ ((__always_inline__)) +static inline uint32_t spu_cma_wait_tag_status_any(uint32_t tagmask) +{ + mfc_write_tag_mask(tagmask); + return mfc_read_tag_status_any(); +} + +__attribute__ ((__always_inline__)) +static inline uint32_t spu_dma_wait_tag_status_all(uint32_t tagmask) +{ + mfc_write_tag_mask(tagmask); + return mfc_read_tag_status_all(); +} + +#define spu_dma_wait_atomic_status() mfc_read_atomic_status() + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/spu/include/sys/spu_atomic.h b/spu/include/sys/spu_atomic.h new file mode 100644 index 00000000..3c0cf072 --- /dev/null +++ b/spu/include/sys/spu_atomic.h @@ -0,0 +1,85 @@ +#ifndef __SPU_ATOMIC_H__ +#define __SPU_ATOMIC_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +uint32_t spu_atomic_nop32(uint32_t *ls, uint64_t ea); +uint64_t spu_atomic_nop64(uint64_t *ls, uint64_t ea); +uint32_t spu_atomic_incr32(uint32_t *ls, uint64_t ea); +uint64_t spu_atomic_incr64(uint64_t *ls, uint64_t ea); +uint32_t spu_atomic_decr32(uint32_t *ls, uint64_t ea); +uint64_t spu_atomic_decr64(uint64_t *ls, uint64_t ea); +uint32_t spu_atomic_test_and_decr32(uint32_t *ls, uint64_t ea); +uint64_t spu_atomic_test_and_decr64(uint64_t *ls, uint64_t ea); +uint32_t spu_atomic_or32(uint32_t *ls, uint64_t ea, uint32_t value); +uint64_t spu_atomic_or64(uint64_t *ls, uint64_t ea, uint64_t value); +uint32_t spu_atomic_add32(uint32_t *ls, uint64_t ea, uint32_t value); +uint64_t spu_atomic_add64(uint64_t *ls, uint64_t ea, uint64_t value); +uint32_t spu_atomic_and32(uint32_t *ls, uint64_t ea, uint32_t value); +uint64_t spu_atomic_and64(uint64_t *ls, uint64_t ea, uint64_t value); +uint32_t spu_atomic_sub32(uint32_t *ls, uint64_t ea, uint32_t value); +uint64_t spu_atomic_sub64(uint64_t *ls, uint64_t ea, uint64_t value); +uint32_t spu_atomic_store32(uint32_t *ls, uint64_t ea, uint32_t value); +uint64_t spu_atomic_store64(uint64_t *ls, uint64_t ea, uint64_t value); +uint32_t spu_atomic_compare_and_swap32(uint32_t *ls, uint64_t ea, uint32_t compare, uint32_t value); +uint64_t spu_atomic_compare_and_swap64(uint64_t *ls, uint64_t ea, uint64_t compare, uint64_t value); + +__attribute__ ((__always_inline__)) +static inline uint32_t spu_atomic_lock_line32(uint32_t *ls, uint64_t ea) +{ + unsigned int i = ((uint32_t)ea & 0x7f) >> 2; + + ea &= ~0x7f; + mfc_getllar(ls, ea, 0, 0); + mfc_read_atomic_status(); + spu_dsync(); + return ls[i]; +} + +__attribute__ ((__always_inline__)) +static inline uint64_t spu_atomic_lock_line64(uint64_t *ls, uint64_t ea) +{ + unsigned int i = ((uint32_t)ea & 0x7f) >> 3; + + ea &= ~0x7f; + mfc_getllar(ls, ea, 0, 0); + mfc_read_atomic_status(); + spu_dsync(); + return ls[i]; +} + +__attribute__ ((__always_inline__)) +static inline int spu_atomic_store_conditional32(uint32_t *ls, uint64_t ea, uint32_t value) +{ + unsigned int i = ((uint32_t)ea & 0x7f) >> 2; + + ls[i] = value; + ea &= ~0x7f; + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + return mfc_read_atomic_status(); +} + +__attribute__ ((__always_inline__)) +static inline int spu_atomic_store_conditional64(uint64_t *ls, uint64_t ea, uint64_t value) +{ + unsigned int i = ((uint32_t)ea & 0x7f) >> 3; + + ls[i] = value; + ea &= ~0x7f; + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + return mfc_read_atomic_status(); +} + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/spu/include/sys/spu_printf.h b/spu/include/sys/spu_printf.h new file mode 100644 index 00000000..44d0ff8d --- /dev/null +++ b/spu/include/sys/spu_printf.h @@ -0,0 +1,19 @@ +#ifndef __SPU_PRINTF_H__ +#define __SPU_PRINTF_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int _spu_call_event_va_arg(uint32_t _spup, const char *fmt, ...); + +#define spu_printf(fmt, args...) \ + _spu_call_event_va_arg(EVENT_PRINTF_PORT<") +endif + +include $(PSL1GHT)/spu_rules + +BUILD := build + +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PLATFORM)),) +#--------------------------------------------------------------------------------- +export BASEDIR := $(CURDIR) +export DEPS := $(BASEDIR)/deps +export LIBS := $(BASEDIR)/lib + +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + +export LIBDIR := $(LIBS)/$(PLATFORM) +export DEPSDIR := $(DEPS)/$(PLATFORM) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +LIBRARY := $(LIBDIR)/libspuatomic + +#--------------------------------------------------------------------------------- +INCLUDES := -I$(BASEDIR) -I$(BASEDIR)/../include -I$(BASEDIR)/../include/sys + +CFLAGS := -O2 -Wall $(MACHDEP) -DLIBRT_INTERNAL $(INCLUDES) +ASFLAGS := $(MACHDEP) -D__ASSEMBLY__ $(INCLUDES) + +#--------------------------------------------------------------------------------- +VPATH := $(BASEDIR) + +#--------------------------------------------------------------------------------- +OBJS := spu_atomic.o + +all: spu + +#--------------------------------------------------------------------------------- +spu: +#--------------------------------------------------------------------------------- + @[ -d $(LIBS)/spu ] || mkdir -p $(LIBS)/spu + @[ -d $(DEPS)/spu ] || mkdir -p $(DEPS)/spu + @[ -d spu ] || mkdir -p spu + @$(MAKE) PLATFORM=spu lib -C spu -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +install: all +#--------------------------------------------------------------------------------- + @[ -d $(PSL1GHT)/spu/lib ] || mkdir -p $(PSL1GHT)/spu/lib + @cp -frv $(CURDIR)/lib/spu/*.a $(PSL1GHT)/spu/lib + +#--------------------------------------------------------------------------------- +$(LIBRARY).a: $(OBJS) +#--------------------------------------------------------------------------------- + +.PHONY: lib spu install + +#--------------------------------------------------------------------------------- +lib: $(LIBRARY).a +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +clean: +#--------------------------------------------------------------------------------- + @echo clean ... + @rm -rf spu + @rm -rf $(DEPS) + @rm -rf $(LIBS) + +-include $(DEPSDIR)/*.d diff --git a/spu/libspuatomic/spu_atomic.c b/spu/libspuatomic/spu_atomic.c new file mode 100755 index 00000000..f7b5efa4 --- /dev/null +++ b/spu/libspuatomic/spu_atomic.c @@ -0,0 +1,409 @@ +#include "spu_atomic.h" + +uint32_t spu_atomic_add32(uint32_t *ls, uint64_t ea, uint32_t value) +{ + uint32_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 2; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + ls[i] = (old_value + value); + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint64_t spu_atomic_add64(uint64_t *ls, uint64_t ea, uint64_t value) +{ + uint64_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 3; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + ls[i] = (old_value + value); + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint32_t spu_atomic_and32(uint32_t *ls, uint64_t ea, uint32_t value) +{ + uint32_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 2; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + ls[i] = (old_value&value); + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint64_t spu_atomic_and64(uint64_t *ls, uint64_t ea, uint64_t value) +{ + uint64_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 3; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + ls[i] = (old_value&value); + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint32_t spu_atomic_compare_and_swap32(uint32_t *ls, uint64_t ea, uint32_t compare, uint32_t value) +{ + uint32_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 2; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + if(old_value != compare) return old_value; + + ls[i] = value; + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return value; +} + +uint64_t spu_atomic_compare_and_swap64(uint64_t *ls, uint64_t ea, uint64_t compare, uint64_t value) +{ + uint64_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 3; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + if(old_value != compare) return old_value; + + ls[i] = value; + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return value; +} + +uint32_t spu_atomic_decr32(uint32_t *ls, uint64_t ea) +{ + uint32_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 2; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + ls[i] = (old_value - 1); + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint64_t spu_atomic_decr64(uint64_t *ls, uint64_t ea) +{ + uint64_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 3; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + ls[i] = (old_value - 1); + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint32_t spu_atomic_incr32(uint32_t *ls, uint64_t ea) +{ + uint32_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 2; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + ls[i] = (old_value + 1); + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint64_t spu_atomic_incr64(uint64_t *ls, uint64_t ea) +{ + uint64_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 3; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + ls[i] = (old_value + 1); + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint32_t spu_atomic_nop32(uint32_t *ls, uint64_t ea) +{ + unsigned int i = ((uint32_t)ea & 0x7f) >> 2; + + ea &= ~0x7f; + mfc_getllar(ls, ea, 0, 0); + mfc_read_atomic_status(); + spu_dsync(); + return ls[i]; +} + +uint64_t spu_atomic_nop64(uint64_t *ls, uint64_t ea) +{ + unsigned int i = ((uint32_t)ea & 0x7f) >> 3; + + ea &= ~0x7f; + mfc_getllar(ls, ea, 0, 0); + mfc_read_atomic_status(); + spu_dsync(); + return ls[i]; +} + +uint32_t spu_atomic_or32(uint32_t *ls, uint64_t ea, uint32_t value) +{ + uint32_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 2; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + ls[i] = (old_value|value); + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint64_t spu_atomic_or64(uint64_t *ls, uint64_t ea, uint64_t value) +{ + uint64_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 3; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + ls[i] = (old_value|value); + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint32_t spu_atomic_store32(uint32_t *ls, uint64_t ea, uint32_t value) +{ + uint32_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 2; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + ls[i] = value; + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint64_t spu_atomic_store64(uint64_t *ls, uint64_t ea, uint64_t value) +{ + uint64_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 3; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + ls[i] = value; + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint32_t spu_atomic_sub32(uint32_t *ls, uint64_t ea, uint32_t value) +{ + uint32_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 2; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + ls[i] = (old_value - value); + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint64_t spu_atomic_sub64(uint64_t *ls, uint64_t ea, uint64_t value) +{ + uint64_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 3; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + ls[i] = (old_value - value); + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint32_t spu_atomic_test_and_decr32(uint32_t *ls, uint64_t ea) +{ + uint32_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 2; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + if(old_value == 0) break; + + ls[i] = (old_value - 1); + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} + +uint64_t spu_atomic_test_and_decr64(uint64_t *ls, uint64_t ea) +{ + uint64_t old_value = 0; + unsigned int i = ((uint32_t)ea & 0x7f) >> 3; + + ea &= ~0x7f; + do { + mfc_getllar(ls, ea, 0, 0); + (void)mfc_read_atomic_status(); + spu_dsync(); + + old_value = ls[i]; + if(old_value == 0) break; + + ls[i] = (old_value - 1); + + spu_dsync(); + mfc_putllc(ls, ea, 0, 0); + } while(__builtin_expect(mfc_read_atomic_status(), 0)); + + return old_value; +} diff --git a/spu/libspudma/Makefile b/spu/libspudma/Makefile new file mode 100644 index 00000000..682e7fdc --- /dev/null +++ b/spu/libspudma/Makefile @@ -0,0 +1,81 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(PSL1GHT)),) +$(error "Please set PSL1GHT in your environment. export PSL1GHT=") +endif + +include $(PSL1GHT)/spu_rules + +BUILD := build + +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PLATFORM)),) +#--------------------------------------------------------------------------------- +export BASEDIR := $(CURDIR) +export DEPS := $(BASEDIR)/deps +export LIBS := $(BASEDIR)/lib + +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + +export LIBDIR := $(LIBS)/$(PLATFORM) +export DEPSDIR := $(DEPS)/$(PLATFORM) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +LIBRARY := $(LIBDIR)/libspudma + +#--------------------------------------------------------------------------------- +INCLUDES := -I$(BASEDIR) -I$(BASEDIR)/../include -I$(BASEDIR)/../include/dma + +CFLAGS := -O2 -Wall $(MACHDEP) -DLIBRT_INTERNAL $(INCLUDES) +ASFLAGS := $(MACHDEP) -D__ASSEMBLY__ $(INCLUDES) + +#--------------------------------------------------------------------------------- +VPATH := $(BASEDIR) + +#--------------------------------------------------------------------------------- +OBJS := spu_dma.o + +all: spu + +#--------------------------------------------------------------------------------- +spu: +#--------------------------------------------------------------------------------- + @[ -d $(LIBS)/spu ] || mkdir -p $(LIBS)/spu + @[ -d $(DEPS)/spu ] || mkdir -p $(DEPS)/spu + @[ -d spu ] || mkdir -p spu + @$(MAKE) PLATFORM=spu lib -C spu -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +install: all +#--------------------------------------------------------------------------------- + @[ -d $(PSL1GHT)/spu/lib ] || mkdir -p $(PSL1GHT)/spu/lib + @cp -frv $(CURDIR)/lib/spu/*.a $(PSL1GHT)/spu/lib + +#--------------------------------------------------------------------------------- +$(LIBRARY).a: $(OBJS) +#--------------------------------------------------------------------------------- + +.PHONY: lib spu install + +#--------------------------------------------------------------------------------- +lib: $(LIBRARY).a +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +clean: +#--------------------------------------------------------------------------------- + @echo clean ... + @rm -rf spu + @rm -rf $(DEPS) + @rm -rf $(LIBS) + +-include $(DEPSDIR)/*.d diff --git a/spu/libspudma/spu_dma.c b/spu/libspudma/spu_dma.c new file mode 100755 index 00000000..8cd3c55d --- /dev/null +++ b/spu/libspudma/spu_dma.c @@ -0,0 +1,28 @@ +#include "spu_dma.h" + +void spu_dma_large_cmd(uintptr_t ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t cmd) +{ + vec_uint4 tmp; + while(size) { + tmp = spu_sel((vec_uint4)si_from_uint(1), (vec_uint4)si_from_uint(2), spu_cmpgt((vec_uint4)si_from_uint(size), 1)); + tmp = spu_sel((vec_uint4)si_from_uint(4), tmp, spu_cmpeq(spu_cmpgt((vec_uint4)si_from_uint(size), 3), 0)); + tmp = spu_sel((vec_uint4)si_from_uint(8), tmp, spu_cmpeq(spu_cmpgt((vec_uint4)si_from_uint(size), 7), 0)); + tmp = spu_sel(spu_and((vec_uint4)si_from_uint(size), -16), tmp, spu_cmpeq(spu_cmpgt((vec_uint4)si_from_uint(size), 15), 0)); + tmp = spu_sel((vec_uint4)si_from_uint(MAX_DMA_BLOCK_SIZE), tmp, spu_cmpeq(spu_cmpgt((vec_uint4)si_from_uint(size), 16383), 0)); + + uint32_t chunk = spu_extract(tmp, 0); + spu_mfcdma64((volatile void*)ls,mfc_ea2h(ea),mfc_ea2l(ea),chunk,tag,cmd); + + size -= chunk; + ls = (uintptr_t)((uint32_t)ls + chunk); + ea += chunk; + } +} + +void spu_dma_and_wait(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t cmd) +{ + spu_mfcdma64((volatile void*)ls,mfc_ea2h(ea),mfc_ea2l(ea),size,tag,cmd); + mfc_write_tag_mask(1< /dev/null || 10.4) + OSXCFLAGS := -mmacosx-version-min=$(OSX_MIN) + OSXCXXFLAGS := $(OSXCFLAGS) + CXXFLAGS += -fvisibility=hidden + CFLAGS += -I/usr/local/include + LDFLAGS += -mmacosx-version-min=$(OSX_MIN) -Wl,-syslibroot,$(SDK) -L/opt/local/lib -lz +endif + +ifneq (,$(findstring BSD,$(UNAME))) + CFLAGS += -I/usr/local/include + LDFLAGS += -L/usr/local/lib -lz +endif + +ifneq (,$(findstring SunOS,$(UNAME))) + CFLAGS += -I/opt/csw/include + LDFLAGS += -L/opt/csw/lib -R/opt/csw/lib -lz +endif + +ifneq (,$(findstring Linux,$(UNAME))) + LDFLAGS += -lz + OS := Linux +endif + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + + +export OUTPUT := $(CURDIR)/$(TARGET)$(EXEEXT) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) + +export CC := gcc +export CXX := g++ +export AR := ar +export OBJCOPY := objcopy + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) + +export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT) + +#--------------------------------------------------------------------------------- +install: $(BUILD) + @echo Installing $(TARGET)$(EXEEXT) + @[ -d $(PS3DEV)/bin ] || mkdir -p $(PS3DEV)/bin + @install -m 755 $(OUTPUT) $(PS3DEV)/bin + +#--------------------------------------------------------------------------------- +run: + $(OUTPUT) + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT) : $(OFILES) + @echo linking ... $(notdir $@) + @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + +#--------------------------------------------------------------------------------- +# Compile Targets for C/C++ +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +%.o : %.cpp + @echo $(notdir $<) + @$(CXX) -E -MMD $(CFLAGS) $< > /dev/null + @$(CXX) $(OSXCXXFLAGS) $(CFLAGS) -o $@ -c $< + +#--------------------------------------------------------------------------------- +%.o : %.c + @echo $(notdir $<) + @$(CC) -E -MMD $(CFLAGS) $< > /dev/null + @$(CC) $(OSXCFLAGS) $(CFLAGS) -o $@ -c $< + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/tools/fself/include/common.h b/tools/fself/include/common.h new file mode 100644 index 00000000..450ce74b --- /dev/null +++ b/tools/fself/include/common.h @@ -0,0 +1,53 @@ +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include +#include +#include +#include + +#ifdef WIN32 +#include +#include +#else +#include +#endif + +#include "types.h" + +#ifdef WIN32 +#define MKDIR(x,y) mkdir(x) +#else +#define MKDIR(x,y) mkdir(x,y) +#endif + +#ifdef __BIG_ENDIAN__ +#define swap16(x) (x) +#define swap32(x) (x) +#define swap64(x) (x) +#else +#define swap16(x) ((((uint16_t)(x) & 0xff00) >> 8) | \ + (((uint16_t)(x) & 0x00ff) << 8)) +#define swap32(x) ((((uint32_t)(x) & 0xff000000) >> 24) | \ + (((uint32_t)(x) & 0x00ff0000) >> 8) | \ + (((uint32_t)(x) & 0x0000ff00) << 8) | \ + (((uint32_t)(x) & 0x000000ff) << 24)) +#define swap64(x) \ + ((((uint64_t)(x) & 0xff00000000000000ull) >> 56) | \ + ((uint64_t)((x) & 0x00ff000000000000ull) >> 40) | \ + ((uint64_t)((x) & 0x0000ff0000000000ull) >> 24) | \ + ((uint64_t)((x) & 0x000000ff00000000ull) >> 8) | \ + ((uint64_t)((x) & 0x00000000ff000000ull) << 8) | \ + ((uint64_t)((x) & 0x0000000000ff0000ull) << 24) | \ + ((uint64_t)((x) & 0x000000000000ff00ull) << 40) | \ + ((uint64_t)((x) & 0x00000000000000ffull) << 56)) +#endif + +#define align(x,y) (((x) + ((y) - 1)) & ~((y - 1))) + +#define error_done(err, msg) do { \ + fprintf(stderr,msg); \ + exit (err); \ + } while(0) + +#endif diff --git a/tools/fself/include/self.h b/tools/fself/include/self.h new file mode 100644 index 00000000..6eb683fb --- /dev/null +++ b/tools/fself/include/self.h @@ -0,0 +1,159 @@ +#ifndef __SELF_H__ +#define __SELF_H__ + +#include "common.h" +#include "tools.h" + +#define SCE_MAGIC 0x53434500 + +typedef struct { + uint32_t magic; + uint32_t version; + uint16_t flags; + uint16_t type; + uint32_t metadata_offset; + uint64_t header_len; + uint64_t elf_filesize; + uint64_t unknown; + uint64_t appinfo_offset; + uint64_t elf_offset; + uint64_t phdr_offset; + uint64_t shdr_offset; + uint64_t section_info_offset; + uint64_t sceversion_offset; + uint64_t controlinfo_offset; + uint64_t controlinfo_size; + uint64_t padding; +} __attribute__((packed)) SELF; + +typedef struct { + uint64_t authid; + uint32_t vendor_id; + uint32_t self_type; + uint32_t version; + uint8_t padding[12]; +} __attribute__((packed)) APP_INFO; + +typedef struct { + uint64_t offset; + uint64_t size; + uint32_t compressed; // 2=compressed + uint32_t unknown1; + uint32_t unknown2; + uint32_t encrypted; // 1=encrypted +} __attribute__((packed)) SECTION_INFO; + +typedef struct { + uint32_t unknown1; + uint32_t unknown2; + uint32_t size; + uint32_t unknown3; + uint16_t section_idx; + uint16_t unknown4; + uint32_t unknown5; + uint32_t unknown6; + uint32_t unknown7; + uint64_t offset; + uint64_t str_size; +} __attribute__((packed)) SCEVERSION_INFO; + +typedef struct { + uint32_t type; // 1==control flags; 2==file digest; 3==npdrm header + uint32_t size; + uint64_t cont_flag; // 1==next block; 0==end block + union { + // type 1 + struct { + uint32_t control_flags[8]; + } control_flags; + + // type 2 + struct { + uint8_t digest1[20]; + uint8_t digest2[20]; + uint8_t padding[8]; + } file_digest; + + struct { + uint32_t data[32]; + } npdrm; + }; +} __attribute__((packed)) CONTROL_INFO; + +typedef struct { + //uint8_t ignore[32]; + uint8_t key[16]; + uint8_t key_pad[16]; + uint8_t iv[16]; + uint8_t iv_pad[16]; +} __attribute__((packed)) METADATA_INFO; + +typedef struct { + uint64_t signature_input_length; + uint32_t unknown1; + uint32_t section_count; + uint32_t key_count; + uint32_t signature_info_size; + uint64_t unknown2; +} __attribute__((packed)) METADATA_HEADER; + +typedef struct { + uint64_t data_offset; + uint64_t data_size; + uint32_t type; // 1 = shdr, 2 == phdr + uint32_t program_idx; + uint32_t unknown; + uint32_t sha1_idx; + uint32_t encrypted; // 3=yes; 1=no + uint32_t key_idx; + uint32_t iv_idx; + uint32_t compressed; // 2=yes; 1=no +} __attribute__((packed)) METADATA_SECTION_HEADER; + +typedef struct { + uint8_t sha1[20]; + uint8_t padding[12]; + uint8_t hmac_key[64]; +} __attribute__((packed)) SECTION_HASH; + +typedef struct { + uint32_t unknown1; + uint32_t signature_size; + uint64_t unknown2; + uint64_t unknown3; + uint64_t unknown4; + uint64_t unknown5; + uint32_t unknown6; + uint32_t unknown7; +} __attribute__((packed)) SIGNATURE_INFO; + +typedef struct { + uint8_t r[21]; + uint8_t s[21]; + uint8_t padding[6]; +} __attribute__((packed)) SIGNATURE; + + +typedef struct { + uint8_t *data; + uint64_t size; + uint64_t offset; +} SELF_SECTION; + +void self_read_headers(FILE *in,SELF *self,APP_INFO *app_info,ELF *elf,ELF_PHDR **phdr, ELF_SHDR **shdr, SECTION_INFO **section_info,SCEVERSION_INFO *sceversion_info,CONTROL_INFO **control_info); + +void self_write_self_header(char *self_data,uint64_t offset,SELF *in); +void self_write_appinfo_header(char *self_data,uint64_t offset,APP_INFO *in); +void self_write_elf_header(char *self_data,uint64_t offset,ELF *in); +void self_write_phdr_header(char *self_data,uint64_t offset,ELF *elf_hdr,ELF_PHDR *in); +void self_write_sectioninfo_header(char *self_data,uint64_t offset,ELF *elf_hdr,SECTION_INFO *in); +void self_write_sceversion_header(char *self_data,uint64_t offset,SCEVERSION_INFO *in); +void self_write_control_info_header(char *self_data,uint64_t offset,CONTROL_INFO *in,int npdrm); + +void self_build_app_info_header(APP_INFO *app_info,int npdrm); +void self_build_sceversion_header(SCEVERSION_INFO *info,const void *elf_data,uint64_t elfOffset); +void self_build_controlinfo_header(CONTROL_INFO **control_info,int *info_len,const void *elf_data,int elf_len,int npdrm); +void self_build_self_header(SELF *self,ELF *elf_hdr,uint64_t *headerEnd,uint64_t *elfOffset,int elf_filesize,int ctrl_info_size,int npdrm); +void self_build_elf_section_info_headers(ELF *elf_hdr,ELF_PHDR **phdrs,ELF_SHDR **shdrs,SECTION_INFO **sec_info,const void *elf_data,uint64_t elfOffset); + +#endif diff --git a/tools/fself/include/sha1.h b/tools/fself/include/sha1.h new file mode 100644 index 00000000..b355d228 --- /dev/null +++ b/tools/fself/include/sha1.h @@ -0,0 +1,72 @@ +/* + * sha1.h + * + * Copyright (C) 1998 + * Paul E. Jones + * All Rights Reserved + * + * This software is licensed as "freeware." Permission to distribute + * this software in source and binary forms is hereby granted without + * a fee. THIS SOFTWARE IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * THE AUTHOR SHALL NOT BE HELD LIABLE FOR ANY DAMAGES RESULTING + * FROM THE USE OF THIS SOFTWARE, EITHER DIRECTLY OR INDIRECTLY, INCLUDING, + * BUT NOT LIMITED TO, LOSS OF DATA OR DATA BEING RENDERED INACCURATE. + * + * This software is licensed as "freeware." Permission to distribute + * this software in source and binary forms is hereby granted without + * a fee. THIS SOFTWARE IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * THE AUTHOR SHALL NOT BE HELD LIABLE FOR ANY DAMAGES RESULTING + * FROM THE USE OF THIS SOFTWARE, EITHER DIRECTLY OR INDIRECTLY, INCLUDING, + * BUT NOT LIMITED TO, LOSS OF DATA OR DATA BEING RENDERED INACCURATE. + * + ***************************************************************************** + * $Id: sha1.h,v 1.2 2004/03/27 18:00:33 paulej Exp $ + ***************************************************************************** + * + * Description: + * This class implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * Many of the variable names in the SHA1Context, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +/* + * This structure will hold context information for the hashing + * operation + */ +typedef struct SHA1Context +{ + unsigned Message_Digest[5]; /* Message Digest (output) */ + + unsigned Length_Low; /* Message length in bits */ + unsigned Length_High; /* Message length in bits */ + + unsigned char Message_Block[64]; /* 512-bit message blocks */ + int Message_Block_Index; /* Index into message block array */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corruped? */ +} SHA1Context; + +/* + * Function Prototypes + */ +void SHA1Reset(SHA1Context *); +int SHA1Result(SHA1Context *); +void SHA1Input( SHA1Context *, + const unsigned char *, + unsigned); + +#endif diff --git a/tools/fself/include/tools.h b/tools/fself/include/tools.h new file mode 100644 index 00000000..c9250440 --- /dev/null +++ b/tools/fself/include/tools.h @@ -0,0 +1,63 @@ +#ifndef __TOOLS_H__ +#define __TOOLS_H__ + +#include "common.h" + +typedef struct { + uint8_t ident[16]; + uint16_t type; + uint16_t machine; + uint32_t version; + uint64_t entry_point; + uint64_t phdr_offset; + uint64_t shdr_offset; + uint16_t flags; + uint32_t header_size; + uint16_t phent_size; + uint16_t phnum; + uint16_t shent_size; + uint16_t shnum; + uint16_t shstrndx; +} __attribute__((packed)) ELF; + +typedef struct { + uint32_t type; + uint32_t flags; + uint64_t offset_in_file; + uint64_t vitual_addr; + uint64_t phys_addr; + uint64_t segment_size; + uint64_t segment_mem_size; + uint64_t alignment; +} __attribute__((packed)) ELF_PHDR; + +typedef struct { + uint32_t name_idx; + uint32_t type; + uint64_t flags; + uint64_t virtual_addr; + uint64_t offset_in_file; + uint64_t segment_size; + uint32_t link; + uint32_t info; + uint64_t addr_align; + uint64_t entry_size; +} __attribute__((packed)) ELF_SHDR; + +void elf_read_elf_header(const void *elf_data,ELF *elf_hdr); +void elf_read_phdr_header(const void *elf_data,ELF *elf_hdr,ELF_PHDR **elf_phdr); +void elf_read_shdr_header(const void *elf_data,ELF *elf_hdr,ELF_SHDR **elf_shdr); +int elf_get_shstr_idx(const void *elf_data,const char *section_name); +uint64_t elf_get_shstr_off(const void *elf_data,const char *section_name); + +void elf_write_elf_header(void *elf_data,ELF *elf_hdr); +void elf_write_phdr_header(void *elf_data,ELF *elf_hdr,ELF_PHDR *phdr); + +void sha1(const void *data,uint32_t len,uint8_t *digest); +void sha1_hmac(const void *data,uint32_t len,uint8_t *key,uint8_t *digest); + +void get_rand(uint8_t *bfr,uint32_t size); + +void fail(const char *a, ...); + +#endif diff --git a/tools/fself/include/types.h b/tools/fself/include/types.h new file mode 100644 index 00000000..a7042d4d --- /dev/null +++ b/tools/fself/include/types.h @@ -0,0 +1,20 @@ +#ifndef __TYPES_H__ +#define __TYPES_H__ + +#if _MSC_VER>1300 +typedef signed char int8_t; +typedef unsigned char uint8_t; + +typedef signed short int16_t; +typedef unsigned short uint16_t; + +typedef signed int int32_t; +typedef unsigned int uint32_t; + +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif + +#endif diff --git a/tools/fself/source/main.c b/tools/fself/source/main.c new file mode 100644 index 00000000..dcb0eaff --- /dev/null +++ b/tools/fself/source/main.c @@ -0,0 +1,94 @@ +#include "self.h" + +void usage() +{ + printf("fself [options] input.elf output.self\n"); + printf("If output is not specified, fself will default to EBOOT.BIN\n"); + printf("Options:\n"); + printf("\t-n create npdrm self, to be used with pkg.py\n"); +} + +int main(int argc,char *argv[]) +{ + int j,i,npdrm; + FILE *f = NULL; + ELF elf_header; + SELF self_header; + APP_INFO app_info; + ELF_PHDR *elf_phdr; + ELF_SHDR *elf_shdr; + SECTION_INFO *sec_info; + SCEVERSION_INFO sceversion_info; + CONTROL_INFO *control_info = NULL; + char *iofile[] = { NULL, "EBOOT.BIN" }; + + if(argc<2) usage(); + + npdrm = 0; + for(i=1;imagic = swap32 (self->magic); + self->version = swap32 (self->version); + self->flags = swap16 (self->flags); + self->type = swap16 (self->type); + self->metadata_offset = swap32 (self->metadata_offset); + self->header_len = swap64 (self->header_len); + self->elf_filesize = swap64 (self->elf_filesize); + self->appinfo_offset = swap64 (self->appinfo_offset); + self->elf_offset = swap64 (self->elf_offset); + self->phdr_offset = swap64 (self->phdr_offset); + self->shdr_offset = swap64 (self->shdr_offset); + self->section_info_offset = swap64 (self->section_info_offset); + self->sceversion_offset = swap64 (self->sceversion_offset); + self->controlinfo_offset = swap64 (self->controlinfo_offset); + self->controlinfo_size = swap64 (self->controlinfo_size); + + if (self->magic != SCE_MAGIC) { + fail("not a SELF\n"); + } + + // APP INFO + if (app_info) { + fseek (in, self->appinfo_offset, SEEK_SET); + if (fread (app_info, sizeof(APP_INFO), 1, in) != 1) { + fail("Couldn't read APP INFO header"); + } + app_info->authid = swap64 (app_info->authid); + app_info->vendor_id = swap32 (app_info->vendor_id); + app_info->self_type = swap32 (app_info->self_type); + app_info->version = swap32 (app_info->version); + } + + // ELF + if (elf) { + fseek (in, self->elf_offset, SEEK_SET); + if (fread (elf, sizeof(ELF), 1, in) != 1) { + fail("Couldn't read ELF header"); + } + elf->type = swap16 (elf->type); + elf->machine = swap16 (elf->machine); + elf->version = swap32 (elf->version); + elf->entry_point = swap64 (elf->entry_point); + elf->phdr_offset = swap64 (elf->phdr_offset); + elf->shdr_offset = swap64 (elf->shdr_offset); + elf->flags = swap16 (elf->flags); + elf->header_size = swap32 (elf->header_size); + elf->phent_size = swap16 (elf->phent_size); + elf->phnum = swap16 (elf->phnum); + elf->shent_size = swap16 (elf->shent_size); + elf->shnum = swap16 (elf->shnum); + elf->shstrndx = swap16 (elf->shstrndx); + } + + // PHDR and SECTION INFO + if (phdr || section_info) { + uint16_t phnum = 0; + uint16_t i; + + if (elf) { + phnum = elf->phnum; + } else { + fseek (in, self->elf_offset + 52, SEEK_SET); + fread (&phnum, sizeof(uint16_t), 1, in); + } + + if (phdr) { + ELF_PHDR *elf_phdr = NULL; + + elf_phdr = malloc (sizeof(ELF_PHDR) * phnum); + + fseek (in, self->phdr_offset, SEEK_SET); + if (fread (elf_phdr, sizeof(ELF_PHDR), phnum, in) != phnum) { + fail("Couldn't read ELF PHDR header"); + } + + for (i = 0; i < phnum; i++) { + elf_phdr[i].type = swap32 (elf_phdr[i].type); + elf_phdr[i].flags = swap32 (elf_phdr[i].flags); + elf_phdr[i].offset_in_file = swap64 (elf_phdr[i].offset_in_file); + elf_phdr[i].vitual_addr = swap64 (elf_phdr[i].vitual_addr); + elf_phdr[i].phys_addr = swap64 (elf_phdr[i].phys_addr); + elf_phdr[i].segment_size = swap64 (elf_phdr[i].segment_size); + elf_phdr[i].segment_mem_size = swap64 (elf_phdr[i].segment_mem_size); + elf_phdr[i].alignment = swap64 (elf_phdr[i].alignment); + } + + *phdr = elf_phdr; + } + + // SECTION INFO + if (section_info) { + SECTION_INFO *sections = NULL; + + sections = malloc (sizeof(SECTION_INFO) * phnum); + + fseek (in, self->section_info_offset, SEEK_SET); + if (fread (sections, sizeof(SECTION_INFO), phnum, in) != phnum) { + fail("Couldn't read SECTION INFO header"); + } + + for (i = 0; i < phnum; i++) { + sections[i].offset = swap64 (sections[i].offset); + sections[i].size = swap64 (sections[i].size); + sections[i].compressed = swap32 (sections[i].compressed); + sections[i].encrypted = swap32 (sections[i].encrypted); + } + + *section_info = sections; + } + } + + if (sceversion_info) { + fseek (in, self->sceversion_offset, SEEK_SET); + if (fread (sceversion_info, sizeof(SCEVERSION_INFO), 1, in) != 1) { + fail("Couldn't read SCE VERSION INFO header"); + } + sceversion_info->unknown1 = swap32(sceversion_info->unknown1); + sceversion_info->unknown2 = swap32(sceversion_info->unknown2); + sceversion_info->size = swap32(sceversion_info->size); + sceversion_info->unknown3 = swap32(sceversion_info->unknown3); + sceversion_info->section_idx = swap16(sceversion_info->section_idx); + sceversion_info->unknown4 = swap16(sceversion_info->unknown4); + sceversion_info->unknown5 = swap32(sceversion_info->unknown5); + sceversion_info->unknown6 = swap32(sceversion_info->unknown6); + sceversion_info->unknown7 = swap32(sceversion_info->unknown7); + sceversion_info->offset = swap64(sceversion_info->offset); + sceversion_info->str_size = swap64(sceversion_info->str_size); + } + + // CONTROL INFO + if (control_info) { + uint32_t i,offset = 0; + uint32_t index = 0; + CONTROL_INFO *info = NULL; + + while (offset < self->controlinfo_size) { + + info = realloc (info, sizeof(CONTROL_INFO) * (index + 1)); + + fseek (in, self->controlinfo_offset + offset, SEEK_SET); + + if (fread (info + index, sizeof(CONTROL_INFO), 1, in) != 1) { + fail("Couldn't read CONTROL INFO header"); + } + + info[index].type = swap32 (info[index].type); + info[index].size = swap32 (info[index].size); + info[index].cont_flag = swap64(info[index].cont_flag); + if (info[index].type == 1) { + for(i=0;i<8;i++) + info[index].control_flags.control_flags[i] = swap32(info[index].control_flags.control_flags[i]); + } + + offset += info[index].size; + index++; + } + *control_info = info; + } + + // SHDR + if (shdr) { + uint16_t shnum = 0; + uint16_t i; + ELF_SHDR *elf_shdr = NULL; + + if (elf) { + shnum = elf->shnum; + } else { + fseek (in, self->elf_offset + 56, SEEK_SET); + fread (&shnum, sizeof(uint16_t), 1, in); + } + + if (shnum > 0 && self->shdr_offset != 0) { + elf_shdr = malloc (sizeof(ELF_SHDR) * shnum); + + fseek (in, self->shdr_offset, SEEK_SET); + if (fread (elf_shdr, sizeof(ELF_SHDR), shnum, in) != shnum) { + fail("Couldn't read ELF SHDR header"); + } + + for (i = 0; i < shnum; i++) { + elf_shdr[i].name_idx = swap32 (elf_shdr[i].name_idx); + elf_shdr[i].type = swap32 (elf_shdr[i].type); + elf_shdr[i].flags = swap64 (elf_shdr[i].flags); + elf_shdr[i].virtual_addr = swap64 (elf_shdr[i].virtual_addr); + elf_shdr[i].offset_in_file = swap64 (elf_shdr[i].offset_in_file); + elf_shdr[i].segment_size = swap64 (elf_shdr[i].segment_size); + elf_shdr[i].link = swap32 (elf_shdr[i].link); + elf_shdr[i].info = swap32 (elf_shdr[i].info); + elf_shdr[i].addr_align = swap64 (elf_shdr[i].addr_align); + elf_shdr[i].entry_size = swap64 (elf_shdr[i].entry_size); + } + + *shdr = elf_shdr; + } + } +} + +void self_build_controlinfo_header(CONTROL_INFO **control_info,int *info_len,const void *elf_data,int elf_len,int npdrm) +{ + CONTROL_INFO *info = calloc((npdrm ? 3 : 2),sizeof(CONTROL_INFO)); + const char control_info_digest1[20] = { 0x62,0x7c,0xb1,0x80,0x8a,0xb9,0x38,0xe3,0x2c,0x8c,0x09,0x17,0x08,0x72,0x6a,0x57,0x9e,0x25,0x86,0xe4 }; + + info[0].type = 1; + info[0].size = 48; + info[0].cont_flag = 1; + + info[1].type = 2; + info[1].size = 64; + + memcpy(info[1].file_digest.digest1,control_info_digest1,20); + sha1(elf_data,elf_len,info[1].file_digest.digest2); + + if(npdrm) { + info[1].cont_flag = 1; + + info[2].type = 3; + info[2].size = 144; + } + + *control_info = info; + *info_len = 112 + (npdrm ? 144 : 0); +} + +void self_build_self_header(SELF *self,ELF *elf_hdr,uint64_t *headerEnd,uint64_t *elfOffset,int elf_filesize,int ctrl_info_size,int npdrm) +{ + memset(self,0,sizeof(SELF)); + + self->magic = SCE_MAGIC; + self->version = 2; + self->flags = 0x8000; + self->unknown = 3; + self->type = 1; + self->elf_filesize = elf_filesize; + self->appinfo_offset = align(sizeof(SELF),0x10); + self->elf_offset = align(self->appinfo_offset + sizeof(APP_INFO),0x10); + self->phdr_offset = self->elf_offset + sizeof(ELF); + self->section_info_offset = align(self->phdr_offset + (elf_hdr->phnum*sizeof(ELF_PHDR)),0x10); + self->sceversion_offset = align(self->section_info_offset + (elf_hdr->phnum*sizeof(SECTION_INFO)),0x10); + self->controlinfo_offset = align(self->sceversion_offset + sizeof(SCEVERSION_INFO),0x10); + self->controlinfo_size = ctrl_info_size; + + *headerEnd = self->controlinfo_offset + ctrl_info_size; + *elfOffset = align(*headerEnd + (npdrm ? 0x5c0 : 0x550),0x80); + + self->header_len = *elfOffset; + self->metadata_offset = (uint32_t)*headerEnd - 0x20; + self->shdr_offset = *elfOffset + elf_hdr->shdr_offset; +} + +void self_build_app_info_header(APP_INFO *app_info,int npdrm) +{ + memset(app_info,0,sizeof(APP_INFO)); + + app_info->authid = 0x1010000001000003ULL; + app_info->vendor_id = 0x01000002; + app_info->self_type = npdrm ? 0x08 : 0x04; + app_info->version = 0x00010000; +} + +void self_build_elf_section_info_headers(ELF *elf_hdr,ELF_PHDR **phdrs,ELF_SHDR **shdrs,SECTION_INFO **sec_info,const void *elf_data,uint64_t elfOffset) +{ + int i; + ELF_PHDR *phdr; + ELF_SHDR *shdr; + SECTION_INFO *secinfo; + + elf_read_phdr_header(elf_data,elf_hdr,&phdr); + elf_read_shdr_header(elf_data,elf_hdr,&shdr); + + secinfo = calloc(elf_hdr->phnum,sizeof(SECTION_INFO)); + + for(i=0;iphnum;i++) { + secinfo[i].offset = phdr[i].offset_in_file + elfOffset; + secinfo[i].size = phdr[i].segment_size; + secinfo[i].compressed = 1; + if(phdr[i].type==1) secinfo[i].encrypted = 2; + } + + *phdrs = phdr; + *shdrs = shdr; + *sec_info = secinfo; +} + +void self_build_sceversion_header(SCEVERSION_INFO *info,const void *elf_data,uint64_t elfOffset) +{ + int sce_idx = elf_get_shstr_idx(elf_data,".sceversion"); + uint64_t sce_off = elf_get_shstr_off(elf_data,".sceversion"); + + memset(info,0,sizeof(SCEVERSION_INFO)); + + info->unknown1 = 1; + info->unknown2 = 1; + info->size = sizeof(SCEVERSION_INFO); + info->section_idx = sce_idx; + info->unknown4 = 1; + info->unknown6 = 1; + info->offset = sce_off + elfOffset; + info->str_size = strlen((char*)elf_data + sce_off); +} + +void self_write_self_header(char *self_data,uint64_t offset,SELF *in) +{ + SELF *out = (SELF*)((char*)self_data + offset); + + out->magic = swap32(in->magic); + out->version = swap32(in->version); + out->flags = swap16(in->flags); + out->type = swap16(in->type); + out->metadata_offset = swap32(in->metadata_offset); + out->header_len = swap64(in->header_len); + out->elf_filesize = swap64(in->elf_filesize); + out->unknown = swap64(in->unknown); + out->appinfo_offset = swap64(in->appinfo_offset); + out->elf_offset = swap64(in->elf_offset); + out->phdr_offset = swap64(in->phdr_offset); + out->shdr_offset = swap64(in->shdr_offset); + out->section_info_offset = swap64(in->section_info_offset); + out->sceversion_offset = swap64(in->sceversion_offset); + out->controlinfo_offset = swap64(in->controlinfo_offset); + out->controlinfo_size = swap64(in->controlinfo_size); + out->padding = swap64(in->padding); +} + +void self_write_appinfo_header(char *self_data,uint64_t offset,APP_INFO *in) +{ + APP_INFO *out = (APP_INFO*)((char*)self_data + offset); + + out->authid = swap64(in->authid); + out->vendor_id = swap32(in->vendor_id); + out->self_type = swap32(in->self_type); + out->version = swap32(in->version); + memcpy(out->padding,in->padding,sizeof(in->padding)); +} + +void self_write_elf_header(char *self_data,uint64_t offset,ELF *in) +{ + char *elf_data = (char*)self_data + offset; + elf_write_elf_header(elf_data,in); +} + +void self_write_phdr_header(char *self_data,uint64_t offset,ELF *elf_hdr,ELF_PHDR *in) +{ + char *elf_data = (char*)self_data + offset; + elf_write_phdr_header(elf_data,elf_hdr,in); +} + +void self_write_sectioninfo_header(char *self_data,uint64_t offset,ELF *elf_hdr,SECTION_INFO *in) +{ + uint16_t i; + SECTION_INFO *out = (SECTION_INFO*)((char*)self_data + offset); + + for(i=0;iphnum;i++) { + out[i].offset = swap64(in[i].offset); + out[i].size = swap64(in[i].size); + out[i].compressed = swap32(in[i].compressed); // 2=compressed + out[i].unknown1 = swap32(in[i].unknown1); + out[i].unknown2 = swap32(in[i].unknown2); + out[i].encrypted = swap32(in[i].encrypted); // 1=encrypted + } +} + +void self_write_sceversion_header(char *self_data,uint64_t offset,SCEVERSION_INFO *in) +{ + SCEVERSION_INFO *out = (SCEVERSION_INFO*)((char*)self_data + offset); + + out->unknown1 = swap32(in->unknown1); + out->unknown2 = swap32(in->unknown2); + out->size = swap32(in->size); + out->unknown3 = swap32(in->unknown3); + out->section_idx = swap16(in->section_idx); + out->unknown4 = swap16(in->unknown4); + out->unknown5 = swap32(in->unknown5); + out->unknown6 = swap32(in->unknown6); + out->unknown7 = swap32(in->unknown7); + out->offset = swap64(in->offset); + out->str_size = swap64(in->str_size); +} + +void self_write_control_info_header(char *self_data,uint64_t offset,CONTROL_INFO *in,int npdrm) +{ + uint16_t i,j; + uint16_t cnt = npdrm ? 3 : 2; + + for(i=0;itype = swap32(in[i].type); + out->size = swap32(in[i].size); + out->cont_flag = swap64(in[i].cont_flag); + + switch(in[i].type) { + case 1: + for(j=0;j<8;j++) out->control_flags.control_flags[j] = swap32(in[i].control_flags.control_flags[j]); + break; + case 2: + memcpy(out->file_digest.digest1,in[i].file_digest.digest1,0x14); + memcpy(out->file_digest.digest2,in[i].file_digest.digest2,0x14); + memcpy(out->file_digest.padding,in[i].file_digest.padding,0x08); + break; + case 3: + for(j=0;j<8;j++) out->npdrm.data[j] = swap32(in[i].npdrm.data[j]); + break; + default: + break; + } + + offset += in[i].size; + } +} diff --git a/tools/fself/source/sha1.c b/tools/fself/source/sha1.c new file mode 100644 index 00000000..88629da5 --- /dev/null +++ b/tools/fself/source/sha1.c @@ -0,0 +1,380 @@ +/* + * sha1.c + * + * Copyright (C) 1998 + * Paul E. Jones + * All Rights Reserved + * + * This software is licensed as "freeware." Permission to distribute + * this software in source and binary forms is hereby granted without + * a fee. THIS SOFTWARE IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * THE AUTHOR SHALL NOT BE HELD LIABLE FOR ANY DAMAGES RESULTING + * FROM THE USE OF THIS SOFTWARE, EITHER DIRECTLY OR INDIRECTLY, INCLUDING, + * BUT NOT LIMITED TO, LOSS OF DATA OR DATA BEING RENDERED INACCURATE. + * + ***************************************************************************** + * $Id: sha1.c,v 1.2 2004/03/27 18:00:33 paulej Exp $ + ***************************************************************************** + * + * Description: + * This file implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * The Secure Hashing Standard, which uses the Secure Hashing + * Algorithm (SHA), produces a 160-bit message digest for a + * given data stream. In theory, it is highly improbable that + * two messages will produce the same message digest. Therefore, + * this algorithm can serve as a means of providing a "fingerprint" + * for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code was + * written with the expectation that the processor has at least + * a 32-bit machine word size. If the machine word size is larger, + * the code should still function properly. One caveat to that + * is that the input functions taking characters and character + * arrays assume that only 8 bits of information are stored in each + * character. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated for + * messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is a + * multiple of the size of an 8-bit character. + * + */ + +#include "sha1.h" + +/* + * Define the circular shift macro + */ +#define SHA1CircularShift(bits,word) \ + ((((word) << (bits)) & 0xFFFFFFFF) | \ + ((word) >> (32-(bits)))) + +/* Function prototypes */ +void SHA1ProcessMessageBlock(SHA1Context *); +void SHA1PadMessage(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Reset(SHA1Context *context) +{ + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Message_Digest[0] = 0x67452301; + context->Message_Digest[1] = 0xEFCDAB89; + context->Message_Digest[2] = 0x98BADCFE; + context->Message_Digest[3] = 0x10325476; + context->Message_Digest[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array within the SHA1Context provided + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * + * Returns: + * 1 if successful, 0 if it failed. + * + * Comments: + * + */ +int SHA1Result(SHA1Context *context) +{ + + if (context->Corrupted) + { + return 0; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + context->Computed = 1; + } + + return 1; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion of + * the message. + * + * Parameters: + * context: [in/out] + * The SHA-1 context to update + * message_array: [in] + * An array of characters representing the next portion of the + * message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Input( SHA1Context *context, + const unsigned char *message_array, + unsigned length) +{ + if (!length) + { + return; + } + + if (context->Computed || context->Corrupted) + { + context->Corrupted = 1; + return; + } + + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + /* Force it to 32 bits */ + context->Length_Low &= 0xFFFFFFFF; + if (context->Length_Low == 0) + { + context->Length_High++; + /* Force it to 32 bits */ + context->Length_High &= 0xFFFFFFFF; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in the SHAContext, especially the + * single character names, were used because those were the names + * used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const unsigned K[] = /* Constants defined in SHA-1 */ + { + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + unsigned temp; /* Temporary word value */ + unsigned W[80]; /* Word sequence */ + unsigned A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = ((unsigned) context->Message_Block[t * 4]) << 24; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]); + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Message_Digest[0]; + B = context->Message_Digest[1]; + C = context->Message_Digest[2]; + D = context->Message_Digest[3]; + E = context->Message_Digest[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Message_Digest[0] = + (context->Message_Digest[0] + A) & 0xFFFFFFFF; + context->Message_Digest[1] = + (context->Message_Digest[1] + B) & 0xFFFFFFFF; + context->Message_Digest[2] = + (context->Message_Digest[2] + C) & 0xFFFFFFFF; + context->Message_Digest[3] = + (context->Message_Digest[3] + D) & 0xFFFFFFFF; + context->Message_Digest[4] = + (context->Message_Digest[4] + E) & 0xFFFFFFFF; + + context->Message_Block_Index = 0; +} + +/* + * SHA1PadMessage + * + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call SHA1ProcessMessageBlock() + * appropriately. When it returns, it can be assumed that the + * message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (context->Length_High >> 24) & 0xFF; + context->Message_Block[57] = (context->Length_High >> 16) & 0xFF; + context->Message_Block[58] = (context->Length_High >> 8) & 0xFF; + context->Message_Block[59] = (context->Length_High) & 0xFF; + context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF; + context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF; + context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF; + context->Message_Block[63] = (context->Length_Low) & 0xFF; + + SHA1ProcessMessageBlock(context); +} diff --git a/tools/fself/source/tools.c b/tools/fself/source/tools.c new file mode 100644 index 00000000..74bdc9af --- /dev/null +++ b/tools/fself/source/tools.c @@ -0,0 +1,241 @@ +#include "tools.h" +#include "sha1.h" + +static void sha1finalize(struct SHA1Context *ctx,uint8_t *digest) +{ + uint32_t i; + + for(i = 0; i < 5; i++) { + *digest++ = ctx->Message_Digest[i] >> 24 & 0xff; + *digest++ = ctx->Message_Digest[i] >> 16 & 0xff; + *digest++ = ctx->Message_Digest[i] >> 8 & 0xff; + *digest++ = ctx->Message_Digest[i] & 0xff; + } +} + +void fail(const char *a, ...) +{ + char msg[1024]; + va_list va; + + va_start(va, a); + vsnprintf(msg, sizeof msg, a, va); + fprintf(stderr, "%s\n", msg); + perror("perror"); + + exit(1); +} + +void elf_read_elf_header(const void *elf_data,ELF *elf_hdr) +{ + memcpy(elf_hdr,elf_data,sizeof(ELF)); + + elf_hdr->type = swap16 (elf_hdr->type); + elf_hdr->machine = swap16 (elf_hdr->machine); + elf_hdr->version = swap32 (elf_hdr->version); + elf_hdr->entry_point = swap64 (elf_hdr->entry_point); + elf_hdr->phdr_offset = swap64 (elf_hdr->phdr_offset); + elf_hdr->shdr_offset = swap64 (elf_hdr->shdr_offset); + elf_hdr->flags = swap16 (elf_hdr->flags); + elf_hdr->header_size = swap32 (elf_hdr->header_size); + elf_hdr->phent_size = swap16 (elf_hdr->phent_size); + elf_hdr->phnum = swap16 (elf_hdr->phnum); + elf_hdr->shent_size = swap16 (elf_hdr->shent_size); + elf_hdr->shnum = swap16 (elf_hdr->shnum); + elf_hdr->shstrndx = swap16 (elf_hdr->shstrndx); +} + +void elf_read_phdr_header(const void *elf_data,ELF *elf_hdr,ELF_PHDR **elf_phdr) +{ + uint16_t i; + ELF_PHDR *phdr = NULL; + + phdr = malloc (sizeof(ELF_PHDR) * elf_hdr->phnum); + + memcpy(phdr,(char*)elf_data + elf_hdr->phdr_offset,sizeof(ELF_PHDR)*elf_hdr->phnum); + + for (i = 0; i < elf_hdr->phnum; i++) { + phdr[i].type = swap32 (phdr[i].type); + phdr[i].flags = swap32 (phdr[i].flags); + phdr[i].offset_in_file = swap64 (phdr[i].offset_in_file); + phdr[i].vitual_addr = swap64 (phdr[i].vitual_addr); + phdr[i].phys_addr = swap64 (phdr[i].phys_addr); + phdr[i].segment_size = swap64 (phdr[i].segment_size); + phdr[i].segment_mem_size = swap64 (phdr[i].segment_mem_size); + phdr[i].alignment = swap64 (phdr[i].alignment); + } + + *elf_phdr = phdr; +} + +void elf_read_shdr_header(const void *elf_data,ELF *elf_hdr,ELF_SHDR **elf_shdr) +{ + uint16_t i; + ELF_SHDR *shdr = NULL; + + shdr = malloc(sizeof(ELF_SHDR) * elf_hdr->shnum); + + memcpy(shdr,(char*)elf_data + elf_hdr->shdr_offset,sizeof(ELF_SHDR)*elf_hdr->shnum); + + for(i=0;ishnum;i++) { + shdr[i].name_idx = swap32(shdr[i].name_idx); + shdr[i].type = swap32(shdr[i].type); + shdr[i].flags = swap64(shdr[i].flags); + shdr[i].virtual_addr = swap64(shdr[i].virtual_addr); + shdr[i].offset_in_file = swap64(shdr[i].offset_in_file); + shdr[i].segment_size = swap64(shdr[i].segment_size); + shdr[i].link = swap32(shdr[i].link); + shdr[i].info = swap32(shdr[i].info); + shdr[i].addr_align = swap64(shdr[i].addr_align); + shdr[i].entry_size = swap64(shdr[i].entry_size); + } + + *elf_shdr = shdr; +} + +int elf_get_shstr_idx(const void *elf_data,const char *section_name) +{ + uint16_t i; + ELF elf_hdr; + const char *name = NULL; + ELF_SHDR *elf_shdr = NULL; + ELF_SHDR *shstr_sec = NULL; + + elf_read_elf_header(elf_data,&elf_hdr); + elf_read_shdr_header(elf_data,&elf_hdr,&elf_shdr); + + shstr_sec = &elf_shdr[elf_hdr.shstrndx]; + for(i=0;ioffset_in_file + elf_shdr[i].name_idx; + if(strncmp(name,section_name,strlen(section_name))==0) break; + } + free(elf_shdr); + + if(i>=elf_hdr.shnum) return -1; + + return i; +} + +uint64_t elf_get_shstr_off(const void *elf_data,const char *section_name) +{ + uint16_t i; + ELF elf_hdr; + uint64_t off = -1; + const char *name = NULL; + ELF_SHDR *elf_shdr = NULL; + ELF_SHDR *shstr_sec = NULL; + + elf_read_elf_header(elf_data,&elf_hdr); + elf_read_shdr_header(elf_data,&elf_hdr,&elf_shdr); + + shstr_sec = &elf_shdr[elf_hdr.shstrndx]; + for(i=0;ioffset_in_file + elf_shdr[i].name_idx; + if(strncmp(name,section_name,strlen(section_name))==0) break; + } + if(iident,elf_hdr->ident,sizeof(elf_hdr->ident)); + out->type = swap16(elf_hdr->type); + out->machine = swap16(elf_hdr->machine); + out->version = swap32(elf_hdr->version); + out->entry_point = swap64(elf_hdr->entry_point); + out->phdr_offset = swap64(elf_hdr->phdr_offset); + out->shdr_offset = swap64(elf_hdr->shdr_offset); + out->flags = swap16(elf_hdr->flags); + out->header_size = swap32(elf_hdr->header_size); + out->phent_size = swap16(elf_hdr->phent_size); + out->phnum = swap16(elf_hdr->phnum); + out->shent_size = swap16(elf_hdr->shent_size); + out->shnum = swap16(elf_hdr->shnum); + out->shstrndx = swap16(elf_hdr->shstrndx); +} + +void elf_write_phdr_header(void *elf_data,ELF *elf_hdr,ELF_PHDR *phdr) +{ + uint16_t i; + ELF_PHDR *elf_phdr = (ELF_PHDR*)elf_data; + + for(i=0;iphnum;i++) { + elf_phdr[i].type = swap32(phdr[i].type); + elf_phdr[i].flags = swap32(phdr[i].flags); + elf_phdr[i].offset_in_file = swap64(phdr[i].offset_in_file); + elf_phdr[i].vitual_addr = swap64(phdr[i].vitual_addr); + elf_phdr[i].phys_addr = swap64(phdr[i].phys_addr); + elf_phdr[i].segment_size = swap64(phdr[i].segment_size); + elf_phdr[i].segment_mem_size = swap64(phdr[i].segment_mem_size); + elf_phdr[i].alignment = swap64(phdr[i].alignment); + } +} + +void sha1(const void *data,uint32_t len,uint8_t *digest) +{ + struct SHA1Context ctx; + + SHA1Reset(&ctx); + SHA1Input(&ctx, data, len); + SHA1Result(&ctx); + + sha1finalize(&ctx, digest); +} + +void sha1_hmac(const void *data,uint32_t len,uint8_t *key,uint8_t *digest) +{ + uint32_t i; + uint8_t tmp[84]; // opad + hash(ipad + message) + uint8_t ipad[64]; + struct SHA1Context ctx; + + SHA1Reset(&ctx); + + for(i=0;i<64;i++) { + tmp[i] = key[i] ^ 0x5c; // opad + ipad[i] = key[i] ^ 0x36; + } + + SHA1Input(&ctx, ipad, 64); + SHA1Input(&ctx, data, len); + SHA1Result(&ctx); + + sha1finalize(&ctx, tmp + 64); + + sha1(tmp, 84, digest); + +} + +#ifdef WIN32 +void get_rand(uint8_t *bfr,uint32_t size) +{ + HCRYPTPROV hProv; + + if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + fail("unable to open random"); + + if (!CryptGenRandom(hProv, size, bfr)) + fail("unable to read random numbers"); + + CryptReleaseContext(hProv, 0); +} +#else +void get_rand(uint8_t *bfr,uint32_t size) +{ + FILE *fp; + + fp = fopen("/dev/urandom", "r"); + if (fp == NULL) + fail("unable to open random"); + + if (fread(bfr, size, 1, fp) != 1) + fail("unable to read random numbers"); + + fclose(fp); +} +#endif From a900e30191938c951c4587ddf91835f10ba44e18 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Fri, 10 Jul 2020 12:47:56 +0200 Subject: [PATCH 04/56] - add spumars sample - improve cgcomp (add more shader functions) - change commands.c to macro style code gen - add more methods to rsx lib - add spu_printf - changes to related subprojects --- .gitignore | 2 + common/libspumars/ppu/ppu/mars_kernel.c | 106 +- ppu/include/lv2/spu.h | 2 + ppu/include/rsx/commands.h | 603 +---- ppu/include/rsx/commands_inc.h | 675 ++++++ ppu/include/rsx/gcm_sys.h | 1590 +++++++------ ppu/include/rsx/nv40.h | 78 +- ppu/include/rsx/rsx.h | 23 +- ppu/include/rsx/rsx_function_macros.h | 50 + ppu/include/rsx/rsx_program.h | 73 +- ppu/include/sys/spu.h | 13 + ppu/include/sys/spu_thread_printf.h | 17 + ppu/librsx/buffer.c | 44 +- ppu/librsx/buffer_impl.h | 31 + ppu/librsx/commands.c | 1037 +-------- ppu/librsx/commands_impl.h | 1993 +++++++++++++++++ ppu/librsx/fragment_program.c | 8 +- ppu/librsx/init.c | 52 +- ppu/librsx/rsx_internal.h | 32 +- ppu/librsx/vertex_program.c | 9 +- ppu/sprx/libgcm_sys/exports.h | 10 +- ppu/sprx/libgcm_sys/gcm_wrapper.c | 28 + ppu/sprx/liblv2/Makefile | 3 +- ppu/sprx/liblv2/exports.h | 14 +- ppu/sprx/liblv2/spu_printf_wrapper.c | 175 +- ppu/sprx/liblv2/spu_thread_printf.c | 319 +++ ppu/sprx/liblv2/sys_spu_wrapper.c | 30 + samples/graphics/cairo/source/rsxutil.c | 12 +- samples/graphics/rsxtest/source/main.cpp | 41 +- samples/graphics/rsxtest/source/rsxutil.cpp | 12 +- samples/graphics/rsxtest_spu/source/main.cpp | 41 +- .../graphics/rsxtest_spu/source/rsxutil.cpp | 12 +- samples/spu/Makefile | 2 +- samples/spu/spumars/Makefile | 145 ++ samples/spu/spumars/source/main.c | 52 + samples/spu/spumars/spu/Makefile | 126 ++ samples/spu/spumars/spu/source/main.c | 22 + samples/spu/spuparallel/source/main.c | 4 +- spu/include/sys/spu_event.h | 4 +- spu_rules | 19 +- tools/cgcomp/include/compilerfp.h | 22 +- .../include/{compiler.h => compilervp.h} | 37 +- tools/cgcomp/include/fpparser.h | 29 - tools/cgcomp/include/nvfx_shader.h | 27 +- tools/cgcomp/include/parser.h | 17 +- tools/cgcomp/include/types.h | 42 +- tools/cgcomp/include/vpparser.h | 2 +- tools/cgcomp/source/compilerfp.cpp | 383 +++- .../source/{compiler.cpp => compilervp.cpp} | 298 ++- tools/cgcomp/source/fpparser.cpp | 77 +- tools/cgcomp/source/main.cpp | 92 +- tools/cgcomp/source/parser.cpp | 111 +- tools/cgcomp/source/vpparser.cpp | 97 +- 53 files changed, 5809 insertions(+), 2934 deletions(-) create mode 100644 ppu/include/rsx/commands_inc.h create mode 100644 ppu/include/rsx/rsx_function_macros.h create mode 100644 ppu/include/sys/spu_thread_printf.h create mode 100644 ppu/librsx/buffer_impl.h create mode 100644 ppu/librsx/commands_impl.h create mode 100644 ppu/sprx/liblv2/spu_thread_printf.c create mode 100644 ppu/sprx/liblv2/sys_spu_wrapper.c create mode 100644 samples/spu/spumars/Makefile create mode 100644 samples/spu/spumars/source/main.c create mode 100644 samples/spu/spumars/spu/Makefile create mode 100644 samples/spu/spumars/spu/source/main.c rename tools/cgcomp/include/{compiler.h => compilervp.h} (60%) rename tools/cgcomp/source/{compiler.cpp => compilervp.cpp} (58%) diff --git a/.gitignore b/.gitignore index cddced40..4542cfc5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,11 +2,13 @@ *.elf *.self *.pkg +*.bin *.o *.a *.d *.so *.dll +*.task *.elf.map build/ .idea/ diff --git a/common/libspumars/ppu/ppu/mars_kernel.c b/common/libspumars/ppu/ppu/mars_kernel.c index c9a87e6d..ad40cc9e 100644 --- a/common/libspumars/ppu/ppu/mars_kernel.c +++ b/common/libspumars/ppu/ppu/mars_kernel.c @@ -140,46 +140,46 @@ __attribute__((aligned(128))) const unsigned char mars_kernel_entry[] = { 0x18,0x20,0x81,0x83,0x33,0x82,0xdb,0x8c,0x16,0x01,0x02,0x07,0x35,0x80,0x00,0x05, 0x08,0x21,0xc1,0x88,0x08,0x21,0x84,0x09,0x18,0x22,0x84,0x8b,0x08,0x23,0x05,0x83, 0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x3f,0xbe,0x41,0x83,0x3f,0xe1,0x01,0x82, -0x14,0x1c,0x01,0x03,0x35,0x00,0x00,0x00,0x14,0x00,0x41,0x8b,0x24,0x00,0x40,0x80, -0x04,0x00,0x01,0x8a,0x35,0x90,0x00,0x00,0x24,0xfe,0x00,0x81,0x04,0x00,0x02,0x06, -0x1c,0xe0,0x00,0x81,0x40,0x80,0x00,0x05,0x40,0x80,0x00,0x09,0x00,0x20,0x00,0x00, -0x40,0x80,0x00,0x07,0x20,0x00,0x0f,0x0b,0x42,0x11,0x80,0x08,0x40,0x80,0x00,0x05, -0x1c,0x1e,0x04,0x0c,0x35,0x90,0x00,0x00,0x40,0x80,0x00,0x09,0x40,0x80,0x00,0x07, -0x40,0x80,0x04,0x0d,0x40,0x80,0x00,0x8e,0x40,0x20,0x00,0x7f,0x38,0x83,0x44,0x03, -0x1c,0x02,0x04,0x08,0x12,0x7f,0xfe,0x91,0x3b,0x82,0x01,0x82,0x3f,0xbe,0x41,0x04, -0x3f,0xbe,0x81,0x0f,0x3f,0xe1,0x02,0x10,0x3f,0xe1,0x07,0x91,0x14,0x04,0x08,0x12, +0x14,0x1c,0x01,0x03,0x35,0x00,0x00,0x00,0x14,0x00,0x41,0x8c,0x24,0x00,0x40,0x80, +0x04,0x00,0x01,0x89,0x35,0x90,0x00,0x00,0x24,0xfe,0x00,0x81,0x04,0x00,0x02,0x06, +0x1c,0xe0,0x00,0x81,0x40,0x80,0x00,0x05,0x40,0x80,0x00,0x08,0x00,0x20,0x00,0x00, +0x40,0x80,0x00,0x07,0x20,0x00,0x0f,0x0c,0x42,0x11,0x80,0x02,0x40,0x80,0x00,0x05, +0x40,0x80,0x00,0x08,0x35,0x90,0x00,0x00,0x40,0x80,0x00,0x07,0x40,0x80,0x04,0x0d, +0x40,0x80,0x00,0x8e,0x40,0x80,0x07,0x8a,0x40,0x20,0x00,0x7f,0x38,0x83,0x41,0x03, +0x1c,0x02,0x01,0x02,0x12,0x7f,0xfe,0x91,0x3b,0x80,0x81,0x8b,0x3f,0xbe,0x45,0x84, +0x3f,0xbe,0x85,0x8f,0x3f,0xe1,0x02,0x10,0x3f,0xe1,0x07,0x91,0x14,0x04,0x08,0x12, 0x14,0x08,0x08,0x13,0x56,0xc0,0x09,0x14,0x5a,0x04,0x42,0x95,0x00,0x20,0x00,0x00, 0x56,0xc0,0x09,0x96,0x00,0x20,0x00,0x00,0x22,0x00,0x27,0x14,0x80,0xa1,0x48,0x95, -0x40,0x80,0x00,0x87,0x78,0x02,0x06,0x18,0x20,0x7f,0xf6,0x18,0x12,0x02,0x46,0x89, +0x40,0x80,0x00,0x87,0x1c,0xff,0xc5,0x0a,0x21,0x7f,0xf6,0x0a,0x12,0x02,0x46,0x89, 0x42,0x11,0xc0,0x04,0x33,0x83,0x61,0x83,0x24,0x01,0x00,0x85,0x24,0x00,0x80,0x86, -0x24,0x01,0x80,0x87,0x24,0x01,0x40,0x89,0x24,0x00,0xc0,0x8a,0x24,0x01,0xc0,0x8b, -0x33,0x02,0x42,0x00,0x34,0x01,0xc0,0x9e,0x34,0x01,0x00,0x99,0x34,0x00,0x80,0x9a, -0x35,0x90,0x00,0x00,0x34,0x01,0x80,0x9b,0x34,0x01,0x40,0x9c,0x34,0x00,0xc0,0x9d, -0x40,0x20,0x00,0x7f,0x20,0x00,0x1d,0x9e,0x18,0x06,0x8d,0x3e,0x33,0x82,0xb8,0x8b, -0x42,0x11,0xc0,0x3f,0x32,0xfe,0xfe,0x84,0x40,0x80,0x0e,0x33,0x18,0x0f,0x9f,0xb2, -0x35,0x90,0x00,0x00,0x14,0x3f,0xcd,0xc5,0x1c,0x0a,0x99,0x43,0x38,0x8c,0xd9,0x41, -0x42,0x7f,0xff,0xc0,0x3f,0xbf,0x22,0xc7,0x14,0x3f,0xcc,0xc8,0x38,0x8c,0xd9,0x42, -0x14,0x3f,0xce,0x46,0x3e,0xa3,0x19,0x44,0x3f,0xbf,0x24,0x4b,0x3f,0x82,0x23,0xcc, -0x3b,0x90,0xe0,0xc9,0x3f,0x82,0x25,0x8a,0x3f,0xe2,0x26,0x4d,0x18,0x30,0x24,0xca, -0x3f,0xbf,0x25,0x4f,0x3f,0x60,0x66,0xce,0x3f,0xe2,0x45,0x06,0x18,0x22,0xe7,0x85, -0x08,0x33,0x82,0x8c,0x3f,0xe1,0x06,0x07,0x14,0xff,0x83,0x8d,0x08,0x31,0x86,0x8e, -0x18,0x30,0x07,0x03,0x3f,0xbf,0x01,0x82,0x18,0x21,0x01,0x0f,0x08,0x21,0x87,0x90, -0x3f,0xe1,0x08,0x11,0xb7,0xb0,0x88,0xc4,0x40,0x20,0x00,0x7f,0x28,0x8c,0xd9,0x3d, -0x42,0x11,0xc0,0x12,0x33,0x83,0x34,0x15,0x40,0x80,0x02,0x18,0x12,0x02,0x5c,0x8c, -0x1c,0x01,0x09,0x13,0x33,0x83,0x32,0x09,0x1c,0x20,0x00,0x81,0x3e,0xc1,0x09,0x14, -0x04,0x00,0x09,0x04,0x33,0x83,0x41,0x83,0x3b,0x84,0xca,0x96,0x1c,0x00,0x4b,0x17, -0xb1,0x02,0x4b,0x94,0x28,0x86,0x09,0x08,0x34,0x00,0x40,0x80,0x32,0x02,0x56,0x80, -0x7d,0x00,0x0b,0x17,0x00,0x20,0x00,0x00,0x81,0x22,0x47,0x17,0x32,0x7f,0xd9,0x00, -0x20,0x7f,0xf6,0x1d,0x18,0x06,0x8d,0x1f,0x42,0x11,0xc0,0x20,0x40,0x80,0x0e,0x22, -0x18,0x07,0xd0,0x21,0x42,0x7f,0xff,0xa7,0x1c,0x0a,0x90,0xa4,0x38,0x88,0x90,0xa3, -0x14,0x00,0x8e,0xa5,0x40,0x80,0x00,0x2e,0x3b,0x89,0x11,0xa6,0x18,0x29,0xd3,0x28, -0x3f,0xbf,0x14,0x29,0x20,0x00,0x04,0xa5,0x3f,0x3f,0x94,0xaa,0x3f,0xe1,0x15,0x2b, -0x14,0x0f,0xd5,0xae,0x7e,0x0f,0xd7,0x2c,0x56,0xc0,0x16,0x2d,0x23,0x00,0x01,0xad, -0x1d,0x00,0x57,0x2e,0x00,0x20,0x00,0x00,0x14,0x3f,0xd7,0x2f,0x33,0x82,0x92,0xb4, -0x42,0x11,0xc0,0x31,0x12,0x7f,0xe8,0x8d,0x40,0x80,0x0e,0x33,0x3f,0xbf,0x17,0xb0, -0x18,0x07,0xd8,0xb2,0x38,0x8c,0xd9,0x37,0x18,0x2d,0x14,0xb5,0x3f,0x82,0x18,0x38, -0x3e,0xa3,0x19,0x36,0x3f,0xe2,0x1c,0x39,0x3f,0x60,0x9c,0xba,0x08,0x2e,0x9a,0xbb, -0x3f,0xe1,0x1d,0xbc,0xb7,0xad,0xde,0x36,0x32,0x7f,0xe2,0x00,0x00,0x20,0x00,0x00, +0x24,0x01,0x80,0x87,0x24,0x01,0x40,0x88,0x24,0x00,0xc0,0x89,0x24,0x01,0xc0,0x8c, +0x33,0x02,0x42,0x00,0x34,0x01,0xc0,0x9d,0x34,0x01,0x00,0x98,0x34,0x00,0x80,0x99, +0x35,0x90,0x00,0x00,0x34,0x01,0x80,0x9a,0x34,0x01,0x40,0x9b,0x34,0x00,0xc0,0x9c, +0x40,0x20,0x00,0x7f,0x20,0x00,0x1d,0x9d,0x18,0x06,0x4c,0xbd,0x33,0x82,0xb8,0x86, +0x42,0x11,0xc0,0x3e,0x32,0xfe,0xfe,0x8b,0x40,0x80,0x0e,0x32,0x18,0x0f,0x5f,0x31, +0x35,0x90,0x00,0x00,0x14,0x3f,0xcd,0x44,0x1c,0x0a,0x98,0xc2,0x38,0x8c,0x98,0xc0, +0x42,0x7f,0xff,0xbf,0x3f,0xbf,0x22,0x46,0x14,0x3f,0xcc,0x47,0x38,0x8c,0x98,0xc1, +0x14,0x3f,0xcd,0xc5,0x3e,0xa3,0x18,0xc3,0x3f,0xbf,0x23,0xca,0x3f,0x82,0x23,0x4b, +0x3b,0x90,0xa0,0x48,0x3f,0x82,0x25,0x4f,0x3f,0xe2,0x25,0xcc,0x18,0x2f,0xe4,0x49, +0x3f,0xbf,0x24,0xce,0x3f,0x60,0x66,0x4d,0x3f,0xe2,0x67,0x89,0x18,0x21,0xa7,0x0c, +0x08,0x33,0x46,0x05,0x3f,0xe1,0x02,0x87,0x14,0xff,0x83,0x8d,0x08,0x31,0x46,0x8e, +0x18,0x2f,0xc7,0x03,0x3f,0xbf,0x01,0x82,0x18,0x22,0xc1,0x04,0x08,0x22,0x42,0x0f, +0x3f,0xe1,0x07,0x90,0xb7,0x90,0x48,0x43,0x40,0x20,0x00,0x7f,0x28,0x8c,0x98,0xbc, +0x42,0x11,0xc0,0x11,0x33,0x83,0x34,0x14,0x40,0x80,0x02,0x0a,0x12,0x02,0x5c,0x8c, +0x1c,0x01,0x08,0x92,0x33,0x83,0x32,0x17,0x1c,0x20,0x00,0x81,0x3e,0xc1,0x08,0x93, +0x04,0x00,0x08,0x84,0x33,0x83,0x41,0x83,0x3b,0x84,0x8a,0x15,0x1c,0x00,0x4a,0x96, +0xb1,0x05,0xcb,0x13,0x28,0x82,0x88,0x88,0x34,0x00,0x40,0x80,0x32,0x02,0x56,0x80, +0x7d,0x00,0x0b,0x17,0x00,0x20,0x00,0x00,0x81,0x02,0x07,0x17,0x32,0x7f,0xd9,0x00, +0x20,0x7f,0xf6,0x1c,0x18,0x06,0x4c,0x9e,0x42,0x11,0xc0,0x1f,0x40,0x80,0x0e,0x21, +0x18,0x07,0x8f,0xa0,0x42,0x7f,0xff,0xa6,0x1c,0x0a,0x90,0x23,0x38,0x88,0x50,0x22, +0x14,0x00,0x8e,0x24,0x40,0x80,0x00,0x2d,0x3b,0x88,0xd1,0x25,0x18,0x29,0x92,0xa7, +0x3f,0xbf,0x13,0xa8,0x20,0x00,0x04,0xa4,0x3f,0x3f,0x94,0x29,0x3f,0xe1,0x14,0xaa, +0x14,0x0f,0xd5,0x2d,0x7e,0x0f,0xd6,0xab,0x56,0xc0,0x15,0xac,0x23,0x00,0x01,0xac, +0x1d,0x00,0x56,0xad,0x00,0x20,0x00,0x00,0x14,0x3f,0xd6,0xae,0x33,0x82,0x92,0xb3, +0x42,0x11,0xc0,0x30,0x12,0x7f,0xe8,0x8d,0x40,0x80,0x0e,0x32,0x3f,0xbf,0x17,0x2f, +0x18,0x07,0x98,0x31,0x38,0x8c,0x98,0xb6,0x18,0x2c,0xd4,0x34,0x3f,0x82,0x17,0xb7, +0x3e,0xa3,0x18,0xb5,0x3f,0xe2,0x1b,0xb8,0x3f,0x60,0x9c,0x39,0x08,0x2e,0x5a,0x3a, +0x3f,0xe1,0x1d,0x3b,0xb7,0x8d,0x9d,0xb5,0x32,0x7f,0xe2,0x00,0x00,0x20,0x00,0x00, 0x12,0x7f,0xb1,0x0c,0x3f,0x82,0x02,0x05,0x40,0x80,0x00,0x04,0x33,0x82,0x82,0x82, 0x24,0x00,0x40,0x80,0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81,0x3f,0xe3,0x02,0x86, 0x18,0x20,0x81,0x83,0x08,0x21,0x81,0x87,0x40,0x80,0x00,0x03,0x24,0x00,0x80,0x87, @@ -447,26 +447,26 @@ __attribute__((aligned(128))) const unsigned char mars_kernel_entry[] = { 0x08,0x22,0x84,0x8b,0x56,0xc0,0x05,0x8c,0x23,0x00,0x2d,0x0c,0x32,0x80,0x80,0x8d, 0x16,0x1f,0xc6,0x8e,0x18,0x23,0x81,0x8f,0x78,0x01,0xc7,0x90,0x36,0x00,0x08,0x11, 0x4c,0x02,0xc8,0x92,0x20,0x00,0x2a,0x92,0x14,0x1f,0xc2,0x25,0x21,0x00,0x29,0xa5, -0x40,0x82,0x00,0x07,0x21,0xa0,0x00,0x87,0x40,0x80,0x00,0x37,0x40,0x80,0x01,0x99, -0x04,0x00,0x02,0x18,0x3f,0xbf,0x01,0x9b,0x1c,0x04,0x02,0x1a,0x3e,0x80,0xc2,0x13, -0x04,0x00,0x03,0x9c,0x3e,0x80,0x02,0x16,0x40,0x80,0x01,0x1d,0x3e,0x80,0x82,0x17, -0x1c,0x03,0xc2,0x14,0x3f,0xe1,0x0d,0x9e,0x40,0x80,0x00,0x95,0x3f,0xe1,0x01,0x83, -0x04,0x00,0x1b,0x9f,0x40,0x80,0x40,0x20,0x40,0x80,0x68,0x21,0x40,0x80,0x5a,0x22, -0x12,0x00,0x00,0x17,0x21,0xa0,0x08,0x18,0x21,0xa0,0x08,0x9e,0x21,0xa0,0x09,0x03, -0x21,0xa0,0x09,0xa0,0x21,0xa0,0x0a,0x1f,0x21,0xa0,0x0a,0xa1,0x01,0xa0,0x0d,0x85, +0x40,0x82,0x00,0x07,0x21,0xa0,0x00,0x87,0x40,0x80,0x00,0x37,0x40,0x80,0x40,0x1a, +0x04,0x00,0x02,0x18,0x3f,0xbf,0x01,0x94,0x04,0x00,0x1b,0x99,0x3f,0xe1,0x01,0x83, +0x40,0x80,0x68,0x1b,0x3e,0x80,0xc2,0x13,0x40,0x80,0x5a,0x1c,0x3e,0x80,0x02,0x16, +0x40,0x80,0x01,0x9d,0x3f,0xe1,0x0a,0x1f,0x1c,0x04,0x02,0x1e,0x3e,0x80,0x82,0x17, +0x04,0x00,0x03,0xa0,0x40,0x80,0x01,0x21,0x1c,0x03,0xc2,0x22,0x40,0x80,0x00,0x95, +0x12,0x00,0x00,0x17,0x21,0xa0,0x08,0x18,0x21,0xa0,0x08,0x9f,0x21,0xa0,0x09,0x03, +0x21,0xa0,0x09,0x9a,0x21,0xa0,0x0a,0x19,0x21,0xa0,0x0a,0x9b,0x01,0xa0,0x0d,0x85, 0x20,0x00,0x14,0xb7,0x34,0x00,0x02,0x28,0x3f,0x83,0x54,0x29,0x7e,0x00,0x54,0xaa, -0x56,0xc0,0x15,0x2b,0x00,0x20,0x00,0x00,0x23,0x00,0x03,0xab,0x38,0x87,0x42,0x2c, -0x3b,0x85,0x16,0x2d,0x7a,0x09,0x56,0xae,0x56,0xc0,0x17,0x2f,0x00,0x20,0x00,0x00, -0x23,0x00,0x02,0x2f,0x01,0xa0,0x00,0x05,0x21,0xa0,0x01,0x1c,0x32,0x7f,0xf4,0x80, +0x56,0xc0,0x15,0x2b,0x00,0x20,0x00,0x00,0x23,0x00,0x03,0xab,0x38,0x88,0x42,0x2c, +0x3b,0x88,0x96,0x2d,0x7a,0x09,0x56,0xae,0x56,0xc0,0x17,0x2f,0x00,0x20,0x00,0x00, +0x23,0x00,0x02,0x2f,0x01,0xa0,0x00,0x05,0x21,0xa0,0x01,0x20,0x32,0x7f,0xf4,0x80, 0x1d,0x00,0x52,0xb1,0x34,0x00,0x02,0x30,0x04,0x00,0x16,0xa5,0xb6,0x4c,0x0a,0x96, -0x24,0x00,0x02,0x32,0x38,0x87,0x42,0x33,0xb6,0x8c,0xd8,0x97,0x28,0x87,0x42,0x34, -0x00,0x60,0x00,0x00,0x21,0xa0,0x08,0x18,0x21,0xa0,0x08,0x9e,0x21,0xa0,0x09,0x03, -0x21,0xa0,0x09,0xa0,0x21,0xa0,0x0a,0x1f,0x21,0xa0,0x0a,0xa2,0x01,0xa0,0x0d,0xb5, +0x24,0x00,0x02,0x32,0x38,0x88,0x42,0x33,0xb6,0x8c,0xd8,0x97,0x28,0x88,0x42,0x34, +0x00,0x60,0x00,0x00,0x21,0xa0,0x08,0x18,0x21,0xa0,0x08,0x9f,0x21,0xa0,0x09,0x03, +0x21,0xa0,0x09,0x9a,0x21,0xa0,0x0a,0x19,0x21,0xa0,0x0a,0x9c,0x01,0xa0,0x0d,0xb5, 0x14,0x00,0x5a,0xb6,0x21,0x7f,0xeb,0xb6,0x1c,0x00,0x5b,0xb7,0x7c,0x00,0x9b,0xb8, 0x20,0x7f,0xea,0x38,0x40,0x82,0x00,0x04,0x21,0xa0,0x01,0x04,0x40,0x80,0x00,0x03, -0x35,0x00,0x00,0x00,0x12,0x7f,0xf7,0x8a,0x40,0x20,0x00,0x7f,0x38,0x86,0x42,0x23, -0x40,0x20,0x00,0x7f,0x38,0x86,0x42,0x24,0x3b,0x86,0x91,0xa5,0x1d,0x00,0x52,0xa6, -0xb4,0xe9,0x13,0x13,0x28,0x86,0x42,0x27,0x40,0x20,0x00,0x7f,0x32,0x7f,0xf2,0x80, +0x35,0x00,0x00,0x00,0x12,0x7f,0xf7,0x8a,0x40,0x20,0x00,0x7f,0x38,0x87,0x42,0x23, +0x40,0x20,0x00,0x7f,0x38,0x87,0x42,0x24,0x3b,0x87,0x91,0xa5,0x1d,0x00,0x52,0xa6, +0xb4,0xe9,0x13,0x13,0x28,0x87,0x42,0x27,0x40,0x20,0x00,0x7f,0x32,0x7f,0xf2,0x80, 0x40,0x80,0x00,0x83,0x32,0x7f,0xf9,0x80,0x40,0x80,0x02,0x83,0x32,0x7f,0xf8,0x80, 0x40,0x80,0x00,0x07,0x12,0x00,0x22,0x89,0x7c,0x00,0x02,0x06,0x78,0x01,0xc1,0x85, 0x0c,0x00,0x03,0x09,0x36,0x00,0x02,0x82,0x4c,0x02,0xc1,0x08,0x0c,0x00,0x04,0x0a, diff --git a/ppu/include/lv2/spu.h b/ppu/include/lv2/spu.h index d308297f..2a3e1515 100644 --- a/ppu/include/lv2/spu.h +++ b/ppu/include/lv2/spu.h @@ -154,6 +154,8 @@ s32 sysSpuPrintfDetachThread(sys_spu_thread_t thread); */ s32 sysSpuPrintfFinalize(); +s32 sysSpuImageOpenELF(sysSpuImage* image, const char* path); + #ifdef __cplusplus } #endif diff --git a/ppu/include/rsx/commands.h b/ppu/include/rsx/commands.h index 2e1a29e1..84e0e3a1 100644 --- a/ppu/include/rsx/commands.h +++ b/ppu/include/rsx/commands.h @@ -14,590 +14,25 @@ These are functions to enqueue commands into the RSX's command buffer. extern "C" { #endif -/*! \brief Set drawing direction of front face. -\param context Pointer to the context object. -\param dir Drawing direction of front face. Possible values are: -- \ref GCM_FRONTFACE_CW -- \ref GCM_FRONTFACE_CCW -*/ -void rsxSetFrontFace(gcmContextData *context,u32 dir); - -/*! \brief Set culling mode. -\param context Pointer to context object. -\param cull Type of cull mode. Possible values are: -- \ref GCM_CULL_FRONT -- \ref GCM_CULL_BACK -- \ref GCM_CULL_ALL -*/ -void rsxSetCullFace(gcmContextData *context,u32 cull); - -/*! \brief Enable/Disable face culling. -\param context Pointer to the context object. -\param enable Enable flag. Possible values are: - - \ref GCM_TRUE - - \ref GCM_FALSE - */ -void rsxSetCullFaceEnable(gcmContextData *context,u32 enable); - -/*! \brief Enable/Disable write to depth buffer. -\param context Pointer to the context object. -\param enable Enable flag. Possible values are: -- \ref GCM_TRUE -- \ref GCM_FALSE -*/ -void rsxSetDepthWriteEnable(gcmContextData *context,u32 enable); - -/*! \brief Stop the render sequence. - -Stops the rendering for a primitive, started by \ref rsxDrawVertexBegin. -\param context Pointer to the context object. -*/ -void rsxDrawVertexEnd(gcmContextData *context); - -/*! \brief Set the shading model for the render sequence. -\param context Pointer to the context object. -\param shadeModel Type of shading model. Possible values are: -- \ref GCM_SHADE_MODEL_FLAT -- \ref GCM_SHADE_MODEL_SMOOTH -*/ -void rsxSetShadeModel(gcmContextData *context,u32 shadeModel); - -/*! \brief Start the render sequence. - -Starts the rendering for a primitive. -\param context Pointer to the context object. -\param type Type of primitive to render. Possible values are: -- \ref GCM_TYPE_POINTS -- \ref GCM_TYPE_LINES -- \ref GCM_TYPE_LINE_LOOP -- \ref GCM_TYPE_LINE_STRIP -- \ref GCM_TYPE_TRIANGLES -- \ref GCM_TYPE_TRIANGLE_STRIP -- \ref GCM_TYPE_TRIANGLE_FAN -- \ref GCM_TYPE_QUADS -- \ref GCM_TYPE_QUAD_STRIP -- \ref GCM_TYPE_POLYGON -*/ -void rsxDrawVertexBegin(gcmContextData *context,u32 type); - -void rsxDrawVertex2f(gcmContextData *context,u8 idx,f32 x,f32 y); -void rsxDrawVertex3f(gcmContextData *context,u8 idx,f32 x,f32 y,f32 z); -void rsxDrawVertex4f(gcmContextData *context,u8 idx,f32 x,f32 y,f32 z,f32 w); -void rsxSetScissor(gcmContextData *context,u16 x,u16 y,u16 w,u16 h); - -/*! \brief Specify the value used for depth buffer comparisons. -\param context Pointer to the context object. -\param func Specifies the depth comparison function. Possible values are: - - \ref GCM_NEVER - - \ref GCM_LESS - - \ref GCM_EQUAL - - \ref GCM_LEQUAL - - \ref GCM_GREATER - - \ref GCM_NOTEQUAL - - \ref GCM_GEQUAL - - \ref GCM_ALWAYS -*/ -void rsxSetDepthFunc(gcmContextData *context,u32 func); - -/*! \brief Enable or disable the depth test. - -If depth test is enabled, the GPU performs depth comparisons and updates the -depth buffer. -Note that even if the depth buffer exists and the depth mask is non-zero, -the depth buffer is not updated if the depth test is disabled. -\param context Pointer to the context object. -\param enable Enable flag. Possible values are: - - \ref GCM_TRUE - - \ref GCM_FALSE -*/ -void rsxSetDepthTestEnable(gcmContextData *context,u32 enable); - -/*! \brief Clear the render surface. - -This function clears the chosen selection of color components, depth and stencil -values for all pixels in the destination surface. The clear color can be chosen -using \ref rsxSetClearColor, and the clear value for the depth buffer is set -using \ref rsxSetClearDepthValue. - -\param context Pointer to the context object. -\param clear_mask A selection of components to be cleared. Must be an OR -combination of the following values: - - \ref GCM_CLEAR_Z - - \ref GCM_CLEAR_S - - \ref GCM_CLEAR_R - - \ref GCM_CLEAR_G - - \ref GCM_CLEAR_B - - \ref GCM_CLEAR_A -The value of \ref GCM_CLEAR_M can also be chosen, to clear all RGBA components, -depth and stencil buffers. -*/ -void rsxClearSurface(gcmContextData *context,u32 clear_mask); - -/*! \brief Set the clear depth value. - -This value is used by the \ref rsxClearSurface function. -\param context Pointer to the context object. -\param value Color value -*/ -void rsxSetClearDepthValue(gcmContextData *context,u32 value); -void rsxSetReturnCommand(gcmContextData *context); -void rsxSetCallCommand(gcmContextData *context,u32 offset); -void rsxSetJumpCommand(gcmContextData *context,u32 offset); -void rsxSetNopCommand(gcmContextData *context,u32 count); - -/*! \brief Set the clear color. - -The clear color value is used by the \ref rsxClearSurface function. -\param context Pointer to the context object. -\param color The clear color value. -*/ -void rsxSetClearColor(gcmContextData *context,u32 color); - -/*! \brief Enable or disable write access to the framebuffer color components. -\param context Pointer to the context object. -\param mask A selection of the components to enable write access. It is an OR -combination of the following values: - - \ref GCM_COLOR_MASK_B - - \ref GCM_COLOR_MASK_G - - \ref GCM_COLOR_MASK_R - - \ref GCM_COLOR_MASK_A -*/ -void rsxSetColorMask(gcmContextData *context,u32 mask); - -/*! \brief Enable or disable write access to the framebuffer color components -(Multiple Render Target output). -\param context Pointer to the context object. -\param mask A selection of the components to enable write access. It is an OR -combination of the following values: - - \ref GCM_COLOR_MASK_B - - \ref GCM_COLOR_MASK_G - - \ref GCM_COLOR_MASK_R - - \ref GCM_COLOR_MASK_A -*/ -void rsxSetColorMaskMRT(gcmContextData *context,u32 mask); - -/*! \brief Setup the render surface. - -This function is used to setup the render target where RSX should render the frame into. -\param context Pointer to the context object. -\param surface Pointer to the surface object. -*/ -void rsxSetSurface(gcmContextData *context,gcmSurface *surface); -void rsxSetReferenceCommand(gcmContextData *context,u32 ref_value); - -/*! \brief Enqueues a Wait for label command. -\param context Pointer to the context object. -\param index Label index -\param valuie Label value -*/ -void rsxSetWaitLabel(gcmContextData *context,u8 index,u32 value); - -/*! \brief Enqueues a Write Command label command. -\param context Pointer to the context object. -\param index Label index -\param value Label value -*/ -void rsxSetWriteCommandLabel(gcmContextData *context,u8 index,u32 value); - -/*! \brief Enqueues a Write Backend label command. -\param context Pointer to the context object. -\param index Label index -\param value Label value -*/ -void rsxSetWriteBackendLabel(gcmContextData *context,u8 index,u32 value); - -void rsxSetViewportClip(gcmContextData *context,u8 sel,u16 width,u16 height); - -/*! \brief Set viewport. - -This function sets the viewport.
-The origin (0,0) of the normalized device coordinate points to the center of the screen.
-Performing viewport conversion, where the upper left corner is the origin is as follows: -\code - x = X; - y = Y; - width = WIDTH; - height = HEIGHT; - min = 0.0f; - max = 1.0f; - scale[0] = width * 0.5f; - scale[1] = height * 0.5f; - scale[2] = (max - min) * 0.5f; - offset[0] = x + width * 0.5f; - offset[1] = y + height * 0.5f; - offset[2] = (max + min) * 0.5f; -\endcode -

-Performing viewport conversion, where the lower left corner is the origin is as follows (this is equivalent to glViewport): -\code - x = X; - y = WINDOW_HEIGHT - Y - HEIGHT; - width = WIDTH; - height = HEIGHT; - min = 0.0f; - max = 1.0f; - scale[0] = width * 0.5f; - scale[1] = height * -0.5f; - scale[2] = (max - min) * 0.5f; - offset[0] = x + width * 0.5f; - offset[1] = y + height * 0.5f; - offset[2] = (max + min) * 0.5f; -\endcode -\param context Pointer to the context object. -\param x Origin of the viewport rectangle in pixels (0 - 4095). Initial value is (0,0). -\param y Origin of the viewport rectangle in pixels (0 - 4095). Initial value is (0,0). -\param width Width of the viewport (0 - 4096). Initial value is 4096. -\param height Height of the viewport (0 - 4096). Initial value is 4096. -\param min Minimum Z clip value. Initial value is 0.0. -\param max Maximum Z clip value. Initial value is 1.0. -\param scale Scale values to be used for viewport conversion. Initial values are (2048.0,2048.0,0.5,0.0). -\param offset Offset values to be used for viewport conversion. Initial values are (2048.0,2048.0,0.5,0.0). -*/ -void rsxSetViewport(gcmContextData *context,u16 x,u16 y,u16 width,u16 height,f32 min,f32 max,const f32 scale[4],const f32 offset[4]); - -/*! \brief Invalidates a texture cache. -\param context Pointer to the context object. -\param type Type of texture cache to be invalidated. Possible values are: - - \ref GCM_INVALIDATE_TEXTURE - - \ref GCM_INVALIDATE_VERTEX_TEXTURE -*/ -void rsxInvalidateTextureCache(gcmContextData *context,u32 type); - -/*! \brief Loads a texture. -\param context Pointer to the context object. -\param index Texture index. -\param texture Pointer to the texture data. -*/ -void rsxLoadTexture(gcmContextData *context,u8 index,const gcmTexture *texture); - -/*! \brief Set texture control parameters. -\param context Pointer to the context object. -\param index Texture index. -\param enable Enable flag. Possible values are: - - \ref GCM_TRUE - - \ref GCM_FALSE -\param minlod minimum level of detail. -\param maxlod maximum level of detail. -\param maxaniso sample level of the anisotropic filter. Possible values are: - - \ref GCM_TEXTURE_MAX_ANISO_1 - - \ref GCM_TEXTURE_MAX_ANISO_2 - - \ref GCM_TEXTURE_MAX_ANISO_4 - - \ref GCM_TEXTURE_MAX_ANISO_6 - - \ref GCM_TEXTURE_MAX_ANISO_8 - - \ref GCM_TEXTURE_MAX_ANISO_10 - - \ref GCM_TEXTURE_MAX_ANISO_12 - - \ref GCM_TEXTURE_MAX_ANISO_16 -\todo finish args documentation. -*/ -void rsxTextureControl(gcmContextData *context,u8 index,u32 enable,u16 minlod,u16 maxlod,u8 maxaniso); -void rsxTextureFilter(gcmContextData *context,u8 index,u8 min,u8 mag,u8 conv); -void rsxTextureWrapMode(gcmContextData *context,u8 index,u8 wraps,u8 wrapt,u8 wrapr,u8 unsignedRemap,u8 zfunc,u8 gamma); - -/*! \brief Load a compiled vertex shader program. -\param context Pointer to the context object -\param program Pointer to the vertex program configuration -\param ucode Pointer to the shader micro code -*/ -void rsxLoadVertexProgram(gcmContextData *context,rsxVertexProgram *program,const void *ucode); - -/*! \brief Load a compiled fragment shader program. -\param context Pointer to the context object -\param program Pointer to the fragment program configuration -\param offset Memory offset of fragment program -\param location Memory location type where the program relies. Possible values are: -- \ref GCM_LOCATION_RSX -- \ref GCM_LOCATION_CELL -*/ -void rsxLoadFragmentProgramLocation(gcmContextData *context,rsxFragmentProgram *program,u32 offset,u32 location); -void rsxZControl(gcmContextData *context,u8 cullNearFar,u8 zClampEnable,u8 cullIgnoreW); -void rsxLoadVertexProgramBlock(gcmContextData *context,rsxVertexProgram *program,const void *ucode); -void rsxLoadVertexProgramParameterBlock(gcmContextData *context,u32 base_const,u32 const_cnt,const f32 *value); -void rsxSetVertexProgramParameter(gcmContextData *context,rsxVertexProgram *program,s32 index,const f32 *value); -void rsxSetFragmentProgramParameter(gcmContextData *context,rsxFragmentProgram *program,s32 index,const f32 *value,u32 offset); -void rsxDrawVertexArray(gcmContextData *context,u32 type,u32 start,u32 count); -void rsxBindVertexArrayAttrib(gcmContextData *context,u8 attr,u32 offset,u8 stride,u8 elems,u8 dtype,u8 location); -void rsxDrawIndexArray(gcmContextData *context,u32 type,u32 offset,u32 count,u32 data_type,u32 location); -void rsxInlineTransfer(gcmContextData *context,const u32 dstOffset,const void *srcAddress,const u32 sizeInWords,const u8 location); -void rsxSetUserClipPlaneControl(gcmContextData *context,u32 plane0,u32 plane1,u32 plane2,u32 plane3,u32 plane4,u32 plane5); - -/*! \brief Specify pixel arithmetic. - -In RGBA mode, pixels can be drawn using a function that blends the incoming -(source) RGBA values with the RGBA values that are already in the frame buffer -(the destination values). Blending is initially disabled. -Use \ref rsxSetBlendEnable to enable and disable blending. - -\c rsxSetBlendFunc defines the operation of blending when it is enabled. -\p sfcolor and and \p sfalpha specify which method is used to scale the source -color and alpha components. -\p dfcolor and and \p dfalpha specify which method is used to scale the -destination color and alpha components. -The possible methods are described in the following table. -Each method defines four scale factors, one each for red, green, blue, and -alpha. -In the table and in subsequent equations, source and destination color -components are referred to as Rs, Gs, Bs, -As and Rd, Gd, Bd, Ad, -respectively. -The color specified by \ref rsxSetBlendColor is referred to as -Rc, Gc, Bc, Ac. -They are understood to have integer values between 0 and -kR, kG, kB, kA, where - -kc = 2mc - 1 - -and mR, mG, mB, mA is the number of -red, green, blue, and alpha bitplanes. - -Source and destination scale factors are referred to as -sR, sG, sB, sA and dR, -dG, dB, dA. -The scale factors described in the table, denoted fR, fG, -fB, fA, represent either source or destination -factors. All scale factors have range [0,1]. - - - - - - - - - - - - - - - - - - -
Parameter fR fG fB fA
\ref GCM_ZERO 0 0 0 0
\ref GCM_ONE 1 1 1 1
\ref GCM_SRC_COLOR Rs/kR Gs/kG Bs/kB As/kA
\ref GCM_ONE_MINUS_SRC_COLOR 1-Rs/kR1-Gs/kG1-Bs/kB1-As/kA
\ref GCM_DST_COLOR Rd/kR Gd/kG Bd/kB Ad/kA
\ref GCM_ONE_MINUS_DST_COLOR 1-Rd/kR1-Gd/kG1-Bd/kB1-Ad/kA
\ref GCM_SRC_ALPHA As/kA As/kA As/kA As/kA
\ref GCM_ONE_MINUS_SRC_ALPHA 1-As/kA1-As/kA1-As/kA1-As/kA
\ref GCM_DST_ALPHA Ad/kA Ad/kA Ad/kA Ad/kA
\ref GCM_ONE_MINUS_DST_ALPHA 1-Ad/kA1-Ad/kA1-Ad/kA1-Ad/kA
\ref GCM_CONSTANT_COLOR Rc Gc Bc Ac
\ref GCM_ONE_MINUS_CONSTANT_COLOR1-Rc 1-Gc 1-Bc 1-Ac
\ref GCM_CONSTANT_ALPHA Ac Ac Ac Ac
\ref GCM_ONE_MINUS_CONSTANT_ALPHA1-Ac 1-Ac 1-Ac 1-Ac
\ref GCM_SRC_ALPHA_SATURATE i i i 1
-In the table, - -i = min(As/kA, 1 - Ad/kA) - -To determine the blended RGBA values of a pixel when drawing in RGBA mode, -the equation defined by \ref rsxSetBlendEquation us used. In the default mode -(\ref GCM_FUNC_ADD for RGB and alpha equations), the equations are the -following: - - - Rd = min(kR, RssR + RddR) - - Gd = min(kG, GssG + GddG) - - Bd = min(kB, BssB + BddB) - - Ad = min(kA, AssA + AddA) - -Despite the apparent precision of the above equations, blending arithmetic is -not exactly specified, because blending operates with imprecise integer color -values. -However, a blend factor that should be equal to 1 is guaranteed not to modify -its multiplicand, and a blend factor equal to 0 reduces its multiplicand to 0. -For example, when \p sfcolor is \ref GCM_SRC_ALPHA, \p fdcolor is -\ref GCM_ONE_MINUS_SRC_ALPHA, and As is equal to kA, -the equations reduce to simple replacement: - -Rd = Rs ; -Gd = Gs ; -Bd = Bs - -\par Examples - -Transparency is best implemented using blend function -(\p sfcolor = \p sfalpha = \ref GCM_SRC_ALPHA, -\p dfcolor = \p dfalpha = \ref GCM_ONE_MINUS_SRC_ALPHA) with primitives sorted from -farthest to nearest. Note that this transparency calculation does not require -the presence of alpha bitplanes in the frame buffer. - -Blend function (\ref GCM_SRC_ALPHA, \ref GCM_ONE_MINUS_SRC_ALPHA) is also useful -for rendering antialiased points and lines in arbitrary order. - -Polygon antialiasing is optimized using blend function -(\ref GCM_SRC_ALPHA_SATURATE, \ref GCM_ONE) with polygons sorted from nearest -to farthest. -Destination alpha bitplanes, which must be present for this blend function to -operate correctly, store the accumulated coverage. - -\par Notes - -Incoming (source) alpha is correctly thought of as a material opacity, ranging -from 1.0 ( kA ), representing complete opacity, to 0.0 (0), -representing complete transparency. - - -\param context Pointer to the context object -\param sfcolor Specifies how the red, green, and blue source blending factors are computed. -\param dfcolor Specifies how the red, green, and blue source blending factors are computed. -\param sfalpha Specifies how the alpha source blending factor is computed. -\param dfalpha Specifies how the alpha destination blending factor is computed. -*/ -void rsxSetBlendFunc(gcmContextData *context,u16 sfcolor,u16 dfcolor,u16 sfalpha,u16 dfalpha); - -/*! \brief Set the blend equation. - -The blend equations determine how a new pixel (the “source” color) -is combined with a pixel already in the framebuffer (the -“destination” color). -This function specifies one blend equation for the RGB-color components -and one blend equation for the alpha component. - -These equations use the source and destination blend factors specified by -\ref rsxSetBlendFunc. See \ref rsxSetBlendFunc for a description of the various -blend factors. - -In the equations that follow, source and destination color components are -referred to as Rs, Gs, Bs, As -and Rd, Gd, Bd, Ad, respectively. -The result color is referred to as Rr, Gr, Br, -Ar. -The source and destination blend factors are denoted -sR, sG, sB, sA and dR, -dG, dB, dA, respectively. -For these equations all color components are understood to have values in the -range [0,1]. - - - - - - - - -
Mode Rr Gr Br Ar
\ref GCM_FUNC_ADD RssR+RddRGssG+GddGBssB+BddBAssA+AddA
\ref GCM_MIN min(Rs,Rd) min(Gs,Gd) min(Bs,Bd) min(As,Ad)
\ref GCM_MAX max(Rs,Rd) max(Gs,Gd) max(Bs,Bd) max(As,Ad)
\ref GCM_FUNC_SUBTRACT RssR-RddRGssG-GddGBssB-BddBAssA-AddA
\ref GCM_FUNC_REVERSE_SUBTRACTRddR-RssRGddG-GssGBddB-BssBAddA-AssA
- -The results of these equations are clamped to the range [0,1]. - -The \ref GCM_MIN and \ref GCM_MAX equations are useful for applications that -analyze image data (image thresholding against a constant color, for example). -The \ref GCM_FUNC_ADD equation is useful for antialiasing and transparency, -among other things. - -Initially, both the RGB blend equation and the alpha blend equation are set -to \ref GCM_FUNC_ADD. - -\par Notes - -The \ref GCM_MIN, and \ref GCM_MAX equations do not use the source or -destination factors, only the source and destination colors. - -\param context Pointer to the context object -\param color Specifies the RGB blend equation, how the red, green, and blue -components of the source and destination colors are combined. -\param alpha Specifies the alpha blend equation, how the alpha component of -the source and destination colors are combined. -*/ -void rsxSetBlendEquation(gcmContextData *context,u16 color,u16 alpha); - - -/*! \brief Set the blending constant color. -\param context Pointer to the context object -\param color0 all A, R, G, B components in 8-bit component mode -\param color1 reserved for 16-bit components -*/ -void rsxSetBlendColor(gcmContextData *context,u32 color0,u32 color1); - -/*! \brief Enable or disable blending. -\param context Pointer to the context object -\param enable - - \c GCM_TRUE : enable blending - - \c GCM_FALSE : disable blending -*/ -void rsxSetBlendEnable(gcmContextData *context,u32 enable); - -void rsxSetTransformBranchBits(gcmContextData *context,u32 branchBits); - -/*! \brief Configuration the mode for an upcoming asynchronous RSX DMA transfer. -\param context Pointer to the context object -\param mode Specify source and destination memory areas. Possible values are: -- \ref GCM_TRANSFER_LOCAL_TO_LOCAL -- \ref GCM_TRANSFER_MAIN_TO_LOCAL -- \ref GCM_TRANSFER_LOCAL_TO_MAIN -- \ref GCM_TRANSFER_MAIN_TO_MAIN -*/ -void rsxSetTransferDataMode(gcmContextData *context,u8 mode); - -/*! \brief Specify the memory locations for an RSX DMA transfer. This function should be called after rsxSetTransferDataMode() and rsxSetTransferDataFormat(). -\param context Pointer to the context object -\param dst Destination memory offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). -\param src Source memory offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). -*/ -void rsxSetTransferDataOffset(gcmContextData *context,u32 dst,u32 src); - -/*! \brief Format an upcoming asynchronous RSX DMA transfer. -\param context Pointer to the context object -\param inpitch Pitch size, in bytes, of the source buffer (e.g., for a buffer that represents a rectangular image, this would be the width multiplied by the number of bytes in each pixel). -\param outpitch Pitch size, in bytes, of the destination buffer (e.g., for a buffer that represents a rectangular image, this would be the width multiplied by the number of bytes in each pixel). -\param linelength Size, in bytes, of each line of data that will be transfered. -\param linecount Number of lines of data to transfer. -\param inbytes Number of bytes for each block (e.g., pixel) of data to be transfered: 1, 2, or 4. Will perform scatter-gather transfer if different from outbytes. -\param outbytes Number of bytes for each block (e.g., pixel) of data to be transfered: 1, 2, or 4. Will perform scatter-gather transfer if different from inbytes. -*/ -void rsxSetTransferDataFormat(gcmContextData *context,s32 inpitch,s32 outpitch,u32 linelength,u32 linecount,u8 inbytes,u8 outbytes); - -/*! \brief Initiate an asynchronous RSX DMA transfer. -\param context Pointer to the context object -\param mode Specify source and destination memory areas. Possible values are: -- \ref GCM_TRANSFER_LOCAL_TO_LOCAL -- \ref GCM_TRANSFER_MAIN_TO_LOCAL -- \ref GCM_TRANSFER_LOCAL_TO_MAIN -- \ref GCM_TRANSFER_MAIN_TO_MAIN -\param dst Destination memory offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). -\param outpitch Pitch size, in bytes, of the destination buffer (e.g., for a buffer that represents a rectangular image, this would be the width multiplied by the number of bytes in each pixel). -\param src Source memory offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). -\param inpitch Pitch size, in bytes, of the source buffer (e.g., for a buffer that represents a rectangular image, this would be the width multiplied by the number of bytes in each pixel). -\param linelength Size, in bytes, of each line of data that will be transfered. -\param linecount Number of lines of data to transfer. -*/ -void rsxSetTransferData(gcmContextData *context,u8 mode,u32 dst,u32 outpitch,u32 src,u32 inpitch,u32 linelength,u32 linecount); - -/*! \brief Configure an upcoming asynchronous RSX blit. -\param context Pointer to the context object -\param mode Specify source and destination memory areas. Possible values are: -- \ref GCM_TRANSFER_LOCAL_TO_LOCAL -- \ref GCM_TRANSFER_MAIN_TO_LOCAL -- \ref GCM_TRANSFER_LOCAL_TO_MAIN -- \ref GCM_TRANSFER_MAIN_TO_MAIN -\param surface Transfer surface mode. Possible values are: -- \ref GCM_TRANSFER_SURFACE -- \ref GCM_TRANSFER_SWIZZLE -*/ -void rsxSetTransferScaleMode(gcmContextData *context,const u8 mode,const u8 surface); - -/*! \brief Initiate an asynchronous RSX blit. -\param context Pointer to the context object -\param scale Specify the transfer geometry & parameters. -\param surface Specify the surface to blit to. -*/ -void rsxSetTransferScaleSurface(gcmContextData *context,const gcmTransferScale *scale,const gcmTransferSurface *surface); - -/*! \brief Initialiate an asynchronous transfer of a rectangular image from one area of memory to another. -\param context Pointer to the context object -\param mode Specify source and destination memory areas. Possible values are: -- \ref GCM_TRANSFER_LOCAL_TO_LOCAL -- \ref GCM_TRANSFER_MAIN_TO_LOCAL -- \ref GCM_TRANSFER_LOCAL_TO_MAIN -- \ref GCM_TRANSFER_MAIN_TO_MAIN -\param dstOffset Destination memory offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). -\param dstPitch Pitch size, in bytes, of the destination image data (width multiplied by the number of bytes in each pixel). -\param dstX Origin of the destination data, relative to the beginning of the destination buffer. -\param dstY Origin of the destination data, relative to the beginning of the destination buffer. -\param srcOffset Source memory offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). -\param srcPitch Pitch size, in bytes, of the source image data (width multiplied by the number of bytes in each pixel). -\param srcX Origin of the source rectangle, relative to the beginning of the source buffer. -\param srcY Origin of the source rectangle, relative to the beginning of the source buffer. -\param width Width of the transfer rectangle. -\param height Height of the transfer rectangle. -\param bytesPerPixel Number of bytes per pixel to transfer: 2 or 4. -*/ -void rsxSetTransferImage(gcmContextData *context,const u8 mode,const u32 dstOffset,const u32 dstPitch,const u32 dstX,const u32 dstY,const u32 srcOffset,const u32 srcPitch,const u32 srcX,const u32 srcY,const u32 width,const u32 height,const u32 bytesPerPixel); -void rsxSetTimeStamp(gcmContextData *context,u32 index); - -#if 0 -/*! \brief Unfinished -*/ -void rsxSetTransferScaleSwizzle(gcmContextData *context,const gcmTransferScale *scale,const gcmTransferSwizzle *swizzle); -#endif +#define RSX_INTERNAL 0 + +#define RSX_UNSAFE 1 +#define RSX_FUNCTION_MACROS +#include +#include +#undef RSX_FUNCTION_MACROS +#include +#undef RSX_UNSAFE + +#define RSX_UNSAFE 0 +#define RSX_FUNCTION_MACROS +#include +#include +#undef RSX_FUNCTION_MACROS +#include +#undef RSX_UNSAFE + +#undef RSX_INTERNAL #ifdef __cplusplus } diff --git a/ppu/include/rsx/commands_inc.h b/ppu/include/rsx/commands_inc.h new file mode 100644 index 00000000..59694cee --- /dev/null +++ b/ppu/include/rsx/commands_inc.h @@ -0,0 +1,675 @@ +/*! \brief Set drawing direction of front face. +\param context Pointer to the context object. +\param dir Drawing direction of front face. Possible values are: +- \ref GCM_FRONTFACE_CW +- \ref GCM_FRONTFACE_CCW +*/ +void RSX_FUNC(SetFrontFace)(gcmContextData *context,u32 dir); + +/*! \brief Set culling mode. +\param context Pointer to context object. +\param cull Type of cull mode. Possible values are: +- \ref GCM_CULL_FRONT +- \ref GCM_CULL_BACK +- \ref GCM_CULL_ALL +*/ +void RSX_FUNC(SetCullFace)(gcmContextData *context,u32 cull); + +/*! \brief Enable/Disable face culling. +\param context Pointer to the context object. +\param enable Enable flag. Possible values are: + - \ref GCM_TRUE + - \ref GCM_FALSE + */ +void RSX_FUNC(SetCullFaceEnable)(gcmContextData *context,u32 enable); + +/*! \brief Control front-facing polygon rendering. +\param context Pointer to the context object. +\param enable Drawing mode. Possible values are: + - \ref GCM_POLYGON_MODE_POINT + - \ref GCM_POLYGON_MODE_LINE + - \ref GCM_POLYGON_MODE_FILL + */ +void RSX_FUNC(SetFrontPolygonMode)(gcmContextData *context,const u32 mode); + +/*! \brief Control back-facing polygon rendering. +\param context Pointer to the context object. +\param enable Drawing mode. Possible values are: + - \ref GCM_POLYGON_MODE_POINT + - \ref GCM_POLYGON_MODE_LINE + - \ref GCM_POLYGON_MODE_FILL + */ +void RSX_FUNC(SetBackPolygonMode)(gcmContextData *context,const u32 mode); + +void RSX_FUNC(SetPolygonOffsetFillEnable)(gcmContextData *context,const u32 enable); +void RSX_FUNC(SetPolygonOffset)(gcmContextData *context,const f32 factor,const f32 units); + +/*! \brief Enable/Disable write to depth buffer. +\param context Pointer to the context object. +\param enable Enable flag. Possible values are: +- \ref GCM_TRUE +- \ref GCM_FALSE +*/ +void RSX_FUNC(SetDepthWriteEnable)(gcmContextData *context,u32 enable); + +/*! \brief Stop the render sequence. + +Stops the rendering for a primitive, started by \ref rsxDrawVertexBegin. +\param context Pointer to the context object. +*/ +void RSX_FUNC(DrawVertexEnd)(gcmContextData *context); + +/*! \brief Set the shading model for the render sequence. +\param context Pointer to the context object. +\param shadeModel Type of shading model. Possible values are: +- \ref GCM_SHADE_MODEL_FLAT +- \ref GCM_SHADE_MODEL_SMOOTH +*/ +void RSX_FUNC(SetShadeModel)(gcmContextData *context,u32 shadeModel); + +/*! \brief Start the render sequence. + +Starts the rendering for a primitive. +\param context Pointer to the context object. +\param type Type of primitive to render. Possible values are: +- \ref GCM_TYPE_POINTS +- \ref GCM_TYPE_LINES +- \ref GCM_TYPE_LINE_LOOP +- \ref GCM_TYPE_LINE_STRIP +- \ref GCM_TYPE_TRIANGLES +- \ref GCM_TYPE_TRIANGLE_STRIP +- \ref GCM_TYPE_TRIANGLE_FAN +- \ref GCM_TYPE_QUADS +- \ref GCM_TYPE_QUAD_STRIP +- \ref GCM_TYPE_POLYGON +*/ +void RSX_FUNC(DrawVertexBegin)(gcmContextData *context,u32 type); +void RSX_FUNC(DrawVertex1f)(gcmContextData *context,const u8 idx,const f32 v); +void RSX_FUNC(DrawVertex2f)(gcmContextData *context,const u8 idx,const f32 v[2]); +void RSX_FUNC(DrawVertex3f)(gcmContextData *context,const u8 idx,const f32 v[3]); +void RSX_FUNC(DrawVertex4f)(gcmContextData *context,const u8 idx,const f32 v[4]); +void RSX_FUNC(DrawVertex4s)(gcmContextData *context,const u8 idx,const s16 v[4]); +void RSX_FUNC(DrawVertexScaled4s)(gcmContextData *context,const u8 idx,const s16 v[4]); +void RSX_FUNC(DrawVertex2s)(gcmContextData *context,const u8 idx,const s16 v[2]); +void RSX_FUNC(DrawVertex4ub)(gcmContextData *context,const u8 idx,const u8 v[4]); +void RSX_FUNC(SetScissor)(gcmContextData *context,u16 x,u16 y,u16 w,u16 h); + +/*! \brief Specify the value used for depth buffer comparisons. +\param context Pointer to the context object. +\param func Specifies the depth comparison function. Possible values are: + - \ref GCM_NEVER + - \ref GCM_LESS + - \ref GCM_EQUAL + - \ref GCM_LEQUAL + - \ref GCM_GREATER + - \ref GCM_NOTEQUAL + - \ref GCM_GEQUAL + - \ref GCM_ALWAYS +*/ +void RSX_FUNC(SetDepthFunc)(gcmContextData *context,u32 func); + +/*! \brief Enable or disable the depth test. + +If depth test is enabled, the GPU performs depth comparisons and updates the +depth buffer. +Note that even if the depth buffer exists and the depth mask is non-zero, +the depth buffer is not updated if the depth test is disabled. +\param context Pointer to the context object. +\param enable Enable flag. Possible values are: + - \ref GCM_TRUE + - \ref GCM_FALSE +*/ +void RSX_FUNC(SetDepthTestEnable)(gcmContextData *context,u32 enable); + +/*! \brief Clear the render surface. + +This function clears the chosen selection of color components, depth and stencil +values for all pixels in the destination surface. The clear color can be chosen +using \ref rsxSetClearColor, and the clear value for the depth buffer is set +using \ref rsxSetClearDepthValue. + +\param context Pointer to the context object. +\param clear_mask A selection of components to be cleared. Must be an OR +combination of the following values: + - \ref GCM_CLEAR_Z + - \ref GCM_CLEAR_S + - \ref GCM_CLEAR_R + - \ref GCM_CLEAR_G + - \ref GCM_CLEAR_B + - \ref GCM_CLEAR_A +The value of \ref GCM_CLEAR_M can also be chosen, to clear all RGBA components, +depth and stencil buffers. +*/ +void RSX_FUNC(ClearSurface)(gcmContextData *context,u32 clear_mask); + +/*! \brief Set the clear depth value. + +This value is used by the \ref rsxClearSurface function. +\param context Pointer to the context object. +\param value Color value +*/ +void RSX_FUNC(SetClearDepthStencil)(gcmContextData *context,const u32 value); +void RSX_FUNC(SetReturnCommand)(gcmContextData *context); +void RSX_FUNC(SetCallCommand)(gcmContextData *context,u32 offset); +void RSX_FUNC(SetJumpCommand)(gcmContextData *context,u32 offset); +void RSX_FUNC(SetNopCommand)(gcmContextData *context,u32 count); +void RSX_FUNC(SetSkipNop)(gcmContextData *context,const u32 count); + +/*! \brief Set the clear color. + +The clear color value is used by the \ref rsxClearSurface function. +\param context Pointer to the context object. +\param color The clear color value. +*/ +void RSX_FUNC(SetClearColor)(gcmContextData *context,u32 color); + +/*! \brief Enable or disable write access to the framebuffer color components. +\param context Pointer to the context object. +\param mask A selection of the components to enable write access. It is an OR +combination of the following values: + - \ref GCM_COLOR_MASK_B + - \ref GCM_COLOR_MASK_G + - \ref GCM_COLOR_MASK_R + - \ref GCM_COLOR_MASK_A +*/ +void RSX_FUNC(SetColorMask)(gcmContextData *context,u32 mask); + +/*! \brief Enable or disable write access to the framebuffer color components +(Multiple Render Target output). +\param context Pointer to the context object. +\param mask A selection of the components to enable write access. It is an OR +combination of the following values: + - \ref GCM_COLOR_MASK_B + - \ref GCM_COLOR_MASK_G + - \ref GCM_COLOR_MASK_R + - \ref GCM_COLOR_MASK_A +*/ +void RSX_FUNC(SetColorMaskMRT)(gcmContextData *context,u32 mask); + +void RSX_FUNC(SetPointSpriteControl)(gcmContextData *context,const u32 enable,const u32 rmode,const u32 texcoordMask); +void RSX_FUNC(SetPointSize)(gcmContextData *context,const f32 size); +void RSX_FUNC(SetAntialiasingControl)(gcmContextData *context,const u32 enable,const u32 alphaToCoverage,const u32 alphaToOne,const u32 sampleMask); +void RSX_FUNC(SetCylindricalWrap)(gcmContextData *context,const u32 enable); +void RSX_FUNC(SetStencilFunc)(gcmContextData *context,const u32 func,const u32 ref,const u32 mask); +void RSX_FUNC(SetStencilMask)(gcmContextData *context,const u32 mask); +void RSX_FUNC(SetStencilOp)(gcmContextData *context,const u32 fail,const u32 depthFail,const u32 depthPass); +void RSX_FUNC(SetStencilTestEnable)(gcmContextData *context,const u32 enable); +void RSX_FUNC(SetBackStencilFunc)(gcmContextData *context,const u32 func,const u32 ref,const u32 mask); +void RSX_FUNC(SetBackStencilMask)(gcmContextData *context,const u32 mask); +void RSX_FUNC(SetBackStencilOp)(gcmContextData *context,const u32 fail,const u32 depthFail,const u32 depthPass); +void RSX_FUNC(SetTwoSidedStencilTestEnable)(gcmContextData *context,const u32 enable); +void RSX_FUNC(SetTwoSideLightEnable)(gcmContextData *context,const u32 enable); +void RSX_FUNC(SetRenderEnable)(gcmContextData *context,const u8 mode,const u32 index); +void RSX_FUNC(SetReport)(gcmContextData *context,const u32 type,const u32 index); +void RSX_FUNC(SetClearReport)(gcmContextData *context,const u32 type); +void RSX_FUNC(SetSCullControl)(gcmContextData *context,const u8 sFunc,const u8 sRef,const u8 sMask); +void RSX_FUNC(SetZCullEnable)(gcmContextData *context, const u32 depth, const u32 stencil); +void RSX_FUNC(SetClearZCullSurface)(gcmContextData *context, const u32 depth, const u32 stencil); +void RSX_FUNC(SetZCullLimit)(gcmContextData *context,const u16 moveforwardlimit,const u16 pushbacklimit); +void RSX_FUNC(SetZCullControl)(gcmContextData *context,const u8 zculldir,const u8 zcullformat); +void RSX_FUNC(SetZCullStatsEnable)(gcmContextData *context,const u32 enable); +void RSX_FUNC(SetPolygonSmoothEnable)(gcmContextData *context,const u32 enable); + +/*! \brief Setup the render surface. + +This function is used to setup the render target where RSX should render the frame into. +\param context Pointer to the context object. +\param surface Pointer to the surface object. +*/ +void RSX_FUNC(SetSurface)(gcmContextData *context,gcmSurface *surface); +void RSX_FUNC(SetReferenceCommand)(gcmContextData *context,u32 ref_value); + +/*! \brief Enqueues a Wait for label command. +\param context Pointer to the context object. +\param index Label index +\param valuie Label value +*/ +void RSX_FUNC(SetWaitLabel)(gcmContextData *context,u8 index,u32 value); + +/*! \brief Enqueues a Write Command label command. +\param context Pointer to the context object. +\param index Label index +\param value Label value +*/ +void RSX_FUNC(SetWriteCommandLabel)(gcmContextData *context,u8 index,u32 value); + +/*! \brief Enqueues a Write Backend label command. +\param context Pointer to the context object. +\param index Label index +\param value Label value +*/ +void RSX_FUNC(SetWriteBackendLabel)(gcmContextData *context,u8 index,u32 value); + +void RSX_FUNC(SetWriteTextureLabel)(gcmContextData *context,const u8 index,const u32 value); + +void RSX_FUNC(SetViewportClip)(gcmContextData *context,u8 sel,u16 width,u16 height); + +/*! \brief Set viewport. + +This function sets the viewport.
+The origin (0,0) of the normalized device coordinate points to the center of the screen.
+Performing viewport conversion, where the upper left corner is the origin is as follows: +\code + x = X; + y = Y; + width = WIDTH; + height = HEIGHT; + min = 0.0f; + max = 1.0f; + scale[0] = width * 0.5f; + scale[1] = height * 0.5f; + scale[2] = (max - min) * 0.5f; + offset[0] = x + width * 0.5f; + offset[1] = y + height * 0.5f; + offset[2] = (max + min) * 0.5f; +\endcode +

+Performing viewport conversion, where the lower left corner is the origin is as follows (this is equivalent to glViewport): +\code + x = X; + y = WINDOW_HEIGHT - Y - HEIGHT; + width = WIDTH; + height = HEIGHT; + min = 0.0f; + max = 1.0f; + scale[0] = width * 0.5f; + scale[1] = height * -0.5f; + scale[2] = (max - min) * 0.5f; + offset[0] = x + width * 0.5f; + offset[1] = y + height * 0.5f; + offset[2] = (max + min) * 0.5f; +\endcode +\param context Pointer to the context object. +\param x Origin of the viewport rectangle in pixels (0 - 4095). Initial value is (0,0). +\param y Origin of the viewport rectangle in pixels (0 - 4095). Initial value is (0,0). +\param width Width of the viewport (0 - 4096). Initial value is 4096. +\param height Height of the viewport (0 - 4096). Initial value is 4096. +\param min Minimum Z clip value. Initial value is 0.0. +\param max Maximum Z clip value. Initial value is 1.0. +\param scale Scale values to be used for viewport conversion. Initial values are (2048.0,2048.0,0.5,0.0). +\param offset Offset values to be used for viewport conversion. Initial values are (2048.0,2048.0,0.5,0.0). +*/ +void RSX_FUNC(SetViewport)(gcmContextData *context,u16 x,u16 y,u16 width,u16 height,f32 min,f32 max,const f32 scale[4],const f32 offset[4]); + +/*! \brief Invalidates a texture cache. +\param context Pointer to the context object. +\param type Type of texture cache to be invalidated. Possible values are: + - \ref GCM_INVALIDATE_TEXTURE + - \ref GCM_INVALIDATE_VERTEX_TEXTURE +*/ +void RSX_FUNC(InvalidateTextureCache)(gcmContextData *context,u32 type); + +void RSX_FUNC(InvalidateVertexCache)(gcmContextData *context); + +/*! \brief Loads a texture. +\param context Pointer to the context object. +\param index Texture index. +\param texture Pointer to the texture data. +*/ +void RSX_FUNC(LoadTexture)(gcmContextData *context,u8 index,const gcmTexture *texture); + +/*! \brief Set texture control parameters. +\param context Pointer to the context object. +\param index Texture index. +\param enable Enable flag. Possible values are: + - \ref GCM_TRUE + - \ref GCM_FALSE +\param minlod minimum level of detail. +\param maxlod maximum level of detail. +\param maxaniso sample level of the anisotropic filter. Possible values are: + - \ref GCM_TEXTURE_MAX_ANISO_1 + - \ref GCM_TEXTURE_MAX_ANISO_2 + - \ref GCM_TEXTURE_MAX_ANISO_4 + - \ref GCM_TEXTURE_MAX_ANISO_6 + - \ref GCM_TEXTURE_MAX_ANISO_8 + - \ref GCM_TEXTURE_MAX_ANISO_10 + - \ref GCM_TEXTURE_MAX_ANISO_12 + - \ref GCM_TEXTURE_MAX_ANISO_16 +\todo finish args documentation. +*/ +void RSX_FUNC(TextureControl)(gcmContextData *context,u8 index,u32 enable,u16 minlod,u16 maxlod,u8 maxaniso); +void RSX_FUNC(TextureFilter)(gcmContextData *context,u8 index,u8 min,u8 mag,u8 conv); +void RSX_FUNC(TextureWrapMode)(gcmContextData *context,u8 index,u8 wraps,u8 wrapt,u8 wrapr,u8 unsignedRemap,u8 zfunc,u8 gamma); +void RSX_FUNC(TextureBorderColor)(gcmContextData *context,const u8 index,const u32 color); +void RSX_FUNC(TextureOptimization)(gcmContextData *context,const u8 index,const u8 slope,const u8 iso,const u8 aniso); +void RSX_FUNC(TextureAnisoSpread)(gcmContextData *context,const u8 index,const u8 reduceSamplesEnable,const u8 hReduceSamplesEnable,const u8 vReduceSamplesEnable,const u8 spacingSelect,const u8 hSpacingSelect,const u8 vSpacingSelect); + + +void RSX_FUNC(LoadVertexTexture)(gcmContextData *context,const u8 index,const gcmTexture *texture); +void RSX_FUNC(VertexTextureControl)(gcmContextData *context,const u8 index,const u32 enable,const u16 minlod,const u16 maxlod); +void RSX_FUNC(VertexTextureFilter)(gcmContextData *context,const u8 index,const u16 bias); +void RSX_FUNC(VertexTextureWrapMode)(gcmContextData *context,const u8 index,const u8 wraps,const u8 wrapt); +void RSX_FUNC(VertexTextureBorderColor)(gcmContextData *context,const u8 index,const u32 color); + +/*! \brief Load a compiled vertex shader program. +\param context Pointer to the context object +\param program Pointer to the vertex program configuration +\param ucode Pointer to the shader micro code +*/ +void RSX_FUNC(LoadVertexProgram)(gcmContextData *context,rsxVertexProgram *program,const void *ucode); + +/*! \brief Load a compiled fragment shader program. +\param context Pointer to the context object +\param program Pointer to the fragment program configuration +\param offset Memory offset of fragment program +\param location Memory location type where the program relies. Possible values are: +- \ref GCM_LOCATION_RSX +- \ref GCM_LOCATION_CELL +*/ +void RSX_FUNC(LoadFragmentProgramLocation)(gcmContextData *context,rsxFragmentProgram *program,u32 offset,u32 location); +void RSX_FUNC(UpdateFragmentProgramLocation)(gcmContextData *context,const u32 offset,const u32 location); +void RSX_FUNC(SetZControl)(gcmContextData *context,u8 cullNearFar,u8 zClampEnable,u8 cullIgnoreW); +void RSX_FUNC(SetZPixelCountEnable)(gcmContextData *context,const u32 enable); +void RSX_FUNC(LoadVertexProgramBlock)(gcmContextData *context,rsxVertexProgram *program,const void *ucode); +void RSX_FUNC(LoadVertexProgramParameterBlock)(gcmContextData *context,u32 base_const,u32 const_cnt,const f32 *value); +void RSX_FUNC(SetVertexProgramParameter)(gcmContextData *context,rsxVertexProgram *program,s32 index,const f32 *value); +void RSX_FUNC(SetFragmentProgramParameter)(gcmContextData *context,rsxFragmentProgram *program,s32 index,const f32 *value,u32 offset,u32 location); +void RSX_FUNC(DrawVertexArray)(gcmContextData *context,u32 type,u32 start,u32 count); +void RSX_FUNC(BindVertexArrayAttrib)(gcmContextData *context,u8 attr,u32 offset,u8 stride,u8 elems,u8 dtype,u8 location); +void RSX_FUNC(DrawIndexArray)(gcmContextData *context,u8 type,u32 offset,u32 count,u8 data_type,u8 location); +void RSX_FUNC(DrawInlineIndexArray16)(gcmContextData *context,u8 type,u32 start,u32 count,u16 *data); +void RSX_FUNC(DrawInlineIndexArray32)(gcmContextData *context,u8 type,u32 start,u32 count,u32 *data); +void RSX_FUNC(InlineTransfer)(gcmContextData *context,const u32 dstOffset,const void *srcAddress,const u32 sizeInWords,const u8 location); +void RSX_FUNC(SetUserClipPlaneControl)(gcmContextData *context,u32 plane0,u32 plane1,u32 plane2,u32 plane3,u32 plane4,u32 plane5); +void RSX_FUNC(SetAlphaFunc)(gcmContextData *context,const u32 alphaFunc,const u32 ref); +void RSX_FUNC(SetAlphaTestEnable)(gcmContextData *context,const u32 enable); +void RSX_FUNC(SetBlendEnableMrt)(gcmContextData *context, const u32 mrt1, const u32 mrt2, const u32 mrt3); +void RSX_FUNC(SetBlendOptimization)(gcmContextData *context,const u32 enable); +void RSX_FUNC(SetLogicOp)(gcmContextData *context,const u32 op); +void RSX_FUNC(SetLogicOpEnable)(gcmContextData *context,const u32 enable); +void RSX_FUNC(SetFogMode)(gcmContextData *context,const u32 mode); +void RSX_FUNC(SetFogParams)(gcmContextData *context,const f32 p0,const f32 p1); +void RSX_FUNC(SetVertexAttribOutputMask)(gcmContextData *context,const u32 mask); + +/*! \brief Specify pixel arithmetic. + +In RGBA mode, pixels can be drawn using a function that blends the incoming +(source) RGBA values with the RGBA values that are already in the frame buffer +(the destination values). Blending is initially disabled. +Use \ref rsxSetBlendEnable to enable and disable blending. + +\c rsxSetBlendFunc defines the operation of blending when it is enabled. +\p sfcolor and and \p sfalpha specify which method is used to scale the source +color and alpha components. +\p dfcolor and and \p dfalpha specify which method is used to scale the +destination color and alpha components. +The possible methods are described in the following table. +Each method defines four scale factors, one each for red, green, blue, and +alpha. +In the table and in subsequent equations, source and destination color +components are referred to as Rs, Gs, Bs, +As and Rd, Gd, Bd, Ad, +respectively. +The color specified by \ref rsxSetBlendColor is referred to as +Rc, Gc, Bc, Ac. +They are understood to have integer values between 0 and +kR, kG, kB, kA, where + +kc = 2mc - 1 + +and mR, mG, mB, mA is the number of +red, green, blue, and alpha bitplanes. + +Source and destination scale factors are referred to as +sR, sG, sB, sA and dR, +dG, dB, dA. +The scale factors described in the table, denoted fR, fG, +fB, fA, represent either source or destination +factors. All scale factors have range [0,1]. + + + + + + + + + + + + + + + + + + +
Parameter fR fG fB fA
\ref GCM_ZERO 0 0 0 0
\ref GCM_ONE 1 1 1 1
\ref GCM_SRC_COLOR Rs/kR Gs/kG Bs/kB As/kA
\ref GCM_ONE_MINUS_SRC_COLOR 1-Rs/kR1-Gs/kG1-Bs/kB1-As/kA
\ref GCM_DST_COLOR Rd/kR Gd/kG Bd/kB Ad/kA
\ref GCM_ONE_MINUS_DST_COLOR 1-Rd/kR1-Gd/kG1-Bd/kB1-Ad/kA
\ref GCM_SRC_ALPHA As/kA As/kA As/kA As/kA
\ref GCM_ONE_MINUS_SRC_ALPHA 1-As/kA1-As/kA1-As/kA1-As/kA
\ref GCM_DST_ALPHA Ad/kA Ad/kA Ad/kA Ad/kA
\ref GCM_ONE_MINUS_DST_ALPHA 1-Ad/kA1-Ad/kA1-Ad/kA1-Ad/kA
\ref GCM_CONSTANT_COLOR Rc Gc Bc Ac
\ref GCM_ONE_MINUS_CONSTANT_COLOR1-Rc 1-Gc 1-Bc 1-Ac
\ref GCM_CONSTANT_ALPHA Ac Ac Ac Ac
\ref GCM_ONE_MINUS_CONSTANT_ALPHA1-Ac 1-Ac 1-Ac 1-Ac
\ref GCM_SRC_ALPHA_SATURATE i i i 1
+In the table, + +i = min(As/kA, 1 - Ad/kA) + +To determine the blended RGBA values of a pixel when drawing in RGBA mode, +the equation defined by \ref rsxSetBlendEquation us used. In the default mode +(\ref GCM_FUNC_ADD for RGB and alpha equations), the equations are the +following: + + - Rd = min(kR, RssR + RddR) + - Gd = min(kG, GssG + GddG) + - Bd = min(kB, BssB + BddB) + - Ad = min(kA, AssA + AddA) + +Despite the apparent precision of the above equations, blending arithmetic is +not exactly specified, because blending operates with imprecise integer color +values. +However, a blend factor that should be equal to 1 is guaranteed not to modify +its multiplicand, and a blend factor equal to 0 reduces its multiplicand to 0. +For example, when \p sfcolor is \ref GCM_SRC_ALPHA, \p fdcolor is +\ref GCM_ONE_MINUS_SRC_ALPHA, and As is equal to kA, +the equations reduce to simple replacement: + +Rd = Rs ; +Gd = Gs ; +Bd = Bs + +\par Examples + +Transparency is best implemented using blend function +(\p sfcolor = \p sfalpha = \ref GCM_SRC_ALPHA, +\p dfcolor = \p dfalpha = \ref GCM_ONE_MINUS_SRC_ALPHA) with primitives sorted from +farthest to nearest. Note that this transparency calculation does not require +the presence of alpha bitplanes in the frame buffer. + +Blend function (\ref GCM_SRC_ALPHA, \ref GCM_ONE_MINUS_SRC_ALPHA) is also useful +for rendering antialiased points and lines in arbitrary order. + +Polygon antialiasing is optimized using blend function +(\ref GCM_SRC_ALPHA_SATURATE, \ref GCM_ONE) with polygons sorted from nearest +to farthest. +Destination alpha bitplanes, which must be present for this blend function to +operate correctly, store the accumulated coverage. + +\par Notes + +Incoming (source) alpha is correctly thought of as a material opacity, ranging +from 1.0 ( kA ), representing complete opacity, to 0.0 (0), +representing complete transparency. + + +\param context Pointer to the context object +\param sfcolor Specifies how the red, green, and blue source blending factors are computed. +\param dfcolor Specifies how the red, green, and blue source blending factors are computed. +\param sfalpha Specifies how the alpha source blending factor is computed. +\param dfalpha Specifies how the alpha destination blending factor is computed. +*/ +void RSX_FUNC(SetBlendFunc)(gcmContextData *context,u16 sfcolor,u16 dfcolor,u16 sfalpha,u16 dfalpha); + +/*! \brief Set the blend equation. + +The blend equations determine how a new pixel (the “source” color) +is combined with a pixel already in the framebuffer (the +“destination” color). +This function specifies one blend equation for the RGB-color components +and one blend equation for the alpha component. + +These equations use the source and destination blend factors specified by +\ref rsxSetBlendFunc. See \ref rsxSetBlendFunc for a description of the various +blend factors. + +In the equations that follow, source and destination color components are +referred to as Rs, Gs, Bs, As +and Rd, Gd, Bd, Ad, respectively. +The result color is referred to as Rr, Gr, Br, +Ar. +The source and destination blend factors are denoted +sR, sG, sB, sA and dR, +dG, dB, dA, respectively. +For these equations all color components are understood to have values in the +range [0,1]. + + + + + + + + +
Mode Rr Gr Br Ar
\ref GCM_FUNC_ADD RssR+RddRGssG+GddGBssB+BddBAssA+AddA
\ref GCM_MIN min(Rs,Rd) min(Gs,Gd) min(Bs,Bd) min(As,Ad)
\ref GCM_MAX max(Rs,Rd) max(Gs,Gd) max(Bs,Bd) max(As,Ad)
\ref GCM_FUNC_SUBTRACT RssR-RddRGssG-GddGBssB-BddBAssA-AddA
\ref GCM_FUNC_REVERSE_SUBTRACTRddR-RssRGddG-GssGBddB-BssBAddA-AssA
+ +The results of these equations are clamped to the range [0,1]. + +The \ref GCM_MIN and \ref GCM_MAX equations are useful for applications that +analyze image data (image thresholding against a constant color, for example). +The \ref GCM_FUNC_ADD equation is useful for antialiasing and transparency, +among other things. + +Initially, both the RGB blend equation and the alpha blend equation are set +to \ref GCM_FUNC_ADD. + +\par Notes + +The \ref GCM_MIN, and \ref GCM_MAX equations do not use the source or +destination factors, only the source and destination colors. + +\param context Pointer to the context object +\param color Specifies the RGB blend equation, how the red, green, and blue +components of the source and destination colors are combined. +\param alpha Specifies the alpha blend equation, how the alpha component of +the source and destination colors are combined. +*/ +void RSX_FUNC(SetBlendEquation)(gcmContextData *context,u16 color,u16 alpha); + + +/*! \brief Set the blending constant color. +\param context Pointer to the context object +\param color0 all A, R, G, B components in 8-bit component mode +\param color1 reserved for 16-bit components +*/ +void RSX_FUNC(SetBlendColor)(gcmContextData *context,u32 color0,u32 color1); + +/*! \brief Enable or disable blending. +\param context Pointer to the context object +\param enable + - \c GCM_TRUE : enable blending + - \c GCM_FALSE : disable blending +*/ +void RSX_FUNC(SetBlendEnable)(gcmContextData *context,u32 enable); + +void RSX_FUNC(SetTransformBranchBits)(gcmContextData *context,u32 branchBits); + +/*! \brief Configuration the mode for an upcoming asynchronous RSX DMA transfer. +\param context Pointer to the context object +\param mode Specify source and destination memory areas. Possible values are: +- \ref GCM_TRANSFER_LOCAL_TO_LOCAL +- \ref GCM_TRANSFER_MAIN_TO_LOCAL +- \ref GCM_TRANSFER_LOCAL_TO_MAIN +- \ref GCM_TRANSFER_MAIN_TO_MAIN +*/ +void RSX_FUNC(SetTransferDataMode)(gcmContextData *context,u8 mode); + +/*! \brief Specify the memory locations for an RSX DMA transfer. This function should be called after rsxSetTransferDataMode() and rsxSetTransferDataFormat(). +\param context Pointer to the context object +\param dst Destination memory offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). +\param src Source memory offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). +*/ +void RSX_FUNC(SetTransferDataOffset)(gcmContextData *context,u32 dst,u32 src); + +/*! \brief Format an upcoming asynchronous RSX DMA transfer. +\param context Pointer to the context object +\param inpitch Pitch size, in bytes, of the source buffer (e.g., for a buffer that represents a rectangular image, this would be the width multiplied by the number of bytes in each pixel). +\param outpitch Pitch size, in bytes, of the destination buffer (e.g., for a buffer that represents a rectangular image, this would be the width multiplied by the number of bytes in each pixel). +\param linelength Size, in bytes, of each line of data that will be transfered. +\param linecount Number of lines of data to transfer. +\param inbytes Number of bytes for each block (e.g., pixel) of data to be transfered: 1, 2, or 4. Will perform scatter-gather transfer if different from outbytes. +\param outbytes Number of bytes for each block (e.g., pixel) of data to be transfered: 1, 2, or 4. Will perform scatter-gather transfer if different from inbytes. +*/ +void RSX_FUNC(SetTransferDataFormat)(gcmContextData *context,s32 inpitch,s32 outpitch,u32 linelength,u32 linecount,u8 inbytes,u8 outbytes); + +/*! \brief Initiate an asynchronous RSX DMA transfer. +\param context Pointer to the context object +\param mode Specify source and destination memory areas. Possible values are: +- \ref GCM_TRANSFER_LOCAL_TO_LOCAL +- \ref GCM_TRANSFER_MAIN_TO_LOCAL +- \ref GCM_TRANSFER_LOCAL_TO_MAIN +- \ref GCM_TRANSFER_MAIN_TO_MAIN +\param dst Destination memory offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). +\param outpitch Pitch size, in bytes, of the destination buffer (e.g., for a buffer that represents a rectangular image, this would be the width multiplied by the number of bytes in each pixel). +\param src Source memory offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). +\param inpitch Pitch size, in bytes, of the source buffer (e.g., for a buffer that represents a rectangular image, this would be the width multiplied by the number of bytes in each pixel). +\param linelength Size, in bytes, of each line of data that will be transfered. +\param linecount Number of lines of data to transfer. +*/ +void RSX_FUNC(SetTransferData)(gcmContextData *context,u8 mode,u32 dst,u32 outpitch,u32 src,u32 inpitch,u32 linelength,u32 linecount); + +/*! \brief Configure an upcoming asynchronous RSX blit. +\param context Pointer to the context object +\param mode Specify source and destination memory areas. Possible values are: +- \ref GCM_TRANSFER_LOCAL_TO_LOCAL +- \ref GCM_TRANSFER_MAIN_TO_LOCAL +- \ref GCM_TRANSFER_LOCAL_TO_MAIN +- \ref GCM_TRANSFER_MAIN_TO_MAIN +\param surface Transfer surface mode. Possible values are: +- \ref GCM_TRANSFER_SURFACE +- \ref GCM_TRANSFER_SWIZZLE +*/ +void RSX_FUNC(SetTransferScaleMode)(gcmContextData *context,const u8 mode,const u8 surface); + +/*! \brief Initiate an asynchronous RSX blit. +\param context Pointer to the context object +\param scale Specify the transfer geometry & parameters. +\param surface Specify the surface to blit to. +*/ +void RSX_FUNC(SetTransferScaleSurface)(gcmContextData *context,const gcmTransferScale *scale,const gcmTransferSurface *surface); + +/*! \brief Initialiate an asynchronous transfer of a rectangular image from one area of memory to another. +\param context Pointer to the context object +\param mode Specify source and destination memory areas. Possible values are: +- \ref GCM_TRANSFER_LOCAL_TO_LOCAL +- \ref GCM_TRANSFER_MAIN_TO_LOCAL +- \ref GCM_TRANSFER_LOCAL_TO_MAIN +- \ref GCM_TRANSFER_MAIN_TO_MAIN +\param dstOffset Destination memory offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). +\param dstPitch Pitch size, in bytes, of the destination image data (width multiplied by the number of bytes in each pixel). +\param dstX Origin of the destination data, relative to the beginning of the destination buffer. +\param dstY Origin of the destination data, relative to the beginning of the destination buffer. +\param srcOffset Source memory offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). +\param srcPitch Pitch size, in bytes, of the source image data (width multiplied by the number of bytes in each pixel). +\param srcX Origin of the source rectangle, relative to the beginning of the source buffer. +\param srcY Origin of the source rectangle, relative to the beginning of the source buffer. +\param width Width of the transfer rectangle. +\param height Height of the transfer rectangle. +\param bytesPerPixel Number of bytes per pixel to transfer: 2 or 4. +*/ +void RSX_FUNC(SetTransferImage)(gcmContextData *context,const u8 mode,const u32 dstOffset,const u32 dstPitch,const u32 dstX,const u32 dstY,const u32 srcOffset,const u32 srcPitch,const u32 srcX,const u32 srcY,const u32 width,const u32 height,const u32 bytesPerPixel); +void RSX_FUNC(SetTimeStamp)(gcmContextData *context,u32 index); + +void RSX_FUNC(SetConvertSwizzleFormat)(gcmContextData *context,const u32 dstOffset,const u32 dstWidth,const u32 dstHeight,const u32 dstX,const u32 dstY,const u32 srcOffset,const u32 srcPitch,const u32 srcX,const u32 srcY,const u32 width,const u32 height,const u32 bytesPerPixel,const u32 mode); + +void RSX_FUNC(SetTransferScaleSwizzle)(gcmContextData *context,const gcmTransferScale *scale,const gcmTransferSwizzle *swizzle); + +void RSX_FUNC(SetWaitForIdle)(gcmContextData *context); + +/*! \brief Flushes the RSX command buffer. + +This ensures all remaining commands in the command buffer are executed, and +that the buffer is empty when that function returns. +\param context Pointer to the context object. +*/ +void RSX_FUNC(FlushBuffer)(gcmContextData *context); + +/*! \brief Reset the RSX command buffer. +\param context Pointer to the context object. +*/ +void RSX_FUNC(ResetCommandBuffer)(gcmContextData *context); +void RSX_FUNC(Finish)(gcmContextData *context,u32 ref_value); diff --git a/ppu/include/rsx/gcm_sys.h b/ppu/include/rsx/gcm_sys.h index 04077907..7b426733 100644 --- a/ppu/include/rsx/gcm_sys.h +++ b/ppu/include/rsx/gcm_sys.h @@ -8,393 +8,691 @@ #include /*! \brief true boolean value */ -#define GCM_TRUE 1 +#define GCM_TRUE 1 /*! \brief false boolean value */ -#define GCM_FALSE 0 +#define GCM_FALSE 0 + +/*! \brief Traditional FIFO mode. Checks the get pointer in the traditional + way for any opening in the next segment. May sleep-wait if the get + pointer is pointing to the next segment. +*/ +#define GCM_DEFAULT_FIFO_MODE_TRADITIONAL 0 + +/* \brief Optimized FIFO mode. Uses the \ref rsxSetWriteTextureLable() to manage the + command buffer. It handles each segment buffer as an index and monitors + states of the segment buffers by reading the label value executed + by \ref rsxSetWriteTextureLabel(). +*/ +#define GCM_DEFAULT_FIFO_MODE_OPTIMIZE 1 + +/* \brief Conditional FIFO mode. Uses the \ref rsxSetWriteCommandLabel() to manage the + the command buffer. The management of the command buffer is carried out in + the same manner as \ref GCM_DEFAULT_FIFO_MODE_OPTIMIZE. +*/ +#define GCM_DEFAULT_FIFO_MODE_CONDITIONAL 2 /*! \brief flip on horizontal sync, accurate mode */ -#define GCM_FLIP_HSYNC 1 +#define GCM_FLIP_HSYNC 1 /*! \brief flip on vertical sync */ -#define GCM_FLIP_VSYNC 2 +#define GCM_FLIP_VSYNC 2 /*! \brief flip on horizontal sync, inaccurate mode */ -#define GCM_FLIP_HSYNC_AND_BREAK_EVERYTHING 3 +#define GCM_FLIP_HSYNC_AND_BREAK_EVERYTHING 3 /*! \brief maximum count of multiple render targets */ -#define GCM_MAX_MRT_COUNT 4 +#define GCM_MAX_MRT_COUNT 4 -#define GCM_DMA_MEMORY_FRAME_BUFFER (0xFEED0000) -#define GCM_DMA_MEMORY_HOST_BUFFER (0xFEED0001) +#define GCM_DMA_MEMORY_FRAME_BUFFER (0xFEED0000) +#define GCM_DMA_MEMORY_HOST_BUFFER (0xFEED0001) -#define GCM_CONTEXT_SURFACE2D (0x313371C3) -#define GCM_CONTEXT_SWIZZLE2D (0x31337A73) +#define GCM_CONTEXT_SURFACE2D (0x313371C3) +#define GCM_CONTEXT_SWIZZLE2D (0x31337A73) -#define GCM_TRANSFER_LOCAL_TO_LOCAL 0 -#define GCM_TRANSFER_MAIN_TO_LOCAL 1 -#define GCM_TRANSFER_LOCAL_TO_MAIN 2 -#define GCM_TRANSFER_MAIN_TO_MAIN 3 +#define GCM_TRANSFER_LOCAL_TO_LOCAL 0 +#define GCM_TRANSFER_MAIN_TO_LOCAL 1 +#define GCM_TRANSFER_LOCAL_TO_MAIN 2 +#define GCM_TRANSFER_MAIN_TO_MAIN 3 -#define GCM_TF_COLOR_R5G5B5 3 -#define GCM_TF_COLOR_X8R8G8B8 5 -#define GCM_TF_COLOR_A8R8G8B8 8 +/*! \brief Memory buffer is located in RSX memory. */ +#define GCM_LOCATION_RSX 0 +/*! \brief Memory buffer is located in main memory. */ +#define GCM_LOCATION_CELL 1 +/*! \brief Memory buffer is located in report memory. */ +#define GCM_LOCATION_REPORT 2 + +#define GCM_SURFACE_X1R5G5B5_Z1R5G5B5 1 +#define GCM_SURFACE_X1R5G5B5_O1R5G5B5 2 +#define GCM_SURFACE_R5G5B5 3 +#define GCM_SURFACE_X8R8G8B8_Z8R8G8B8 4 +#define GCM_SURFACE_X8R8G8B8 5 +#define GCM_SURFACE_A8R8G8B8 8 +#define GCM_SURFACE_B8 9 +#define GCM_SURFACE_G8B8 10 +#define GCM_SURFACE_F_W16Z16Y16X16 11 +#define GCM_SURFACE_F_W32Z32Y32X32 12 +#define GCM_SURFACE_F_X32 13 +#define GCM_SURFACE_X8B8G8R8_Z8B8G8R8 14 +#define GCM_SURFACE_X8B8G8R8_O8B8G8R8 15 +#define GCM_SURFACE_A8B8G8R8 16 /*! \brief 16-bit depth buffer */ -#define GCM_TF_ZETA_Z16 1 +#define GCM_SURFACE_ZETA_Z16 1 /*! \brief 24-bit depth buffer and 8-bit stencil buffer. */ -#define GCM_TF_ZETA_Z24S8 2 +#define GCM_SURFACE_ZETA_Z24S8 2 /*! \brief Render target is linear */ -#define GCM_TF_TYPE_LINEAR 1 +#define GCM_SURFACE_TYPE_LINEAR 1 /*! \brief Render target is swizzled */ -#define GCM_TF_TYPE_SWIZZLE 2 - -/*! \brief Memory buffer is located in RSX memory. */ -#define GCM_LOCATION_RSX 0 -/*! \brief Memory buffer is located in main memory. */ -#define GCM_LOCATION_CELL 1 +#define GCM_SURFACE_TYPE_SWIZZLE 2 /*! \brief Do not use render target */ -#define GCM_TF_TARGET_NONE 0 +#define GCM_SURFACE_TARGET_NONE 0 /*! \brief Render target 0 */ -#define GCM_TF_TARGET_0 1 +#define GCM_SURFACE_TARGET_0 1 /*! \brief Render target 1 */ -#define GCM_TF_TARGET_1 2 +#define GCM_SURFACE_TARGET_1 2 /*! \brief Render target 0 and 1 */ -#define GCM_TF_TARGET_MRT1 0x13 +#define GCM_SURFACE_TARGET_MRT1 0x13 /*! \brief Render target 0,1 and 2 */ -#define GCM_TF_TARGET_MRT2 0x17 +#define GCM_SURFACE_TARGET_MRT2 0x17 /*! \brief Render target 0,1,2 and 3 */ -#define GCM_TF_TARGET_MRT3 0x1f +#define GCM_SURFACE_TARGET_MRT3 0x1f /*! \brief Do not use multiple samples. */ -#define GCM_TF_CENTER_1 0 +#define GCM_SURFACE_CENTER_1 0 +#define GCM_SURFACE_DIAGONAL_CENTERED_2 3 +#define GCM_SURFACE_SQUARE_CENTERED_4 4 +#define GCM_SURFACE_SQUARE_ROTATED_4 5 /*! \brief blue color component */ -#define GCM_COLOR_MASK_B 0x00000001 +#define GCM_COLOR_MASK_B 0x00000001 /*! \brief green color component */ -#define GCM_COLOR_MASK_G 0x00000100 +#define GCM_COLOR_MASK_G 0x00000100 /*! \brief red color component */ -#define GCM_COLOR_MASK_R 0x00010000 +#define GCM_COLOR_MASK_R 0x00010000 /*! \brief alpha component */ -#define GCM_COLOR_MASK_A 0x01000000 +#define GCM_COLOR_MASK_A 0x01000000 + +#define GCM_COLOR_MASK_MRT1_A 0x00000010 +#define GCM_COLOR_MASK_MRT1_R 0x00000020 +#define GCM_COLOR_MASK_MRT1_G 0x00000040 +#define GCM_COLOR_MASK_MRT1_B 0x00000080 +#define GCM_COLOR_MASK_MRT2_A 0x00000100 +#define GCM_COLOR_MASK_MRT2_R 0x00000200 +#define GCM_COLOR_MASK_MRT2_G 0x00000400 +#define GCM_COLOR_MASK_MRT2_B 0x00000800 +#define GCM_COLOR_MASK_MRT3_A 0x00001000 +#define GCM_COLOR_MASK_MRT3_R 0x00002000 +#define GCM_COLOR_MASK_MRT3_G 0x00004000 +#define GCM_COLOR_MASK_MRT3_B 0x00008000 /*! \brief clear the Z buffer (depth buffer) */ -#define GCM_CLEAR_Z 0x01 +#define GCM_CLEAR_Z 0x01 /*! \brief clear the stencil buffer */ -#define GCM_CLEAR_S 0x02 +#define GCM_CLEAR_S 0x02 /*! \brief clear the red components */ -#define GCM_CLEAR_R 0x10 +#define GCM_CLEAR_R 0x10 /*! \brief clear the green components */ -#define GCM_CLEAR_G 0x20 +#define GCM_CLEAR_G 0x20 /*! \brief clear the blue components */ -#define GCM_CLEAR_B 0x40 +#define GCM_CLEAR_B 0x40 /*! \brief clear the alpha components */ -#define GCM_CLEAR_A 0x80 +#define GCM_CLEAR_A 0x80 /*! \brief clear all RGBA components, Z buffer and stencil buffer */ -#define GCM_CLEAR_M 0xf3 +#define GCM_CLEAR_M 0xf3 /*! \brief depth test never passes. */ -#define GCM_NEVER 0x0200 +#define GCM_NEVER 0x0200 /*! \brief depth test passes if the incoming depth value is less than the stored depth value. */ -#define GCM_LESS 0x0201 +#define GCM_LESS 0x0201 /*! \brief depth test passes if the incoming depth value is equal to the stored depth value. */ -#define GCM_EQUAL 0x0202 +#define GCM_EQUAL 0x0202 /*! \brief depth test passes if the incoming depth value is less than or equal to the stored depth value. */ -#define GCM_LEQUAL 0x0203 +#define GCM_LEQUAL 0x0203 /*! \brief depth test passes if the incoming depth value is greater than the stored depth value. */ -#define GCM_GREATER 0x0204 +#define GCM_GREATER 0x0204 /*! \brief depth test passes if the incoming depth value is not equal to the stored depth value. */ -#define GCM_NOTEQUAL 0x0205 +#define GCM_NOTEQUAL 0x0205 /*! \brief depth test passes if the incoming depth value is greater than or equal to the stored depth value. */ -#define GCM_GEQUAL 0x0206 +#define GCM_GEQUAL 0x0206 /*! \brief depth test always passes. */ -#define GCM_ALWAYS 0x0207 +#define GCM_ALWAYS 0x0207 /*! \brief culling of front face */ -#define GCM_CULL_FRONT 0x0404 +#define GCM_CULL_FRONT 0x0404 /*! \brief culling of back face */ -#define GCM_CULL_BACK 0x0405 +#define GCM_CULL_BACK 0x0405 /*! \brief culling of front and back faces */ -#define GCM_CULL_ALL 0x0408 +#define GCM_CULL_ALL 0x0408 + +/*! \brief render the polygon by points */ +#define GCM_POLYGON_MODE_POINT 0x1b00 +/*! \brief render the polygon by lines */ +#define GCM_POLYGON_MODE_LINE 0x1b01 +/*! \brief render the polygon by filling */ +#define GCM_POLYGON_MODE_FILL 0x1b02 /*! \brief front face is to be drawn clock wise */ -#define GCM_FRONTFACE_CW 0x0900 +#define GCM_FRONTFACE_CW 0x0900 /*! \brief front face is to be drawn counter clock wise */ -#define GCM_FRONTFACE_CCW 0x0901 +#define GCM_FRONTFACE_CCW 0x0901 + +#define GCM_CLEAR 0x1500 +#define GCM_AND 0x1501 +#define GCM_AND_REVERSE 0x1502 +#define GCM_COPY 0x1503 +#define GCM_AND_INVERTED 0x1504 +#define GCM_NOOP 0x1505 +#define GCM_XOR 0x1506 +#define GCM_OR 0x1507 +#define GCM_NOR 0x1508 +#define GCM_EQUIV 0x1509 +#define GCM_INVERT 0x150A +#define GCM_OR_REVERSE 0x150B +#define GCM_COPY_INVERTED 0x150C +#define GCM_OR_INVERTED 0x150D +#define GCM_NAND 0x150E +#define GCM_SET 0x150F + +/*! \brief keep current stencil buffer value */ +#define GCM_KEEP 0x1E00 +/*! \brief set stencil buffer value to 0 */ +#define GCM_REPLACE 0x1E01 +/*! \brief increment current stencil buffer value. clamp to 255 */ +#define GCM_INCR 0x1E02 +/*! \brief decrement current stencil buffer value. clamp to 0 */ +#define GCM_DECR 0x1E03 +/*! \brief increment current stencil buffer value. when incrementing a stencil value of 255, wrap around to 0 */ +#define GCM_INCR_WRAP 0x8507 +/*! \brief decrement current stencil buffer value. when decrementing a stencil value of 0, wrap around to 255 */ +#define GCM_DECR_WRAP 0x8508 /*! \brief render POINTS primitive */ -#define GCM_TYPE_POINTS 1 +#define GCM_TYPE_POINTS 1 /*! \brief render LINES primitive */ -#define GCM_TYPE_LINES 2 +#define GCM_TYPE_LINES 2 /*! \brief render LINE_LOOP primitive */ -#define GCM_TYPE_LINE_LOOP 3 +#define GCM_TYPE_LINE_LOOP 3 /*! \brief render LINE_STRIP primitive */ -#define GCM_TYPE_LINE_STRIP 4 +#define GCM_TYPE_LINE_STRIP 4 /*! \brief render TRIANGLES primitive */ -#define GCM_TYPE_TRIANGLES 5 +#define GCM_TYPE_TRIANGLES 5 /*! \brief render TRIANGLE_STRIP primitive */ -#define GCM_TYPE_TRIANGLE_STRIP 6 +#define GCM_TYPE_TRIANGLE_STRIP 6 /*! \brief render TRIANGLE_FAN primitive */ -#define GCM_TYPE_TRIANGLE_FAN 7 +#define GCM_TYPE_TRIANGLE_FAN 7 /*! \brief render QUADS primitive */ -#define GCM_TYPE_QUADS 8 +#define GCM_TYPE_QUADS 8 /*! \brief render QUAD_STRIP primitive */ -#define GCM_TYPE_QUAD_STRIP 9 +#define GCM_TYPE_QUAD_STRIP 9 /*! \brief render POLYGON primitive */ -#define GCM_TYPE_POLYGON 10 +#define GCM_TYPE_POLYGON 10 /*! \brief invalidate texture cache for fragment programs */ -#define GCM_INVALIDATE_TEXTURE 1 +#define GCM_INVALIDATE_TEXTURE 1 /*! \brief invalidate texture cache for vertex programs */ -#define GCM_INVALIDATE_VERTEX_TEXTURE 2 +#define GCM_INVALIDATE_VERTEX_TEXTURE 2 /*! \brief texture is 1D. */ -#define GCM_TEXTURE_DIMS_1D 1 +#define GCM_TEXTURE_DIMS_1D 1 /*! \brief texture is 2D. */ -#define GCM_TEXTURE_DIMS_2D 2 +#define GCM_TEXTURE_DIMS_2D 2 /*! \brief texture is 3D. */ -#define GCM_TEXTURE_DIMS_3D 3 - -#define GCM_TEXTURE_FORMAT_SWZ 0x00 -#define GCM_TEXTURE_FORMAT_LIN 0x20 -#define GCM_TEXTURE_FORMAT_NRM 0x40 - -#define GCM_TEXTURE_FORMAT_L8 1 -#define GCM_TEXTURE_FORMAT_A1R5G5B5 2 -#define GCM_TEXTURE_FORMAT_A4R4G4B4 3 -#define GCM_TEXTURE_FORMAT_R5G6B5 4 -#define GCM_TEXTURE_FORMAT_A8R8G8B8 5 -#define GCM_TEXTURE_FORMAT_DXT1 6 -#define GCM_TEXTURE_FORMAT_DXT3 7 -#define GCM_TEXTURE_FORMAT_DXT5 8 -#define GCM_TEXTURE_FORMAT_A8L8 24 +#define GCM_TEXTURE_DIMS_3D 3 + +/*! \brief texture uses swizzle format */ +#define GCM_TEXTURE_FORMAT_SWZ 0x00 +/*! \brief texture uses linear format */ +#define GCM_TEXTURE_FORMAT_LIN 0x20 +/*! \brief texture uses normalized texture coordinates */ +#define GCM_TEXTURE_FORMAT_NRM 0x00 +/*! \brief texture uses unnormalized texture coordinates */ +#define GCM_TEXTURE_FORMAT_UNRM 0x40 + +/*! \brief texture color format is 8-bit unsigned integer */ +#define GCM_TEXTURE_FORMAT_B8 1 +/*! \brief texture color format is 1-bit alpha and three 5-bit unsigned integers */ +#define GCM_TEXTURE_FORMAT_A1R5G5B5 2 +/*! \brief texture color format is four 4-bit unsigned values */ +#define GCM_TEXTURE_FORMAT_A4R4G4B4 3 +/*! \brief texture color format is 5-bit, 6-bit and 5-bit unsigned integers */ +#define GCM_TEXTURE_FORMAT_R5G6B5 4 +/*! \brief texture color format is four 8-bit unsigned integers */ +#define GCM_TEXTURE_FORMAT_A8R8G8B8 5 +/*! \brief texture color format is 4x4 pixels compressed to 8 bytes */ +#define GCM_TEXTURE_FORMAT_DXT1 6 +/*! \brief texture color format is 4x4 pixels compressed to 16 bytes */ +#define GCM_TEXTURE_FORMAT_DXT23 7 +/*! \brief texture color format is 4x4 pixels compressed to 16 bytes */ +#define GCM_TEXTURE_FORMAT_DXT45 8 +/*! \brief texture color format is two 8-bit unsigned integers */ +#define GCM_TEXTURE_FORMAT_G8B8 11 +/*! \brief texture color format is 6-bit, 5-bit and 5-bit unsigned integers */ +#define GCM_TEXTURE_FORMAT_R6G5B5 15 +/*! \brief texture color format is 24-bit fixed and 8-bit dummy data */ +#define GCM_TEXTURE_FORMAT_DEPTH24_D8 16 +/*! \brief texture color format is 24-bit float and 8-bit dummy data */ +#define GCM_TEXTURE_FORMAT_DEPTH24_D8_FLOAT 17 +/*! \brief texture color format is 16-bit fixed */ +#define GCM_TEXTURE_FORMAT_DEPTH16 18 +/*! \brief texture color format is 16-bit float */ +#define GCM_TEXTURE_FORMAT_DEPTH16_FLOAT 19 +/*! \brief texture color format is 16-bit integer */ +#define GCM_TEXTURE_FORMAT_X16 20 +/*! \brief texture color format is two 16-bit integers */ +#define GCM_TEXTURE_FORMAT_Y16_X16 21 +/*! \brief texture color format is three 5-bit unsigned integers and one 1-bit value */ +#define GCM_TEXTURE_FORMAT_R5G6B5A1 23 +/*! \brief texture color format is two 16-bit unsigned values compressed to two 8-bit values */ +#define GCM_TEXTURE_FORMAT_COMPRESSED_HILO8 24 +/*! \brief texture color format is two 16-bit signed values compressed to two 8-bit values */ +#define GCM_TEXTURE_FORMAT_COMPRESSED_HILO8_S8 25 +/*! \brief texture color format is four 16-bit float values */ +#define GCM_TEXTURE_FORMAT_W16_Z16_Y16_X16_FLOAT 26 +/*! \brief texture color format is four 32-bit float values */ +#define GCM_TEXTURE_FORMAT_W32_Z32_Y32_X32_FLOAT 27 +/*! \brief texture color format is one 32-bit float value */ +#define GCM_TEXTURE_FORMAT_X32_FLOAT 28 +/*! \brief texture color format is 1-bit dummy data and 5-bit unsigned integers */ +#define GCM_TEXTURE_FORMAT_D1R5G5B5 29 +/*! \brief texture color format is 8-bit dummy data and 8-bit unsigned integers */ +#define GCM_TEXTURE_FORMAT_D8R8G8B8 30 +/*! \brief texture color format is two 16-bit float values */ +#define GCM_TEXTURE_FORMAT_Y16_X16_FLOAT 31 +/*! \brief texture color format is two pixels compressed to 32 bits in YUV format */ +#define GCM_TEXTURE_FORMAT_COMPRESSED_B8R8_G8R8 45 +/*! \brief texture color format is two pixels compressed to 32 bits in YUV format */ +#define GCM_TEXTURE_FORMAT_COMPRESSED_R8B8_R8G8 46 + /*! \brief shift value for texture remapping type corresponding to the blue component */ -#define GCM_TEXTURE_REMAP_TYPE_B_SHIFT 14 +#define GCM_TEXTURE_REMAP_TYPE_B_SHIFT 14 /*! \brief shift value for texture remapping type corresponding to the green component */ -#define GCM_TEXTURE_REMAP_TYPE_G_SHIFT 12 +#define GCM_TEXTURE_REMAP_TYPE_G_SHIFT 12 /*! \brief shift value for texture remapping type corresponding to the red component */ -#define GCM_TEXTURE_REMAP_TYPE_R_SHIFT 10 +#define GCM_TEXTURE_REMAP_TYPE_R_SHIFT 10 /*! \brief shift value for texture remapping type corresponding to the alpha component */ -#define GCM_TEXTURE_REMAP_TYPE_A_SHIFT 8 +#define GCM_TEXTURE_REMAP_TYPE_A_SHIFT 8 /*! \brief shift value for texture remapping component color corresponding to the blue component */ -#define GCM_TEXTURE_REMAP_COLOR_B_SHIFT 6 +#define GCM_TEXTURE_REMAP_COLOR_B_SHIFT 6 /*! \brief shift value for texture remapping component color corresponding to the green component */ -#define GCM_TEXTURE_REMAP_COLOR_G_SHIFT 4 +#define GCM_TEXTURE_REMAP_COLOR_G_SHIFT 4 /*! \brief shift value for texture remapping component color corresponding to the red component */ -#define GCM_TEXTURE_REMAP_COLOR_R_SHIFT 2 +#define GCM_TEXTURE_REMAP_COLOR_R_SHIFT 2 /*! \brief shift value for texture remapping component color corresponding to the alpha component */ -#define GCM_TEXTURE_REMAP_COLOR_A_SHIFT 0 +#define GCM_TEXTURE_REMAP_COLOR_A_SHIFT 0 /*! \brief remap component to all zero bits */ -#define GCM_TEXTURE_REMAP_TYPE_ZERO 0 +#define GCM_TEXTURE_REMAP_TYPE_ZERO 0 /*! \brief remap component to all one bits */ -#define GCM_TEXTURE_REMAP_TYPE_ONE 1 +#define GCM_TEXTURE_REMAP_TYPE_ONE 1 /*! \brief remap component to specified component */ -#define GCM_TEXTURE_REMAP_TYPE_REMAP 2 +#define GCM_TEXTURE_REMAP_TYPE_REMAP 2 /*! \brief remap component to alpha component */ -#define GCM_TEXTURE_REMAP_COLOR_A 0 +#define GCM_TEXTURE_REMAP_COLOR_A 0 /*! \brief remap component to red component */ -#define GCM_TEXTURE_REMAP_COLOR_R 1 +#define GCM_TEXTURE_REMAP_COLOR_R 1 /*! \brief remap component to green component */ -#define GCM_TEXTURE_REMAP_COLOR_G 2 +#define GCM_TEXTURE_REMAP_COLOR_G 2 /*! \brief remap component to blue component */ -#define GCM_TEXTURE_REMAP_COLOR_B 3 +#define GCM_TEXTURE_REMAP_COLOR_B 3 /*! \brief x1 sample */ -#define GCM_TEXTURE_MAX_ANISO_1 0 +#define GCM_TEXTURE_MAX_ANISO_1 0 /*! \brief x2 sample */ -#define GCM_TEXTURE_MAX_ANISO_2 1 +#define GCM_TEXTURE_MAX_ANISO_2 1 /*! \brief x4 sample */ -#define GCM_TEXTURE_MAX_ANISO_4 2 +#define GCM_TEXTURE_MAX_ANISO_4 2 /*! \brief x6 sample */ -#define GCM_TEXTURE_MAX_ANISO_6 3 +#define GCM_TEXTURE_MAX_ANISO_6 3 /*! \brief x8 sample */ -#define GCM_TEXTURE_MAX_ANISO_8 4 +#define GCM_TEXTURE_MAX_ANISO_8 4 /*! \brief x10 sample */ -#define GCM_TEXTURE_MAX_ANISO_10 5 +#define GCM_TEXTURE_MAX_ANISO_10 5 /*! \brief x12 sample */ -#define GCM_TEXTURE_MAX_ANISO_12 6 +#define GCM_TEXTURE_MAX_ANISO_12 6 /*! \brief x16 sample */ -#define GCM_TEXTURE_MAX_ANISO_16 7 - -#define GCM_TEXTURE_NEAREST 1 -#define GCM_TEXTURE_LINEAR 2 -#define GCM_TEXTURE_NEAREST_MIPMAP_NEAREST 3 -#define GCM_TEXTURE_LINEAR_MIPMAP_NEAREST 4 -#define GCM_TEXTURE_NEAREST_MIPMAP_LINEAR 5 -#define GCM_TEXTURE_LINEAR_MIPMAP_LINEAR 6 - -#define GCM_TEXTURE_CONVOLUTION_QUINCUNX 1 -#define GCM_TEXTURE_CONVOLUTION_GAUSSIAN 2 -#define GCM_TEXTURE_CONVOLUTION_QUINCUNX_ALT 3 - -#define GCM_TEXTURE_REPEAT 1 -#define GCM_TEXTURE_MIRRORED_REPEAT 2 -#define GCM_TEXTURE_CLAMP_TO_EDGE 3 -#define GCM_TEXTURE_CLAMP_TO_BORDER 4 -#define GCM_TEXTURE_CLAMP 5 -#define GCM_TEXTURE_MIRROR_CLAMP_TO_EDGE 6 -#define GCM_TEXTURE_MIRROR_CLAMP_TO_BORDER 7 -#define GCM_TEXTURE_MIRROR_CLAMP 8 - -#define GCM_TEXTURE_ZFUNC_NEVER 0 -#define GCM_TEXTURE_ZFUNC_LESS 1 -#define GCM_TEXTURE_ZFUNC_EQUAL 2 -#define GCM_TEXTURE_ZFUNC_LEQUAL 3 -#define GCM_TEXTURE_ZFUNC_GREATER 4 -#define GCM_TEXTURE_ZFUNC_NOTEQUAL 5 -#define GCM_TEXTURE_ZFUNC_GEQUAL 6 -#define GCM_TEXTURE_ZFUNC_ALWAYS 7 - -#define GCM_VERTEX_ATTRIB_POS 0 -#define GCM_VERTEX_ATTRIB_WEIGHT 1 -#define GCM_VERTEX_ATTRIB_NORMAL 2 -#define GCM_VERTEX_ATTRIB_COLOR0 3 -#define GCM_VERTEX_ATTRIB_COLOR1 4 -#define GCM_VERTEX_ATTRIB_FOG 5 -#define GCM_VERTEX_ATTRIB_COLOR_INDEX 6 -#define GCM_VERTEX_ATTRIB_POINT_SIZE 6 /*alias*/ -#define GCM_VERTEX_ATTRIB_EDGEFLAG 7 -#define GCM_VERTEX_ATTRIB_TEX0 8 -#define GCM_VERTEX_ATTRIB_TEX1 9 -#define GCM_VERTEX_ATTRIB_TEX2 10 -#define GCM_VERTEX_ATTRIB_TEX3 11 -#define GCM_VERTEX_ATTRIB_TEX4 12 -#define GCM_VERTEX_ATTRIB_TEX5 13 -#define GCM_VERTEX_ATTRIB_TEX6 14 -#define GCM_VERTEX_ATTRIB_TEX7 15 - -#define GCM_VERTEX_DATA_TYPE_F32 2 -#define GCM_VERTEX_DATA_TYPE_U8 4 - -#define GCM_INDEX_TYPE_32B 0 -#define GCM_INDEX_TYPE_16B 1 - -#define GCM_USER_CLIP_PLANE_DISABLE 0 -#define GCM_USER_CLIP_PLANE_LT 1 -#define GCM_USER_CLIP_PLANE_GE 2 +#define GCM_TEXTURE_MAX_ANISO_16 7 + +#define GCM_FOG_MODE_LINEAR 0x2601 +#define GCM_FOG_MODE_EXP 0x0800 +#define GCM_FOG_MODE_EXP2 0x0801 +#define GCM_FOG_MODE_EXP_ABS 0x0802 +#define GCM_FOG_MODE_EXP2_ABS 0x0803 +#define GCM_FOG_MODE_LINEAR_ABS 0x0804 + +#define GCM_POINT_SPRITE_TEX0 (1<<8) +#define GCM_POINT_SPRITE_TEX1 (1<<9) +#define GCM_POINT_SPRITE_TEX2 (1<<10) +#define GCM_POINT_SPRITE_TEX3 (1<<11) +#define GCM_POINT_SPRITE_TEX4 (1<<12) +#define GCM_POINT_SPRITE_TEX5 (1<<13) +#define GCM_POINT_SPRITE_TEX6 (1<<14) +#define GCM_POINT_SPRITE_TEX7 (1<<15) +#define GCM_POINT_SPRITE_TEX8 (1<<16) +#define GCM_POINT_SPRITE_TEX9 (1<<17) + +#define GCM_POINT_SPRITE_RMODE_ZERO 0 +#define GCM_POINT_SPRITE_RMODE_FROM_R 1 +#define GCM_POINT_SPRITE_RMODE_FROM_S 2 + +#define GCM_TEXTURE_NEAREST 1 +#define GCM_TEXTURE_LINEAR 2 +#define GCM_TEXTURE_NEAREST_MIPMAP_NEAREST 3 +#define GCM_TEXTURE_LINEAR_MIPMAP_NEAREST 4 +#define GCM_TEXTURE_NEAREST_MIPMAP_LINEAR 5 +#define GCM_TEXTURE_LINEAR_MIPMAP_LINEAR 6 +#define GCM_TEXTURE_CONVOLUTUIN_MIN 7 +#define GCM_TEXTURE_CONVOLUTUIN_MAG 4 + +#define GCM_TEXTURE_CONVOLUTION_QUINCUNX 1 +#define GCM_TEXTURE_CONVOLUTION_GAUSSIAN 2 +#define GCM_TEXTURE_CONVOLUTION_QUINCUNX_ALT 3 + +#define GCM_TEXTURE_REPEAT 1 +#define GCM_TEXTURE_MIRRORED_REPEAT 2 +#define GCM_TEXTURE_CLAMP_TO_EDGE 3 +#define GCM_TEXTURE_BORDER 4 +#define GCM_TEXTURE_CLAMP 5 +#define GCM_TEXTURE_MIRROR_ONCE_CLAMP_TO_EDGE 6 +#define GCM_TEXTURE_MIRROR_ONCE_CLAMP_TO_BORDER 7 +#define GCM_TEXTURE_MIRROR_ONCE_CLAMP 8 + +#define GCM_TEXTURE_UNSIGNED_REMAP_NORMAL 0 +#define GCM_TEXTURE_UNSIGNED_REMAP_BIASED 1 + +#define GCM_TEXTURE_GAMMA_R (1<<0) +#define GCM_TEXTURE_GAMMA_G (1<<1) +#define GCM_TEXTURE_GAMMA_B (1<<2) +#define GCM_TEXTURE_GAMMA_A (1<<3) + +#define GCM_TEXTURE_ZFUNC_NEVER 0 +#define GCM_TEXTURE_ZFUNC_LESS 1 +#define GCM_TEXTURE_ZFUNC_EQUAL 2 +#define GCM_TEXTURE_ZFUNC_LEQUAL 3 +#define GCM_TEXTURE_ZFUNC_GREATER 4 +#define GCM_TEXTURE_ZFUNC_NOTEQUAL 5 +#define GCM_TEXTURE_ZFUNC_GEQUAL 6 +#define GCM_TEXTURE_ZFUNC_ALWAYS 7 + +#define GCM_TEXTURE_ISO_LOW 0 +#define GCM_TEXTURE_ISO_HIGH 1 + +#define GCM_TEXTURE_ANISO_LOW 0 +#define GCM_TEXTURE_ANISO_HIGH 1 + +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX0_U (1<<0) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX0_V (1<<1) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX0_P (1<<2) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX0_Q (1<<3) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX1_U (1<<4) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX1_V (1<<5) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX1_P (1<<6) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX1_Q (1<<7) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX2_U (1<<8) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX2_V (1<<9) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX2_P (1<<10) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX2_Q (1<<11) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX3_U (1<<12) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX3_V (1<<13) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX3_P (1<<14) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX3_Q (1<<15) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX4_U (1<<16) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX4_V (1<<17) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX4_P (1<<18) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX4_Q (1<<19) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX5_U (1<<20) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX5_V (1<<21) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX5_P (1<<22) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX5_Q (1<<23) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX6_U (1<<24) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX6_V (1<<25) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX6_P (1<<26) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX6_Q (1<<27) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX7_U (1<<28) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX7_V (1<<29) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX7_P (1<<30) +#define GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX7_Q (1<<31) + +#define GCM_VERTEX_ATTRIB_POS 0 +#define GCM_VERTEX_ATTRIB_WEIGHT 1 +#define GCM_VERTEX_ATTRIB_NORMAL 2 +#define GCM_VERTEX_ATTRIB_COLOR0 3 +#define GCM_VERTEX_ATTRIB_COLOR1 4 +#define GCM_VERTEX_ATTRIB_FOG 5 +#define GCM_VERTEX_ATTRIB_COLOR_INDEX 6 +#define GCM_VERTEX_ATTRIB_POINT_SIZE 6 /*alias*/ +#define GCM_VERTEX_ATTRIB_EDGEFLAG 7 +#define GCM_VERTEX_ATTRIB_TEX0 8 +#define GCM_VERTEX_ATTRIB_TEX1 9 +#define GCM_VERTEX_ATTRIB_TEX2 10 +#define GCM_VERTEX_ATTRIB_TEX3 11 +#define GCM_VERTEX_ATTRIB_TEX4 12 +#define GCM_VERTEX_ATTRIB_TEX5 13 +#define GCM_VERTEX_ATTRIB_TEX6 14 +#define GCM_VERTEX_ATTRIB_TANGENT 14 /*alias*/ +#define GCM_VERTEX_ATTRIB_TEX7 15 +#define GCM_VERTEX_ATTRIB_BINORMAL 15 /*alias*/ + +#define GCM_VERTEX_DATA_TYPE_F32 2 +#define GCM_VERTEX_DATA_TYPE_U8 4 + +#define GCM_INDEX_TYPE_32B 0 +#define GCM_INDEX_TYPE_16B 1 + +#define GCM_USER_CLIP_PLANE_DISABLE 0 +#define GCM_USER_CLIP_PLANE_LT 1 +#define GCM_USER_CLIP_PLANE_GE 2 + +#define GCM_ATTRIB_OUTPUT_MASK_FRONTDIFFUSE (1<< 0) +#define GCM_ATTRIB_OUTPUT_MASK_FRONTSPECULAR (1<< 1) +#define GCM_ATTRIB_OUTPUT_MASK_BACKDIFFUSE (1<< 2) +#define GCM_ATTRIB_OUTPUT_MASK_BACKSPECULAR (1<< 3) +#define GCM_ATTRIB_OUTPUT_MASK_FOG (1<< 4) +#define GCM_ATTRIB_OUTPUT_MASK_POINTSIZE (1<< 5) +#define GCM_ATTRIB_OUTPUT_MASK_UC0 (1<< 6) +#define GCM_ATTRIB_OUTPUT_MASK_UC1 (1<< 7) +#define GCM_ATTRIB_OUTPUT_MASK_UC2 (1<< 8) +#define GCM_ATTRIB_OUTPUT_MASK_UC3 (1<< 9) +#define GCM_ATTRIB_OUTPUT_MASK_UC4 (1<<10) +#define GCM_ATTRIB_OUTPUT_MASK_UC5 (1<<11) +#define GCM_ATTRIB_OUTPUT_MASK_TEX8 (1<<12) +#define GCM_ATTRIB_OUTPUT_MASK_TEX9 (1<<13) +#define GCM_ATTRIB_OUTPUT_MASK_TEX0 (1<<14) +#define GCM_ATTRIB_OUTPUT_MASK_TEX1 (1<<15) +#define GCM_ATTRIB_OUTPUT_MASK_TEX2 (1<<16) +#define GCM_ATTRIB_OUTPUT_MASK_TEX3 (1<<17) +#define GCM_ATTRIB_OUTPUT_MASK_TEX4 (1<<18) +#define GCM_ATTRIB_OUTPUT_MASK_TEX5 (1<<19) +#define GCM_ATTRIB_OUTPUT_MASK_TEX6 (1<<20) +#define GCM_ATTRIB_OUTPUT_MASK_TEX7 (1<<21) /*! \brief Flat shading */ -#define GCM_SHADE_MODEL_FLAT 0x1D00 +#define GCM_SHADE_MODEL_FLAT 0x1D00 /*! \brief Smooth shading */ -#define GCM_SHADE_MODEL_SMOOTH 0x1D01 +#define GCM_SHADE_MODEL_SMOOTH 0x1D01 /*! \brief blend factors are zero */ -#define GCM_ZERO 0 +#define GCM_ZERO 0 /*! \brief blend factors are one */ -#define GCM_ONE 1 +#define GCM_ONE 1 /*! \brief blend factors are the source color components */ -#define GCM_SRC_COLOR 0x0300 +#define GCM_SRC_COLOR 0x0300 /*! \brief blend factors are one minus source color components */ -#define GCM_ONE_MINUS_SRC_COLOR 0x0301 +#define GCM_ONE_MINUS_SRC_COLOR 0x0301 /*! \brief blend factors are the source alpha component */ -#define GCM_SRC_ALPHA 0x0302 +#define GCM_SRC_ALPHA 0x0302 /*! \brief blend factors are one minus the source alpha component */ -#define GCM_ONE_MINUS_SRC_ALPHA 0x0303 +#define GCM_ONE_MINUS_SRC_ALPHA 0x0303 /*! \brief blend factors are the destination alpha component */ -#define GCM_DST_ALPHA 0x0304 +#define GCM_DST_ALPHA 0x0304 /*! \brief blend factors are one minus the destination alpha component */ -#define GCM_ONE_MINUS_DST_ALPHA 0x0305 +#define GCM_ONE_MINUS_DST_ALPHA 0x0305 /*! \brief blend factors are the destination color components */ -#define GCM_DST_COLOR 0x0306 +#define GCM_DST_COLOR 0x0306 /*! \brief blend factors are one minus the destination color components */ -#define GCM_ONE_MINUS_DST_COLOR 0x0307 +#define GCM_ONE_MINUS_DST_COLOR 0x0307 /*! \brief blend factors are set to saturate the output */ -#define GCM_SRC_ALPHA_SATURATE 0x0308 +#define GCM_SRC_ALPHA_SATURATE 0x0308 /*! \brief blend factors are the constant color components */ -#define GCM_CONSTANT_COLOR 0x8001 +#define GCM_CONSTANT_COLOR 0x8001 /*! \brief blend factors are one minus the constant color components */ -#define GCM_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GCM_ONE_MINUS_CONSTANT_COLOR 0x8002 /*! \brief blend factors are the constant color alpha component */ -#define GCM_CONSTANT_ALPHA 0x8003 +#define GCM_CONSTANT_ALPHA 0x8003 /*! \brief blend factors are one minus the constant color alpha component */ -#define GCM_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GCM_ONE_MINUS_CONSTANT_ALPHA 0x8004 /*! \brief use scaled source plus destination */ -#define GCM_FUNC_ADD 0x8006 +#define GCM_FUNC_ADD 0x8006 /*! \brief use the minimum of source and destination color components */ -#define GCM_MIN 0x8007 +#define GCM_FUNC_MIN 0x8007 /*! \brief use the maximum of source and destination color components */ -#define GCM_MAX 0x8008 +#define GCM_FUNC_MAX 0x8008 /*! \brief use scaled source minus destination */ -#define GCM_FUNC_SUBTRACT 0x800a +#define GCM_FUNC_SUBTRACT 0x800a /*! \brief use scaled destination minus source */ -#define GCM_FUNC_REVERSE_SUBTRACT 0x800b +#define GCM_FUNC_REVERSE_SUBTRACT 0x800b /*! \brief use scaled destination minus source (signed) */ -#define GCM_FUNC_REVERSE_SUBTRACT_SIGNED 0xf005 +#define GCM_FUNC_REVERSE_SUBTRACT_SIGNED 0xf005 /*! \brief use scaled source plus destination (signed) */ -#define GCM_FUNC_ADD_SIGNED 0xf006 +#define GCM_FUNC_ADD_SIGNED 0xf006 /*! \brief use scaled destination plus source (signed) */ -#define GCM_FUNC_REVERSE_ADD_SIGNED 0xf007 +#define GCM_FUNC_REVERSE_ADD_SIGNED 0xf007 -#define GCM_TRANSFER_SURFACE 0 -#define GCM_TRANSFER_SWIZZLE 1 +#define GCM_TRANSFER_SURFACE 0 +#define GCM_TRANSFER_SWIZZLE 1 /*! \brief Convert pixel component values using dithering. */ -#define GCM_TRANSFER_CONVERSION_DITHER 0 +#define GCM_TRANSFER_CONVERSION_DITHER 0 /*! \brief Convert pixel component values by truncation (\em ie, ignore lower bits). */ -#define GCM_TRANSFER_CONVERSION_TRUNCATE 1 +#define GCM_TRANSFER_CONVERSION_TRUNCATE 1 /*! \brief Convert pixel component values by subtraction/truncation. */ -#define GCM_TRANSFER_CONVERSION_SUBTRACT_TRUNCATE 2 +#define GCM_TRANSFER_CONVERSION_SUBTRACT_TRUNCATE 2 /*! \brief Pixel format is 1-bit alpha, 5-bit red, 5-bit green, 5-bit blue */ -#define GCM_TRANSFER_SCALE_FORMAT_A1R5G5B5 1 +#define GCM_TRANSFER_SCALE_FORMAT_A1R5G5B5 1 /*! \brief Pixel format is 1-bit unused, 5-bit red, 5-bit green, 5-bit blue */ -#define GCM_TRANSFER_SCALE_FORMAT_X1R5G5B5 2 +#define GCM_TRANSFER_SCALE_FORMAT_X1R5G5B5 2 /*! \brief Pixel format is 8-bit alpha, 8-bit red, 8-bit green, 8-bit blue */ -#define GCM_TRANSFER_SCALE_FORMAT_A8R8G8B8 3 +#define GCM_TRANSFER_SCALE_FORMAT_A8R8G8B8 3 /*! \brief Pixel format is 8-bit unused, 8-bit red, 8-bit green, 8-bit blue */ -#define GCM_TRANSFER_SCALE_FORMAT_X8R8G8B8 4 +#define GCM_TRANSFER_SCALE_FORMAT_X8R8G8B8 4 /*! \brief Pixel format is 8-bit red chrominance, 8-bit luminance, 8-bit blue chrominance, 8-bit alpha */ -#define GCM_TRANSFER_SCALE_FORMAT_CR8YB8CB8YA8 5 +#define GCM_TRANSFER_SCALE_FORMAT_CR8YB8CB8YA8 5 /*! \brief Pixel format is 8-bit luminance, 8-bit red chrominance, 8-bit alpha, 8-bit blue chrominance */ -#define GCM_TRANSFER_SCALE_FORMAT_YB8CR8YA8CB8 6 +#define GCM_TRANSFER_SCALE_FORMAT_YB8CR8YA8CB8 6 /*! \brief Pixel format is 5-bit red, 6-bit green, 5-bit blue */ -#define GCM_TRANSFER_SCALE_FORMAT_R5G6B5 7 +#define GCM_TRANSFER_SCALE_FORMAT_R5G6B5 7 /*! \brief Pixel format is 8-bit grayscale */ -#define GCM_TRANSFER_SCALE_FORMAT_Y8 8 +#define GCM_TRANSFER_SCALE_FORMAT_Y8 8 /*! \brief Pixel format is 8-bit alpha */ -#define GCM_TRANSFER_SCALE_FORMAT_AY8 9 +#define GCM_TRANSFER_SCALE_FORMAT_AY8 9 /*! \brief Pixel format is EYB8ECR8EYA8ECB8 */ -#define GCM_TRANSFER_SCALE_FORMAT_EYB8ECR8EYA8ECB8 0xa +#define GCM_TRANSFER_SCALE_FORMAT_EYB8ECR8EYA8ECB8 0xa /*! \brief Pixel format is ECR8EYB8ECB8EYA8 */ -#define GCM_TRANSFER_SCALE_FORMAT_ECR8EYB8ECB8EYA8 0xb +#define GCM_TRANSFER_SCALE_FORMAT_ECR8EYB8ECB8EYA8 0xb /*! \brief Pixel format is 8-bit alpha, 8-bit blue, 8-bit green, 8-bit red */ -#define GCM_TRANSFER_SCALE_FORMAT_A8B8G8R8 0xc +#define GCM_TRANSFER_SCALE_FORMAT_A8B8G8R8 0xc /*! \brief Pixel format is 8-bit unused, 8-bit blue, 8-bit green, 8-bit red */ -#define GCM_TRANSFER_SCALE_FORMAT_X8B8G8R8 0xd +#define GCM_TRANSFER_SCALE_FORMAT_X8B8G8R8 0xd /*! \brief Copy source image, perform logical \c AND with destination. */ -#define GCM_TRANSFER_OPERATION_SRCCOPY_AND 0 +#define GCM_TRANSFER_OPERATION_SRCCOPY_AND 0 /*! \brief Perform ROP (raster operation), and logical \c AND with destination. */ -#define GCM_TRANSFER_OPERATION_ROP_AND 1 +#define GCM_TRANSFER_OPERATION_ROP_AND 1 /*! \brief Perform blending, and logical \c AND with destination. */ -#define GCM_TRANSFER_OPERATION_BLEND_AND 2 +#define GCM_TRANSFER_OPERATION_BLEND_AND 2 /*! \brief Copy source image. */ -#define GCM_TRANSFER_OPERATION_SRCCOPY 3 +#define GCM_TRANSFER_OPERATION_SRCCOPY 3 /*! \brief Copy pre-multiplied source image. */ -#define GCM_TRANSFER_OPERATION_SRCCOPY_PREMULT 4 +#define GCM_TRANSFER_OPERATION_SRCCOPY_PREMULT 4 /*! \brief Blend pre-multiplied source image. */ -#define GCM_TRANSFER_OPERATION_BLEND_PREMULT 5 +#define GCM_TRANSFER_OPERATION_BLEND_PREMULT 5 /*! \brief Origin is the center of the source image. */ -#define GCM_TRANSFER_ORIGIN_CENTER 1 +#define GCM_TRANSFER_ORIGIN_CENTER 1 /*! \brief Origin is the topleft cornet of the source image. */ -#define GCM_TRANSFER_ORIGIN_CORNER 2 +#define GCM_TRANSFER_ORIGIN_CORNER 2 /*! \brief Use point sampling interpolation. */ -#define GCM_TRANSFER_INTERPOLATOR_NEAREST 0 +#define GCM_TRANSFER_INTERPOLATOR_NEAREST 0 // point sampling /*! \brief Use point linear interpolation. */ -#define GCM_TRANSFER_INTERPOLATOR_LINEAR 1 +#define GCM_TRANSFER_INTERPOLATOR_LINEAR 1 // bilinear interpolation /*! \brief Source surface pixel format is 5-bit red, 6-bit green, 5-bit blue. */ -#define GCM_TRANSFER_SURFACE_FORMAT_R5G6B5 4 +#define GCM_TRANSFER_SURFACE_FORMAT_R5G6B5 4 /*! \brief Source surface pixel format is 8-bit alpha, 8-bit red, 8-bit green, 8-bit blue. */ -#define GCM_TRANSFER_SURFACE_FORMAT_A8R8G8B8 0xa +#define GCM_TRANSFER_SURFACE_FORMAT_A8R8G8B8 0xa /*! \brief Source surface pixel format is Y32. */ -#define GCM_TRANSFER_SURFACE_FORMAT_Y32 0xb +#define GCM_TRANSFER_SURFACE_FORMAT_Y32 0xb + +#define GCM_FREQUENCY_MODULO 1 +#define GCM_FREQUENCY_DIVIDE 0 + +#define GCM_COMPMODE_DISABLED 0 +#define GCM_COMPMODE_C32_2X1 7 +#define GCM_COMPMODE_C32_2X2 8 +#define GCM_COMPMODE_Z32_SEPSTENCIL 9 +#define GCM_COMPMODE_Z32_SEPSTENCIL_REG 10 +#define GCM_COMPMODE_Z32_SEPSTENCIL_REGULAR 10 +#define GCM_COMPMODE_Z32_SEPSTENCIL_DIAGONAL 11 +#define GCM_COMPMODE_Z32_SEPSTENCIL_ROTATED 12 + +#define GCM_ZCULL_Z16 1 +#define GCM_ZCULL_Z24S8 2 + +#define GCM_ZCULL_MSB 0 +#define GCM_ZCULL_LONES 1 + +#define GCM_ZCULL_LESS 0 +#define GCM_ZCULL_GREATER 1 + +#define GCM_SCULL_SFUNC_NEVER 0 +#define GCM_SCULL_SFUNC_LESS 1 +#define GCM_SCULL_SFUNC_EQUAL 2 +#define GCM_SCULL_SFUNC_LEQUAL 3 +#define GCM_SCULL_SFUNC_GREATER 4 +#define GCM_SCULL_SFUNC_NOTEQUAL 5 +#define GCM_SCULL_SFUNC_GEQUAL 6 +#define GCM_SCULL_SFUNC_ALWAYS 7 + +#define GCM_CONDITIONAL 2 + +#define GCM_ZPASS_PIXEL_CNT 1 +#define GCM_ZCULL_STATS 2 +#define GCM_ZCULL_STATS1 3 +#define GCM_ZCULL_STATS2 4 +#define GCM_ZCULL_STATS3 5 + +#define GCM_ZCULL_ALIGN_OFFSET 4096 +#define GCM_ZCULL_ALIGN_WIDTH 64 +#define GCM_ZCULL_ALIGN_HEIGHT 64 +#define GCM_ZCULL_ALIGN_CULLSTART 4096 +#define GCM_ZCULL_COMPRESSION_TAG_BASE_MAX 0x7FF +#define GCM_ZCULL_RAM_SIZE_MAX 0x00300000 + +#define GCM_TILE_ALIGN_OFFSET 0x00010000 +#define GCM_TILE_ALIGN_SIZE 0x00010000 +#define GCM_TILE_LOCAL_ALIGN_HEIGHT 32 +#define GCM_TILE_MAIN_ALIGN_HEIGHT 64 + +#define GCM_TILE_ALIGN_BUFFER_START_BOUNDARY 8 + +#define GCM_FRAGMENT_UCODE_LOCAL_ALIGN_OFFSET 64 +#define GCM_FRAGMENT_UCODE_MAIN_ALIGN_OFFSET 128 + +#define GCM_SURFACE_LINEAR_ALIGN_OFFSET 64 +#define GCM_SURFACE_SWIZZLE_ALIGN_OFFSET 128 + +#define GCM_TEXTURE_SWIZZLE_ALIGN_OFFSET 128 +#define GCM_TEXTURE_CUBEMAP_ALIGN_OFFSET 128 +#define GCM_TEXTURE_SWIZZLED_CUBEMAP_FACE_ALIGN_OFFSET 128 + +#define GCM_VERTEX_TEXTURE_CACHE_LINE_SIZE 32 +#define GCM_L2_TEXTURE_CACHE_LOCAL_LINE_SIZE 64 +#define GCM_L2_TEXTURE_CACHE_MAIN_LINE_SIZE 128 #ifdef __cplusplus extern "C" { @@ -403,375 +701,377 @@ extern "C" { struct _gcmCtxData; typedef s32 (*gcmContextCallback)(struct _gcmCtxData *context,u32 count); -/* START OF STRUCTS */ - /*! \brief RSX Context data structure. - This structure is used for managing and controlling the command buffer. +This structure is used for managing and controlling the command buffer. */ typedef struct _gcmCtxData { - u32 *__restrict begin ATTRIBUTE_PRXPTR; /*!< \brief Start address of command buffer */ - u32 *__restrict end ATTRIBUTE_PRXPTR; /*!< \brief End address of command buffer */ - u32 *__restrict current ATTRIBUTE_PRXPTR; /*!< \brief Current address of command buffer */ - gcmContextCallback callback ATTRIBUTE_PRXPTR; /*!< \brief Callback function that is called when current reaches end */ + u32 *__restrict begin ATTRIBUTE_PRXPTR; /*!< \brief Start address of command buffer */ + u32 *__restrict end ATTRIBUTE_PRXPTR; /*!< \brief End address of command buffer */ + u32 *__restrict current ATTRIBUTE_PRXPTR; /*!< \brief Current address of command buffer */ + gcmContextCallback callback ATTRIBUTE_PRXPTR; /*!< \brief Callback function that is called when current reaches end */ } gcmContextData; /*! \brief RSX control data structure. - This structure is used to control the command buffer. +This structure is used to control the command buffer. */ typedef struct _gcmCtrlRegister { - vu32 put; /*!< \brief member for accessing the PUT register */ - vu32 get; /*!< \brief member for accessing the GET register */ - vu32 ref; /*!< \brief member for accessing the REF register. Initial value is 0xFFFFFFFF */ + vu32 put; /*!< \brief member for accessing the PUT register */ + vu32 get; /*!< \brief member for accessing the GET register */ + vu32 ref; /*!< \brief member for accessing the REF register. Initial value is 0xFFFFFFFF */ } gcmControlRegister; /*! \brief RSX Configuration structure. - This structure holds system informations of RSX. +This structure holds system informations of RSX. */ typedef struct _gcmCfg { - void *localAddress ATTRIBUTE_PRXPTR; /*!< \brief effective start address of RSX memory */ - void *ioAddress ATTRIBUTE_PRXPTR; /*!< \brief effective start address of I/O mapped main memory to be used by RSX */ - u32 localSize; /*!< \brief maximum available size of RSX memory */ - u32 ioSize; /*!< \brief maximum available size of I/O mapped main memory to be used by RSX */ - u32 memoryFreq; /*!< \brief RSX memory clock frequency. */ - u32 coreFreq; /*!< \brief Core clock frequency of RSX */ + void *localAddress ATTRIBUTE_PRXPTR; /*!< \brief effective start address of RSX memory */ + void *ioAddress ATTRIBUTE_PRXPTR; /*!< \brief effective start address of I/O mapped main memory to be used by RSX */ + u32 localSize; /*!< \brief maximum available size of RSX memory */ + u32 ioSize; /*!< \brief maximum available size of I/O mapped main memory to be used by RSX */ + u32 memoryFreq; /*!< \brief RSX memory clock frequency. */ + u32 coreFreq; /*!< \brief Core clock frequency of RSX */ } gcmConfiguration; /*! \brief RSX target surface data structure. - This structure holds settings of the render target that is to be the render buffer. - Set the buffer to use for rendering by passing this structure as the argument when calling \ref rsxSetSurface. */ +This structure holds settings of the render target that is to be the render buffer. +Set the buffer to use for rendering by passing this structure as the argument when calling \ref rsxSetSurface. */ typedef struct _gcmSurface { - /*! \brief Type of render target. - - Possible values are: - - \ref GCM_TF_TYPE_LINEAR - - \ref GCM_TF_TYPE_SWIZZLE - */ - u8 type; - - /*! \brief Antialiasing format type. - - Specifies the mode of multiple samples. Possible values are: - - \ref GCM_TF_CENTER_1 - */ - u8 antiAlias; - - /*! \brief Format of the color buffer. - - Possible values are: - - \ref GCM_TF_COLOR_R5G5B5 - */ - u8 colorFormat; - - /*! \brief Target of the color buffer. - - Specifies the render target to use as a surface. Possible values are: - - \ref GCM_TF_TARGET_NONE - - \ref GCM_TF_TARGET_0 - - \ref GCM_TF_TARGET_1 - - \ref GCM_TF_TARGET_MRT1 - - \ref GCM_TF_TARGET_MRT2 - - \ref GCM_TF_TARGET_MRT3 - */ - u8 colorTarget; - - /*! \brief Location of the color buffer. - - When using multiple render targets, set as many locations as the number of color buffers enabled in colorTarget. - In this system, up to 4 color buffers can be specified for multiple render targets, and the location of each individual color buffer can be specified independently. - Possible values are: - - \ref GCM_LOCATION_RSX - - \ref GCM_LOCATION_CELL - */ - u8 colorLocation[GCM_MAX_MRT_COUNT]; - - /*! \brief Offset from the base address of the color buffer. - - When using multiple render targets, set as many addresses as the number of color buffers specified in colorTarget. - Use \ref rsxAddressToOffset to convert the effective addresses into offset values when specifying the buffer offset. colorOffset should be aligned on a 64 bytes boundery. - */ - u32 colorOffset[GCM_MAX_MRT_COUNT]; - - /*! \brief Size of a color buffer line in bytes. - - When using multiple render targets, specify as many pitch sizes as the number of color buffers specified in colorTarget. - The pitch size should be 64 when rendering in the swizzle format. For all others, the pitch size should be a multiple of 64. - */ - u32 colorPitch[GCM_MAX_MRT_COUNT]; - - /*! \brief Format of the depth buffer. - - Possible values are: - - \ref GCM_TF_ZETA_Z16 - - \ref GCM_TF_ZETA_Z24S8 - */ - u8 depthFormat; - - /*! \brief Location of the depth buffer. - - Possible values are: - - \ref GCM_LOCATION_RSX - - \ref GCM_LOCATION_CELL - */ - u8 depthLocation; - - /*! \brief unused padding bytes. most be 0. */ - u8 _pad[2]; - - /*! \brief Offset from the base address of the depth buffer. - - As in colorOffset use \ref rsxAddressToOffset to convert effective addresses into offset values. depthOffset should be aligned on a 64 bytes boundery. - */ - u32 depthOffset; - - /*! \brief Size of a depth buffer line in bytes. */ - u32 depthPitch; - - /*! \brief Width of the render buffer (1 - 4096). */ - u16 width; - - /*! \brief Height of the render buffer (1 - 4096). */ - u16 height; - - /*! \brief Window offset in x direction (0 - 4095). */ - u16 x; - - /*! \brief Window offset in y direction (0 - 4095). */ - u16 y; + /*! \brief Type of render target. + + Possible values are: + - \ref GCM_SURFACE_TYPE_LINEAR + - \ref GCM_SURFACE_TYPE_SWIZZLE + */ + u8 type; + + /*! \brief Antialiasing format type. + + Specifies the mode of multiple samples. Possible values are: + - \ref GCM_SURFACE_CENTER_1 + - \ref GCM_SURFACE_DIAGONAL_CENTERED_2 + - \ref GCM_SURFACE_SQUARE_CENTERED_4 + - \ref GCM_SURFACE_SQUARE_ROTATED_4 + */ + u8 antiAlias; + + /*! \brief Format of the color buffer. + + Possible values are: + -\ ref GCM_SURFACE_X1R5G5B5_Z1R5G5B5 + -\ ref GCM_SURFACE_X1R5G5B5_O1R5G5B5 + -\ ref GCM_SURFACE_R5G5B5 + -\ ref GCM_SURFACE_X8R8G8B8_Z8R8G8B8 + -\ ref GCM_SURFACE_X8R8G8B8 + -\ ref GCM_SURFACE_A8R8G8B8 + -\ ref GCM_SURFACE_B8 + -\ ref GCM_SURFACE_G8B8 + -\ ref GCM_SURFACE_F_W16Z16Y16X16 + -\ ref GCM_SURFACE_F_W32Z32Y32X32 + -\ ref GCM_SURFACE_F_X32 + -\ ref GCM_SURFACE_X8B8G8R8_Z8B8G8R8 + -\ ref GCM_SURFACE_X8B8G8R8_O8B8G8R8 + -\ ref GCM_SURFACE_A8B8G8R8 + */ + u8 colorFormat; + + /*! \brief Target of the color buffer. + + Specifies the render target to use as a surface. Possible values are: + - \ref GCM_SURFACE_TARGET_NONE + - \ref GCM_SURFACE_TARGET_0 + - \ref GCM_SURFACE_TARGET_1 + - \ref GCM_SURFACE_TARGET_MRT1 + - \ref GCM_SURFACE_TARGET_MRT2 + - \ref GCM_SURFACE_TARGET_MRT3 + */ + u8 colorTarget; + + /*! \brief Location of the color buffer. + + When using multiple render targets, set as many locations as the number of color buffers enabled in colorTarget. + In this system, up to 4 color buffers can be specified for multiple render targets, and the location of each individual color buffer can be specified independently. + Possible values are: + - \ref GCM_LOCATION_RSX + - \ref GCM_LOCATION_CELL + */ + u8 colorLocation[GCM_MAX_MRT_COUNT]; + + /*! \brief Offset from the base address of the color buffer. + + When using multiple render targets, set as many addresses as the number of color buffers specified in colorTarget. + Use \ref rsxAddressToOffset to convert the effective addresses into offset values when specifying the buffer offset. colorOffset should be aligned on a 64 bytes boundery. + */ + u32 colorOffset[GCM_MAX_MRT_COUNT]; + + /*! \brief Size of a color buffer line in bytes. + + When using multiple render targets, specify as many pitch sizes as the number of color buffers specified in colorTarget. + The pitch size should be 64 when rendering in the swizzle format. For all others, the pitch size should be a multiple of 64. + */ + u32 colorPitch[GCM_MAX_MRT_COUNT]; + + /*! \brief Format of the depth buffer. + + Possible values are: + - \ref GCM_SURFACE_ZETA_Z16 + - \ref GCM_SURFACE_ZETA_Z24S8 + */ + u8 depthFormat; + + /*! \brief Location of the depth buffer. + + Possible values are: + - \ref GCM_LOCATION_RSX + - \ref GCM_LOCATION_CELL + */ + u8 depthLocation; + + /*! \brief unused padding bytes. most be 0. */ + u8 _pad[2]; + + /*! \brief Offset from the base address of the depth buffer. + + As in colorOffset use \ref rsxAddressToOffset to convert effective addresses into offset values. depthOffset should be aligned on a 64 bytes boundery. + */ + u32 depthOffset; + + /*! \brief Size of a depth buffer line in bytes. */ + u32 depthPitch; + + /*! \brief Width of the render buffer (1 - 4096). */ + u16 width; + + /*! \brief Height of the render buffer (1 - 4096). */ + u16 height; + + /*! \brief Window offset in x direction (0 - 4095). */ + u16 x; + + /*! \brief Window offset in y direction (0 - 4095). */ + u16 y; } gcmSurface; /*! \brief RSX Texture data structure. */ typedef struct _gcmTexture { - /*! \brief Texture format. - - This is an OR-ed combination of the following values: - - \ref GCM_TEXTURE_FORMAT_SWZ - - \ref GCM_TEXTURE_FORMAT_LIN - - \ref GCM_TEXTURE_FORMAT_NRM - - \ref GCM_TEXTURE_FORMAT_L8 - - \ref GCM_TEXTURE_FORMAT_A1R5G5B5 - - \ref GCM_TEXTURE_FORMAT_A4R4G4B4 - - \ref GCM_TEXTURE_FORMAT_R5G6B5 - - \ref GCM_TEXTURE_FORMAT_A8R8G8B8 - - \ref GCM_TEXTURE_FORMAT_DXT1 - - \ref GCM_TEXTURE_FORMAT_DXT3 - - \ref GCM_TEXTURE_FORMAT_DXT5 - - \ref GCM_TEXTURE_FORMAT_A8L8 - */ - u8 format; - - /*! \brief Indicates if this is a mip-mapped texture. - - Possible values are: - - \ref GCM_TRUE - - \ref GCM_FALSE - */ - u8 mipmap; - - /*! \brief Texture dimension. - - Possible values are: - - \ref GCM_TEXTURE_DIMS_1D - - \ref GCM_TEXTURE_DIMS_2D - - \ref GCM_TEXTURE_DIMS_3D - */ - u8 dimension; - - /*! \brief Indicates if this is a cube-mapped texture. - - Possible values are: - - \ref GCM_TRUE - - \ref GCM_FALSE - */ - u8 cubemap; - - /*! \brief Color remapping bitfield. - - Each of the texture color components (red, green, blue, alpha) can be - remapped according to a specified remapping type. The type specifies - that the component is either set to zero, all one bits, or takes value - of one of the source components. All remapping types and values are to - be OR-ed together. - - For instance, to have the alpha component set to zero, the red and blue - components swapped and the green component kept as-is, set the following - value: - - (\ref GCM_TEXTURE_REMAP_TYPE_ZERO << \ref GCM_TEXTURE_REMAP_TYPE_A_SHIFT)\n - | (\ref GCM_TEXTURE_REMAP_TYPE_REMAP << \ref GCM_TEXTURE_REMAP_TYPE_R_SHIFT)\n - | (\ref GCM_TEXTURE_REMAP_COLOR_B << \ref GCM_TEXTURE_REMAP_COLOR_R_SHIFT)\n - | (\ref GCM_TEXTURE_REMAP_TYPE_REMAP << \ref GCM_TEXTURE_REMAP_TYPE_G_SHIFT)\n - | (\ref GCM_TEXTURE_REMAP_COLOR_G << \ref GCM_TEXTURE_REMAP_COLOR_G_SHIFT)\n - | (\ref GCM_TEXTURE_REMAP_TYPE_REMAP << \ref GCM_TEXTURE_REMAP_TYPE_B_SHIFT)\n - | (\ref GCM_TEXTURE_REMAP_COLOR_R << \ref GCM_TEXTURE_REMAP_COLOR_B_SHIFT) - */ - u32 remap; - - /*! \brief Texture width in pixels. */ - u16 width; - /*! \brief Texture height in pixels. */ - u16 height; - /*! \brief Texture depth. */ - u16 depth; - /*! \brief Location of texture. - Possible values are: - - \ref GCM_LOCATION_RSX - - \ref GCM_LOCATION_CELL - */ - u8 location; - /*! \brief unused padding byte. */ - u8 _pad; - /*! \brief Size of a texture line in bytes. */ - u32 pitch; - /*! \brief Offset of texture data. */ - u32 offset; + /*! \brief Texture format. + + This is an OR-ed combination of the following values: + - \ref GCM_TEXTURE_FORMAT_SWZ + - \ref GCM_TEXTURE_FORMAT_LIN + - \ref GCM_TEXTURE_FORMAT_NRM + - \ref GCM_TEXTURE_FORMAT_B8 + - \ref GCM_TEXTURE_FORMAT_A1R5G5B5 + - \ref GCM_TEXTURE_FORMAT_A4R4G4B4 + - \ref GCM_TEXTURE_FORMAT_R5G6B5 + - \ref GCM_TEXTURE_FORMAT_A8R8G8B8 + - \ref GCM_TEXTURE_FORMAT_DXT1 + - \ref GCM_TEXTURE_FORMAT_DXT3 + - \ref GCM_TEXTURE_FORMAT_DXT5 + - \ref GCM_TEXTURE_FORMAT_A8L8 + */ + u8 format; + + /*! \brief Indicates if this is a mip-mapped texture. + + Possible values are: + - \ref GCM_TRUE + - \ref GCM_FALSE + */ + u8 mipmap; + + /*! \brief Texture dimension. + + Possible values are: + - \ref GCM_TEXTURE_DIMS_1D + - \ref GCM_TEXTURE_DIMS_2D + - \ref GCM_TEXTURE_DIMS_3D + */ + u8 dimension; + + /*! \brief Indicates if this is a cube-mapped texture. + + Possible values are: + - \ref GCM_TRUE + - \ref GCM_FALSE + */ + u8 cubemap; + + /*! \brief Color remapping bitfield. + + Each of the texture color components (red, green, blue, alpha) can be + remapped according to a specified remapping type. The type specifies + that the component is either set to zero, all one bits, or takes value + of one of the source components. All remapping types and values are to + be OR-ed together. + + For instance, to have the alpha component set to zero, the red and blue + components swapped and the green component kept as-is, set the following + value: + + (\ref GCM_TEXTURE_REMAP_TYPE_ZERO << \ref GCM_TEXTURE_REMAP_TYPE_A_SHIFT)\n + | (\ref GCM_TEXTURE_REMAP_TYPE_REMAP << \ref GCM_TEXTURE_REMAP_TYPE_R_SHIFT)\n + | (\ref GCM_TEXTURE_REMAP_COLOR_B << \ref GCM_TEXTURE_REMAP_COLOR_R_SHIFT)\n + | (\ref GCM_TEXTURE_REMAP_TYPE_REMAP << \ref GCM_TEXTURE_REMAP_TYPE_G_SHIFT)\n + | (\ref GCM_TEXTURE_REMAP_COLOR_G << \ref GCM_TEXTURE_REMAP_COLOR_G_SHIFT)\n + | (\ref GCM_TEXTURE_REMAP_TYPE_REMAP << \ref GCM_TEXTURE_REMAP_TYPE_B_SHIFT)\n + | (\ref GCM_TEXTURE_REMAP_COLOR_R << \ref GCM_TEXTURE_REMAP_COLOR_B_SHIFT) + */ + u32 remap; + + /*! \brief Texture width in pixels. */ + u16 width; + /*! \brief Texture height in pixels. */ + u16 height; + /*! \brief Texture depth. */ + u16 depth; + /*! \brief Location of texture. + + Possible values are: + - \ref GCM_LOCATION_RSX + - \ref GCM_LOCATION_CELL + */ + u8 location; + /*! \brief unused padding byte. */ + u8 _pad; + /*! \brief Size of a texture line in bytes. */ + u32 pitch; + /*! \brief Offset of texture data. */ + u32 offset; } gcmTexture; -/*! \brief Specify scaled image blit geometry and format for rsxSetTransferScaleSurface(). */ +/*! \brief Specify scaled image blit geometry and format for rsxSetTransferImage() */ typedef struct _gcmTransferScale { - /*! \brief Conversion to perform when converting pixels to lower bit precision. - - Possible values: - - \ref GCM_TRANSFER_CONVERSION_DITHER - - \ref GCM_TRANSFER_CONVERSION_TRUNCATE - - \ref GCM_TRANSFER_CONVERSION_SUBTRACT_TRUNCATE - */ - u32 conversion; - - /*! \brief Format of source image pixels. - - Possible values: - - \ref GCM_TRANSFER_SCALE_FORMAT_A1R5G5B5 - - \ref GCM_TRANSFER_SCALE_FORMAT_X1R5G5B5 - - \ref GCM_TRANSFER_SCALE_FORMAT_A8R8G8B8 - - \ref GCM_TRANSFER_SCALE_FORMAT_X8R8G8B8 - - \ref GCM_TRANSFER_SCALE_FORMAT_CR8YB8CB8YA8 - - \ref GCM_TRANSFER_SCALE_FORMAT_YB8CR8YA8CB8 - - \ref GCM_TRANSFER_SCALE_FORMAT_R5G6B5 - - \ref GCM_TRANSFER_SCALE_FORMAT_Y8 - - \ref GCM_TRANSFER_SCALE_FORMAT_AY8 - - \ref GCM_TRANSFER_SCALE_FORMAT_EYB8ECR8EYA8ECB8 - - \ref GCM_TRANSFER_SCALE_FORMAT_ECR8EYB8ECB8EYA8 - - \ref GCM_TRANSFER_SCALE_FORMAT_A8B8G8R8 - - \ref GCM_TRANSFER_SCALE_FORMAT_X8B8G8R8 - */ - u32 format; - - /*! \brief Blit operation. - - Possible values: - - \ref GCM_TRANSFER_OPERATION_SRCCOPY_AND - - \ref GCM_TRANSFER_OPERATION_ROP_AND - - \ref GCM_TRANSFER_OPERATION_BLEND_AND - - \ref GCM_TRANSFER_OPERATION_SRCCOPY - - \ref GCM_TRANSFER_OPERATION_SRCCOPY_PREMULT - - \ref GCM_TRANSFER_OPERATION_BLEND_PREMULT - */ - u32 operation; - - /*! \brief X origin of clipping rectangle, within the destination surface. */ - s16 clipX; - - /*! \brief Y origin of clipping rectangle, within the destination surface. */ - s16 clipY; - - /*! \brief Width of clipping rectangle, within the destination surface. */ - u16 clipW; - - /*! \brief Height of clipping rectangle, within the destination surface. */ - u16 clipH; - - /*! \brief X origin of destination rectangle. */ - s16 outX; - - /*! \brief Y origin of destination rectangle. */ - s16 outY; - - /*! \brief Width of the destination rectangle. */ - u16 outW; - - /*! \brief Height of the destination rectangle. */ - u16 outH; - - /*! \brief Ratio in X direction of the source rectangle size to the destination rectangle size, encoded as a 32-bit signed fixed-point number. Such a value can be obtained from a floating point number by rsxGetFixedSint32(). */ - s32 ratioX; - - /*! \brief Ratio in Y direction of the source rectangle size to the destination rectangle size, encoded as a 32-bit signed fixed-point number. Such a value can be obtained from a floating point number by rsxGetFixedSint32(). */ - s32 ratioY; - - /*! \brief Width of the source rectangle. */ - u16 inW; - - /*! \brief Height of the source rectangle. */ - u16 inH; - - /*! \brief Pitch size, in bytes, of the source image data (width multiplied by the number of bytes in each pixel). */ - u16 pitch; - - /*! \brief How the origin of each pixel is determined. - - Possible values: - - \ref GCM_TRANSFER_ORIGIN_CENTER - - \ref GCM_TRANSFER_ORIGIN_CORNER - */ - u8 origin; - - /*! \brief Sampling for scaled blits. - - Possible values: - - \ref GCM_TRANSFER_INTERPOLATOR_NEAREST - - \ref GCM_TRANSFER_INTERPOLATOR_LINEAR - */ - u8 interp; - - /*! \brief Image data offset, e.g., a value returned by gcmAddressToOffset() - or gcmMapMainMemory(). */ - u32 offset; - - /*! \brief X origin of the blit rectangle in the source image. - Format is 16-bit fixed point, see rsxGetFixedUint16(). */ - u16 inX; - - /*! \brief Y origin of the blit rectangle in the source image. - Format is 16-bit fixed point, see rsxGetFixedUint16(). */ - u16 inY; + /*! \brief Not sure what this dones. Possible values: + - \ref GCM_TRANSFER_CONVERSION_DITHER + - \ref GCM_TRANSFER_CONVERSION_TRUNCATE + - \ref GCM_TRANSFER_CONVERSION_SUBTRACT_TRUNCATE + */ + u32 conversion; + + /*! \brief Format of image data. Possible values: + - \ref GCM_TRANSFER_SCALE_FORMAT_A1R5G5B5 + - \ref GCM_TRANSFER_SCALE_FORMAT_X1R5G5B5 + - \ref GCM_TRANSFER_SCALE_FORMAT_A8R8G8B8 + - \ref GCM_TRANSFER_SCALE_FORMAT_X8R8G8B8 + - \ref GCM_TRANSFER_SCALE_FORMAT_CR8YB8CB8YA8 + - \ref GCM_TRANSFER_SCALE_FORMAT_YB8CR8YA8CB8 + - \ref GCM_TRANSFER_SCALE_FORMAT_R5G6B5 + - \ref GCM_TRANSFER_SCALE_FORMAT_Y8 + - \ref GCM_TRANSFER_SCALE_FORMAT_AY8 + - \ref GCM_TRANSFER_SCALE_FORMAT_EYB8ECR8EYA8ECB8 + - \ref GCM_TRANSFER_SCALE_FORMAT_ECR8EYB8ECB8EYA8 + - \ref GCM_TRANSFER_SCALE_FORMAT_A8B8G8R8 + - \ref GCM_TRANSFER_SCALE_FORMAT_X8B8G8R8 + */ + u32 format; + + /*! \brief Blit operation. Possible values: + - \ref GCM_TRANSFER_OPERATION_SRCCOPY_AND + - \ref GCM_TRANSFER_OPERATION_ROP_AND + - \ref GCM_TRANSFER_OPERATION_BLEND_AND + - \ref GCM_TRANSFER_OPERATION_SRCCOPY + - \ref GCM_TRANSFER_OPERATION_SRCCOPY_PREMULT + - \ref GCM_TRANSFER_OPERATION_BLEND_PREMULT + */ + u32 operation; + + /*! \brief X origin of clipping rectangle, within the destination surface. */ + s16 clipX; + + /*! \brief Y origin of clipping rectangle, within the destination surface. */ + s16 clipY; + + /*! \brief Width of clipping rectangle, within the destination surface. */ + u16 clipW; + + /*! \brief Height of clipping rectangle, within the destination surface. */ + u16 clipH; + + /*! \brief X origin of destination rectangle. */ + s16 outX; + + /*! \brief Y origin of destination rectangle. */ + s16 outY; + + /*! \brief Width of the destination rectangle. */ + u16 outW; + + /*! \brief Height of the destination rectangle. */ + u16 outH; + + /*! \brief Ratio in X direction of the source rectangle size to the destination rectangle size, encoded as a 32-bit signed fixed-point number. Such a value can be obtained from a floating point number by rsxGetFixedSint32(). */ + s32 ratioX; + + /*! \brief Ratio in Y direction of the source rectangle size to the destination rectangle size, encoded as a 32-bit signed fixed-point number. Such a value can be obtained from a floating point number by rsxGetFixedSint32(). */ + s32 ratioY; + + /*! \brief Width of the source rectangle. */ + u16 inW; + + /*! \brief Height of the source rectangle. */ + u16 inH; + + /*! \brief Pitch size, in bytes, of the source image data (width multiplied by the number of bytes in each pixel). */ + u16 pitch; + + /*! \brief How the origin of each pixel is determined. Possible values: + - \ref GCM_TRANSFER_ORIGIN_CENTER + - \ref GCM_TRANSFER_ORIGIN_CORNER + */ + u8 origin; + + /*! \brief Sampling for scaled blits. Possible values: + - \ref GCM_TRANSFER_INTERPOLATOR_NEAREST: no interpolation + - \ref GCM_TRANSFER_INTERPOLATOR_LINEAR: bilinear interpolation + */ + u8 interp; + + /*! \brief Image data offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). */ + u32 offset; + + /*! \brief X origin of destination rectangle. */ + u16 inX; + + /*! \brief Y origin of destination rectangle. */ + u16 inY; } gcmTransferScale; -/*! \brief Specify destination surface characteristics for rsxSetTransferScaleSurface(). */ +/*! \brief Specify destination surface characteristics for rsxSetTransferImage(). */ typedef struct _gcmTransferSurface { - /*! \brief Format of destination surface. Possible values are: - - \ref GCM_TRANSFER_SURFACE_FORMAT_R5G6B5 - - \ref GCM_TRANSFER_SURFACE_FORMAT_A8R8G8B8 - - \ref GCM_TRANSFER_SURFACE_FORMAT_Y32 - */ - u32 format; + /*! \brief Format of destination surface. Possible values are: + - \ref GCM_TRANSFER_SURFACE_FORMAT_R5G6B5 + - \ref GCM_TRANSFER_SURFACE_FORMAT_A8R8G8B8 + - \ref GCM_TRANSFER_SURFACE_FORMAT_Y32 + */ + u32 format; - /*! \brief Pitch for destination surface (width multipied by the number of bytes per pixel). */ - u16 pitch; + /*! \brief Pitch for destination surface (width multipied by the number of bytes per pixel). */ + u16 pitch; - /*! \brief unused padding bytes. most be 0. */ - u8 _pad0[2]; + /*! \brief unused padding bytes. most be 0. */ + u8 _pad0[2]; - /*! \brief Destination suface memory offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). */ - u32 offset; + /*! \brief Destination suface memory offset, e.g., a value returned by gcmAddressToOffset() or gcmMapMainMemory(). */ + u32 offset; } gcmTransferSurface; typedef struct _gcmTransferSwizzle { - u16 format; - u8 width; - u8 height; - u32 offset; + u16 format; + u8 width; + u8 height; + u32 offset; } gcmTransferSwizzle; typedef struct _gcmTileInfo @@ -812,12 +1112,6 @@ typedef struct _gcmOffsetTable u16 *ea; /* io -> ea */ } gcmOffsetTable; -typedef struct _gcmCast -{ - u32 u; - float f; -} gcmCast; - typedef struct _reportData { u64 timer; @@ -831,118 +1125,129 @@ typedef struct _notifyData u64 zero; } gcmNotifyData; -/* END STRUCTS */ - -/* - * system functions - */ - /*! \brief Initialize the RSX context. - \param ctx Pointer to where the effective address of the allocated context structure will be stored. - \param cmdSize The command buffer size. - \param ioSize The allocated IO buffer size. - \param ioAddress Pointer to an allocated buffer of \p ioSize bytes. - \return zero if no error occured, nonzero otherwise. + +\param ctx Pointer to where the effective address of the allocated context + structure will be stored. +\param cmdSize The command buffer size. +\param ioSize The allocated IO buffer size. +\param ioAddress Pointer to an allocated buffer of \p ioSize bytes. +\return zero if no error occured, nonzero otherwise. */ -s32 gcmInitBody(gcmContextData* ATTRIBUTE_PRXPTR *ctx,const u32 cmdSize,const u32 ioSize,const void *ioAddress); +s32 gcmInitBody(gcmContextData **ctx,const u32 cmdSize,const u32 ioSize,const void *ioAddress); /*! \brief Converts an effective address in RSX memory to an offset. - \param address The effective address to be converted. - \param offset A pointer to the returned offset value. - \return zero if no error occured, nonzero otherwise. +\param address The effective address to be converted. +\param offset A pointer to the returned offset value. +\return zero if no error occured, nonzero otherwise. */ -s32 gcmAddressToOffset(void *address,u32 *offset); +s32 gcmAddressToOffset(const void *address,u32 *offset); -/*! \brief Maps a memory block in main memory for RSX to access it. - \param address Pointer to the block to be mapped. - \param size Size of the block in bytes. - \param offset A pointer to the returned mapped offset value. - \return zero if no error occured, nonzero otherwise. +/*! \brief Converts an offset to an effective address in RSX memory. +\param offset The offset to be converted. +\param address A pointer to the returned effective address. +\return zero if no error occured, nonzero otherwise. */ -s32 gcmMapMainMemory(const void *address,const u32 size,u32 *offset); +s32 gcmIoOffsetToAddress(u32 offset,void **address); /*! \brief Retrieves the RSX configuration. - \param config A pointer to the configuration structure to be updated. - \return zero if no error occured, nonzero otherwise. +\param config A pointer to the configuration structure to be updated. +\return zero if no error occured, nonzero otherwise. */ s32 gcmGetConfiguration(gcmConfiguration *config); -/*! \brief Converts an offset to an effective address in RSX memory. - \param offset The offset to be converted. - \param address A pointer to the returned effective address. - \return zero if no error occured, nonzero otherwise. -*/ -s32 gcmIoOffsetToAddress(u32 offset,u32 *address); +/*! \brief Gets the flip status. +Once a flip occurred, querying for a subsequent flip requires the flip status +to be reset using \ref gcmResetFlipStatus. +\return zero if no flip occured, nonzero otherwise. +*/ +u32 gcmGetFlipStatus(); -/* - * display functions - */ +/*! \brief Enqueues a flip command into the command buffer. +\param context Pointer to the context object. +\param bufferId Framebuffer id to flip to (as configured with + \ref gcmSetDisplayBuffer). +\return zero if no error occured, nonzero otherwise. +*/ +s32 gcmSetFlip(gcmContextData *context,const u8 bufferId); /*! \brief Configures a display framebuffer. - \param bufferId The buffer id (0-7). - \param offset The offset of the allocated memory block (see \ref rsxAddressToOffset). - \param pitch The size of a buffer line in bytes. - \param width The buffer width in pixels. - \param height The buffer height in pixels. - \return zero if no error occured, nonzero otherwise. +\param bufferId The buffer id (0-7). +\param offset The offset of the allocated memory block (see \ref rsxAddressToOffset). +\param pitch The size of a buffer line in bytes. +\param width The buffer width in pixels. +\param height The buffer height in pixels. +\return zero if no error occured, nonzero otherwise. */ -s32 gcmSetDisplayBuffer(u32 bufferId,u32 offset,u32 pitch,u32 width,u32 height); +s32 gcmSetDisplayBuffer(const u8 bufferId,const u32 offset,const u32 pitch,const u32 width,const u32 height); -/*! \brief Set flip mode. - \param mode The specified flip mode. Possible vales are: - - \ref GCM_FLIP_HSYNC - - \ref GCM_FLIP_VSYNC - - \ref GCM_FLIP_HSYNC_AND_BREAK_EVERYTHING +/*! \brief Maps a memory block in main memory for RSX to access it. +\param address Pointer to the block to be mapped. +\param size Size of the block in bytes. +\param offset A pointer to the returned mapped offset value. +\return zero if no error occured, nonzero otherwise. +*/ +s32 gcmMapMainMemory(const void *address,const u32 size,u32 *offset); + +/*! \brief Get address of specified label. +\param index The label index whose address is to be obtained. +\return Pointer to the label address. */ -void gcmSetFlipMode(s32 mode); +u32* gcmGetLabelAddress(const u8 index); /*! \brief Reset the flip status. */ void gcmResetFlipStatus(); -/*! \brief Gets the flip status. - Once a flip occurred, querying for a subsequent flip requires the flip status to be reset using \ref gcmResetFlipStatus. - \return zero if a flip occured, nonzero otherwise. -*/ -s32 gcmGetFlipStatus(); - -/*! \brief Enqueues a flip command into the command buffer. - \param context Pointer to the context object. - \param bufferId Framebuffer id to flip to (as configured with - \ref gcmSetDisplayBuffer). - \return zero if no error occured, nonzero otherwise. +/*! \brief Set flip mode. +\param mode The specified flip mode. Possible vales are: + - \ref GCM_FLIP_HSYNC + - \ref GCM_FLIP_VSYNC + - \ref GCM_FLIP_HSYNC_AND_BREAK_EVERYTHING */ -s32 gcmSetFlip(gcmContextData *context,u32 bufferId); +void gcmSetFlipMode(const u32 mode); /*! \brief Wait for a flip to be completed. - \param context Pointer to the context object. +\param context Pointer to the context object. */ void gcmSetWaitFlip(gcmContextData *context); +/*! \brief Perform preprocessing for display output +\param context Pointer to context object +\param id buffer id +*/ +u32 gcmSetPrepareFlip(gcmContextData *context,const u8 id); + +/*! \brief Register a callback function to be called upon VBlank. -/* - * render functions - */ +This function registers a callback function to be called upon VBlank and after VSync. The registered callback can be canceled if NULL is specified for handler. +\param handler Callback function to register +*/ +void gcmSetVBlankHandler(void (*handler)(const u32 head)); -/*! \brief Get address of specified label. - \param index The label index whose address is to be obtained. - \return Pointer to the label address. +/*! \brief Register a callback function for when a flip is executed. + +This function registers a callback function to be called when RSX actually carries out a flip. The registered callback can be canceled if NULL is specified for handler. +\param handler Callback function to register */ -u32* gcmGetLabelAddress(const u8 index); +void gcmSetFlipHandler(void (*handler)(const u32 head)); +/*! \brief Register a graphics callback function. -/* previously missing / new additions */ +This function registers a callback function to be executed when an exception occurs in the graphics pipeline. The registered callback can be canceled if NULL is specified for handler. +\param handler Callback function to register +*/ +void gcmSetGraphicsHandler(void (*handler)(const u32 val)); +void gcmSetDefaultCommandBuffer(); -/* - * system functions - */ -s32 gcmInitSystemMode(const u64 mode); +/*! \brief Obtain registers for controlling the command buffer. +\return Pointer to a \ref gcmControlRegister structure +*/ gcmControlRegister* gcmGetControlRegister(); + +s32 gcmInitSystemMode(const u64 mode); u32 gcmGetTiledPitchSize(const u32 size); -void gcmSetVBlankHandler(void (*handler)(const u32 head)); void gcmSetSecondVHandler(void (*handler)(const u32 head)); -void gcmSetGraphicsHandler(void (*handler)(const u32 val)); -void gcmSetFlipHandler(void (*handler)(const u32 head)); void gcmSetQueueHandler(void (*handler)(const u32 head)); void gcmSetUserHandler(void (*handler)(const u32 cause)); void gcmSetDebugOutputLevel(s32 level); @@ -982,15 +1287,9 @@ s32 gcmSetCursorImageOffset(const u32 offset); s32 gcmSetCursorPosition(const s32 x, const s32 y); s32 gcmUpdateCursor(); - /* * flow control functions */ -/* TODO: uint32_t *cellGcmGetCurrentBuffer(void); */ -void cellGcmSetCurrentBuffer(const uint32_t *addr, const size_t size); -void gcmSetDefaultCommandBuffer(); -/* TODO: void cellGcmSetUserCallback(CellGcmContextCallback callback); */ -/* TODO: void cellGcmSetupContextData(CellGcmContextData *context, const uint32_t *addr, const uint32_t size, CellGcmContextCallback callback); */ s32 gcmSetDefaultCommandBufferAndSegmentWordSize(const u32 bufferSize, const u32 segmentSize); u32 gcmGetDefaultCommandWordSize(); u32 gcmGetDefaultSegmentWordSize(); @@ -1020,13 +1319,6 @@ gcmReportData* gcmGetReportDataAddressLocation(const u32 index, const u32 locati u64 gcmGetTimeStampLocation(const u32 index, const u32 location); u32 gcmGetReportDataLocation(const u32 index, const u32 location); - -/* - * data xfer / format conversion functions - */ -/* TODO: void cellGcmConvertSwizzleFormat(void *swizzledTexture, const void *texture, const uint32_t dstx0, const uint32_t dsty0, const uint32_t dstz0, const uint32_t dstWidth, const uint32_t dstHeight, const uint32_t dstDepth, const uint32_t srcx0, const uint32_t srcy0, const uint32_t srcz0, const uint32_t srcx1, const uint32_t srcy1, const uint32_t srcz1, const uint32_t srcWidth, const uint32_t srcHeight, const uint32_t srcDepth, const uint32_t dstLog2cdepth, const uint32_t srcColordepth, const uint32_t border, const uint32_t dim, void (*copyTexel)(void *dst, const void *src)); */ - - /* * notify functions */ @@ -1045,28 +1337,10 @@ const gcmDisplayInfo* gcmGetDisplayInfo(); */ s32 gcmDumpGraphicsError(); - -/* TODO: CELL_GCM_DECL CELL_GCM_FUNC_TYPE CELL_GCM_FUNC(SetUserCommand)(CELL_GCM_ARGS(const u32 cause)); */ -/* inline __attribute__((always_inline)) void gcmSetUserCommand(void (*handler)(const u32 cause)); */ void gcmSetUserCommand(void (*handler)(const u32 cause)); -/* TODO: CELL_GCM_GLOBAL_DECL CELL_GCM_FUNC_TYPE_UINT32 CELL_GCM_FUNC(SetPrepareFlip)(CELL_GCM_ARGS(const u8 id)) */ -/* inline __attribute__((always_inline)) u32 gcmSetPrepareFlip(const u8 id); */ -u32 gcmSetPrepareFlip(const u8 id); - -/* TODO: gcmTerminate() no prototype */ -/* TODO: gcmFunc1 no prototype */ -/* TODO: gcmFunc2 no prototype */ -/* TODO: gcmFunc3 no prototype */ -/* TODO: gcmFunc4 no prototype */ -/* TODO: gcmFunc12 no prototype */ -/* TODO: gcmFunc13 no prototype */ -/* TODO: gcmFunc15 no prototype */ -/* TODO: gcmFunc38 no prototype */ - #ifdef __cplusplus - } + } #endif #endif - diff --git a/ppu/include/rsx/nv40.h b/ppu/include/rsx/nv40.h index 9bbd6e18..34bb2a2f 100644 --- a/ppu/include/rsx/nv40.h +++ b/ppu/include/rsx/nv40.h @@ -4326,16 +4326,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define NV40_SWIZZLED_SURFACE 0x0000309e -#define NV3062TCL 0x00003062 - -#define NV3062TCL_SET_CONTEXT_DMA_IMAGE_DEST 0x00006188 -#define NV3062TCL_SET_COLOR_FORMAT 0x00006300 -#define NV3062TCL_SET_OFFSET_DEST 0x0000630c - -#define NC308ATCL 0x0000308a - -#define NV308ATCL_POINT 0x0000a304 -#define NV308ATCL_COLOR 0x0000a400 #define NV406ETCL 0x0000406e @@ -4351,6 +4341,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define NV40TCL_REF_CNT 0x00000050 #define NV40TCL_NOP 0x00000100 #define NV40TCL_NOTIFY 0x00000104 +#define NV40TCL_WAIT_FOR_IDLE 0x00000110 +#define NV40TCL_PM_TRIGGER 0x00000140 #define NV40TCL_DMA_NOTIFY 0x00000180 #define NV40TCL_DMA_TEXTURE0 0x00000184 #define NV40TCL_DMA_TEXTURE1 0x00000188 @@ -4408,6 +4400,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define NV40TCL_RT_ENABLE_COLOR1 (1 << 1) #define NV40TCL_RT_ENABLE_COLOR0 (1 << 0) #define NV40TCL_ZETA_PITCH 0x0000022c +#define NV40TCL_CYLINDRICAL_WRAP 0x00000238 +#define NV40TCL_CYLINDRICAL_WRAP1 0x0000023c #define NV40TCL_COLOR2_PITCH 0x00000280 #define NV40TCL_COLOR3_PITCH 0x00000284 #define NV40TCL_COLOR2_OFFSET 0x00000288 @@ -4611,6 +4605,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define NV40TCL_SHADE_MODEL 0x00000368 #define NV40TCL_SHADE_MODEL_FLAT 0x00001d00 #define NV40TCL_SHADE_MODEL_SMOOTH 0x00001d01 +#define NV40TCL_BLEND_ENABLE_MRT 0x0000036c #define NV40TCL_MRT_COLOR_MASK 0x00000370 #define NV40TCL_MRT_COLOR_MASK_BUFFER1_A (1 << 4) #define NV40TCL_MRT_COLOR_MASK_BUFFER1_R (1 << 5) @@ -4648,7 +4643,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define NV40TCL_DEPTH_RANGE_FAR 0x00000398 #define NV40TCL_LINE_WIDTH 0x000003b8 #define NV40TCL_LINE_SMOOTH_ENABLE 0x000003bc -#define NV40TCL_UNK03C0(x) (0x000003c0+((x)*4)) +#define NV40TCL_TEX_ANISO_SPREAD(x) (0x000003c0+((x)*4)) #define NV40TCL_UNK03C0__SIZE 0x00000010 #define NV40TCL_UNK0400(x) (0x00000400+((x)*4)) #define NV40TCL_UNK0400__SIZE 0x00000010 @@ -4673,6 +4668,14 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define NV40TCL_FP_ADDRESS_OFFSET_MASK 0xffffff00 #define NV40TCL_FP_ADDRESS_DMA1 (1 << 1) #define NV40TCL_FP_ADDRESS_DMA0 (1 << 0) +#define NV40TCL_VP_TEXTURE_OFFSET(x) (0x00000900+((x)*32)) +#define NV40TCL_VP_TEXTURE_FORMAT(x) (0x00000904+((x)*32)) +#define NV40TCL_VP_TEXTURE_ADDRESS(x) (0x00000908+((x)*32)) +#define NV40TCL_VP_TEXTURE_CONTROL0(x) (0x0000090c+((x)*32)) +#define NV40TCL_VP_TEXTURE_CONTROL3(x) (0x00000910+((x)*32)) +#define NV40TCL_VP_TEXTURE_FILTER(x) (0x00000914+((x)*32)) +#define NV40TCL_VP_TEXTURE_IMAGE_RECT(x) (0x00000918+((x)*32)) +#define NV40TCL_VP_TEXTURE_BORDER_COLOR(x) (0x0000091c+((x)*32)) #define NV40TCL_VIEWPORT_HORIZ 0x00000a00 #define NV40TCL_VIEWPORT_HORIZ_W_SHIFT 16 #define NV40TCL_VIEWPORT_HORIZ_W_MASK 0xffff0000 @@ -4692,9 +4695,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define NV40TCL_VIEWPORT_SCALE_Y 0x00000a34 #define NV40TCL_VIEWPORT_SCALE_Z 0x00000a38 #define NV40TCL_VIEWPORT_SCALE_W 0x00000a3c -#define NV40TCL_POLYGON_OFFSET_FILL_ENABLE 0x00000a60 +#define NV40TCL_POLYGON_OFFSET_POINT_ENABLE 0x00000a60 #define NV40TCL_POLYGON_OFFSET_LINE_ENABLE 0x00000a64 -#define NV40TCL_POLYGON_OFFSET_POINT_ENABLE 0x00000a68 +#define NV40TCL_POLYGON_OFFSET_FILL_ENABLE 0x00000a68 #define NV40TCL_DEPTH_FUNC 0x00000a6c #define NV40TCL_DEPTH_FUNC_NEVER 0x00000200 #define NV40TCL_DEPTH_FUNC_LESS 0x00000201 @@ -4709,10 +4712,25 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define NV40TCL_DEPTH_TEST_ENABLE 0x00000a74 #define NV40TCL_POLYGON_OFFSET_FACTOR 0x00000a78 #define NV40TCL_POLYGON_OFFSET_UNITS 0x00000a7c +#define NV40TCL_VTX_ATTR_4I_SCALED_0(x) (0x00000a80+((x)*8)) +#define NV40TCL_VTX_ATTR_4I_SCALED_0__SIZE 0x00000010 +#define NV40TCL_VTX_ATTR_4I_SCALED_0_Y_SHIFT 16 +#define NV40TCL_VTX_ATTR_4I_SCALED_0_Y_MASK 0xffff0000 +#define NV40TCL_VTX_ATTR_4I_SCALED_0_X_SHIFT 0 +#define NV40TCL_VTX_ATTR_4I_SCALED_0_X_MASK 0x0000ffff +#define NV40TCL_VTX_ATTR_4I_SCALED_1(x) (0x00000a84+((x)*8)) +#define NV40TCL_VTX_ATTR_4I_SCALED_1__SIZE 0x00000010 +#define NV40TCL_VTX_ATTR_4I_SCALED_1_W_SHIFT 16 +#define NV40TCL_VTX_ATTR_4I_SCALED_1_W_MASK 0xffff0000 +#define NV40TCL_VTX_ATTR_4I_SCALED_1_Z_SHIFT 0 +#define NV40TCL_VTX_ATTR_4I_SCALED_1_Z_MASK 0x0000ffff +#define NV40TCL_TEX_CONTROL2(x) (0x00000b00+((x)*4)) #define NV40TCL_TEX_COORD_CONTROL(x) (0x00000b40+((x)*4)) #define NV40TCL_TEX_COORD_CONTROL__SIZE 0x00000008 #define NV40TCL_VP_UPLOAD_INST(x) (0x00000b80+((x)*16)) #define NV40TCL_VP_UPLOAD_INST__SIZE 0x00000004 +#define NV40TCL_TWO_SIDE_LIGHT_EN 0x0000142c +#define NV40TCL_CLEAR_ZCULL_SURFACE 0x00001438 #define NV40TCL_CLIP_PLANE_ENABLE 0x00001478 #define NV40TCL_CLIP_PLANE_ENABLE_PLANE0 (1 << 2) #define NV40TCL_CLIP_PLANE_ENABLE_PLANE1 (1 << 6) @@ -4734,6 +4752,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define NV40TCL_VTXBUF_ADDRESS_DMA1 (1 << 31) #define NV40TCL_VTXBUF_ADDRESS_OFFSET_SHIFT 0 #define NV40TCL_VTXBUF_ADDRESS_OFFSET_MASK 0x0fffffff +#define NV40TCL_VTX_CACHE_INVALIDATE2 0x00001710 #define NV40TCL_VTX_CACHE_INVALIDATE 0x00001714 #define NV40TCL_VTXFMT(x) (0x00001740+((x)*4)) #define NV40TCL_VTXFMT__SIZE 0x00000010 @@ -4746,12 +4765,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define NV40TCL_VTXFMT_STRIDE_SHIFT 8 #define NV40TCL_VTXFMT_STRIDE_MASK 0x0000ff00 #define NV40TCL_QUERY_RESET 0x000017c8 -#define NV40TCL_QUERY_UNK17CC 0x000017cc +#define NV40TCL_QUERY_ENABLE 0x000017cc #define NV40TCL_QUERY_GET 0x00001800 #define NV40TCL_QUERY_GET_UNK24_SHIFT 24 #define NV40TCL_QUERY_GET_UNK24_MASK 0xff000000 #define NV40TCL_QUERY_GET_OFFSET_SHIFT 0 #define NV40TCL_QUERY_GET_OFFSET_MASK 0x00ffffff +#define NV40TCL_ZCULL_STATS_ENABLE 0x00001804 #define NV40TCL_BEGIN_END 0x00001808 #define NV40TCL_BEGIN_END_STOP 0x00000000 #define NV40TCL_BEGIN_END_POINTS 0x00000001 @@ -4812,18 +4832,29 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define NV40TCL_VTX_ATTR_2I_Y_MASK 0xffff0000 #define NV40TCL_VTX_ATTR_2I_X_SHIFT 0 #define NV40TCL_VTX_ATTR_2I_X_MASK 0x0000ffff -#define NV40TCL_VTX_ATTR_4I_0(x) (0x00001900+((x)*8)) +#define NV40TCL_VTX_ATTR_4UB(x) (0x00001940+((x)*4)) +#define NV40TCL_VTX_ATTR_4UB__SIZE 0x00000010 +#define NV40TCL_VTX_ATTR_4UB_Y_SHIFT 8 +#define NV40TCL_VTX_ATTR_4UB_Y_MASK 0x0000ff00 +#define NV40TCL_VTX_ATTR_4UB_X_SHIFT 0 +#define NV40TCL_VTX_ATTR_4UB_X_MASK 0x000000ff +#define NV40TCL_VTX_ATTR_4UB_W_SHIFT 24 +#define NV40TCL_VTX_ATTR_4UB_W_MASK 0xff000000 +#define NV40TCL_VTX_ATTR_4UB_Z_SHIFT 16 +#define NV40TCL_VTX_ATTR_4UB_Z_MASK 0x00ff0000 +#define NV40TCL_VTX_ATTR_4I_0(x) (0x00001980+((x)*8)) #define NV40TCL_VTX_ATTR_4I_0__SIZE 0x00000010 #define NV40TCL_VTX_ATTR_4I_0_Y_SHIFT 16 #define NV40TCL_VTX_ATTR_4I_0_Y_MASK 0xffff0000 #define NV40TCL_VTX_ATTR_4I_0_X_SHIFT 0 #define NV40TCL_VTX_ATTR_4I_0_X_MASK 0x0000ffff -#define NV40TCL_VTX_ATTR_4I_1(x) (0x00001904+((x)*8)) +#define NV40TCL_VTX_ATTR_4I_1(x) (0x00001984+((x)*8)) #define NV40TCL_VTX_ATTR_4I_1__SIZE 0x00000010 #define NV40TCL_VTX_ATTR_4I_1_W_SHIFT 16 #define NV40TCL_VTX_ATTR_4I_1_W_MASK 0xffff0000 #define NV40TCL_VTX_ATTR_4I_1_Z_SHIFT 0 #define NV40TCL_VTX_ATTR_4I_1_Z_MASK 0x0000ffff + #define NV40TCL_TEX_OFFSET(x) (0x00001a00+((x)*32)) #define NV40TCL_TEX_OFFSET__SIZE 0x00000010 #define NV40TCL_TEX_FORMAT(x) (0x00001a04+((x)*32)) @@ -4995,7 +5026,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define NV40TCL_FP_CONTROL_KIL (1 << 7) #define NV40TCL_SEMAPHORE_OFFSET 0x00001d6c #define NV40TCL_SEMAPHORE_BACKENDWRITE_RELEASE 0x00001d70 +#define NV40TCL_SEMAPHORE_TEXTUREREAD_RELEASE 0x00001d74 #define NV40TCL_DEPTH_CONTROL 0x00001d78 +#define NV40TCL_ANTI_ALIASING_CONTROL 0x00001d7c +#define NV40TCL_ZCULL_ENABLE 0x00001d84 #define NV40TCL_SHADER_WINDOW 0x00001d88 #define NV40TCL_CLEAR_VALUE_DEPTH 0x00001d8c #define NV40TCL_CLEAR_VALUE_COLOR 0x00001d90 @@ -5012,10 +5046,17 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define NV40TCL_LINE_STIPPLE_PATTERN_FACTOR_MASK 0x0000ffff #define NV40TCL_LINE_STIPPLE_PATTERN_PATTERN_SHIFT 16 #define NV40TCL_LINE_STIPPLE_PATTERN_PATTERN_MASK 0xffff0000 +#define NV40TCL_VTX_ATTR_1F_X(x) (0x00001c00+((x)*4)) +#define NV40TCL_VTX_ATTR_1F_X__SIZE 0x00000004 +#define NV40TCL_RENDER_ENABLE 0x00001e98 #define NV40TCL_VP_UPLOAD_FROM_ID 0x00001e9c #define NV40TCL_VP_START_FROM_ID 0x00001ea0 -#define NV40TCL_POINT_SIZE 0x00001ee0 -#define NV40TCL_POINT_SPRITE 0x00001ee8 +#define NV40TCL_ZCULL_CONTROL0 0x00001ea4 +#define NV40TCL_ZCULL_CONTROL1 0x00001ea8 +#define NV40TCL_SCULL_CONTROL 0x00001eac +#define NV40TCL_POINT_SIZE 0x00001ee0 +#define NV40TCL_POINT_PARAMS_ENABLE 0x00001ee4 +#define NV40TCL_POINT_SPRITE_CONTROL 0x00001ee8 #define NV40TCL_TRANSFORM_TIMEOUT 0x00001ef8 #define NV40TCL_VP_UPLOAD_CONST_ID 0x00001efc #define NV40TCL_VP_UPLOAD_CONST_X(x) (0x00001f00+((x)*16)) @@ -5027,6 +5068,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define NV40TCL_VP_UPLOAD_CONST_W(x) (0x00001f0c+((x)*16)) #define NV40TCL_VP_UPLOAD_CONST_W__SIZE 0x00000004 #define NV40TCL_TEX_CACHE_CTL 0x00001fd8 +#define NV40TCL_DST_COL_REDUCE 0x00001fe0 #define NV40TCL_VP_ATTRIB_EN 0x00001ff0 #define NV40TCL_VP_RESULT_EN 0x00001ff4 #define NV40TCL_VP_TRANSFORM_BRANCH_BITS 0x00001ff8 diff --git a/ppu/include/rsx/rsx.h b/ppu/include/rsx/rsx.h index 45713124..6e1fb70a 100644 --- a/ppu/include/rsx/rsx.h +++ b/ppu/include/rsx/rsx.h @@ -94,27 +94,20 @@ size is a multiple of 1 megabyte. This function initializes a heap structure in RSX memory space, allowing dynamic memory allocation using \ref rsxMalloc, \ref rsxMemalign and \ref rsxFree. +\param context Pointer to receive the address of the default command buffer \param cmdSize The command buffer size. \param ioSize The allocated IO buffer size. \param ioAddress Pointer to an allocated buffer of \p ioSize bytes. -\return Pointer to the allocated context structure. -*/ -gcmContextData* rsxInit(const u32 cmdSize,const u32 ioSize,const void *ioAddress); - -/*! \brief Flushes the RSX command buffer. - -This ensures all remaining commands in the command buffer are executed, and -that the buffer is empty when that function returns. -\param context Pointer to the context object. +\return zero if no error occured, nonzero otherwise. */ -void rsxFlushBuffer(gcmContextData *context); +s32 rsxInit(gcmContextData **context,const u32 cmdSize,const u32 ioSize,const void *ioAddress); -/*! \brief Reset the RSX command buffer. -\param context Pointer to the context object. -*/ -void rsxResetCommandBuffer(gcmContextData *context); -void rsxFinish(gcmContextData *context,u32 ref_value); void rsxSetupContextData(gcmContextData *context,const u32 *addr,const u32 size,gcmContextCallback cb); +void rsxSetCurrentBuffer(gcmContextData **context,const u32 *addr,const u32 size); +void rsxSetDefaultCommandBuffer(gcmContextData **context); +void rsxSetUserCallback(gcmContextCallback cb); + +u32* rsxGetCurrentBuffer(); /*! \brief Converts a pointer value in RSX memory to an offset. \param ptr The pointer whose value is to be converted. diff --git a/ppu/include/rsx/rsx_function_macros.h b/ppu/include/rsx/rsx_function_macros.h new file mode 100644 index 00000000..bffc3a14 --- /dev/null +++ b/ppu/include/rsx/rsx_function_macros.h @@ -0,0 +1,50 @@ +#ifdef RSX_FUNCTION_MACROS + +#if RSX_UNSAFE + #define RSX_FUNC(func) rsx##func##Unsafe +#else + #define RSX_FUNC(func) rsx##func +#endif + +#if RSX_INTERNAL + +#if RSX_UNSAFE + #define RSX_FUNC_INTERNAL(func) __rsx##func##Unsafe + #define RSX_CONTEXT_CURRENT_BEGIN(count) do {} while(0) +#else + s32 __attribute__((noinline)) rsxContextCallback(gcmContextData *context,u32 count) + { + register s32 result asm("r3"); + __asm__ __volatile__ ( + "stdu 1,-128(1)\n" + "mr 31,2\n" + "lwz 0,0(%3)\n" + "lwz 2,4(%3)\n" + "mtctr 0\n" + "bctrl\n" + "mr 2,31\n" + "addi 1,1,128\n" + : "+r"(result) + : "r"(context), "r"(count), "b"(context->callback) + : "r31", "r0", "lr" + ); + return result; + } + + #define RSX_FUNC_INTERNAL(func) __rsx##func + #define RSX_CONTEXT_CURRENT_BEGIN(count) do { \ + if((context->current + (count)) > context->end) { \ + if(rsxContextCallback(context,(count))!=0) return; \ + } \ + } while(0) +#endif + +#endif + +#endif + +#ifndef RSX_FUNCTION_MACROS +#undef RSX_CONTEXT_CURRENT_BEGIN +#undef RSX_FUNC +#undef RSX_FUNC_INTERNAL +#endif diff --git a/ppu/include/rsx/rsx_program.h b/ppu/include/rsx/rsx_program.h index a66d0976..460bb541 100644 --- a/ppu/include/rsx/rsx_program.h +++ b/ppu/include/rsx/rsx_program.h @@ -8,15 +8,19 @@ #include #define PARAM_FLOAT 0 -#define PARAM_FLOAT2 1 -#define PARAM_FLOAT3 2 -#define PARAM_FLOAT4 3 -#define PARAM_FLOAT4x4 4 -#define PARAM_SAMPLER1D 5 -#define PARAM_SAMPLER2D 6 -#define PARAM_SAMPLER3D 7 -#define PARAM_SAMPLERCUBE 8 -#define PARAM_SAMPLERRECT 9 +#define PARAM_FLOAT1 1 +#define PARAM_FLOAT2 2 +#define PARAM_FLOAT3 3 +#define PARAM_FLOAT4 4 +#define PARAM_FLOAT3x4 5 +#define PARAM_FLOAT4x4 6 +#define PARAM_FLOAT3x3 7 +#define PARAM_FLOAT4x3 8 +#define PARAM_SAMPLER1D 9 +#define PARAM_SAMPLER2D 10 +#define PARAM_SAMPLER3D 11 +#define PARAM_SAMPLERCUBE 12 +#define PARAM_SAMPLERRECT 13 #define PARAM_UNKNOWN 0xff #ifdef __cplusplus @@ -29,19 +33,22 @@ This data structure is filled by cgcomp, the offline compiler for shader program typedef struct rsx_vp { u16 magic; /*!< \brief magic identifier */ - u16 num_attrib; /*!< \brief number of used input attributes in the vertex program */ - u32 attrib_off; /*!< \brief offset to the attribute name table */ + u16 _pad0; /*!< \brief unused padding word. most be 0 */ + + u16 num_regs; /*!< \brief number of used registers in the vertex program */ + u16 num_attr; /*!< \brief number of used input attributes in the vertex program */ + u16 num_const; /*!< \brief number of used constants in the vertex program */ + u16 num_insn; /*!< \brief number of vertex shader instructions */ + + u32 attr_off; /*!< \brief offset to the attribute name table */ + u32 const_off; /*!< \brief offset to the constant name table */ + u32 ucode_off; /*!< \brief offset to the shader's micro code */ u32 input_mask; /*!< \brief mask of input attributes in the vertex shader */ u32 output_mask; /*!< \brief mask of result attributes passed to the fragment shader */ u16 const_start; /*!< \brief start address in vertex shader's constant block memory */ - u16 num_const; /*!< \brief number of used constants in the vertex program */ - u32 const_off; /*!< \brief offset to the constant name table */ - - u16 start_insn; /*!< \brief start address to load the vertex program to */ - u16 num_insn; /*!< \brief number of vertex shader instructions */ - u32 ucode_off; /*!< \brief offset to the shader's micro code */ + u16 insn_start; /*!< \brief start address to load the vertex program to */ } rsxVertexProgram; /*! \brief Fragment program data structure. @@ -50,23 +57,23 @@ This data structure is filled by cgcomp, the offline compiler for shader program typedef struct rsx_fp { u16 magic; /*!< \brief magic identifier */ - u16 num_attrib; /*!< \brief number of used input attributes in the fragment program */ - u32 attrib_off; /*!< \brief offset to the attribute name table */ + u16 _pad0; /*!< \brief unused padding word. most be 0 */ + + u16 num_regs; /*!< \brief number of used registers in the fragment program */ + u16 num_attr; /*!< \brief number of used input attributes in the fragment program */ + u16 num_const; /*!< \brief number of used constants in the fragment program */ + u16 num_insn; /*!< \brief number of fragment program instructions */ + + u32 attr_off; /*!< \brief offset to the attribute name table */ + u32 const_off; /*!< \brief offset to the constant name table */ + u32 ucode_off; /*!< \brief offset to the shaders's micro code */ - u32 num_regs; /*!< \brief number of used registers in the fragment program */ u32 fp_control; /*!< \brief fragment program control mask */ u16 texcoords; /*!< \brief bit mask of all used texture coords in the fragment program */ u16 texcoord2D; /*!< \brief bit mask of used 2D texture coords in the fragment program */ u16 texcoord3D; /*!< \brief bit mask of used 3D texture coords in the fragment program */ - - u16 _pad0; /*!< \brief unused padding word. most be 0 */ - - u16 num_const; /*!< \brief number of used constants in the fragment program */ - u32 const_off; /*!< \brief offset to the constant name table */ - - u16 num_insn; /*!< \brief number of fragment program instructions */ - u32 ucode_off; /*!< \brief offset to the shaders's micro code */ + u16 _pad1; /*!< \brief unused padding word. most be 0 */ } rsxFragmentProgram; /*! \brief Program const data structure. */ @@ -109,9 +116,10 @@ typedef struct rsx_attrib /*! \brief Get Ucode from RSX vertex program. \param vp Pointer the to vertex program structure. -\return Pointer to the ucode. +\param ucode Pointer-pointer to receive the vertex program ucode. +\param size Pointer to receive the vertex program ucode size. */ -void* rsxVertexProgramGetUCode(rsxVertexProgram *vp); +void rsxVertexProgramGetUCode(rsxVertexProgram *vp,void **ucode,u32 *size); /*! \brief Get the list of vertex program consts. \param vp Pointer the to vertex program structure. @@ -141,9 +149,10 @@ s32 rsxVertexProgramGetAttrib(rsxVertexProgram *vp,const char *name); /*! \brief Get Ucode from RSX fragment program. \param fp Pointer the to fragment program structure. -\return Pointer to the ucode. +\param ucode Pointer-pointer to receive the fragment program ucode. +\param size Pointer to receive the fragment program ucode size. */ -void* rsxFragmentProgramGetUCode(rsxFragmentProgram *fp,u32 *size); +void rsxFragmentProgramGetUCode(rsxFragmentProgram *fp,void **ucode,u32 *size); /*! \brief Get the list of fragment program consts. \param fp Pointer the to fragment program structure. diff --git a/ppu/include/sys/spu.h b/ppu/include/sys/spu.h index 137914ba..337ae63a 100644 --- a/ppu/include/sys/spu.h +++ b/ppu/include/sys/spu.h @@ -162,6 +162,19 @@ to the Cell Broadband Engine documentation. //! Configure signal notification register 2 to OR mode #define SPU_SIGNAL2_OR 0x02 +/* exception causes */ +#define SPU_EXCEPTION_DMA_ALIGNMENT 0x0001U +#define SPU_EXCEPTION_DMA_COMMAND 0x0002U +#define SPU_EXCEPTION_SPU_ERROR 0x0004U +#define SPU_EXCEPTION_MFC_FIR 0x0008U +#define SPU_EXCEPTION_MFC_SEGMENT 0x0010U +#define SPU_EXCEPTION_MFC_STORAGE 0x0020U +#define SPU_EXCEPTION_STOP_CALL 0x0100U +#define SPU_EXCEPTION_STOP_BREAK 0x0200U +#define SPU_EXCEPTION_HALT 0x0400U +#define SPU_EXCEPTION_UNKNOWN_SIGNAL 0x0800U +#define SPU_EXCEPTION_NO_VALUE 0x0U + //! Base of memory-mapped SPU thread resources #define SPU_THREAD_BASE 0xF0000000ULL //! Offset between resources for consecutive SPU threads diff --git a/ppu/include/sys/spu_thread_printf.h b/ppu/include/sys/spu_thread_printf.h new file mode 100644 index 00000000..ef3e51ac --- /dev/null +++ b/ppu/include/sys/spu_thread_printf.h @@ -0,0 +1,17 @@ +#ifndef __LV2_SPU_THREAD_PRINTF_H__ +#define __LV2_SPU_THREAD_PRINTF_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +s32 spu_thread_printf(sys_spu_thread_t id, u32 arg_addr); +s32 spu_thread_sprintf(char *buf, sys_spu_thread_t id, u32 arg_addr); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/ppu/librsx/buffer.c b/ppu/librsx/buffer.c index 451b0630..1dfa80f8 100644 --- a/ppu/librsx/buffer.c +++ b/ppu/librsx/buffer.c @@ -4,34 +4,20 @@ #include #include -void rsxResetCommandBuffer(gcmContextData *context) -{ - u32 offset = 0x1000; // init state offset; - rsxSetJumpCommand(context,offset); +#define RSX_INTERNAL 0 - __sync(); - - gcmControlRegister volatile *ctrl = gcmGetControlRegister(context); - ctrl->put = offset; - while(ctrl->get!=offset) usleep(30); -} - -void rsxFlushBuffer(gcmContextData *context) -{ - u32 offset = 0; - gcmControlRegister volatile *ctrl = gcmGetControlRegister(context); - - __sync(); - gcmAddressToOffset(context->current,&offset); - ctrl->put = offset; -} - -void rsxFinish(gcmContextData *context,u32 ref_value) -{ - rsxSetReferenceCommand(context,ref_value); - rsxFlushBuffer(context); - - gcmControlRegister volatile *ctrl = gcmGetControlRegister(context); - while(ctrl->ref!=ref_value) usleep(30); -} +#define RSX_UNSAFE 1 +#define RSX_FUNCTION_MACROS +#include +#include "buffer_impl.h" +#undef RSX_FUNCTION_MACROS +#include +#undef RSX_UNSAFE +#define RSX_UNSAFE 0 +#define RSX_FUNCTION_MACROS +#include +#include "buffer_impl.h" +#undef RSX_FUNCTION_MACROS +#include +#undef RSX_UNSAFE diff --git a/ppu/librsx/buffer_impl.h b/ppu/librsx/buffer_impl.h new file mode 100644 index 00000000..bb13d0ca --- /dev/null +++ b/ppu/librsx/buffer_impl.h @@ -0,0 +1,31 @@ +void RSX_FUNC(ResetCommandBuffer)(gcmContextData *context) +{ + u32 offset = 0x1000; // init state offset; + RSX_FUNC(SetJumpCommand)(context,offset); + + __sync(); + + gcmControlRegister volatile *ctrl = gcmGetControlRegister(context); + ctrl->put = offset; + while(ctrl->get!=offset) usleep(30); +} + +void RSX_FUNC(FlushBuffer)(gcmContextData *context) +{ + u32 offset = 0; + gcmControlRegister volatile *ctrl = gcmGetControlRegister(context); + + __sync(); + gcmAddressToOffset(context->current,&offset); + ctrl->put = offset; +} + +void RSX_FUNC(Finish)(gcmContextData *context,u32 ref_value) +{ + RSX_FUNC(SetReferenceCommand)(context,ref_value); + RSX_FUNC(FlushBuffer)(context); + + gcmControlRegister volatile *ctrl = gcmGetControlRegister(context); + while(ctrl->ref!=ref_value) usleep(30); +} + diff --git a/ppu/librsx/commands.c b/ppu/librsx/commands.c index 17ae1232..1a6ac57a 100644 --- a/ppu/librsx/commands.c +++ b/ppu/librsx/commands.c @@ -3,9 +3,10 @@ #include #include -#include "commands.h" #include "rsx_internal.h" +#define RSX_INTERNAL 1 + #ifndef RSX_MEMCPY #define RSX_MEMCPY __builtin_memcpy #endif @@ -18,1024 +19,20 @@ static __inline__ f32 swapF32_16(f32 v) return d.f; } -s32 __attribute__((noinline)) rsxContextCallback(gcmContextData *context,u32 count) -{ - register s32 result asm("r3"); - asm volatile ( - "stdu 1,-128(1)\n" - "mr 31,2\n" - "lwz 0,0(%1)\n" - "lwz 2,4(%1)\n" - "mtctr 0\n" - "bctrl\n" - "mr 2,31\n" - "addi 1,1,128\n" - : "+r"(result) - : "b"(context->callback) - : "r31", "r0", "lr" - ); - return result; -} - -void rsxSetReturnCommand(gcmContextData *context) -{ - RSX_CONTEXT_CURRENT_BEGIN(1); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD_FLAG_RETURN; - RSX_CONTEXT_CURRENT_END(1); -} - -void rsxSetCallCommand(gcmContextData *context,u32 offset) -{ - RSX_CONTEXT_CURRENT_BEGIN(1); - RSX_CONTEXT_CURRENTP[0] = (offset | RSX_METHOD_FLAG_CALL); - RSX_CONTEXT_CURRENT_END(1); -} - -void rsxSetJumpCommand(gcmContextData *context,u32 offset) -{ - RSX_CONTEXT_CURRENT_BEGIN(1); - RSX_CONTEXT_CURRENTP[0] = (offset | RSX_METHOD_FLAG_JUMP); - RSX_CONTEXT_CURRENT_END(1); -} - -void rsxSetNopCommand(gcmContextData *context,u32 count) -{ - u32 i; - - RSX_CONTEXT_CURRENT_BEGIN(count); - for(i=0;i>16)&0xff) | ((value&0xff)<<16); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_SEMAPHORE_OFFSET,1); - RSX_CONTEXT_CURRENTP[1] = offset; - RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_SEMAPHORE_BACKENDWRITE_RELEASE,1); - RSX_CONTEXT_CURRENTP[3] = wvalue; - RSX_CONTEXT_CURRENT_END(4); -} - -void rsxSetWaitLabel(gcmContextData *context,u8 index,u32 value) -{ - u32 offset = 0x10*index; - - RSX_CONTEXT_CURRENT_BEGIN(4); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV406ETCL_SEMAPHORE_OFFSET,1); - RSX_CONTEXT_CURRENTP[1] = offset; - RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV406ETCL_SEMAPHORE_ACQUIRE,1); - RSX_CONTEXT_CURRENTP[3] = value; - RSX_CONTEXT_CURRENT_END(4); -} - -void rsxSetWriteCommandLabel(gcmContextData *context,u8 index,u32 value) -{ - u32 offset = 0x10*index; - - RSX_CONTEXT_CURRENT_BEGIN(4); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV406ETCL_SEMAPHORE_OFFSET,1); - RSX_CONTEXT_CURRENTP[1] = offset; - RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV406ETCL_SEMAPHORE_RELEASE,1); - RSX_CONTEXT_CURRENTP[3] = value; - RSX_CONTEXT_CURRENT_END(4); -} - -void rsxSetSurface(gcmContextData *context,gcmSurface *surface) -{ - RSX_CONTEXT_CURRENT_BEGIN(32); - - u32 log2Width = 31 - __cntlzw(surface->width); - u32 log2Height = 31 - __cntlzw(surface->height); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_DMA_COLOR0,1); - RSX_CONTEXT_CURRENTP[1] = GCM_DMA_MEMORY_FRAME_BUFFER + surface->colorLocation[0]; - - RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_DMA_COLOR1,1); - RSX_CONTEXT_CURRENTP[3] = GCM_DMA_MEMORY_FRAME_BUFFER + surface->colorLocation[1]; - - RSX_CONTEXT_CURRENTP[4] = RSX_METHOD(NV40TCL_DMA_COLOR2,2); - RSX_CONTEXT_CURRENTP[5] = GCM_DMA_MEMORY_FRAME_BUFFER + surface->colorLocation[2]; - RSX_CONTEXT_CURRENTP[6] = GCM_DMA_MEMORY_FRAME_BUFFER + surface->colorLocation[3]; - - RSX_CONTEXT_CURRENTP[7] = RSX_METHOD(NV40TCL_DMA_ZETA,1); - RSX_CONTEXT_CURRENTP[8] = GCM_DMA_MEMORY_FRAME_BUFFER + surface->depthLocation; - - RSX_CONTEXT_CURRENTP[9] = RSX_METHOD(NV40TCL_RT_FORMAT,6); - RSX_CONTEXT_CURRENTP[10] = ((log2Height<antiAlias<type<depthFormat<colorFormat<colorPitch[0]; - RSX_CONTEXT_CURRENTP[12] = surface->colorOffset[0]; - RSX_CONTEXT_CURRENTP[13] = surface->depthOffset; - RSX_CONTEXT_CURRENTP[14] = surface->colorOffset[1]; - RSX_CONTEXT_CURRENTP[15] = surface->colorPitch[1]; - - RSX_CONTEXT_CURRENTP[16] = RSX_METHOD(NV40TCL_ZETA_PITCH,1); - RSX_CONTEXT_CURRENTP[17] = surface->depthPitch; - - RSX_CONTEXT_CURRENTP[18] = RSX_METHOD(NV40TCL_COLOR2_PITCH,4); - RSX_CONTEXT_CURRENTP[19] = surface->colorPitch[2]; - RSX_CONTEXT_CURRENTP[20] = surface->colorPitch[3]; - RSX_CONTEXT_CURRENTP[21] = surface->colorOffset[2]; - RSX_CONTEXT_CURRENTP[22] = surface->colorOffset[3]; - - RSX_CONTEXT_CURRENTP[23] = RSX_METHOD(NV40TCL_RT_ENABLE,1); - RSX_CONTEXT_CURRENTP[24] = surface->colorTarget; - - RSX_CONTEXT_CURRENTP[25] = RSX_METHOD(NV40TCL_WINDOW_OFFSET,1); - RSX_CONTEXT_CURRENTP[26] = ((surface->y<<16) | surface->x); - - RSX_CONTEXT_CURRENTP[27] = RSX_METHOD(NV40TCL_RT_HORIZ,2); - RSX_CONTEXT_CURRENTP[28] = ((surface->width<<16) | surface->x); - RSX_CONTEXT_CURRENTP[29] = ((surface->height<<16) | surface->y); - - RSX_CONTEXT_CURRENTP[30] = RSX_METHOD(NV40TCL_SHADER_WINDOW,1); - RSX_CONTEXT_CURRENTP[31] = ((0<<16) | (1<<12) | surface->height); - - RSX_CONTEXT_CURRENT_END(32); -} - -void rsxSetColorMask(gcmContextData *context,u32 mask) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_COLOR_MASK,1); - RSX_CONTEXT_CURRENTP[1] = mask; - - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxSetColorMaskMRT(gcmContextData *context,u32 mask) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_MRT_COLOR_MASK,1); - RSX_CONTEXT_CURRENTP[1] = mask; - - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxSetShadeModel(gcmContextData *context,u32 shadeModel) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_SHADE_MODEL,1); - RSX_CONTEXT_CURRENTP[1] = shadeModel; - - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxSetViewport(gcmContextData *context,u16 x,u16 y,u16 width,u16 height,f32 min,f32 max,const f32 scale[4],const f32 offset[4]) -{ - ieee32 _min,_max; - ieee32 _offset[4],_scale[4]; - - _min.f = min; - _max.f = max; - - _scale[0].f = scale[0]; - _scale[1].f = scale[1]; - _scale[2].f = scale[2]; - _scale[3].f = scale[3]; - - _offset[0].f = offset[0]; - _offset[1].f = offset[1]; - _offset[2].f = offset[2]; - _offset[3].f = offset[3]; - - RSX_CONTEXT_CURRENT_BEGIN(24); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VIEWPORT_HORIZ,2); - RSX_CONTEXT_CURRENTP[1] = ((width<<16) | x); - RSX_CONTEXT_CURRENTP[2] = ((height<<16) | y); - - RSX_CONTEXT_CURRENTP[3] = RSX_METHOD(NV40TCL_DEPTH_RANGE,2); - RSX_CONTEXT_CURRENTP[4] = _min.u; - RSX_CONTEXT_CURRENTP[5] = _max.u; - - RSX_CONTEXT_CURRENTP[6] = RSX_METHOD(NV40TCL_VIEWPORT_OFFSET,8); - RSX_CONTEXT_CURRENTP[7] = _offset[0].u; - RSX_CONTEXT_CURRENTP[8] = _offset[1].u; - RSX_CONTEXT_CURRENTP[9] = _offset[2].u; - RSX_CONTEXT_CURRENTP[10] = _offset[3].u; - RSX_CONTEXT_CURRENTP[11] = _scale[0].u; - RSX_CONTEXT_CURRENTP[12] = _scale[1].u; - RSX_CONTEXT_CURRENTP[13] = _scale[2].u; - RSX_CONTEXT_CURRENTP[14] = _scale[3].u; - - RSX_CONTEXT_CURRENTP[15] = RSX_METHOD(NV40TCL_VIEWPORT_OFFSET,8); - RSX_CONTEXT_CURRENTP[16] = _offset[0].u; - RSX_CONTEXT_CURRENTP[17] = _offset[1].u; - RSX_CONTEXT_CURRENTP[18] = _offset[2].u; - RSX_CONTEXT_CURRENTP[19] = _offset[3].u; - RSX_CONTEXT_CURRENTP[20] = _scale[0].u; - RSX_CONTEXT_CURRENTP[21] = _scale[1].u; - RSX_CONTEXT_CURRENTP[22] = _scale[2].u; - RSX_CONTEXT_CURRENTP[23] = _scale[3].u; - - RSX_CONTEXT_CURRENT_END(24); -} - -void rsxSetViewportClip(gcmContextData *context,u8 sel,u16 width,u16 height) -{ - RSX_CONTEXT_CURRENT_BEGIN(3); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VIEWPORT_CLIP_HORIZ(sel),2); - RSX_CONTEXT_CURRENTP[1] = ((width-1) << 16); - RSX_CONTEXT_CURRENTP[2] = ((height-1) << 16); - - RSX_CONTEXT_CURRENT_END(3); -} - -void rsxSetUserClipPlaneControl(gcmContextData *context,u32 plane0,u32 plane1,u32 plane2,u32 plane3,u32 plane4,u32 plane5) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_CLIP_PLANE_ENABLE,1); - RSX_CONTEXT_CURRENTP[1] = ((plane5 << 20) | (plane4 << 16) | (plane3 << 12) | (plane2 << 8) | (plane1 << 4) | plane0); - - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxSetDepthTestEnable(gcmContextData *context,u32 enable) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_DEPTH_TEST_ENABLE,1); - RSX_CONTEXT_CURRENTP[1] = enable; - - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxSetDepthFunc(gcmContextData *context,u32 func) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_DEPTH_FUNC,1); - RSX_CONTEXT_CURRENTP[1] = func; - - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxSetDepthWriteEnable(gcmContextData *context,u32 enable) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_DEPTH_WRITE_ENABLE,1); - RSX_CONTEXT_CURRENTP[1] = enable; - - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxSetCullFaceEnable(gcmContextData *context,u32 enable) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_CULL_FACE_ENABLE,1); - RSX_CONTEXT_CURRENTP[1] = enable; - - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxSetCullFace(gcmContextData *context,u32 cull) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_CULL_FACE,1); - RSX_CONTEXT_CURRENTP[1] = cull; - - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxSetFrontFace(gcmContextData *context,u32 dir) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_FRONT_FACE,1); - RSX_CONTEXT_CURRENTP[1] = dir; - - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxClearSurface(gcmContextData *context,u32 clear_mask) -{ - RSX_CONTEXT_CURRENT_BEGIN(4); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_CLEAR_BUFFERS,1); - RSX_CONTEXT_CURRENTP[1] = clear_mask; - RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_NOP,1); - RSX_CONTEXT_CURRENTP[3] = 0; - - RSX_CONTEXT_CURRENT_END(4); -} - -void rsxLoadVertexProgramBlock(gcmContextData *context,rsxVertexProgram *program,const void *ucode) -{ - u32 pos = 0; - const u32 *data = (const u32*)ucode; - u32 startIndex = program->start_insn; - u32 i,instCount = program->num_insn; - - RSX_CONTEXT_CURRENT_BEGIN(6+5*instCount); - - RSX_CONTEXT_CURRENTP[pos++] = RSX_METHOD(NV40TCL_VP_UPLOAD_FROM_ID,2); - RSX_CONTEXT_CURRENTP[pos++] = startIndex; - RSX_CONTEXT_CURRENTP[pos++] = startIndex; - - for(i=0;iinput_mask; - RSX_CONTEXT_CURRENTP[pos++] = program->output_mask; - - RSX_CONTEXT_CURRENT_END(6+5*instCount); -} - -void rsxLoadFragmentProgramLocation(gcmContextData *context,rsxFragmentProgram *program,u32 offset,u32 location) -{ - u32 i,fpcontrol; - u32 texcoords,texcoord2D,texcoord3D; - - RSX_CONTEXT_CURRENT_BEGIN(2); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_FP_ADDRESS,1); - RSX_CONTEXT_CURRENTP[1] = ((location + 1) | offset); - RSX_CONTEXT_CURRENT_END(2); - - texcoords = program->texcoords; - texcoord2D = program->texcoord2D; - texcoord3D = program->texcoord3D; - for(i=0;texcoords;i++) { - if(texcoords&1) { - RSX_CONTEXT_CURRENT_BEGIN(2); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_COORD_CONTROL(i),1); - RSX_CONTEXT_CURRENTP[1] = (((texcoord3D&1) << 4) | (texcoord2D&1)); - RSX_CONTEXT_CURRENT_END(2); - } - texcoords >>= 1; - texcoord2D >>= 1; - texcoord3D >>= 1; - } - - { - RSX_CONTEXT_CURRENT_BEGIN(2); - - fpcontrol = program->fp_control | (program->num_regs << NV40TCL_FP_CONTROL_TEMP_COUNT_SHIFT); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_FP_CONTROL,1); - RSX_CONTEXT_CURRENTP[1] = fpcontrol; - - RSX_CONTEXT_CURRENT_END(2); - } -} - -void rsxLoadVertexProgramParameterBlock(gcmContextData *context,u32 base_const,u32 const_cnt,const f32 *value) -{ - u32 i,curr = 0; - - RSX_CONTEXT_CURRENT_BEGIN(const_cnt*6); - - for(i=0;iconst_start; - rsxProgramConst *consts = rsxVertexProgramGetConsts(program); - - rsxLoadVertexProgramBlock(context,program,ucode); - - for(i=0;inum_const;i++) { - if(consts[i].is_internal) - rsxLoadVertexProgramParameterBlock(context,consts[i].index + base_const,1,(f32*)consts[i].values); - } -} - -void rsxSetVertexProgramParameter(gcmContextData *context,rsxVertexProgram *program,s32 index,const f32 *value) -{ - u32 base_const = program->const_start; - rsxProgramConst *consts = rsxVertexProgramGetConsts(program); - - rsxLoadVertexProgramParameterBlock(context,consts[index].index + base_const,consts[index].count,value); -} - -void rsxSetFragmentProgramParameter(gcmContextData *context,rsxFragmentProgram *program,s32 index,const f32 *value,u32 offset) -{ - s32 i; - f32 params[4] = {0.0f,0.0f,0.0f,0.0f}; - rsxProgramConst *consts = rsxFragmentProgramGetConsts(program); - - switch(consts[index].type) { - case PARAM_FLOAT4x4: - { - s32 j,cnt = consts[index].count; - for(j=0;jnum;i++) - rsxInlineTransfer(context,offset + co_table->offset[i],params,4,GCM_LOCATION_RSX); - } - } - return; - } - - case PARAM_FLOAT4: - params[3] = swapF32_16(value[3]); - case PARAM_FLOAT3: - params[2] = swapF32_16(value[2]); - case PARAM_FLOAT2: - params[1] = swapF32_16(value[1]); - case PARAM_FLOAT: - params[0] = swapF32_16(value[0]); - break; - } - - if(consts[index].index!=0xffffffff) { - rsxConstOffsetTable *co_table = rsxFragmentProgramGetConstOffsetTable(program,consts[index].index); - - for(i=0;inum;i++) - rsxInlineTransfer(context,offset + co_table->offset[i],params,4,GCM_LOCATION_RSX); - } -} - -void rsxDrawVertexBegin(gcmContextData *context,u32 type) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_BEGIN_END,1); - RSX_CONTEXT_CURRENTP[1] = type; - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxDrawVertexEnd(gcmContextData *context) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_BEGIN_END,1); - RSX_CONTEXT_CURRENTP[1] = NV40TCL_BEGIN_END_STOP; - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxDrawVertex2f(gcmContextData *context,u8 idx,f32 x,f32 y) -{ - ieee32 d[2]; - d[0].f = x; d[1].f = y; - - RSX_CONTEXT_CURRENT_BEGIN(3); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_2F_X(idx),2); - RSX_CONTEXT_CURRENTP[1] = d[0].u; - RSX_CONTEXT_CURRENTP[2] = d[1].u; - - RSX_CONTEXT_CURRENT_END(3); -} - -void rsxDrawVertex3f(gcmContextData *context,u8 idx,f32 x,f32 y,f32 z) -{ - ieee32 d[3]; - d[0].f = x; d[1].f = y; d[2].f = z; - - RSX_CONTEXT_CURRENT_BEGIN(4); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_3F_X(idx),3); - RSX_CONTEXT_CURRENTP[1] = d[0].u; - RSX_CONTEXT_CURRENTP[2] = d[1].u; - RSX_CONTEXT_CURRENTP[3] = d[2].u; - - RSX_CONTEXT_CURRENT_END(4); -} - -void rsxDrawVertex4f(gcmContextData *context,u8 idx,f32 x,f32 y,f32 z,f32 w) -{ - ieee32 d[4]; - d[0].f = x; d[1].f = y; d[2].f = z; d[3].f = w; - - RSX_CONTEXT_CURRENT_BEGIN(5); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_4F_X(idx),4); - RSX_CONTEXT_CURRENTP[1] = d[0].u; - RSX_CONTEXT_CURRENTP[2] = d[1].u; - RSX_CONTEXT_CURRENTP[3] = d[2].u; - RSX_CONTEXT_CURRENTP[4] = d[3].u; - - RSX_CONTEXT_CURRENT_END(5); -} - -void rsxInvalidateTextureCache(gcmContextData *context,u32 type) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_CACHE_CTL,1); - RSX_CONTEXT_CURRENTP[1] = type; - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxLoadTexture(gcmContextData *context,u8 index,const gcmTexture *texture) -{ - u32 format,offset,swizzle,size0,size1; - - RSX_CONTEXT_CURRENT_BEGIN(9); - - offset = texture->offset; - format = ((texture->location + 1) | (texture->cubemap << 2) | - (texture->dimension << NV40TCL_TEX_FORMAT_DIMS_SHIFT) | - (texture->format << NV40TCL_TEX_FORMAT_FORMAT_SHIFT) | - (texture->mipmap << NV40TCL_TEX_FORMAT_MIPMAP_COUNT_SHIFT) | - NV40TCL_TEX_FORMAT_NO_BORDER | 0x8000); - swizzle = texture->remap; - size0 = (texture->width << NV40TCL_TEX_SIZE0_W_SHIFT) | texture->height; - size1 = (texture->depth << NV40TCL_TEX_SIZE1_DEPTH_SHIFT) | texture->pitch; - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_OFFSET(index),2); // set offset and format for texture at once - RSX_CONTEXT_CURRENTP[1] = offset; - RSX_CONTEXT_CURRENTP[2] = format; - - RSX_CONTEXT_CURRENTP[3] = RSX_METHOD(NV40TCL_TEX_SWIZZLE(index),1); // set remap order or swizzle respectively for texture - RSX_CONTEXT_CURRENTP[4] = swizzle; - - RSX_CONTEXT_CURRENTP[5] = RSX_METHOD(NV40TCL_TEX_SIZE0(index),1); // set width and height for texture - RSX_CONTEXT_CURRENTP[6] = size0; - - RSX_CONTEXT_CURRENTP[7] = RSX_METHOD(NV40TCL_TEX_SIZE1(index),1); // set pitch and depth for texture - RSX_CONTEXT_CURRENTP[8] = size1; - - RSX_CONTEXT_CURRENT_END(9); -} - -void rsxTextureControl(gcmContextData *context,u8 index,u32 enable,u16 minlod,u16 maxlod,u8 maxaniso) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_ENABLE(index),1); - RSX_CONTEXT_CURRENTP[1] = ((enable << NV40TCL_TEX_ENABLE_SHIFT) | (minlod << NV40TCL_TEX_MINLOD_SHIFT) | (maxlod << NV40TCL_TEX_MAXLOD_SHIFT) | (maxaniso << NV40TCL_TEX_MAXANISO_SHIFT)); - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxTextureFilter(gcmContextData *context,u8 index,u8 min,u8 mag,u8 conv) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_FILTER(index),1); - RSX_CONTEXT_CURRENTP[1] = ((mag << NV40TCL_TEX_FILTER_MAG_SHIFT) | (min << NV40TCL_TEX_FILTER_MIN_SHIFT) | (conv << NV40TCL_TEX_FILTER_CONV_SHIFT)); - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxTextureWrapMode(gcmContextData *context,u8 index,u8 wraps,u8 wrapt,u8 wrapr,u8 unsignedRemap,u8 zfunc,u8 gamma) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_WRAP(index),1); - RSX_CONTEXT_CURRENTP[1] = ((wraps << NV40TCL_TEX_WRAP_S_SHIFT) | - (wrapt << NV40TCL_TEX_WRAP_T_SHIFT) | - (wrapr << NV40TCL_TEX_WRAP_R_SHIFT) | - (unsignedRemap << NV40TCL_TEX_UREMAP_SHIFT) | - (gamma << NV40TCL_TEX_GAMMA_SHIFT) | - (zfunc << NV40TCL_TEX_ZFUNC_SHIFT)); - RSX_CONTEXT_CURRENT_END(2); -} - -void rsxZControl(gcmContextData *context,u8 cullNearFar,u8 zClampEnable,u8 cullIgnoreW) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_DEPTH_CONTROL,1); - RSX_CONTEXT_CURRENTP[1] = (cullNearFar | (zClampEnable<<4) | (cullIgnoreW<<8)); - RSX_CONTEXT_CURRENT_END(2); -} +#define RSX_UNSAFE 1 +#define RSX_FUNCTION_MACROS +#include +#include "commands_impl.h" +#undef RSX_FUNCTION_MACROS +#include +#undef RSX_UNSAFE -void rsxBindVertexArrayAttrib(gcmContextData *context,u8 attr,u32 offset,u8 stride,u8 elems,u8 dtype,u8 location) -{ - RSX_CONTEXT_CURRENT_BEGIN(4); +#define RSX_UNSAFE 0 +#define RSX_FUNCTION_MACROS +#include +#include "commands_impl.h" +#undef RSX_FUNCTION_MACROS +#include +#undef RSX_UNSAFE - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTXBUF_ADDRESS(attr),1); - RSX_CONTEXT_CURRENTP[1] = ((location << 31) | offset); - - RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_VTXFMT(attr),1); - RSX_CONTEXT_CURRENTP[3] = ((stride << NV40TCL_VTXFMT_STRIDE_SHIFT) | (elems << NV40TCL_VTXFMT_SIZE_SHIFT) | dtype); - - RSX_CONTEXT_CURRENT_END(4); -} - -void rsxDrawVertexArray(gcmContextData *context,u32 type,u32 start,u32 count) -{ - RSX_CONTEXT_CURRENT_BEGIN(6); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_CACHE_INVALIDATE,3); - RSX_CONTEXT_CURRENTP[1] = 0; - RSX_CONTEXT_CURRENTP[2] = 0; - RSX_CONTEXT_CURRENTP[3] = 0; - - RSX_CONTEXT_CURRENTP[4] = RSX_METHOD(NV40TCL_BEGIN_END,1); - RSX_CONTEXT_CURRENTP[5] = type; - - RSX_CONTEXT_CURRENT_END(6); - - while(count) { - u32 rem = count; - - if(rem>256) rem = 256; - - RSX_CONTEXT_CURRENT_BEGIN(2); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VB_VERTEX_BATCH,1); - RSX_CONTEXT_CURRENTP[1] = (((rem - 1) << 24) | start); - RSX_CONTEXT_CURRENT_END(2); - - count -= rem; - start += rem; - } - - { - RSX_CONTEXT_CURRENT_BEGIN(2); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_BEGIN_END,1); - RSX_CONTEXT_CURRENTP[1] = NV40TCL_BEGIN_END_STOP; - - RSX_CONTEXT_CURRENT_END(2); - } -} - -void rsxDrawIndexArray(gcmContextData *context,u32 type,u32 offset,u32 count,u32 data_type,u32 location) -{ - u32 start; - - RSX_CONTEXT_CURRENT_BEGIN(7); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_CACHE_INVALIDATE,1); - RSX_CONTEXT_CURRENTP[1] = 0; - - RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_VB_INDEX_BATCH_OFFSET,2); - RSX_CONTEXT_CURRENTP[3] = offset; - RSX_CONTEXT_CURRENTP[4] = (((data_type) << 4) | location); - - RSX_CONTEXT_CURRENTP[5] = RSX_METHOD(NV40TCL_BEGIN_END,1); - RSX_CONTEXT_CURRENTP[6] = type; - - RSX_CONTEXT_CURRENT_END(7); - - start = 0; - while(count) { - u32 rem = count; - - if(rem>256) rem = 256; - - RSX_CONTEXT_CURRENT_BEGIN(2); - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VB_INDEX_BATCH_DRAW,1); - RSX_CONTEXT_CURRENTP[1] = (((rem - 1) << 24) | start); - RSX_CONTEXT_CURRENT_END(2); - - count -= rem; - start += rem; - } - - { - RSX_CONTEXT_CURRENT_BEGIN(2); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_BEGIN_END,1); - RSX_CONTEXT_CURRENTP[1] = NV40TCL_BEGIN_END_STOP; - - RSX_CONTEXT_CURRENT_END(2); - } -} - -void rsxSetScissor(gcmContextData *context,u16 x,u16 y,u16 w,u16 h) -{ - RSX_CONTEXT_CURRENT_BEGIN(3); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_SCISSOR_HORIZ,2); - RSX_CONTEXT_CURRENTP[1] = ((w<<16) | x); - RSX_CONTEXT_CURRENTP[2] = ((h<<16) | y); - - RSX_CONTEXT_CURRENT_END(3); -} - -void rsxInlineTransfer(gcmContextData *context,const u32 dstOffset,const void *srcAddress,const u32 sizeInWords,const u8 location) -{ - u32 *src; - u32 pos,cnt; - u32 padSizeInWords; - u32 alignedVideoOffset; - u32 pixelShift; - - alignedVideoOffset = dstOffset&~0x3f; - pixelShift = (dstOffset&0x3f)>>2; - - padSizeInWords = (sizeInWords + 1)&~0x01; - - RSX_CONTEXT_CURRENT_BEGIN(12 + padSizeInWords); - - pos = 0; - RSX_CONTEXT_CURRENTP[pos++] = RSX_METHOD(NV3062TCL_SET_CONTEXT_DMA_IMAGE_DEST,1); - RSX_CONTEXT_CURRENTP[pos++] = GCM_DMA_MEMORY_FRAME_BUFFER + location; - - RSX_CONTEXT_CURRENTP[pos++] = RSX_METHOD(NV3062TCL_SET_OFFSET_DEST,1); - RSX_CONTEXT_CURRENTP[pos++] = alignedVideoOffset; - - RSX_CONTEXT_CURRENTP[pos++] = RSX_METHOD(NV3062TCL_SET_COLOR_FORMAT,2); - RSX_CONTEXT_CURRENTP[pos++] = GCM_TRANSFER_SURFACE_FORMAT_Y32; - RSX_CONTEXT_CURRENTP[pos++] = ((0x1000 << 16) | 0x1000); - - RSX_CONTEXT_CURRENTP[pos++] = RSX_METHOD(NV308ATCL_POINT,3); - RSX_CONTEXT_CURRENTP[pos++] = ((0 << 16) | pixelShift); - RSX_CONTEXT_CURRENTP[pos++] = ((1 << 16) | sizeInWords); - RSX_CONTEXT_CURRENTP[pos++] = ((1 << 16) | sizeInWords); - - RSX_CONTEXT_CURRENTP[pos++] = RSX_METHOD(NV308ATCL_COLOR,padSizeInWords); - - cnt = 0; - src = (u32*)srcAddress; - while(cnt>4)) << 16) | (width + ((srcX+15)>>4))); - RSX_CONTEXT_CURRENTP[23] = (srcPitch | (GCM_TRANSFER_ORIGIN_CORNER << 16) | (GCM_TRANSFER_INTERPOLATOR_NEAREST << 24)); - RSX_CONTEXT_CURRENTP[24] = srcOffset; - RSX_CONTEXT_CURRENTP[25] = ((srcY << 16) | srcX); - - RSX_CONTEXT_CURRENT_END(26); -} - -void rsxSetTransferScaleMode(gcmContextData *context,const u8 mode,const u8 surface) -{ - RSX_CONTEXT_CURRENT_BEGIN(6); - - RSX_CONTEXT_CURRENTP[0] = RSX_SUBCHANNEL_METHOD(6,NV_MEMORY_TO_MEMORY_FORMAT_DMA_BUFFER_IN,1); - RSX_CONTEXT_CURRENTP[1] = (mode&0x01) ? GCM_DMA_MEMORY_HOST_BUFFER : GCM_DMA_MEMORY_FRAME_BUFFER; - - RSX_CONTEXT_CURRENTP[2] = (surface==GCM_TRANSFER_SWIZZLE) ? RSX_SUBCHANNEL_METHOD(4,NV_MEMORY_TO_MEMORY_FORMAT_DMA_BUFFER_IN,1) : RSX_SUBCHANNEL_METHOD(3,NV_MEMORY_TO_MEMORY_FORMAT_DMA_BUFFER_OUT,1); - RSX_CONTEXT_CURRENTP[3] = (mode&0x02) ? GCM_DMA_MEMORY_HOST_BUFFER : GCM_DMA_MEMORY_FRAME_BUFFER; - - RSX_CONTEXT_CURRENTP[4] = RSX_SUBCHANNEL_METHOD(6,NV01_IMAGE_FROM_CPU_SURFACE,1); - RSX_CONTEXT_CURRENTP[5] = (surface==GCM_TRANSFER_SWIZZLE) ? GCM_CONTEXT_SWIZZLE2D : GCM_CONTEXT_SURFACE2D; - - RSX_CONTEXT_CURRENT_END(6); -} - -void rsxSetTransferScaleSurface(gcmContextData *context,const gcmTransferScale *scale,const gcmTransferSurface *surface) -{ - RSX_CONTEXT_CURRENT_BEGIN(20); - - RSX_CONTEXT_CURRENTP[0] = RSX_SUBCHANNEL_METHOD(3,NV04_CONTEXT_SURFACES_2D_FORMAT,4); - RSX_CONTEXT_CURRENTP[1] = surface->format; - RSX_CONTEXT_CURRENTP[2] = ((surface->pitch << 16) | 0x40); // or'ing with 64 - why? - RSX_CONTEXT_CURRENTP[3] = 0; - RSX_CONTEXT_CURRENTP[4] = surface->offset; - - RSX_CONTEXT_CURRENTP[5] = RSX_SUBCHANNEL_METHOD(6,NV03_STRETCHED_IMAGE_FROM_CPU_OPERATION,9); - RSX_CONTEXT_CURRENTP[6] = scale->conversion; - RSX_CONTEXT_CURRENTP[7] = scale->format; - RSX_CONTEXT_CURRENTP[8] = scale->operation; - RSX_CONTEXT_CURRENTP[9] = ((scale->clipY << 16) | scale->clipX); - RSX_CONTEXT_CURRENTP[10] = ((scale->clipH << 16) | scale->clipW); - RSX_CONTEXT_CURRENTP[11] = ((scale->outY << 16) | scale->outX); - RSX_CONTEXT_CURRENTP[12] = ((scale->outH << 16) | scale->outW); - RSX_CONTEXT_CURRENTP[13] = scale->ratioX; - RSX_CONTEXT_CURRENTP[14] = scale->ratioY; - - RSX_CONTEXT_CURRENTP[15] = RSX_SUBCHANNEL_METHOD(6,NV03_SCALED_IMAGE_FROM_MEMORY_IMAGE_IN_SIZE,4); - RSX_CONTEXT_CURRENTP[16] = ((scale->inH << 16) | scale->inW); - RSX_CONTEXT_CURRENTP[17] = ((scale->pitch) | (scale->origin << 16) | (scale->interp << 24)); - RSX_CONTEXT_CURRENTP[18] = scale->offset; - RSX_CONTEXT_CURRENTP[19] = ((scale->inY << 16) | scale->inX); - - RSX_CONTEXT_CURRENT_END(20); -} - -#if 0 -// This is unfinished -void rsxSetTransferScaleSwizzle(gcmContextData *context,const gcmTransferScale *scale,const gcmTransferSwizzle *swizzle) -{ - RSX_CONTEXT_CURRENT_BEGIN(20); - - RSX_CONTEXT_CURRENTP[0] = RSX_SUBCHANNEL_METHOD(4,0x300,2); - RSX_CONTEXT_CURRENTP[1] = (swizzle->format | (swizzle->width << 16) | (swizzle->height << 24)); - RSX_CONTEXT_CURRENTP[2] = swizzle->offset; - - RSX_CONTEXT_CURRENTP[3] = RSX_SUBCHANNEL_METHOD(6,0x2fc,9);; - RSX_CONTEXT_CURRENTP[4] = GCM_TRANSFER_CONVERSION_TRUNCATE; - RSX_CONTEXT_CURRENTP[5] = scale->format; - RSX_CONTEXT_CURRENTP[6] = GCM_TRANSFER_OPERATION_SRCCOPY; - RSX_CONTEXT_CURRENTP[7] = ((scale->clipY << 16) | scale->clipX); - RSX_CONTEXT_CURRENTP[8] = ((scale->clipH << 16) | scale->clipW); - RSX_CONTEXT_CURRENTP[9] = ((scale->outY << 16) | scale->outX); - RSX_CONTEXT_CURRENTP[10] = ((scale->outH << 16) | scale->outW); - RSX_CONTEXT_CURRENTP[11] = scale->ratioX; - RSX_CONTEXT_CURRENTP[12] = scale->ratioY; - - RSX_CONTEXT_CURRENTP[13] = RSX_SUBCHANNEL_METHOD(6,0x400,4); - RSX_CONTEXT_CURRENTP[14] = ((scale->inH << 16) | scale->inW); - RSX_CONTEXT_CURRENTP[15] = ((scale->pitch) | (scale->origin << 16) | (scale->interp << 24)); - RSX_CONTEXT_CURRENTP[16] = scale->offset; - RSX_CONTEXT_CURRENTP[17] = ((scale->inY << 16) | scale->inX); - - RSX_CONTEXT_CURRENT_END(20); -} -#endif - -void rsxSetTimeStamp(gcmContextData *context,u32 index) -{ - RSX_CONTEXT_CURRENT_BEGIN(2); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_QUERY_GET,1); - RSX_CONTEXT_CURRENTP[2] = (((index << 4)&0x0fffffff) | 0x10000000); - - RSX_CONTEXT_CURRENT_END(2); -} +#undef RSX_INTERNAL diff --git a/ppu/librsx/commands_impl.h b/ppu/librsx/commands_impl.h new file mode 100644 index 00000000..79e1b0ca --- /dev/null +++ b/ppu/librsx/commands_impl.h @@ -0,0 +1,1993 @@ +void RSX_FUNC(SetReturnCommand)(gcmContextData *context) +{ + RSX_CONTEXT_CURRENT_BEGIN(1); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD_FLAG_RETURN; + RSX_CONTEXT_CURRENT_END(1); +} + +void RSX_FUNC(SetCallCommand)(gcmContextData *context,u32 offset) +{ + RSX_CONTEXT_CURRENT_BEGIN(1); + RSX_CONTEXT_CURRENTP[0] = (offset | RSX_METHOD_FLAG_CALL); + RSX_CONTEXT_CURRENT_END(1); +} + +void RSX_FUNC(SetJumpCommand)(gcmContextData *context,u32 offset) +{ + RSX_CONTEXT_CURRENT_BEGIN(1); + RSX_CONTEXT_CURRENTP[0] = (offset | RSX_METHOD_FLAG_JUMP); + RSX_CONTEXT_CURRENT_END(1); +} + +void RSX_FUNC(SetNopCommand)(gcmContextData *context,u32 count) +{ + u32 i; + + RSX_CONTEXT_CURRENT_BEGIN(count); + for(i=0;i>16)&0xff) | ((value&0xff)<<16); + + RSX_CONTEXT_CURRENT_BEGIN(4); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_SEMAPHORE_OFFSET,1); + RSX_CONTEXT_CURRENTP[1] = offset; + RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_SEMAPHORE_BACKENDWRITE_RELEASE,1); + RSX_CONTEXT_CURRENTP[3] = wvalue; + RSX_CONTEXT_CURRENT_END(4); +} + +void RSX_FUNC(SetWriteTextureLabel)(gcmContextData *context,const u8 index,const u32 value) +{ + u32 offset = 0x10*index; + + RSX_CONTEXT_CURRENT_BEGIN(4); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_SEMAPHORE_OFFSET,1); + RSX_CONTEXT_CURRENTP[1] = offset; + RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_SEMAPHORE_TEXTUREREAD_RELEASE,1); + RSX_CONTEXT_CURRENTP[3] = value; + RSX_CONTEXT_CURRENT_END(4); +} + +void RSX_FUNC(SetWaitLabel)(gcmContextData *context,u8 index,u32 value) +{ + u32 offset = 0x10*index; + + RSX_CONTEXT_CURRENT_BEGIN(4); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV406ETCL_SEMAPHORE_OFFSET,1); + RSX_CONTEXT_CURRENTP[1] = offset; + RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV406ETCL_SEMAPHORE_ACQUIRE,1); + RSX_CONTEXT_CURRENTP[3] = value; + RSX_CONTEXT_CURRENT_END(4); +} + +void RSX_FUNC(SetWriteCommandLabel)(gcmContextData *context,u8 index,u32 value) +{ + u32 offset = 0x10*index; + + RSX_CONTEXT_CURRENT_BEGIN(4); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV406ETCL_SEMAPHORE_OFFSET,1); + RSX_CONTEXT_CURRENTP[1] = offset; + RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV406ETCL_SEMAPHORE_RELEASE,1); + RSX_CONTEXT_CURRENTP[3] = value; + RSX_CONTEXT_CURRENT_END(4); +} + +void RSX_FUNC(SetSurface)(gcmContextData *context,gcmSurface *surface) +{ + u32 log2Width = 31 - __cntlzw(surface->width); + u32 log2Height = 31 - __cntlzw(surface->height); + + RSX_CONTEXT_CURRENT_BEGIN(32); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_DMA_COLOR0,1); + RSX_CONTEXT_CURRENTP[1] = GCM_DMA_MEMORY_FRAME_BUFFER + surface->colorLocation[0]; + + RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_DMA_COLOR1,1); + RSX_CONTEXT_CURRENTP[3] = GCM_DMA_MEMORY_FRAME_BUFFER + surface->colorLocation[1]; + + RSX_CONTEXT_CURRENTP[4] = RSX_METHOD(NV40TCL_DMA_COLOR2,2); + RSX_CONTEXT_CURRENTP[5] = GCM_DMA_MEMORY_FRAME_BUFFER + surface->colorLocation[2]; + RSX_CONTEXT_CURRENTP[6] = GCM_DMA_MEMORY_FRAME_BUFFER + surface->colorLocation[3]; + + RSX_CONTEXT_CURRENTP[7] = RSX_METHOD(NV40TCL_DMA_ZETA,1); + RSX_CONTEXT_CURRENTP[8] = GCM_DMA_MEMORY_FRAME_BUFFER + surface->depthLocation; + + RSX_CONTEXT_CURRENTP[9] = RSX_METHOD(NV40TCL_RT_FORMAT,6); + RSX_CONTEXT_CURRENTP[10] = ((log2Height<antiAlias<type<depthFormat<colorFormat<colorPitch[0]; + RSX_CONTEXT_CURRENTP[12] = surface->colorOffset[0]; + RSX_CONTEXT_CURRENTP[13] = surface->depthOffset; + RSX_CONTEXT_CURRENTP[14] = surface->colorOffset[1]; + RSX_CONTEXT_CURRENTP[15] = surface->colorPitch[1]; + + RSX_CONTEXT_CURRENTP[16] = RSX_METHOD(NV40TCL_ZETA_PITCH,1); + RSX_CONTEXT_CURRENTP[17] = surface->depthPitch; + + RSX_CONTEXT_CURRENTP[18] = RSX_METHOD(NV40TCL_COLOR2_PITCH,4); + RSX_CONTEXT_CURRENTP[19] = surface->colorPitch[2]; + RSX_CONTEXT_CURRENTP[20] = surface->colorPitch[3]; + RSX_CONTEXT_CURRENTP[21] = surface->colorOffset[2]; + RSX_CONTEXT_CURRENTP[22] = surface->colorOffset[3]; + + RSX_CONTEXT_CURRENTP[23] = RSX_METHOD(NV40TCL_RT_ENABLE,1); + RSX_CONTEXT_CURRENTP[24] = surface->colorTarget; + + RSX_CONTEXT_CURRENTP[25] = RSX_METHOD(NV40TCL_WINDOW_OFFSET,1); + RSX_CONTEXT_CURRENTP[26] = ((surface->y<<16) | surface->x); + + RSX_CONTEXT_CURRENTP[27] = RSX_METHOD(NV40TCL_RT_HORIZ,2); + RSX_CONTEXT_CURRENTP[28] = ((surface->width<<16) | surface->x); + RSX_CONTEXT_CURRENTP[29] = ((surface->height<<16) | surface->y); + + RSX_CONTEXT_CURRENTP[30] = RSX_METHOD(NV40TCL_SHADER_WINDOW,1); + RSX_CONTEXT_CURRENTP[31] = ((0<<16) | (1<<12) | surface->height); + + RSX_CONTEXT_CURRENT_END(32); +} + +void RSX_FUNC(SetColorMask)(gcmContextData *context,u32 mask) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_COLOR_MASK,1); + RSX_CONTEXT_CURRENTP[1] = mask; + + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetColorMaskMRT)(gcmContextData *context,u32 mask) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_MRT_COLOR_MASK,1); + RSX_CONTEXT_CURRENTP[1] = mask; + + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetShadeModel)(gcmContextData *context,u32 shadeModel) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_SHADE_MODEL,1); + RSX_CONTEXT_CURRENTP[1] = shadeModel; + + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetViewport)(gcmContextData *context,u16 x,u16 y,u16 width,u16 height,f32 min,f32 max,const f32 scale[4],const f32 offset[4]) +{ + ieee32 _min,_max; + ieee32 _offset[4],_scale[4]; + + _min.f = min; + _max.f = max; + + _scale[0].f = scale[0]; + _scale[1].f = scale[1]; + _scale[2].f = scale[2]; + _scale[3].f = scale[3]; + + _offset[0].f = offset[0]; + _offset[1].f = offset[1]; + _offset[2].f = offset[2]; + _offset[3].f = offset[3]; + + RSX_CONTEXT_CURRENT_BEGIN(24); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VIEWPORT_HORIZ,2); + RSX_CONTEXT_CURRENTP[1] = ((width<<16) | x); + RSX_CONTEXT_CURRENTP[2] = ((height<<16) | y); + + RSX_CONTEXT_CURRENTP[3] = RSX_METHOD(NV40TCL_DEPTH_RANGE,2); + RSX_CONTEXT_CURRENTP[4] = _min.u; + RSX_CONTEXT_CURRENTP[5] = _max.u; + + RSX_CONTEXT_CURRENTP[6] = RSX_METHOD(NV40TCL_VIEWPORT_OFFSET,8); + RSX_CONTEXT_CURRENTP[7] = _offset[0].u; + RSX_CONTEXT_CURRENTP[8] = _offset[1].u; + RSX_CONTEXT_CURRENTP[9] = _offset[2].u; + RSX_CONTEXT_CURRENTP[10] = _offset[3].u; + RSX_CONTEXT_CURRENTP[11] = _scale[0].u; + RSX_CONTEXT_CURRENTP[12] = _scale[1].u; + RSX_CONTEXT_CURRENTP[13] = _scale[2].u; + RSX_CONTEXT_CURRENTP[14] = _scale[3].u; + + RSX_CONTEXT_CURRENTP[15] = RSX_METHOD(NV40TCL_VIEWPORT_OFFSET,8); + RSX_CONTEXT_CURRENTP[16] = _offset[0].u; + RSX_CONTEXT_CURRENTP[17] = _offset[1].u; + RSX_CONTEXT_CURRENTP[18] = _offset[2].u; + RSX_CONTEXT_CURRENTP[19] = _offset[3].u; + RSX_CONTEXT_CURRENTP[20] = _scale[0].u; + RSX_CONTEXT_CURRENTP[21] = _scale[1].u; + RSX_CONTEXT_CURRENTP[22] = _scale[2].u; + RSX_CONTEXT_CURRENTP[23] = _scale[3].u; + + RSX_CONTEXT_CURRENT_END(24); +} + +void RSX_FUNC(SetViewportClip)(gcmContextData *context,u8 sel,u16 width,u16 height) +{ + RSX_CONTEXT_CURRENT_BEGIN(3); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VIEWPORT_CLIP_HORIZ(sel),2); + RSX_CONTEXT_CURRENTP[1] = ((width-1) << 16); + RSX_CONTEXT_CURRENTP[2] = ((height-1) << 16); + + RSX_CONTEXT_CURRENT_END(3); +} + +void RSX_FUNC(SetUserClipPlaneControl)(gcmContextData *context,u32 plane0,u32 plane1,u32 plane2,u32 plane3,u32 plane4,u32 plane5) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_CLIP_PLANE_ENABLE,1); + RSX_CONTEXT_CURRENTP[1] = ((plane5 << 20) | (plane4 << 16) | (plane3 << 12) | (plane2 << 8) | (plane1 << 4) | plane0); + + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetDepthTestEnable)(gcmContextData *context,u32 enable) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_DEPTH_TEST_ENABLE,1); + RSX_CONTEXT_CURRENTP[1] = enable; + + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetDepthFunc)(gcmContextData *context,u32 func) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_DEPTH_FUNC,1); + RSX_CONTEXT_CURRENTP[1] = func; + + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetDepthWriteEnable)(gcmContextData *context,u32 enable) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_DEPTH_WRITE_ENABLE,1); + RSX_CONTEXT_CURRENTP[1] = enable; + + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetCullFaceEnable)(gcmContextData *context,u32 enable) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_CULL_FACE_ENABLE,1); + RSX_CONTEXT_CURRENTP[1] = enable; + + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetCullFace)(gcmContextData *context,u32 cull) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_CULL_FACE,1); + RSX_CONTEXT_CURRENTP[1] = cull; + + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetFrontFace)(gcmContextData *context,u32 dir) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_FRONT_FACE,1); + RSX_CONTEXT_CURRENTP[1] = dir; + + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetFrontPolygonMode)(gcmContextData *context,const u32 mode) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_POLYGON_MODE_FRONT,1); + RSX_CONTEXT_CURRENTP[1] = mode; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetBackPolygonMode)(gcmContextData *context,const u32 mode) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_POLYGON_MODE_BACK,1); + RSX_CONTEXT_CURRENTP[1] = mode; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetPolygonOffsetFillEnable)(gcmContextData *context,const u32 enable) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_POLYGON_OFFSET_FILL_ENABLE,1); + RSX_CONTEXT_CURRENTP[1] = enable; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetPolygonOffset)(gcmContextData *context,const f32 factor,const f32 units) +{ + ieee32 d0,d1; + + d0.f = factor; + d1.f = units; + + RSX_CONTEXT_CURRENT_BEGIN(3); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_POLYGON_OFFSET_FACTOR,2); + RSX_CONTEXT_CURRENTP[1] = d0.u; + RSX_CONTEXT_CURRENTP[2] = d1.u; + RSX_CONTEXT_CURRENT_END(3); +} + +void RSX_FUNC(ClearSurface)(gcmContextData *context,u32 clear_mask) +{ + RSX_CONTEXT_CURRENT_BEGIN(4); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_CLEAR_BUFFERS,1); + RSX_CONTEXT_CURRENTP[1] = clear_mask; + RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_NOP,1); + RSX_CONTEXT_CURRENTP[3] = 0; + + RSX_CONTEXT_CURRENT_END(4); +} + +void RSX_FUNC(SetCylindricalWrap)(gcmContextData *context,const u32 enable) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_CYLINDRICAL_WRAP,1); + RSX_CONTEXT_CURRENTP[1] = enable; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetTwoSideLightEnable)(gcmContextData *context,const u32 enable) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TWO_SIDE_LIGHT_EN,1); + RSX_CONTEXT_CURRENTP[1] = enable; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetStencilFunc)(gcmContextData *context,const u32 func,const u32 ref,const u32 mask) +{ + RSX_CONTEXT_CURRENT_BEGIN(4); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_BACK_FUNC_FUNC,3); + RSX_CONTEXT_CURRENTP[1] = func; + RSX_CONTEXT_CURRENTP[2] = ref; + RSX_CONTEXT_CURRENTP[3] = mask; + RSX_CONTEXT_CURRENT_END(4); +} + +void RSX_FUNC(SetStencilMask)(gcmContextData *context,const u32 mask) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_BACK_MASK,1); + RSX_CONTEXT_CURRENTP[1] = mask; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetStencilOp)(gcmContextData *context,const u32 fail,const u32 depthFail,const u32 depthPass) +{ + RSX_CONTEXT_CURRENT_BEGIN(4); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_BACK_OP_FAIL,3); + RSX_CONTEXT_CURRENTP[1] = fail; + RSX_CONTEXT_CURRENTP[2] = depthFail; + RSX_CONTEXT_CURRENTP[3] = depthPass; + RSX_CONTEXT_CURRENT_END(4); +} + +void RSX_FUNC(SetStencilTestEnable)(gcmContextData *context,const u32 enable) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_BACK_ENABLE,1); + RSX_CONTEXT_CURRENTP[1] = enable; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetBackStencilFunc)(gcmContextData *context,const u32 func,const u32 ref,const u32 mask) +{ + RSX_CONTEXT_CURRENT_BEGIN(4); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_FRONT_FUNC_FUNC,3); + RSX_CONTEXT_CURRENTP[1] = func; + RSX_CONTEXT_CURRENTP[2] = ref; + RSX_CONTEXT_CURRENTP[3] = mask; + RSX_CONTEXT_CURRENT_END(4); +} + +void RSX_FUNC(SetBackStencilMask)(gcmContextData *context,const u32 mask) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_FRONT_MASK,1); + RSX_CONTEXT_CURRENTP[1] = mask; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetBackStencilOp)(gcmContextData *context,const u32 fail,const u32 depthFail,const u32 depthPass) +{ + RSX_CONTEXT_CURRENT_BEGIN(4); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_FRONT_OP_FAIL,3); + RSX_CONTEXT_CURRENTP[1] = fail; + RSX_CONTEXT_CURRENTP[2] = depthFail; + RSX_CONTEXT_CURRENTP[3] = depthPass; + RSX_CONTEXT_CURRENT_END(4); +} + +void RSX_FUNC(SetTwoSidedStencilTestEnable)(gcmContextData *context,const u32 enable) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_FRONT_ENABLE,1); + RSX_CONTEXT_CURRENTP[1] = enable; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetRenderEnable)(gcmContextData *context,const u8 mode,const u32 index) +{ + u32 offset = 0x10*index; + + if(mode == GCM_CONDITIONAL) { + RSX_CONTEXT_CURRENT_BEGIN(4); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_NOP,1); + RSX_CONTEXT_CURRENTP[1] = 0; + RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_RENDER_ENABLE,1); + RSX_CONTEXT_CURRENTP[3] = (0x2000000 | offset); + RSX_CONTEXT_CURRENT_END(4); + } else { + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_RENDER_ENABLE,1); + RSX_CONTEXT_CURRENTP[1] = 0x1000000; + RSX_CONTEXT_CURRENT_END(2); + } +} + +void RSX_FUNC(SetReport)(gcmContextData *context,const u32 type,const u32 index) +{ + u32 offset = 0x10*index; + + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_QUERY_GET,1); + RSX_CONTEXT_CURRENTP[1] = ((type<<24) | offset); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetClearReport)(gcmContextData *context,const u32 type) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_QUERY_RESET,1); + RSX_CONTEXT_CURRENTP[1] = type; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetZPixelCountEnable)(gcmContextData *context,const u32 enable) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_QUERY_ENABLE,1); + RSX_CONTEXT_CURRENTP[1] = enable; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetSCullControl)(gcmContextData *context,const u8 sFunc,const u8 sRef,const u8 sMask) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_SCULL_CONTROL,1); + RSX_CONTEXT_CURRENTP[1] = ((sMask<<24) | (sRef<<16) | sFunc); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetZCullLimit)(gcmContextData *context,const u16 moveforwardlimit,const u16 pushbacklimit) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_ZCULL_CONTROL1,1); + RSX_CONTEXT_CURRENTP[1] = ((moveforwardlimit<<16) | pushbacklimit); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetZCullStatsEnable)(gcmContextData *context,const u32 enable) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_ZCULL_STATS_ENABLE,1); + RSX_CONTEXT_CURRENTP[1] = enable; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetZCullControl)(gcmContextData *context,const u8 zculldir,const u8 zcullformat) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_ZCULL_CONTROL0,1); + RSX_CONTEXT_CURRENTP[1] = ((zcullformat<<4) | zculldir); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetClearZCullSurface)(gcmContextData *context,const u32 depth,const u32 stencil) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_CLEAR_ZCULL_SURFACE,1); + RSX_CONTEXT_CURRENTP[1] = ((stencil<<1) | depth); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetZCullEnable)(gcmContextData *context,const u32 depth,const u32 stencil) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_ZCULL_ENABLE,1); + RSX_CONTEXT_CURRENTP[1] = ((stencil<<1) | depth); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetPolygonSmoothEnable)(gcmContextData *context,const u32 enable) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_POLYGON_SMOOTH_ENABLE,1); + RSX_CONTEXT_CURRENTP[1] = enable; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(LoadVertexProgramBlock)(gcmContextData *context,rsxVertexProgram *program,const void *ucode) +{ + u32 pos = 0; + u32 loop, rest; + const u32 *data = (const u32*)ucode; + u32 startIndex = program->insn_start; + u32 i,j,instCount = program->num_insn; + + loop = instCount/8; + rest = (instCount%8)*4; + + RSX_CONTEXT_CURRENT_BEGIN(9 + loop*33 + (rest!=0 ? rest + 1 : 0)); + + RSX_CONTEXT_CURRENTP[pos++] = RSX_METHOD(NV40TCL_VP_UPLOAD_FROM_ID,2); + RSX_CONTEXT_CURRENTP[pos++] = startIndex; + RSX_CONTEXT_CURRENTP[pos++] = startIndex; + + for(i=0;i0) { + RSX_CONTEXT_CURRENTP[pos] = RSX_METHOD(NV40TCL_VP_UPLOAD_INST(0),rest); + for(j=0;jinput_mask; + RSX_CONTEXT_CURRENTP[pos++] = RSX_METHOD(NV40TCL_VP_RESULT_EN,1); + RSX_CONTEXT_CURRENTP[pos++] = (program->output_mask | GCM_ATTRIB_OUTPUT_MASK_POINTSIZE); + + RSX_CONTEXT_CURRENTP[pos++] = RSX_METHOD(NV40TCL_TRANSFORM_TIMEOUT,1); + if(program->num_regs<=32) + RSX_CONTEXT_CURRENTP[pos++] = 0x0020FFFF; + else + RSX_CONTEXT_CURRENTP[pos++] = 0x0030FFFF; + + RSX_CONTEXT_CURRENT_END(9 + loop*33 + (rest!=0 ? rest + 1 : 0)); +} + +void RSX_FUNC(LoadFragmentProgramLocation)(gcmContextData *context,rsxFragmentProgram *program,u32 offset,u32 location) +{ + u32 i; + u32 texcoords,texcoord2D,texcoord3D; + + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_FP_ADDRESS,1); + RSX_CONTEXT_CURRENTP[1] = ((location + 1) | offset); + RSX_CONTEXT_CURRENT_END(2); + + texcoords = program->texcoords; + texcoord2D = program->texcoord2D; + texcoord3D = program->texcoord3D; + for(i=0;texcoords;i++) { + if(texcoords&1) { + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_COORD_CONTROL(i),1); + RSX_CONTEXT_CURRENTP[1] = (((texcoord3D&1) << 4) | (texcoord2D&1)); + RSX_CONTEXT_CURRENT_END(2); + } + texcoords >>= 1; + texcoord2D >>= 1; + texcoord3D >>= 1; + } + + { + u32 num_regs = program->num_regs > 2 ? program->num_regs : 2; + u32 fpcontrol = program->fp_control | (num_regs << NV40TCL_FP_CONTROL_TEMP_COUNT_SHIFT) | (1<<10); + + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_FP_CONTROL,1); + RSX_CONTEXT_CURRENTP[1] = fpcontrol; + RSX_CONTEXT_CURRENT_END(2); + } +} + +void RSX_FUNC(UpdateFragmentProgramLocation)(gcmContextData *context,const u32 offset,const u32 location) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_FP_ADDRESS,1); + RSX_CONTEXT_CURRENTP[1] = ((location + 1) | offset); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(LoadVertexProgramParameterBlock)(gcmContextData *context,u32 base_const,u32 const_cnt,const f32 *value) +{ + u32 i,curr = 0; + + RSX_CONTEXT_CURRENT_BEGIN(const_cnt*6); + + for(i=0;iconst_start; + rsxProgramConst *consts = rsxVertexProgramGetConsts(program); + + RSX_FUNC(LoadVertexProgramBlock)(context,program,ucode); + + for(i=0;inum_const;i++) { + if(consts[i].is_internal) + RSX_FUNC(LoadVertexProgramParameterBlock)(context,consts[i].index + base_const,1,(f32*)consts[i].values); + } +} + +void RSX_FUNC(SetVertexProgramParameter)(gcmContextData *context,rsxVertexProgram *program,s32 index,const f32 *value) +{ + u32 base_const = program->const_start; + f32 params[4] = {0.0f,0.0f,0.0f,0.0f}; + rsxProgramConst *consts = rsxVertexProgramGetConsts(program); + + switch(consts[index].type) { + case PARAM_FLOAT3x4: + case PARAM_FLOAT4x4: + RSX_FUNC(LoadVertexProgramParameterBlock)(context,consts[index].index + base_const,consts[index].count,value); + return; + case PARAM_FLOAT3x3: + case PARAM_FLOAT4x3: + { + u32 i; + + for(i=0;inum;i++) + RSX_FUNC(InlineTransfer)(context,offset + co_table->offset[i],params,4,location); + } + } + return; + } + + case PARAM_FLOAT3x3: + case PARAM_FLOAT4x3: + { + s32 j,cnt = consts[index].count; + for(j=0;jnum;i++) + RSX_FUNC(InlineTransfer)(context,offset + co_table->offset[i],params,4,location); + } + } + return; + } + + case PARAM_FLOAT4: + params[3] = swapF32_16(value[3]); + case PARAM_FLOAT3: + params[2] = swapF32_16(value[2]); + case PARAM_FLOAT2: + params[1] = swapF32_16(value[1]); + case PARAM_FLOAT: + params[0] = swapF32_16(value[0]); + break; + } + + if(consts[index].index!=0xffffffff) { + rsxConstOffsetTable *co_table = rsxFragmentProgramGetConstOffsetTable(program,consts[index].index); + + for(i=0;inum;i++) + RSX_FUNC(InlineTransfer)(context,offset + co_table->offset[i],params,4,location); + } +} + +void RSX_FUNC(DrawVertexBegin)(gcmContextData *context,u32 type) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_BEGIN_END,1); + RSX_CONTEXT_CURRENTP[1] = type; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(DrawVertexEnd)(gcmContextData *context) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_BEGIN_END,1); + RSX_CONTEXT_CURRENTP[1] = NV40TCL_BEGIN_END_STOP; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(DrawVertex1f)(gcmContextData *context,const u8 idx,const f32 v) +{ + ieee32 d; + d.f = v; + + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_1F_X(idx),1); + RSX_CONTEXT_CURRENTP[1] = d.u; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(DrawVertex2f)(gcmContextData *context,u8 idx,const f32 v[2]) +{ + RSX_CONTEXT_CURRENT_BEGIN(3); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_2F_X(idx),2); + RSX_MEMCPY(&RSX_CONTEXT_CURRENTP[1],v,sizeof(f32)*2); + RSX_CONTEXT_CURRENT_END(3); +} + +void RSX_FUNC(DrawVertex3f)(gcmContextData *context,const u8 idx,const f32 v[3]) +{ + RSX_CONTEXT_CURRENT_BEGIN(4); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_3F_X(idx),3); + RSX_MEMCPY(&RSX_CONTEXT_CURRENTP[1],v,sizeof(f32)*3); + RSX_CONTEXT_CURRENT_END(4); +} + +void RSX_FUNC(DrawVertex4f)(gcmContextData *context,const u8 idx,const f32 v[4]) +{ + RSX_CONTEXT_CURRENT_BEGIN(5); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_4F_X(idx),4); + RSX_MEMCPY(&RSX_CONTEXT_CURRENTP[1],v,sizeof(f32)*4); + RSX_CONTEXT_CURRENT_END(5); +} + +void RSX_FUNC(DrawVertex4s)(gcmContextData *context,const u8 idx,const s16 v[4]) +{ + RSX_CONTEXT_CURRENT_BEGIN(3); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_4I_0(idx),2); + RSX_CONTEXT_CURRENTP[1] = (v[0] | (v[1]<<16)); + RSX_CONTEXT_CURRENTP[2] = (v[2] | (v[3]<<16)); + RSX_CONTEXT_CURRENT_END(3); +} + +void RSX_FUNC(DrawVertexScaled4s)(gcmContextData *context,const u8 idx,const s16 v[4]) +{ + RSX_CONTEXT_CURRENT_BEGIN(3); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_4I_SCALED_0(idx),2); + RSX_CONTEXT_CURRENTP[1] = (v[0] | (v[1]<<16)); + RSX_CONTEXT_CURRENTP[2] = (v[2] | (v[3]<<16)); + RSX_CONTEXT_CURRENT_END(3); +} + +void RSX_FUNC(DrawVertex2s)(gcmContextData *context,const u8 idx,const s16 v[2]) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_2I(idx),1); + RSX_CONTEXT_CURRENTP[1] = (v[0] | (v[1]<<16)); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(DrawVertex4ub)(gcmContextData *context,const u8 idx,const u8 v[4]) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_4UB(idx),1); + RSX_CONTEXT_CURRENTP[1] = (v[0] | (v[1]<<8) | (v[2]<<16) | (v[3]<<24)); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(InvalidateVertexCache)(gcmContextData *context) +{ + RSX_CONTEXT_CURRENT_BEGIN(8); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_CACHE_INVALIDATE2,1); + RSX_CONTEXT_CURRENTP[1] = 0; + RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_VTX_CACHE_INVALIDATE,1); + RSX_CONTEXT_CURRENTP[3] = 0; + RSX_CONTEXT_CURRENTP[4] = RSX_METHOD(NV40TCL_VTX_CACHE_INVALIDATE,1); + RSX_CONTEXT_CURRENTP[5] = 0; + RSX_CONTEXT_CURRENTP[6] = RSX_METHOD(NV40TCL_VTX_CACHE_INVALIDATE,1); + RSX_CONTEXT_CURRENTP[7] = 0; + RSX_CONTEXT_CURRENT_END(8); +} + +void RSX_FUNC(InvalidateTextureCache)(gcmContextData *context,u32 type) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_CACHE_CTL,1); + RSX_CONTEXT_CURRENTP[1] = type; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(LoadTexture)(gcmContextData *context,u8 index,const gcmTexture *texture) +{ + u32 format,offset,swizzle,size0,size1; + + RSX_CONTEXT_CURRENT_BEGIN(9); + + offset = texture->offset; + format = ((texture->location + 1) | (texture->cubemap << 2) | + (texture->dimension << NV40TCL_TEX_FORMAT_DIMS_SHIFT) | + (texture->format << NV40TCL_TEX_FORMAT_FORMAT_SHIFT) | + (texture->mipmap << NV40TCL_TEX_FORMAT_MIPMAP_COUNT_SHIFT) | + NV40TCL_TEX_FORMAT_NO_BORDER | 0x8000); + swizzle = texture->remap; + size0 = (texture->width << NV40TCL_TEX_SIZE0_W_SHIFT) | texture->height; + size1 = (texture->depth << NV40TCL_TEX_SIZE1_DEPTH_SHIFT) | texture->pitch; + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_OFFSET(index),2); // set offset and format for texture at once + RSX_CONTEXT_CURRENTP[1] = offset; + RSX_CONTEXT_CURRENTP[2] = format; + + RSX_CONTEXT_CURRENTP[3] = RSX_METHOD(NV40TCL_TEX_SWIZZLE(index),1); // set remap order or swizzle respectively for texture + RSX_CONTEXT_CURRENTP[4] = swizzle; + + RSX_CONTEXT_CURRENTP[5] = RSX_METHOD(NV40TCL_TEX_SIZE0(index),1); // set width and height for texture + RSX_CONTEXT_CURRENTP[6] = size0; + + RSX_CONTEXT_CURRENTP[7] = RSX_METHOD(NV40TCL_TEX_SIZE1(index),1); // set pitch and depth for texture + RSX_CONTEXT_CURRENTP[8] = size1; + + RSX_CONTEXT_CURRENT_END(9); +} + +void RSX_FUNC(LoadVertexTexture)(gcmContextData *context,const u8 index,const gcmTexture *texture) +{ + u32 format,offset,control,imagerect; + + offset = texture->offset; + format = (texture->location + 1) | (texture->dimension << 4) | + (texture->format << 8) | (texture->mipmap << 16); + imagerect = texture->height | (texture->width << 16); + control = texture->pitch; + + RSX_CONTEXT_CURRENT_BEGIN(7); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VP_TEXTURE_OFFSET(index),2); // set offset and format for texture at once + RSX_CONTEXT_CURRENTP[1] = offset; + RSX_CONTEXT_CURRENTP[2] = format; + + RSX_CONTEXT_CURRENTP[3] = RSX_METHOD(NV40TCL_VP_TEXTURE_CONTROL3(index),1); // set pitch for texture + RSX_CONTEXT_CURRENTP[4] = control; + + RSX_CONTEXT_CURRENTP[5] = RSX_METHOD(NV40TCL_VP_TEXTURE_IMAGE_RECT(index),1); // set width and height for texture + RSX_CONTEXT_CURRENTP[6] = imagerect; + + RSX_CONTEXT_CURRENT_END(7); +} + +void RSX_FUNC(TextureControl)(gcmContextData *context,u8 index,u32 enable,u16 minlod,u16 maxlod,u8 maxaniso) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_ENABLE(index),1); + RSX_CONTEXT_CURRENTP[1] = ((enable << NV40TCL_TEX_ENABLE_SHIFT) | (minlod << NV40TCL_TEX_MINLOD_SHIFT) | (maxlod << NV40TCL_TEX_MAXLOD_SHIFT) | (maxaniso << NV40TCL_TEX_MAXANISO_SHIFT)); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(VertexTextureControl)(gcmContextData *context,const u8 index,const u32 enable,const u16 minlod,const u16 maxlod) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VP_TEXTURE_CONTROL0(index),1); + RSX_CONTEXT_CURRENTP[1] = ((enable << NV40TCL_TEX_ENABLE_SHIFT) | ((minlod&0xfff) << NV40TCL_TEX_MINLOD_SHIFT) | ((maxlod&0xfff) << NV40TCL_TEX_MAXLOD_SHIFT)); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(TextureFilter)(gcmContextData *context,u8 index,u8 min,u8 mag,u8 conv) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_FILTER(index),1); + RSX_CONTEXT_CURRENTP[1] = ((mag << NV40TCL_TEX_FILTER_MAG_SHIFT) | (min << NV40TCL_TEX_FILTER_MIN_SHIFT) | (conv << NV40TCL_TEX_FILTER_CONV_SHIFT)); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(VertexTextureFilter)(gcmContextData *context,const u8 index,const u16 bias) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VP_TEXTURE_FILTER(index),1); + RSX_CONTEXT_CURRENTP[1] = bias; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(TextureWrapMode)(gcmContextData *context,u8 index,u8 wraps,u8 wrapt,u8 wrapr,u8 unsignedRemap,u8 zfunc,u8 gamma) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_WRAP(index),1); + RSX_CONTEXT_CURRENTP[1] = ((wraps << NV40TCL_TEX_WRAP_S_SHIFT) | + (wrapt << NV40TCL_TEX_WRAP_T_SHIFT) | + (wrapr << NV40TCL_TEX_WRAP_R_SHIFT) | + (unsignedRemap << NV40TCL_TEX_UREMAP_SHIFT) | + (gamma << NV40TCL_TEX_GAMMA_SHIFT) | + (zfunc << NV40TCL_TEX_ZFUNC_SHIFT)); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(VertexTextureWrapMode)(gcmContextData *context,const u8 index,const u8 wraps,const u8 wrapt) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VP_TEXTURE_ADDRESS(index),1); + RSX_CONTEXT_CURRENTP[1] = (wraps | (wrapt<<8)); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(TextureBorderColor)(gcmContextData *context,const u8 index,const u32 color) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_BORDER_COLOR(index),1); + RSX_CONTEXT_CURRENTP[1] = color; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(VertexTextureBorderColor)(gcmContextData *context,const u8 index,const u32 color) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VP_TEXTURE_BORDER_COLOR(index),1); + RSX_CONTEXT_CURRENTP[1] = color; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(TextureOptimization)(gcmContextData *context,const u8 index,const u8 slope,const u8 iso,const u8 aniso) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_CONTROL2(index),1); + RSX_CONTEXT_CURRENTP[1] = ((0x2d<<8) | (aniso<<7) | (iso<<6) | slope); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(TextureAnisoSpread)(gcmContextData *context,const u8 index,const u8 reduceSamplesEnable,const u8 hReduceSamplesEnable,const u8 vReduceSamplesEnable,const u8 spacingSelect,const u8 hSpacingSelect,const u8 vSpacingSelect) +{ + u32 val = ((spacingSelect&0x7)<<0) | (( reduceSamplesEnable&0x1)<<4) | + ((hSpacingSelect&0x7)<<8) | ((hReduceSamplesEnable&0x1)<<12) | + ((vSpacingSelect&0x7)<<16) | ((vReduceSamplesEnable&0x1)<<20); + + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_ANISO_SPREAD(index),1); + RSX_CONTEXT_CURRENTP[1] = val; + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetZControl)(gcmContextData *context,u8 cullNearFar,u8 zClampEnable,u8 cullIgnoreW) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_DEPTH_CONTROL,1); + RSX_CONTEXT_CURRENTP[1] = (cullNearFar | (zClampEnable<<4) | (cullIgnoreW<<8)); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(BindVertexArrayAttrib)(gcmContextData *context,u8 attr,u32 offset,u8 stride,u8 elems,u8 dtype,u8 location) +{ + RSX_CONTEXT_CURRENT_BEGIN(4); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTXBUF_ADDRESS(attr),1); + RSX_CONTEXT_CURRENTP[1] = ((location << 31) | offset); + + RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_VTXFMT(attr),1); + RSX_CONTEXT_CURRENTP[3] = ((stride << NV40TCL_VTXFMT_STRIDE_SHIFT) | (elems << NV40TCL_VTXFMT_SIZE_SHIFT) | dtype); + + RSX_CONTEXT_CURRENT_END(4); +} + +static inline __attribute__((always_inline)) void RSX_FUNC_INTERNAL(DrawVertexArray)(gcmContextData *context,u32 type,u32 start,u32 count) +{ + u32 i,j,lcount,loop,rest; + + --count; + lcount = count&0xff; + count >>= 8; + + loop = count/RSX_MAX_METHOD_COUNT; + rest = count%RSX_MAX_METHOD_COUNT; + + RSX_CONTEXT_CURRENT_BEGIN(8); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD_NI(NV40TCL_VTX_CACHE_INVALIDATE,3); + RSX_CONTEXT_CURRENTP[1] = 0; + RSX_CONTEXT_CURRENTP[2] = 0; + RSX_CONTEXT_CURRENTP[3] = 0; + + RSX_CONTEXT_CURRENTP[4] = RSX_METHOD(NV40TCL_BEGIN_END,1); + RSX_CONTEXT_CURRENTP[5] = type; + + RSX_CONTEXT_CURRENTP[6] = RSX_METHOD(NV40TCL_VB_VERTEX_BATCH,1); + RSX_CONTEXT_CURRENTP[7] = ((lcount<<24) | start); + + RSX_CONTEXT_CURRENT_END(8); + + start += (lcount + 1); + for(i=0;i < loop;i++) { + RSX_CONTEXT_CURRENT_BEGIN(1 + RSX_MAX_METHOD_COUNT); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD_NI(NV40TCL_VB_VERTEX_BATCH, RSX_MAX_METHOD_COUNT); + RSX_CONTEXT_CURRENTP++; + + for(j=0;j>2; + else + misalignedcount = (((offset + 127)&~127) - offset)>>1; + + odd = (misalignedcount && misalignedcount < count) ? 1 : 0; + + RSX_CONTEXT_CURRENT_BEGIN(7 + odd*2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_CACHE_INVALIDATE, 1); + RSX_CONTEXT_CURRENTP[1] = 0; + + RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_VB_INDEX_BATCH_OFFSET, 2); + RSX_CONTEXT_CURRENTP[3] = offset; + RSX_CONTEXT_CURRENTP[4] = (((data_type) << 4) | location); + + RSX_CONTEXT_CURRENTP[5] = RSX_METHOD(NV40TCL_BEGIN_END, 1); + RSX_CONTEXT_CURRENTP[6] = type; + + start = 0; + if(odd) { + u32 tmp = misalignedcount - 1; + + RSX_CONTEXT_CURRENTP[7] = RSX_METHOD(NV40TCL_VB_INDEX_BATCH_DRAW, 1); + RSX_CONTEXT_CURRENTP[8] = ((tmp<<24) | start); + + start += misalignedcount; + count -= misalignedcount; + } + + RSX_CONTEXT_CURRENT_END(7 + odd*2); + + while(count > 0x7ff00) { + RSX_CONTEXT_CURRENT_BEGIN(1 + RSX_MAX_METHOD_COUNT); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD_NI(NV40TCL_VB_INDEX_BATCH_DRAW, RSX_MAX_METHOD_COUNT); + RSX_CONTEXT_CURRENTP++; + + for(i=0;i < RSX_MAX_METHOD_COUNT;i++) { + RSX_CONTEXT_CURRENTP[0] = (0xff000000 | start); + RSX_CONTEXT_CURRENTP++; + start += 0x100; + } + + count -= 0x7ff00; + } + + mcount = (count + 0xff)>>8; + + RSX_CONTEXT_CURRENT_BEGIN(1 + mcount); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD_NI(NV40TCL_VB_INDEX_BATCH_DRAW, mcount); + RSX_CONTEXT_CURRENTP++; + + while(count > 0x100) { + RSX_CONTEXT_CURRENTP[0] = (0xff000000 | start); + RSX_CONTEXT_CURRENTP++; + start += 0x100; + count -= 0x100; + } + + if(count) { + --count; + RSX_CONTEXT_CURRENTP[0] = ((count<<24) | start); + RSX_CONTEXT_CURRENTP++; + } + + RSX_CONTEXT_CURRENT_BEGIN(2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_BEGIN_END, 1); + RSX_CONTEXT_CURRENTP[1] = NV40TCL_BEGIN_END_STOP; + + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(DrawIndexArray)(gcmContextData *context,u8 type,u32 offset,u32 count,u8 data_type,u8 location) +{ + RSX_FUNC_INTERNAL(DrawIndexArray)(context,type,offset,count,data_type,location); +} + +void RSX_FUNC(DrawInlineIndexArray16)(gcmContextData *context,u8 type,u32 start,u32 count,u16 *data) +{ + u32 odd,lcount; + u32 loop,rest,i,j; + + if(count&1) { + odd = 1; + lcount = count - 1; + } else { + odd = 0; + lcount = count; + } + + data = data + start; + loop = (lcount>>1)/RSX_MAX_METHOD_COUNT; + rest = (lcount>>1)%RSX_MAX_METHOD_COUNT; + + RSX_CONTEXT_CURRENT_BEGIN(6 + odd*2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD_NI(NV40TCL_VTX_CACHE_INVALIDATE, 3); + RSX_CONTEXT_CURRENTP[1] = 0; + RSX_CONTEXT_CURRENTP[2] = 0; + RSX_CONTEXT_CURRENTP[3] = 0; + + RSX_CONTEXT_CURRENTP[4] = RSX_METHOD(NV40TCL_BEGIN_END, 1); + RSX_CONTEXT_CURRENTP[5] = type; + + if(odd) { + RSX_CONTEXT_CURRENTP[6] = RSX_METHOD_NI(NV40TCL_VB_ELEMENT_U32, 1); + RSX_CONTEXT_CURRENTP[7] = data[0]; + data++; + } + + RSX_CONTEXT_CURRENT_END(6 + odd*2); + + for(i=0;i < loop;i++) { + RSX_CONTEXT_CURRENT_BEGIN(1 + RSX_MAX_METHOD_COUNT); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD_NI(NV40TCL_VB_ELEMENT_U16, RSX_MAX_METHOD_COUNT); + RSX_CONTEXT_CURRENTP++; + + for(j=0;j < RSX_MAX_METHOD_COUNT;j++) { + RSX_CONTEXT_CURRENTP[0] = (data[0] | (data[1]<<16)); + RSX_CONTEXT_CURRENTP++; + data += 2; + } + } + + if(rest) { + RSX_CONTEXT_CURRENT_BEGIN(1 + rest); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD_NI(NV40TCL_VB_ELEMENT_U16, rest); + RSX_CONTEXT_CURRENTP++; + + for(j=0;j < rest;j++) { + RSX_CONTEXT_CURRENTP[0] = (data[0] | (data[1]<<16)); + RSX_CONTEXT_CURRENTP++; + data += 2; + } + } + + RSX_CONTEXT_CURRENT_BEGIN(2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_BEGIN_END, 1); + RSX_CONTEXT_CURRENTP[1] = NV40TCL_BEGIN_END_STOP; + + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(DrawInlineIndexArray32)(gcmContextData *context,u8 type,u32 start,u32 count,u32 *data) +{ + u32 i,j; + u32 loop,rest; + + data = data + start; + loop = count/RSX_MAX_METHOD_COUNT; + rest = count%RSX_MAX_METHOD_COUNT; + + RSX_CONTEXT_CURRENT_BEGIN(6); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD_NI(NV40TCL_VTX_CACHE_INVALIDATE, 3); + RSX_CONTEXT_CURRENTP[1] = 0; + RSX_CONTEXT_CURRENTP[2] = 0; + RSX_CONTEXT_CURRENTP[3] = 0; + + RSX_CONTEXT_CURRENTP[4] = RSX_METHOD(NV40TCL_BEGIN_END, 1); + RSX_CONTEXT_CURRENTP[5] = type; + + RSX_CONTEXT_CURRENT_END(6); + + for(i=0;i < loop;i++) { + RSX_CONTEXT_CURRENT_BEGIN(1 + RSX_MAX_METHOD_COUNT); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD_NI(NV40TCL_VB_ELEMENT_U32, RSX_MAX_METHOD_COUNT); + RSX_CONTEXT_CURRENTP++; + + for(j=0;j < RSX_MAX_METHOD_COUNT;j++) { + RSX_CONTEXT_CURRENTP[0] = *data; + RSX_CONTEXT_CURRENTP++; + data++; + } + } + + if(rest) { + RSX_CONTEXT_CURRENT_BEGIN(1 + rest); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD_NI(NV40TCL_VB_ELEMENT_U32, rest); + RSX_CONTEXT_CURRENTP++; + + for(j=0;j < rest;j++) { + RSX_CONTEXT_CURRENTP[0] = *data; + RSX_CONTEXT_CURRENTP++; + data++; + } + } + + RSX_CONTEXT_CURRENT_BEGIN(2); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_BEGIN_END, 1); + RSX_CONTEXT_CURRENTP[1] = NV40TCL_BEGIN_END_STOP; + + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(SetScissor)(gcmContextData *context,u16 x,u16 y,u16 w,u16 h) +{ + RSX_CONTEXT_CURRENT_BEGIN(3); + + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_SCISSOR_HORIZ,2); + RSX_CONTEXT_CURRENTP[1] = ((w<<16) | x); + RSX_CONTEXT_CURRENTP[2] = ((h<<16) | y); + + RSX_CONTEXT_CURRENT_END(3); +} + +void RSX_FUNC(SetAntialiasingControl)(gcmContextData *context,const u32 enable,const u32 alphaToCoverage,const u32 alphaToOne,const u32 sampleMask) +{ + RSX_CONTEXT_CURRENT_BEGIN(2); + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_ANTI_ALIASING_CONTROL,1); + RSX_CONTEXT_CURRENTP[1] = ((sampleMask<<16) | (alphaToOne<<8) | (alphaToCoverage<<4) | enable); + RSX_CONTEXT_CURRENT_END(2); +} + +void RSX_FUNC(InlineTransfer)(gcmContextData *context,const u32 dstOffset,const void *srcAddress,const u32 sizeInWords,const u8 location) +{ + u32 *src; + u32 pixelShift; + u32 cnt,pos = 0; + u32 padSizeInWords; + u32 alignedVideoOffset; + + alignedVideoOffset = dstOffset&~0x3f; + pixelShift = (dstOffset&0x3f)>>2; + + padSizeInWords = (sizeInWords + 1)&~0x01; + + RSX_CONTEXT_CURRENT_BEGIN(12 + padSizeInWords); + + RSX_CONTEXT_CURRENTP[pos++] = RSX_SUBCHANNEL_METHOD(3,NV04_CONTEXT_SURFACES_2D_DMA_IMAGE_DESTIN,1); + RSX_CONTEXT_CURRENTP[pos++] = GCM_DMA_MEMORY_FRAME_BUFFER + location; + + RSX_CONTEXT_CURRENTP[pos++] = RSX_SUBCHANNEL_METHOD(3,NV04_CONTEXT_SURFACES_2D_OFFSET_DESTIN,1); + RSX_CONTEXT_CURRENTP[pos++] = alignedVideoOffset; + + RSX_CONTEXT_CURRENTP[pos++] = RSX_SUBCHANNEL_METHOD(3,NV04_CONTEXT_SURFACES_2D_FORMAT,2); + RSX_CONTEXT_CURRENTP[pos++] = NV04_CONTEXT_SURFACES_2D_FORMAT_Y32; + RSX_CONTEXT_CURRENTP[pos++] = ((0x1000 << 16) | 0x1000); + + RSX_CONTEXT_CURRENTP[pos++] = RSX_SUBCHANNEL_METHOD(5,NV01_IMAGE_FROM_CPU_POINT,3); + RSX_CONTEXT_CURRENTP[pos++] = ((0 << 16) | pixelShift); + RSX_CONTEXT_CURRENTP[pos++] = ((1 << 16) | sizeInWords); + RSX_CONTEXT_CURRENTP[pos++] = ((1 << 16) | sizeInWords); + + RSX_CONTEXT_CURRENTP[pos++] = RSX_SUBCHANNEL_METHOD(5,NV01_IMAGE_FROM_CPU_COLOR(0),padSizeInWords); + + cnt = 0; + src = (u32*)srcAddress; + while(cnt>4)) << 16) | (width + ((srcX+15)>>4))); + RSX_CONTEXT_CURRENTP[23] = (srcPitch | (GCM_TRANSFER_ORIGIN_CORNER << 16) | (GCM_TRANSFER_INTERPOLATOR_NEAREST << 24)); + RSX_CONTEXT_CURRENTP[24] = srcOffset; + RSX_CONTEXT_CURRENTP[25] = ((srcY << 16) | srcX); + + RSX_CONTEXT_CURRENT_END(26); +} + +void RSX_FUNC(SetTransferScaleMode)(gcmContextData *context,const u8 mode,const u8 surface) +{ + RSX_CONTEXT_CURRENT_BEGIN(6); + + RSX_CONTEXT_CURRENTP[0] = RSX_SUBCHANNEL_METHOD(6,NV_MEMORY_TO_MEMORY_FORMAT_DMA_BUFFER_IN,1); + RSX_CONTEXT_CURRENTP[1] = (mode&0x01) ? GCM_DMA_MEMORY_HOST_BUFFER : GCM_DMA_MEMORY_FRAME_BUFFER; + + RSX_CONTEXT_CURRENTP[2] = (surface==GCM_TRANSFER_SWIZZLE) ? RSX_SUBCHANNEL_METHOD(4,NV_MEMORY_TO_MEMORY_FORMAT_DMA_BUFFER_IN,1) : RSX_SUBCHANNEL_METHOD(3,NV_MEMORY_TO_MEMORY_FORMAT_DMA_BUFFER_OUT,1); + RSX_CONTEXT_CURRENTP[3] = (mode&0x02) ? GCM_DMA_MEMORY_HOST_BUFFER : GCM_DMA_MEMORY_FRAME_BUFFER; + + RSX_CONTEXT_CURRENTP[4] = RSX_SUBCHANNEL_METHOD(6,NV01_IMAGE_FROM_CPU_SURFACE,1); + RSX_CONTEXT_CURRENTP[5] = (surface==GCM_TRANSFER_SWIZZLE) ? GCM_CONTEXT_SWIZZLE2D : GCM_CONTEXT_SURFACE2D; + + RSX_CONTEXT_CURRENT_END(6); +} + +void RSX_FUNC(SetTransferScaleSurface)(gcmContextData *context,const gcmTransferScale *scale,const gcmTransferSurface *surface) +{ + RSX_CONTEXT_CURRENT_BEGIN(20); + + RSX_CONTEXT_CURRENTP[0] = RSX_SUBCHANNEL_METHOD(3,NV04_CONTEXT_SURFACES_2D_FORMAT,4); + RSX_CONTEXT_CURRENTP[1] = surface->format; + RSX_CONTEXT_CURRENTP[2] = ((surface->pitch << 16) | 0x40); // or'ing with 64 - why? + RSX_CONTEXT_CURRENTP[3] = 0; + RSX_CONTEXT_CURRENTP[4] = surface->offset; + + RSX_CONTEXT_CURRENTP[5] = RSX_SUBCHANNEL_METHOD(6,NV03_STRETCHED_IMAGE_FROM_CPU_OPERATION,9); + RSX_CONTEXT_CURRENTP[6] = scale->conversion; + RSX_CONTEXT_CURRENTP[7] = scale->format; + RSX_CONTEXT_CURRENTP[8] = scale->operation; + RSX_CONTEXT_CURRENTP[9] = ((scale->clipY << 16) | scale->clipX); + RSX_CONTEXT_CURRENTP[10] = ((scale->clipH << 16) | scale->clipW); + RSX_CONTEXT_CURRENTP[11] = ((scale->outY << 16) | scale->outX); + RSX_CONTEXT_CURRENTP[12] = ((scale->outH << 16) | scale->outW); + RSX_CONTEXT_CURRENTP[13] = scale->ratioX; + RSX_CONTEXT_CURRENTP[14] = scale->ratioY; + + RSX_CONTEXT_CURRENTP[15] = RSX_SUBCHANNEL_METHOD(6,NV03_SCALED_IMAGE_FROM_MEMORY_IMAGE_IN_SIZE,4); + RSX_CONTEXT_CURRENTP[16] = ((scale->inH << 16) | scale->inW); + RSX_CONTEXT_CURRENTP[17] = ((scale->pitch) | (scale->origin << 16) | (scale->interp << 24)); + RSX_CONTEXT_CURRENTP[18] = scale->offset; + RSX_CONTEXT_CURRENTP[19] = ((scale->inY << 16) | scale->inX); + + RSX_CONTEXT_CURRENT_END(20); +} + +void RSX_FUNC(SetTransferScaleSwizzle)(gcmContextData *context,const gcmTransferScale *scale,const gcmTransferSwizzle *swizzle) +{ + RSX_CONTEXT_CURRENT_BEGIN(18); + + RSX_CONTEXT_CURRENTP[0] = RSX_SUBCHANNEL_METHOD(4,NV04_SWIZZLED_SURFACE_FORMAT,2); + RSX_CONTEXT_CURRENTP[1] = ((swizzle->height << 24) | (swizzle->width << 16) | swizzle->format); + RSX_CONTEXT_CURRENTP[2] = swizzle->offset; + + RSX_CONTEXT_CURRENTP[3] = RSX_SUBCHANNEL_METHOD(6,NV04_SCALED_IMAGE_FROM_MEMORY_COLOR_CONVERSION,9);; + RSX_CONTEXT_CURRENTP[4] = GCM_TRANSFER_CONVERSION_TRUNCATE; + RSX_CONTEXT_CURRENTP[5] = scale->format; + RSX_CONTEXT_CURRENTP[6] = GCM_TRANSFER_OPERATION_SRCCOPY; + RSX_CONTEXT_CURRENTP[7] = ((scale->clipY << 16) | scale->clipX); + RSX_CONTEXT_CURRENTP[8] = ((scale->clipH << 16) | scale->clipW); + RSX_CONTEXT_CURRENTP[9] = ((scale->outY << 16) | scale->outX); + RSX_CONTEXT_CURRENTP[10] = ((scale->outH << 16) | scale->outW); + RSX_CONTEXT_CURRENTP[11] = scale->ratioX; + RSX_CONTEXT_CURRENTP[12] = scale->ratioY; + + RSX_CONTEXT_CURRENTP[13] = RSX_SUBCHANNEL_METHOD(6,NV04_SCALED_IMAGE_FROM_MEMORY_SIZE,4); + RSX_CONTEXT_CURRENTP[14] = ((scale->inH << 16) | scale->inW); + RSX_CONTEXT_CURRENTP[15] = ((scale->interp << 24) | (scale->origin << 16) | (scale->pitch)); + RSX_CONTEXT_CURRENTP[16] = scale->offset; + RSX_CONTEXT_CURRENTP[17] = ((scale->inY << 16) | scale->inX); + + RSX_CONTEXT_CURRENT_END(18); +} + +static inline __attribute__((always_inline)) void RSX_FUNC_INTERNAL(SetConvertSwizzleFormat)(gcmContextData *context,u32 dstOffset,u32 dstWidth,u32 dstHeight,u32 dstX,u32 dstY,u32 srcOffset,u32 srcPitch,u32 srcX,u32 srcY,u32 width,u32 height,u32 bytesPerPixel,u32 mode) +{ + const u32 NV_MEM2MEM_MAX_HEIGHT_VALUE = 2047; + const u32 NV_SURFACE_SWIZZLED_MAX_DIM = 10; + u32 dstwlog2 = 31 - __cntlzw(dstWidth); + u32 dsthlog2 = 31 - __cntlzw(dstHeight); + + switch(bytesPerPixel) { + case 2: + case 4: + break; + case 8: + dstWidth <<= 1; + dstX <<= 1; + srcX <<= 1; + width <<= 1; + bytesPerPixel >>= 1; + dstwlog2 += 1; + break; + case 16: + dstWidth <<= 2; + dstX <<= 2; + srcX <<= 2; + width <<= 2; + bytesPerPixel >>= 2; + dstwlog2 += 2; + break; + default: + return; + } + + if((dstwlog2 <= 1) || (dsthlog2 == 0)) { + u32 dstPitch; + u32 linesLeft; + + dstPitch = bytesPerPixel< NV_MEM2MEM_MAX_HEIGHT_VALUE) ? NV_MEM2MEM_MAX_HEIGHT_VALUE : linesLeft; + + // todo: this is incorrect for the vid->vid case + rsxSetTransferData(context,mode,dstOffset,dstPitch,srcOffset,srcPitch,width*bytesPerPixel,actualHeight); + + srcOffset = srcOffset + actualHeight*srcPitch; + dstOffset = dstOffset + actualHeight*dstPitch; + linesLeft -= actualHeight; + } + return; + } else { + u32 yTop,xEnd,yEnd,x,y; + u32 srcFormat,dstFormat,logWidthLimit,logHeightLimit; + u32 srcHandle = (mode&0x01) ? GCM_DMA_MEMORY_HOST_BUFFER : GCM_DMA_MEMORY_FRAME_BUFFER; + u32 dstHandle = (mode&0x02) ? GCM_DMA_MEMORY_HOST_BUFFER : GCM_DMA_MEMORY_FRAME_BUFFER; + + RSX_CONTEXT_CURRENT_BEGIN(6); + RSX_CONTEXT_CURRENTP[0] = RSX_SUBCHANNEL_METHOD(4,NV_MEMORY_TO_MEMORY_FORMAT_DMA_BUFFER_IN,1); + RSX_CONTEXT_CURRENTP[1] = dstHandle; + RSX_CONTEXT_CURRENTP[2] = RSX_SUBCHANNEL_METHOD(6,NV03_SCALED_IMAGE_FROM_MEMORY_DMA_IMAGE,1); + RSX_CONTEXT_CURRENTP[3] = srcHandle; + RSX_CONTEXT_CURRENTP[4] = RSX_SUBCHANNEL_METHOD(6,NV01_IMAGE_FROM_CPU_SURFACE,1); + RSX_CONTEXT_CURRENTP[5] = GCM_CONTEXT_SWIZZLE2D; + RSX_CONTEXT_CURRENT_END(6); + + switch(bytesPerPixel) + { + case 2: + srcFormat = GCM_TRANSFER_SCALE_FORMAT_R5G6B5; + dstFormat = GCM_TRANSFER_SURFACE_FORMAT_R5G6B5; + break; + case 4: + srcFormat = GCM_TRANSFER_SCALE_FORMAT_A8R8G8B8; + dstFormat = GCM_TRANSFER_SURFACE_FORMAT_A8R8G8B8; + break; + case 1: + default: + return; + } + + logWidthLimit = (dstwlog2 > NV_SURFACE_SWIZZLED_MAX_DIM) ? NV_SURFACE_SWIZZLED_MAX_DIM : dstwlog2; + logHeightLimit = (dsthlog2 > NV_SURFACE_SWIZZLED_MAX_DIM) ? NV_SURFACE_SWIZZLED_MAX_DIM : dsthlog2; + + srcOffset += (srcX - dstX)*bytesPerPixel + (srcY - dstY)*srcPitch; + + xEnd = dstX + width; + yEnd = dstY + height; + + yTop = dstY&~((1<< NV_SURFACE_SWIZZLED_MAX_DIM) - 1); + for(y=dstY;y < yEnd;) { + u32 xLeft; + u32 yBottom; + u32 bltHeight; + + yBottom = yTop + (1< (1ul << dsthlog2)) { + yBottom = (1< yEnd) ? yEnd - y : yBottom - y; + + xLeft = dstX&~((1< xEnd ) ? xEnd - x : xRight - x; + + if(!dstwlog2) + blockDstOffset = dstOffset + yTop*bytesPerPixel; + else if(!dsthlog2) + blockDstOffset = dstOffset + xLeft*bytesPerPixel; + else { + u32 log = (dstwlog2 < dsthlog2) ? dstwlog2 : dsthlog2; + u32 doubleLog = log<<1; + u32 upperMask = ~((1<num_insn*sizeof(u32)*4; - return (void*)(((u8*)fp) + fp->ucode_off); + *ucode = (void*)(((u8*)fp) + fp->ucode_off); } rsxProgramConst* rsxFragmentProgramGetConsts(rsxFragmentProgram *fp) @@ -34,7 +34,7 @@ s32 rsxFragmentProgramGetConst(rsxFragmentProgram *fp,const char *name) rsxProgramAttrib* rsxFragmentProgramGetAttribs(rsxFragmentProgram *fp) { - return (rsxProgramAttrib*)(((u8*)fp) + fp->attrib_off); + return (rsxProgramAttrib*)(((u8*)fp) + fp->attr_off); } s32 rsxFragmentProgramGetAttrib(rsxFragmentProgram *fp,const char *name) @@ -42,7 +42,7 @@ s32 rsxFragmentProgramGetAttrib(rsxFragmentProgram *fp,const char *name) u32 i; rsxProgramAttrib *attribs = rsxFragmentProgramGetAttribs(fp); - for(i=0;inum_attrib;i++) { + for(i=0;inum_attr;i++) { char *namePtr; if(!attribs[i].name_off) continue; diff --git a/ppu/librsx/init.c b/ppu/librsx/init.c index ba7852ef..ab9c4440 100644 --- a/ppu/librsx/init.c +++ b/ppu/librsx/init.c @@ -2,17 +2,29 @@ #include #include -gcmContextData* rsxInit(const u32 cmdSize,const u32 ioSize,const void *ioAddress) +static gcmContextData *gGcmContext ATTRIBUTE_PRXPTR = NULL; +static gcmContextData sUserContext = +{ + NULL, + NULL, + NULL, + NULL +}; + +extern s32 gcmInitBodyEx(gcmContextData* ATTRIBUTE_PRXPTR *ctx,const u32 cmdSize,const u32 ioSize,const void *ioAddress); + +s32 rsxInit(gcmContextData **context,const u32 cmdSize,const u32 ioSize,const void *ioAddress) { s32 ret = -1; - gcmContextData *context ATTRIBUTE_PRXPTR; - ret = gcmInitBody(&context,cmdSize,ioSize,ioAddress); + if(context == NULL) return -1; + + ret = gcmInitBodyEx(&gGcmContext,cmdSize,ioSize,ioAddress); if(ret==0) { rsxHeapInit(); - return context; + *context = gGcmContext; } - return NULL; + return ret; } void rsxSetupContextData(gcmContextData *context,const u32 *addr,const u32 size,gcmContextCallback cb) @@ -24,3 +36,33 @@ void rsxSetupContextData(gcmContextData *context,const u32 *addr,const u32 size, context->end = (u32*)(addr + alignedSize - 4); context->callback = (gcmContextCallback)__get_opd32(cb); } + +void rsxSetCurrentBuffer(gcmContextData **context,const u32 *addr,const u32 size) +{ + u32 alignedSize = size&~0x3; + + gGcmContext = &sUserContext; + + sUserContext.begin = (u32*)addr; + sUserContext.current = (u32*)addr; + sUserContext.end = (u32*)((u64)addr + alignedSize - 4); + + *context = gGcmContext; +} + +void rsxSetDefaultCommandBuffer(gcmContextData **context) +{ + gcmSetDefaultCommandBuffer(); + *context = gGcmContext; +} + +void rsxSetUserCallback(gcmContextCallback cb) +{ + sUserContext.callback = cb; +} + +u32* rsxGetCurrentBuffer() +{ + return gGcmContext->current; +} + diff --git a/ppu/librsx/rsx_internal.h b/ppu/librsx/rsx_internal.h index bb97d3d8..a8a8b4ef 100644 --- a/ppu/librsx/rsx_internal.h +++ b/ppu/librsx/rsx_internal.h @@ -4,29 +4,21 @@ #include #include -#define RSX_METHOD_FLAG_NO_INCREMENT (0x40000000) -#define RSX_METHOD_FLAG_JUMP (0x20000000) -#define RSX_METHOD_FLAG_CALL (0x00000002) -#define RSX_METHOD_FLAG_RETURN (0x00020000) +#define RSX_METHOD_FLAG_NO_INCREMENT (0x40000000) +#define RSX_METHOD_FLAG_JUMP (0x20000000) +#define RSX_METHOD_FLAG_CALL (0x00000002) +#define RSX_METHOD_FLAG_RETURN (0x00020000) -#define RSX_MAX_METHOD_COUNT 0x7ff +#define RSX_MAX_METHOD_COUNT 0x7ff -#define RSX_CONTEXT_CURRENTP (context->current) +#define RSX_CONTEXT_CURRENTP (context->current) +#define RSX_CONTEXT_CURRENT_END(x) context->current += (x) -#define RSX_CONTEXT_CURRENT_BEGIN(count) do { \ - if((context->current + (count)) > context->end) { \ - if(rsxContextCallback(context,(count))!=0) return; \ - } \ -} while(0) +#define RSX_METHOD_COUNT_SHIFT (18) +#define RSX_METHOD(method,count) (((count)<current += (x) - -#define RSX_METHOD_COUNT_SHIFT (18) -#define RSX_METHOD(method,count) (((count)<ucode_off); + *size = vp->num_insn*sizeof(u32)*4; + *ucode = (void*)(((u8*)vp) + vp->ucode_off); } rsxProgramConst* rsxVertexProgramGetConsts(rsxVertexProgram *vp) @@ -36,7 +37,7 @@ s32 rsxVertexProgramGetConst(rsxVertexProgram *vp,const char *name) rsxProgramAttrib* rsxVertexProgramGetAttribs(rsxVertexProgram *vp) { - return (rsxProgramAttrib*)(((u8*)vp) + vp->attrib_off); + return (rsxProgramAttrib*)(((u8*)vp) + vp->attr_off); } s32 rsxVertexProgramGetAttrib(rsxVertexProgram *vp,const char *name) @@ -44,7 +45,7 @@ s32 rsxVertexProgramGetAttrib(rsxVertexProgram *vp,const char *name) u32 i; rsxProgramAttrib *attribs = rsxVertexProgramGetAttribs(vp); - for(i=0;inum_attrib;i++) { + for(i=0;inum_attr;i++) { char *namePtr; if(!attribs[i].name_off) continue; diff --git a/ppu/sprx/libgcm_sys/exports.h b/ppu/sprx/libgcm_sys/exports.h index 9cc4ae69..48dc6c82 100644 --- a/ppu/sprx/libgcm_sys/exports.h +++ b/ppu/sprx/libgcm_sys/exports.h @@ -1,7 +1,7 @@ #ifndef __EXPORTS_H__ #define __EXPORTS_H__ -EXPORT(gcmInitBody, 0x15bae46b); +EXPORT(gcmInitBodyEx, 0x15bae46b); EXPORT(gcmSetDisplayBuffer, 0xa53d12ae); EXPORT(gcmGetControlRegister, 0xa547adde); EXPORT(gcmAddressToOffset, 0x21ac3697); @@ -44,7 +44,7 @@ EXPORT(gcmMapMainMemory, 0xa114ec67); EXPORT(gcmMapEaIoAddress, 0x63441cb4); EXPORT(gcmUnmapEaIoAddress, 0xefd00f54); EXPORT(gcmUnmapIoAddress, 0xdb23e867); -EXPORT(gcmIoOffsetToAddress, 0x2a6fba9c); +EXPORT(gcmIoOffsetToAddressEx, 0x2a6fba9c); EXPORT(gcmSetDefaultCommandBuffer, 0xbc982946); EXPORT(gcmSetTileInfo, 0xbd100dbc); EXPORT(gcmGetTiledPitchSize, 0x055bd74d); @@ -57,16 +57,14 @@ EXPORT(gcmGetOffsetTable, 0x2922aed0); EXPORT(gcmGetLastSecondVTime, 0x23ae55a3); EXPORT(gcmSortRemapEaIoAddress, 0x25b40ab4); EXPORT(gcmGetDisplayBufferByFlipIndex, 0x371674cf); -EXPORT(gcmGcmGetDefaultCommandWordSize, 0x5e2ee0f0); -EXPORT(gcmGcmGetDefaultSegmentWordSize, 0x8cdf8c70); +EXPORT(gcmGetDefaultCommandWordSize, 0x5e2ee0f0); +EXPORT(gcmGetDefaultSegmentWordSize, 0x8cdf8c70); EXPORT(gcmSetDefaultCommandBufferAndSegmentWordSize, 0x172c3197); EXPORT(gcmGetDisplayInfo, 0x0e6b0dae); EXPORT(gcmDumpGraphicsError, 0x1f61b3ff); EXPORT(gcmFunc38, 0x688b8ac9); EXPORT(gcmGetCurrentDisplayBufferId, 0x93806525); -EXPORT(gcmGetDefaultCommandWordSize, 0x5e2ee0f0); -EXPORT(gcmGetDefaultSegmentWordSize, 0x8cdf8c70); EXPORT(gcmGetMaxIoMapSize, 0xfb81c03e); EXPORT(gcmGetNotifyDataAddress, 0x21cee035); EXPORT(gcmGetReportDataAddress, 0x9a0159af); diff --git a/ppu/sprx/libgcm_sys/gcm_wrapper.c b/ppu/sprx/libgcm_sys/gcm_wrapper.c index 66d0d6af..2902db71 100644 --- a/ppu/sprx/libgcm_sys/gcm_wrapper.c +++ b/ppu/sprx/libgcm_sys/gcm_wrapper.c @@ -8,6 +8,34 @@ extern void gcmSetSecondVHandlerEx(opd32 *opd); extern void gcmSetUserHandlerEx(opd32 *opd); extern void gcmSetQueueHandlerEx(opd32 *opd); extern void gcmSetUserCommandEx(opd32 *opd); +extern s32 gcmIoOffsetToAddressEx(u32 offset,void* ATTRIBUTE_PRXPTR *address); +extern s32 gcmInitBodyEx(gcmContextData* ATTRIBUTE_PRXPTR *ctx,const u32 cmdSize,const u32 ioSize,const void *ioAddress); + +s32 gcmInitBody(gcmContextData **ctx,const u32 cmdSize,const u32 ioSize,const void *ioAddress) +{ + s32 ret; + gcmContextData *context ATTRIBUTE_PRXPTR; + + if(ctx == NULL) return -1; + + ret = gcmInitBodyEx(&context,cmdSize,ioSize,ioAddress); + *ctx = ret == 0 ? context : NULL; + + return ret; +} + +s32 gcmIoOffsetToAddress(u32 offset,void **address) +{ + s32 ret; + void *addr ATTRIBUTE_PRXPTR; + + if(address == NULL) return -1; + + ret = gcmIoOffsetToAddressEx(offset,&addr); + *address = ret == 0 ? addr : NULL; + + return ret; +} void gcmSetVBlankHandler(void (*handler)(const u32 head)) { diff --git a/ppu/sprx/liblv2/Makefile b/ppu/sprx/liblv2/Makefile index 71060056..9da9e180 100644 --- a/ppu/sprx/liblv2/Makefile +++ b/ppu/sprx/liblv2/Makefile @@ -76,7 +76,8 @@ install: all @cp -frv $(CURDIR)/lib/ppu/*.a $(PSL1GHT)/ppu/lib #--------------------------------------------------------------------------------- -$(LIBRARY).a: sprx.o thread_wrapper.o spu_printf_wrapper.o process_wrapper.o +$(LIBRARY).a: sprx.o thread_wrapper.o spu_printf_wrapper.o process_wrapper.o \ + spu_thread_printf.o sys_spu_wrapper.o #--------------------------------------------------------------------------------- .PHONY: lib ppu install diff --git a/ppu/sprx/liblv2/exports.h b/ppu/sprx/liblv2/exports.h index bd3570c5..ba362e3a 100644 --- a/ppu/sprx/liblv2/exports.h +++ b/ppu/sprx/liblv2/exports.h @@ -139,12 +139,14 @@ EXPORT(sysSpuRawLoad, 0x893305fa); EXPORT(sysSpuRawImageLoad, 0xb995662e); EXPORT(sysSpuImageClose, 0xe0da8efd); EXPORT(sysSpuImageImport, 0xebe5f72f); -EXPORT(sysSpuPrintfAttachGroup, 0xdd0c1e09); /* sysPrxForUser */ -EXPORT(sysSpuPrintfAttachThread, 0x1ae10b92); /* sysPrxForUser */ -EXPORT(sysSpuPrintfDetachGroup, 0x5fdfb2fe); /* sysPrxForUser */ -EXPORT(sysSpuPrintfDetachThread, 0xb3bbcf2a); /* sysPrxForUser */ -EXPORT(sysSpuPrintfFinalize, 0xdd3b27ac); /* sysPrxForUser */ -EXPORT(sysSpuPrintfInitialize, 0x45fe2fce); /* sysPrxForUser */ + +/* the following exports are renamed due to our own _working_ implementation */ +EXPORT(sysSpuPrintfAttachGroupEx, 0xdd0c1e09); /* sysPrxForUser */ +EXPORT(sysSpuPrintfAttachThreadEx, 0x1ae10b92); /* sysPrxForUser */ +EXPORT(sysSpuPrintfDetachGroupEx, 0x5fdfb2fe); /* sysPrxForUser */ +EXPORT(sysSpuPrintfDetachThreadEx, 0xb3bbcf2a); /* sysPrxForUser */ +EXPORT(sysSpuPrintfFinalizeEx, 0xdd3b27ac); /* sysPrxForUser */ +EXPORT(sysSpuPrintfInitializeEx, 0x45fe2fce); /* sysPrxForUser */ /* console */ EXPORT(sysConsoleGetc, 0x8a2f159b); /* sysPrxForUser */ diff --git a/ppu/sprx/liblv2/spu_printf_wrapper.c b/ppu/sprx/liblv2/spu_printf_wrapper.c index 837179b2..55a12c15 100644 --- a/ppu/sprx/liblv2/spu_printf_wrapper.c +++ b/ppu/sprx/liblv2/spu_printf_wrapper.c @@ -1,12 +1,179 @@ #include #include #include -#include +#include +#include +#include +#include +#define SPU_PORT_PRINTF 1 +#define MAX_QUEUE_SIZE 127 +#define TERMINATING_PORT_NAME 0xFEE1DEAD -extern s32 sysSpuPrintfInitializeEx(int prio, opd32 *opdentry); +static u32 spu_printf_initialized = 0; -s32 sysSpuPrintfInitialize(int prio,void (*entry)(void*)) +static sys_event_queue_t eventQ; +static sys_ppu_thread_t spu_printf_handler; +static sys_event_port_t terminating_port; + +static void spu_printf_handler_entry(void *arg) +{ + int ret; + + (void)arg; + + for(;;) { + sys_event_t event; + sys_spu_thread_t thread_id; + + ret = sysEventQueueReceive(eventQ, &event, 0); + + + if (event.source == TERMINATING_PORT_NAME) { + sysThreadExit(0); + } + thread_id = event.data_1; + int sret = spu_thread_printf(thread_id, event.data_3); + ret = sysSpuThreadWriteMb(thread_id, sret); + if (ret) { + sysThreadExit(-1); + } + } + + sysThreadExit(0); +} + +s32 sysSpuPrintfInitialize(int prio, void (*entry)(void*)) +{ + s32 ret; + void (*run_entry)(void*) = entry; + sys_event_queue_attr_t evQAttr = { SYS_EVENT_QUEUE_PRIO, SYS_EVENT_QUEUE_PPU, "" }; + + if(spu_printf_initialized) + return 0; + + if(run_entry == NULL) + run_entry = spu_printf_handler_entry; + + ret = sysEventQueueCreate(&eventQ, &evQAttr, SYS_EVENT_QUEUE_KEY_LOCAL, MAX_QUEUE_SIZE); + if (ret) { + return ret; + } + + ret = sysThreadCreate(&spu_printf_handler, run_entry, &eventQ, 200, 4096, THREAD_JOINABLE, "spu_printf_handler"); + if (ret) { + return ret; + } + + ret = sysEventPortCreate(&terminating_port, SYS_EVENT_PORT_LOCAL, TERMINATING_PORT_NAME); + if (ret) { + return ret; + } + + ret = sysEventPortConnectLocal(terminating_port, eventQ); + if (ret) { + return ret; + } + + spu_printf_initialized = 1; + return 0; +} + +s32 sysSpuPrintfAttachThread(sys_spu_thread_t thread) { - return sysSpuPrintfInitializeEx(prio,(opd32*)__get_opd32(entry)); + int ret; + + if(!spu_printf_initialized) + return -1; + + /*attach event_port for spu_printf to thread*/ + ret = sysSpuThreadConnectEvent(thread, eventQ, SPU_THREAD_EVENT_USER, SPU_PORT_PRINTF); + if (ret) { + return ret; + } + return 0; +} + +s32 sysSpuPrintfDetachThread(sys_spu_thread_t thread) +{ + s32 ret; + + if(!spu_printf_initialized) + return -1; + + /*dettach event_port for spu_printf from thread*/ + ret = sysSpuThreadDisconnectEvent(thread, SPU_THREAD_EVENT_USER, SPU_PORT_PRINTF); + if (ret) { + return ret; + } + return 0; +} + +s32 sysSpuPrintfAttachGroup(sys_spu_group_t group) +{ + int ret; + + if(!spu_printf_initialized) + return -1; + + ret = sysSpuThreadGroupConnectEvent(group, eventQ, SPU_THREAD_EVENT_USER); + if (ret) { + return ret; + } + return 0; +} + +s32 sysSpuPrintfDetachGroup(sys_spu_group_t group) +{ + int ret; + + if(!spu_printf_initialized) + return -1; + + ret = sysSpuThreadGroupDisconnectEvent(group, SPU_THREAD_EVENT_USER); + if (ret) { + return ret; + } + return 0; +} + +s32 sysSpuPrintfFinalize() +{ + s32 ret; + u64 exit_code; + + if(!spu_printf_initialized) + return 0; + + /* + * send event for temination. + */ + ret = sysEventPortSend(terminating_port, 0, 0, 0); + if (ret) { + return ret; + } + /* wait for termination of the handler thread */ + ret = sysThreadJoin(spu_printf_handler, &exit_code); + if (ret) { + return ret; + } + + /* Disconnect and destroy the terminating port */ + ret = sysEventPortDisconnect(terminating_port); + if (ret) { + return ret; + } + ret = sysEventPortDestroy(terminating_port); + if (ret) { + return ret; + } + + /* clean event_queue for spu_printf */ + ret = sysEventQueueDestroy(eventQ, 0); + if (ret) { + return ret; + } + + spu_printf_initialized = 0; + return 0; } diff --git a/ppu/sprx/liblv2/spu_thread_printf.c b/ppu/sprx/liblv2/spu_thread_printf.c new file mode 100644 index 00000000..42d91106 --- /dev/null +++ b/ppu/sprx/liblv2/spu_thread_printf.c @@ -0,0 +1,319 @@ +#include +#include +#include +#include +#include + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +#define is_digit(c) ((c) >= '0' && (c) <= '9') + +#define __DOUTBUFSIZE 256 + +#define do_div(n,base) \ +({ \ + s64 _res; \ + _res = ((u64)n)%(unsigned)base; \ + n = ((u64)n)/(unsigned)base; \ + _res; \ +}) + +#define GET_CHAR(pos) \ +({ \ + u64 _rval; \ + sysSpuThreadReadLocalStorage(id, pos, &_rval, sizeof(char)); \ + (char)_rval; \ +}) + +#define GET_ARG(pos, type) \ +({ \ + u64 _rval; \ + sysSpuThreadReadLocalStorage(id, arg_addr + (1 + pos)*16, &_rval, sizeof(type)); \ + (type)_rval; \ +}) + +static char __outstr[__DOUTBUFSIZE] __attribute__((aligned(16))); + +static int skip_atoi(sys_spu_thread_t id, u64 *fmt_addr) +{ + char c; + int i = 0; + + while(is_digit((c=GET_CHAR(*fmt_addr)))) { + i = i*10 + c - '0'; + (*fmt_addr)++; + } + + return i; +} + +static int __strnlen(sys_spu_thread_t id, u32 arg_addr, s32 maxlen) +{ + char c; + int len = 0; + + for(;;) { + c = GET_CHAR(arg_addr++); + if(c == 0) break; + if(maxlen > -1 && len >= maxlen) break; + len++; + } + return len; +} + +static char* print_string(sys_spu_thread_t id, char *str, u32 arg_addr, s32 field_width, s32 precision, u32 flags) +{ + int i, len; + const char *nil = ""; + + if(arg_addr == 0) { + len = strnlen(nil, precision); + + if(!(flags&LEFT)) + while(len < field_width--) + *str++ = ' '; + for(i=0;i < len;i++) + *str++ = *nil++; + while(len < field_width--) + *str++ = ' '; + return str; + } + + len = __strnlen(id, arg_addr, precision); + if(!(flags&LEFT)) + while(len < field_width--) + *str++ = ' '; + for(i=0;i < len;i++) + *str++ = GET_CHAR(arg_addr++); + while(len < field_width--) + *str++ = ' '; + return str; +} + +static char* number(char *str, s64 num, s32 base, s32 size, s32 precision, s32 type) +{ + int i; + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + + if(type&LARGE) + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if(type&LEFT) + type &= ~ZEROPAD; + if(base < 2 || base > 36) + return NULL; + + sign = 0; + c = (type&ZEROPAD) ? '0' : ' '; + if(type&SIGN) { + if(num < 0) { + sign = '-';sys_mem_container_t + num = -num; + size--; + } else if(type&PLUS) { + sign = '+'; + size--; + } else if(type&SPACE) { + sign = ' '; + size--; + } + } + if(type&SPECIAL) { + if(base == 16) + size -= 2; + else if(base == 8) + size--; + } + + i = 0; + if(num == 0) + tmp[i++] = '0'; + else while(num != 0) + tmp[i++] = digits[do_div(num,base)]; + + if(i > precision) + precision = i; + size -= precision; + + if(!(type&(ZEROPAD + LEFT))) + while(size-- > 0) + *str++ = ' '; + + if(sign) + *str++ = sign; + + if(type&SPECIAL) { + if(base == 8) + *str++ = '0'; + else if(base == 16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + + if(!(type&LEFT)) + while(size-- > 0) + *str++ = c; + while(i < precision--) + *str++ = '0'; + while(i-- > 0) + *str++ = tmp[i]; + while(size-- > 0) + *str++ = ' '; + + return str; +} + +static char* number_double(char *str, double num, s32 size, s32 precision, s32 type) +{ + return NULL; +} + +s32 spu_thread_sprintf(char *buf, sys_spu_thread_t id, u32 arg_addr) +{ + u64 num; + s32 base; + u32 flags; + char *str; + u32 arg_pos; + u64 fmt_addr; + s32 qualifier; + s32 precision; + s32 field_width; + ieee32 v; + + sysSpuThreadReadLocalStorage(id, arg_addr, &fmt_addr, sizeof(u32)); + + arg_pos = 0; + for(str=buf;;fmt_addr++) { + char c; + + c = GET_CHAR(fmt_addr); + if(c == 0) break; + + if(c != '%') { + *str++ = c; + continue; + } + + flags = 0; +repeat: + ++fmt_addr; + switch(GET_CHAR(fmt_addr)) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + field_width = -1; + c = GET_CHAR(fmt_addr); + if(is_digit(c)) + field_width = skip_atoi(id, &fmt_addr); + else if(c == '*') { + ++fmt_addr; + + field_width = GET_ARG(arg_pos++, int); + if(field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + precision = -1; + c = GET_CHAR(fmt_addr); + if(c == '.') { + ++fmt_addr; + c = GET_CHAR(fmt_addr); + if(is_digit(c)) { + precision = skip_atoi(id, &fmt_addr); + } else if(c == '*') { + ++fmt_addr; + precision = GET_ARG(arg_pos++, int); + } + if(precision < 0) + precision = 0; + } + + qualifier = -1; + c = GET_CHAR(fmt_addr); + if(c == 'h' || c == 'l' || c == 'L') { + qualifier = c; + ++fmt_addr; + } + + base = 10; + c = GET_CHAR(fmt_addr); + switch(c) { + case 'c': + if(!(flags&LEFT)) + while(--field_width>0) + *str++ = ' '; + *str++ = GET_ARG(arg_pos++, char); + while(--field_width>0) + *str++ = ' '; + continue; + case 's': + str = print_string(id, str, GET_ARG(arg_pos++, u64), field_width, precision, flags); + continue; + case '%': + *str++ = '%'; + continue; + case 'o': + base = 8; + break; + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + case 'f': + v.u = GET_ARG(arg_pos++, unsigned int); + str = number_double(str, v.f, field_width, precision, flags); + break; + default: + *str++ = '%'; + if(c) + *str++ = c; + else + --fmt_addr; + continue; + } + if(qualifier == 'l') + num = GET_ARG(arg_pos++, u64); + else if(qualifier == 'h') { + num = GET_ARG(arg_pos++, u16); + if(flags&SIGN) + num = (s16)num; + } else if(flags&SIGN) + num = GET_ARG(arg_pos++, s32); + else + num = GET_ARG(arg_pos++, u32); + str = number(str, num, base, field_width, precision, flags); + } + + *str = '\0'; + return str - buf; +} + +s32 spu_thread_printf(sys_spu_thread_t id, u32 arg_addr) +{ + int len; + + len = spu_thread_sprintf(__outstr, id, arg_addr); + fwrite(__outstr, 1, len, stdout); + + return len; +} diff --git a/ppu/sprx/liblv2/sys_spu_wrapper.c b/ppu/sprx/liblv2/sys_spu_wrapper.c new file mode 100644 index 00000000..4a2bb836 --- /dev/null +++ b/ppu/sprx/liblv2/sys_spu_wrapper.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include +#include + +s32 sysSpuImageOpenELF(sysSpuImage* image, const char* path) +{ + s32 ret = -1; + FILE *fd = NULL; + size_t fsize = 0; + void *data = NULL; + + fd = fopen(path, "rb"); + if(fd != NULL) { + fseek(fd,0,SEEK_END); + fsize = ftell(fd); + fseek(fd,0,SEEK_SET); + + data = malloc(fsize); + if(data != NULL) { + fread(data,1,fsize,fd); + ret = sysSpuImageImport(image,data,SPU_IMAGE_PROTECT); + free(data); + } + fclose(fd); + } + + return ret; +} diff --git a/samples/graphics/cairo/source/rsxutil.c b/samples/graphics/cairo/source/rsxutil.c index bce58746..ec417867 100644 --- a/samples/graphics/cairo/source/rsxutil.c +++ b/samples/graphics/cairo/source/rsxutil.c @@ -108,7 +108,7 @@ initScreen (void *host_addr, u32 size) videoResolution res; /* Screen Resolution */ /* Initilise Reality, which sets up the command buffer and shared IO memory */ - context = rsxInit (CB_SIZE, size, host_addr); + rsxInit (&context, CB_SIZE, size, host_addr); if (context == NULL) goto error; @@ -191,8 +191,8 @@ setRenderTarget(gcmContextData *context, rsxBuffer *buffer) { gcmSurface sf; - sf.colorFormat = GCM_TF_COLOR_X8R8G8B8; - sf.colorTarget = GCM_TF_TARGET_0; + sf.colorFormat = GCM_SURFACE_X8R8G8B8; + sf.colorTarget = GCM_SURFACE_TARGET_0; sf.colorLocation[0] = GCM_LOCATION_RSX; sf.colorOffset[0] = buffer->offset; sf.colorPitch[0] = depth_pitch; @@ -207,13 +207,13 @@ setRenderTarget(gcmContextData *context, rsxBuffer *buffer) sf.colorPitch[2] = 64; sf.colorPitch[3] = 64; - sf.depthFormat = GCM_TF_ZETA_Z16; + sf.depthFormat = GCM_SURFACE_ZETA_Z16; sf.depthLocation = GCM_LOCATION_RSX; sf.depthOffset = depth_offset; sf.depthPitch = depth_pitch; - sf.type = GCM_TF_TYPE_LINEAR; - sf.antiAlias = GCM_TF_CENTER_1; + sf.type = GCM_TEXTURE_LINEAR; + sf.antiAlias = GCM_SURFACE_CENTER_1; sf.width = buffer->width; sf.height = buffer->height; diff --git a/samples/graphics/rsxtest/source/main.cpp b/samples/graphics/rsxtest/source/main.cpp index ce736554..bf9d36ef 100644 --- a/samples/graphics/rsxtest/source/main.cpp +++ b/samples/graphics/rsxtest/source/main.cpp @@ -380,15 +380,20 @@ static void setDrawEnv() void init_shader() { u32 fpsize = 0; + u32 vpsize = 0; + + rsxVertexProgramGetUCode(vpo, &vp_ucode, &vpsize); + printf("vpsize: %d\n", vpsize); - vp_ucode = rsxVertexProgramGetUCode(vpo); projMatrix_id = rsxVertexProgramGetConst(vpo,"projMatrix"); modelViewMatrix_id = rsxVertexProgramGetConst(vpo,"modelViewMatrix"); vertexPosition_id = rsxVertexProgramGetAttrib(vpo,"vertexPosition"); vertexNormal_id = rsxVertexProgramGetAttrib(vpo,"vertexNormal"); vertexTexcoord_id = rsxVertexProgramGetAttrib(vpo,"vertexTexcoord"); - fp_ucode = rsxFragmentProgramGetUCode(fpo,&fpsize); + rsxFragmentProgramGetUCode(fpo, &fp_ucode, &fpsize); + printf("fpsize: %d\n", fpsize); + fp_buffer = (u32*)rsxMemalign(64,fpsize); memcpy(fp_buffer,fp_ucode,fpsize); rsxAddressToOffset(fp_buffer,&fp_offset); @@ -442,7 +447,7 @@ void drawFrame() setDrawEnv(); rsxSetClearColor(context,color); - rsxSetClearDepthValue(context,0xffff); + rsxSetClearDepthStencil(context,0xffff); rsxClearSurface(context,GCM_CLEAR_R | GCM_CLEAR_G | GCM_CLEAR_B | @@ -450,7 +455,7 @@ void drawFrame() GCM_CLEAR_S | GCM_CLEAR_Z); - rsxZControl(context,0,1,1); + rsxSetZControl(context,0,1,1); for(i=0;i<8;i++) rsxSetViewportClip(context,i,display_width,display_height); @@ -480,14 +485,14 @@ void drawFrame() rsxSetVertexProgramParameter(context,vpo,projMatrix_id,(float*)&P); rsxSetVertexProgramParameter(context,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); - rsxSetFragmentProgramParameter(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,lightColor_id,lightColor,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,shininess_id,&shininess,fp_offset); + rsxSetFragmentProgramParameter(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,Kd_id,materialColorDiffuse,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,Ks_id,materialColorSpecular,fp_offset); + rsxSetFragmentProgramParameter(context,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); rsxLoadFragmentProgramLocation(context,fpo,fp_offset,GCM_LOCATION_RSX); @@ -526,14 +531,14 @@ void drawFrame() rsxSetVertexProgramParameter(context,vpo,projMatrix_id,(float*)&P); rsxSetVertexProgramParameter(context,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); - rsxSetFragmentProgramParameter(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,lightColor_id,lightColor,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,shininess_id,&shininess,fp_offset); + rsxSetFragmentProgramParameter(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,Kd_id,materialColorDiffuse,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,Ks_id,materialColorSpecular,fp_offset); + rsxSetFragmentProgramParameter(context,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); rsxLoadFragmentProgramLocation(context,fpo,fp_offset,GCM_LOCATION_RSX); diff --git a/samples/graphics/rsxtest/source/rsxutil.cpp b/samples/graphics/rsxtest/source/rsxutil.cpp index 83aa5ef8..01e6a667 100644 --- a/samples/graphics/rsxtest/source/rsxutil.cpp +++ b/samples/graphics/rsxtest/source/rsxutil.cpp @@ -57,8 +57,8 @@ void setRenderTarget(u32 index) { gcmSurface sf; - sf.colorFormat = GCM_TF_COLOR_X8R8G8B8; - sf.colorTarget = GCM_TF_TARGET_0; + sf.colorFormat = GCM_SURFACE_X8R8G8B8; + sf.colorTarget = GCM_SURFACE_TARGET_0; sf.colorLocation[0] = GCM_LOCATION_RSX; sf.colorOffset[0] = color_offset[index]; sf.colorPitch[0] = color_pitch; @@ -73,13 +73,13 @@ void setRenderTarget(u32 index) sf.colorPitch[2] = 64; sf.colorPitch[3] = 64; - sf.depthFormat = GCM_TF_ZETA_Z16; + sf.depthFormat = GCM_SURFACE_ZETA_Z16; sf.depthLocation = GCM_LOCATION_RSX; sf.depthOffset = depth_offset; sf.depthPitch = depth_pitch; - sf.type = GCM_TF_TYPE_LINEAR; - sf.antiAlias = GCM_TF_CENTER_1; + sf.type = GCM_SURFACE_TYPE_LINEAR; + sf.antiAlias = GCM_SURFACE_CENTER_1; sf.width = display_width; sf.height = display_height; @@ -91,7 +91,7 @@ void setRenderTarget(u32 index) void init_screen(void *host_addr,u32 size) { - context = rsxInit(CB_SIZE,size,host_addr); + rsxInit(&context,CB_SIZE,size,host_addr); videoState state; videoGetState(0,0,&state); diff --git a/samples/graphics/rsxtest_spu/source/main.cpp b/samples/graphics/rsxtest_spu/source/main.cpp index d3686660..18d144b6 100644 --- a/samples/graphics/rsxtest_spu/source/main.cpp +++ b/samples/graphics/rsxtest_spu/source/main.cpp @@ -160,15 +160,20 @@ static void setDrawEnv() void init_shader() { u32 fpsize = 0; + u32 vpsize = 0; + + rsxVertexProgramGetUCode(vpo, &vp_ucode, &vpsize); + printf("vpsize: %d\n", vpsize); - vp_ucode = rsxVertexProgramGetUCode(vpo); projMatrix_id = rsxVertexProgramGetConst(vpo,"projMatrix"); modelViewMatrix_id = rsxVertexProgramGetConst(vpo,"modelViewMatrix"); vertexPosition_id = rsxVertexProgramGetAttrib(vpo,"vertexPosition"); vertexNormal_id = rsxVertexProgramGetAttrib(vpo,"vertexNormal"); vertexTexcoord_id = rsxVertexProgramGetAttrib(vpo,"vertexTexcoord"); - fp_ucode = rsxFragmentProgramGetUCode(fpo,&fpsize); + rsxFragmentProgramGetUCode(fpo,&fp_ucode,&fpsize); + printf("fpsize: %d\n", fpsize); + fp_buffer = (u32*)rsxMemalign(128,fpsize); memcpy(fp_buffer,fp_ucode,fpsize); rsxAddressToOffset(fp_buffer,&fp_offset); @@ -236,7 +241,7 @@ void drawFrame() setDrawEnv(); rsxSetClearColor(context,color); - rsxSetClearDepthValue(context,0xffff); + rsxSetClearDepthStencil(context,0xffff); rsxClearSurface(context,GCM_CLEAR_R | GCM_CLEAR_G | GCM_CLEAR_B | @@ -244,7 +249,7 @@ void drawFrame() GCM_CLEAR_S | GCM_CLEAR_Z); - rsxZControl(context,0,1,1); + rsxSetZControl(context,0,1,1); for(i=0;i<8;i++) rsxSetViewportClip(context,i,display_width,display_height); @@ -271,14 +276,14 @@ void drawFrame() rsxSetVertexProgramParameter(context,vpo,projMatrix_id,(float*)&P); rsxSetVertexProgramParameter(context,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); - rsxSetFragmentProgramParameter(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset); - //rsxSetFragmentProgramParameter(context,fpo,lightColor_id,lightColor,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,shininess_id,&shininess,fp_offset); + rsxSetFragmentProgramParameter(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,Kd_id,materialColorDiffuse,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,Ks_id,materialColorSpecular,fp_offset); + rsxSetFragmentProgramParameter(context,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); rsxLoadFragmentProgramLocation(context,fpo,fp_offset,GCM_LOCATION_RSX); @@ -311,14 +316,14 @@ void drawFrame() rsxSetVertexProgramParameter(context,vpo,projMatrix_id,(float*)&P); rsxSetVertexProgramParameter(context,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); - rsxSetFragmentProgramParameter(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,lightColor_id,lightColor,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,shininess_id,&shininess,fp_offset); + rsxSetFragmentProgramParameter(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,Kd_id,materialColorDiffuse,fp_offset); - rsxSetFragmentProgramParameter(context,fpo,Ks_id,materialColorSpecular,fp_offset); + rsxSetFragmentProgramParameter(context,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameter(context,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); rsxLoadFragmentProgramLocation(context,fpo,fp_offset,GCM_LOCATION_RSX); diff --git a/samples/graphics/rsxtest_spu/source/rsxutil.cpp b/samples/graphics/rsxtest_spu/source/rsxutil.cpp index f13a77bf..87692ab7 100644 --- a/samples/graphics/rsxtest_spu/source/rsxutil.cpp +++ b/samples/graphics/rsxtest_spu/source/rsxutil.cpp @@ -59,8 +59,8 @@ void setRenderTarget(u32 index) { gcmSurface sf; - sf.colorFormat = GCM_TF_COLOR_X8R8G8B8; - sf.colorTarget = GCM_TF_TARGET_0; + sf.colorFormat = GCM_SURFACE_X8R8G8B8; + sf.colorTarget = GCM_SURFACE_TARGET_0; sf.colorLocation[0] = GCM_LOCATION_RSX; sf.colorOffset[0] = color_offset[index]; sf.colorPitch[0] = color_pitch; @@ -75,13 +75,13 @@ void setRenderTarget(u32 index) sf.colorPitch[2] = 64; sf.colorPitch[3] = 64; - sf.depthFormat = GCM_TF_ZETA_Z16; + sf.depthFormat = GCM_SURFACE_ZETA_Z16; sf.depthLocation = GCM_LOCATION_RSX; sf.depthOffset = depth_offset; sf.depthPitch = depth_pitch; - sf.type = GCM_TF_TYPE_LINEAR; - sf.antiAlias = GCM_TF_CENTER_1; + sf.type = GCM_SURFACE_TYPE_LINEAR; + sf.antiAlias = GCM_SURFACE_CENTER_1; sf.width = display_width; sf.height = display_height; @@ -95,7 +95,7 @@ void init_screen(void *host_addr,u32 size) { printf("initializing screen....\n"); - context = rsxInit(CB_SIZE,size,host_addr); + rsxInit(&context,CB_SIZE,size,host_addr); videoState state; videoGetState(0,0,&state); diff --git a/samples/spu/Makefile b/samples/spu/Makefile index 0f1e94d9..0d735cb0 100644 --- a/samples/spu/Makefile +++ b/samples/spu/Makefile @@ -1,4 +1,4 @@ -TARGETS := spuchain spudma sputest sputhread spuparallel +TARGETS := spuchain spudma sputest sputhread spuparallel spumars all: @for TARGET in $(TARGETS); do $(MAKE) --no-print-directory -C $$TARGET bin; done diff --git a/samples/spu/spumars/Makefile b/samples/spu/spumars/Makefile new file mode 100644 index 00000000..e06a7101 --- /dev/null +++ b/samples/spu/spumars/Makefile @@ -0,0 +1,145 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PSL1GHT)),) +$(error "Please set PSL1GHT in your environment. export PSL1GHT=") +endif + +include $(PSL1GHT)/ppu_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := + +TITLE := SPU MARS sample - PSL1GHT +APPID := SPUMARS001 +CONTENTID := UP0001-$(APPID)_00-0000000000000000 +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +CFLAGS = -O2 -Wall -mcpu=cell $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lspumars -lrt -llv2 -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export BUILDDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES), -I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(LIBPSL1GHT_INC) \ + -I$(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + $(LIBPSL1GHT_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean bin + +#--------------------------------------------------------------------------------- +$(BUILD): bin + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +bin: + @$(MAKE) --no-print-directory -C spu + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @$(MAKE) --no-print-directory -C spu clean + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).self $(DATA)/spu.bin + +#--------------------------------------------------------------------------------- +run: + ps3load $(OUTPUT).self + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).self: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .bin extension +#--------------------------------------------------------------------------------- +%.bin.o: %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/samples/spu/spumars/source/main.c b/samples/spu/spumars/source/main.c new file mode 100644 index 00000000..7d03ecca --- /dev/null +++ b/samples/spu/spumars/source/main.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "spu_bin.h" + +#define NUM_TASKS 10 + +int main(int argc,char *argv[]) +{ + struct mars_task_args task_args; + struct mars_context *mars_ctx; + struct mars_task_id task_id[NUM_TASKS]; + int i; + u64 barrier_ea; + + sysSpuInitialize(6,0); + sysSpuPrintfInitialize(200, NULL); + + mars_context_create(&mars_ctx, 6, 200, 100); + mars_task_barrier_create(mars_ctx, &barrier_ea, NUM_TASKS); + + for(i=0;i < NUM_TASKS;i++) { + char name[16]; + + sprintf(name, "Task %d", i); + + mars_task_create(mars_ctx, &task_id[i], name, spu_bin, MARS_TASK_CONTEXT_SAVE_SIZE_MAX); + + task_args.type.u64[0] = barrier_ea; + mars_task_schedule(&task_id[i], &task_args, 0); + } + + for(i=0;i < NUM_TASKS;i++) { + mars_task_wait(&task_id[i], NULL); + mars_task_destroy(&task_id[i]); + } + + mars_task_barrier_destroy(barrier_ea); + mars_context_destroy(mars_ctx); + + return 0; +} \ No newline at end of file diff --git a/samples/spu/spumars/spu/Makefile b/samples/spu/spumars/spu/Makefile new file mode 100644 index 00000000..17a6df1b --- /dev/null +++ b/samples/spu/spumars/spu/Makefile @@ -0,0 +1,126 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PSL1GHT)),) +$(error "Please set PSL1GHT in your environment. export PSL1GHT=") +endif + +include $(PSL1GHT)/spu_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := +INCLUDES := include + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +CFLAGS = -O2 -Wall $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lspumars -lsputhread + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export BUILDDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES), -I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(LIBPSL1GHT_INC) \ + -I$(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + $(LIBPSL1GHT_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +../../data/spu.bin: $(OUTPUT).task + cp $< $@ + +$(OUTPUT).task: $(OFILES) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/samples/spu/spumars/spu/source/main.c b/samples/spu/spumars/spu/source/main.c new file mode 100644 index 00000000..d3910c1a --- /dev/null +++ b/samples/spu/spumars/spu/source/main.c @@ -0,0 +1,22 @@ +#include +#include +#include + +#define ITERATIONS 3 + +int mars_task_main(const struct mars_task_args *task_args) +{ + int i; + uint64_t barrier_ea = task_args->type.u64[0]; + + spu_printf("MPU(%d): %s - Hello!\n", mars_task_get_kernel_id(), mars_task_get_name()); + + for(i=0;i < ITERATIONS;i++) { + spu_printf("MPU(%d): %s pre barrier work\n", mars_task_get_kernel_id(), mars_task_get_name()); + mars_task_barrier_notify(barrier_ea); + mars_task_barrier_wait(barrier_ea); + spu_printf("MPU(%d): %s post barrier work\n", mars_task_get_kernel_id(), mars_task_get_name()); + } + + return 0; +} diff --git a/samples/spu/spuparallel/source/main.c b/samples/spu/spuparallel/source/main.c index 7ef041c6..eabdc045 100644 --- a/samples/spu/spuparallel/source/main.c +++ b/samples/spu/spuparallel/source/main.c @@ -41,8 +41,8 @@ int main(int argc, const char* argv[]) { sysSpuImage image; u32 group_id; - sysSpuThreadAttribute attr = { ptr2ea("mythread"), 8+1, SPU_THREAD_ATTR_NONE }; - sysSpuThreadGroupAttribute grpattr = { 7+1, ptr2ea("mygroup"), 0, 0 }; + sysSpuThreadAttribute attr = { "mythread", 8+1, SPU_THREAD_ATTR_NONE }; + sysSpuThreadGroupAttribute grpattr = { 7+1, "mygroup", 0, {0} }; sysSpuThreadArgument arg[6]; u32 cause, status; int i; diff --git a/spu/include/sys/spu_event.h b/spu/include/sys/spu_event.h index 88bbe7df..832ab799 100644 --- a/spu/include/sys/spu_event.h +++ b/spu/include/sys/spu_event.h @@ -14,7 +14,9 @@ #define EVENT_DATA0_MASK 0x00FFFFFF #define EVENT_PORT_SHIFT 24 -#define EVENT_PORT_MAX_NUM 63 +#define EVENT_PORT_MAX_NUM 63 + +#define EVENT_PRINTF_PORT 1U #ifdef __cplusplus extern "C" { diff --git a/spu_rules b/spu_rules index 61759f02..ab1b4f44 100644 --- a/spu_rules +++ b/spu_rules @@ -13,10 +13,27 @@ export PORTLIBS := $(PS3DEV)/portlibs/spu export LIBPSL1GHT_INC := -I$(PSL1GHT)/spu/include -I$(PSL1GHT)/spu/include/simdmath export LIBPSL1GHT_LIB := -L$(PSL1GHT)/spu/lib +export WM_LDFLAGS := -nodefaultlibs \ + -Wl,--defsym=__stack=0x3fe0 -Wl,-Ttext-segment=0x2a00 \ + -Wl,--entry,mars_module_entry -Wl,-u,mars_module_entry \ + -Wl,--gc-sections \ + -Wl,--sort-common \ + -Wl,--sort-section -Wl,alignment \ + -Wl,-s \ + +export TASK_LDFLAGS := -Wl,-Ttext-segment=0x4000 -Wl,--gc-sections + PREFIX := spu- -MACHDEP = +MACHDEP = -mdual-nops -fmodulo-sched -ffunction-sections -fdata-sections include $(PSL1GHT)/base_rules #--------------------------------------------------------------------------------- +%.task: + @echo linking ... $(notdir $@) + @$(LD) $^ $(TASK_LDFLAGS) $(LDFLAGS) $(LIBPATHS) $(LIBS) -o $@ + +%.wm: + @echo linking ... $(notdir $@) + @$(LD) $^ $(WM_LDFLAGS) $(LDFLAGS) $(LIBPATHS) $(LIBS) -o $@ diff --git a/tools/cgcomp/include/compilerfp.h b/tools/cgcomp/include/compilerfp.h index a54f67cb..4aad129a 100644 --- a/tools/cgcomp/include/compilerfp.h +++ b/tools/cgcomp/include/compilerfp.h @@ -36,21 +36,32 @@ class CCompilerFP private: void Prepare(CParser *pParser); - void emit_insn(u8 op,struct nvfx_insn *insn); - void emit_dst(struct nvfx_reg *dst,bool *have_const); - void emit_src(s32 pos,struct nvfx_src *src,bool *have_const); + void emit_insn(struct nvfx_insn *insn,u8 op); + void emit_dst(struct nvfx_insn *insn,bool *have_const); + void emit_src(struct nvfx_insn *insn,s32 pos,bool *have_const); void emit_brk(struct nvfx_insn *insn); void emit_rep(struct nvfx_insn *insn); void emit_if(struct nvfx_insn *insn); + void emit_loop(struct nvfx_insn *insn); + void emit_lrp(struct nvfx_insn *insn); + void emit_pow(struct nvfx_insn *insn); + void emit_lit(struct nvfx_insn *insn); + void emit_ddx(struct nvfx_insn *insn); + void emit_ddy(struct nvfx_insn *insn); + void emit_txl(struct nvfx_insn *insn); + void emit_tex(struct nvfx_insn *insn,u8 op); void fixup_rep(); + void fixup_loop(); void fixup_if(); void fixup_else(); - void grow_insns(int count); + int grow_insns(int count); struct nvfx_reg temp(); void release_temps(); + struct nvfx_reg imm(f32 x, f32 y, f32 z, f32 w); + inline param GetImmData(int index) { s32 i; @@ -94,12 +105,11 @@ class CCompilerFP int m_rTemps; int m_rTempsDiscard; - struct nvfx_reg *m_rTemp; - std::list m_lParameters; std::list m_lConstData; std::stack m_repStack; std::stack m_ifStack; + std::stack m_loopStack; }; #endif diff --git a/tools/cgcomp/include/compiler.h b/tools/cgcomp/include/compilervp.h similarity index 60% rename from tools/cgcomp/include/compiler.h rename to tools/cgcomp/include/compilervp.h index a590be9f..3b7f291d 100644 --- a/tools/cgcomp/include/compiler.h +++ b/tools/cgcomp/include/compilervp.h @@ -14,11 +14,11 @@ struct vertex_program_data class CParser; -class CCompiler +class CCompilerVP { public: - CCompiler(); - virtual ~CCompiler(); + CCompilerVP(); + virtual ~CCompilerVP(); void Compile(CParser *pParser); @@ -33,15 +33,38 @@ class CCompiler private: void Prepare(CParser *pParser); - void emit_insn(u8 opcode,struct nvfx_insn *insn); - void emit_dst(u32 *hw,u8 slot,struct nvfx_insn *insn); - void emit_src(u32 *hw,u8 pos,struct nvfx_src *src); + void emit_insn(struct nvfx_insn *insn,u8 opcode); + void emit_dst(struct nvfx_insn *insn,u8 slot); + void emit_src(struct nvfx_insn *insn,u8 pos); + void emit_pow(struct nvfx_insn *insn); + void emit_abs(struct nvfx_insn *insn); + void emit_sub(struct nvfx_insn *insn); + void emit_tex(struct nvfx_insn *insn); + void emit_lrp(struct nvfx_insn *insn); + void emit_nop(); + + int grow_insns(int count); struct nvfx_reg temp(); struct nvfx_reg constant(s32 pipe,f32 x,f32 y,f32 z,f32 w); void release_temps(); + inline param GetInputAttrib(int index) + { + s32 i; + std::list::iterator it = m_lParameters.begin(); + for(;it!=m_lParameters.end();it++) { + for(i=0;icount;i++) { + if((int)(it->index + i)==index) { + if(!it->is_const && !it->is_internal) + return *it; + } + } + } + return param(); + } + int m_nNumRegs; int m_nInputMask; int m_nOutputMask; @@ -56,9 +79,9 @@ class CCompiler int m_rTemps; int m_rTempsDiscard; - struct nvfx_reg *m_rTemp; struct nvfx_reg *m_rConst; + std::list m_lParameters; std::list m_lConstRelocation; std::list m_lBranchRelocation; }; diff --git a/tools/cgcomp/include/fpparser.h b/tools/cgcomp/include/fpparser.h index 1c0cf8ab..8223acfa 100644 --- a/tools/cgcomp/include/fpparser.h +++ b/tools/cgcomp/include/fpparser.h @@ -7,39 +7,12 @@ #define NV_OPTION_FP2 0x02 #define NV_OPTION_FP3 0x04 -/** - * Bit flags for each type of texture object - * Used for Texture.Unit[]._ReallyEnabled flags. - */ -/*@{*/ -#define TEXTURE_2D_ARRAY_BIT (1 << TEXTURE_2D_ARRAY_INDEX) -#define TEXTURE_1D_ARRAY_BIT (1 << TEXTURE_1D_ARRAY_INDEX) -#define TEXTURE_CUBE_BIT (1 << TEXTURE_CUBE_INDEX) -#define TEXTURE_3D_BIT (1 << TEXTURE_3D_INDEX) -#define TEXTURE_RECT_BIT (1 << TEXTURE_RECT_INDEX) -#define TEXTURE_2D_BIT (1 << TEXTURE_2D_INDEX) -#define TEXTURE_1D_BIT (1 << TEXTURE_1D_INDEX) -/*@}*/ - typedef struct _oparam { std::string alias; s32 index; } oparam; -typedef enum -{ - TEXTURE_2D_ARRAY_INDEX, - TEXTURE_1D_ARRAY_INDEX, - TEXTURE_CUBE_INDEX, - TEXTURE_3D_INDEX, - TEXTURE_RECT_INDEX, - TEXTURE_2D_INDEX, - TEXTURE_1D_INDEX, - NUM_TEXTURE_TARGETS -} texture_index; - - class CFPParser : public CParser { public: @@ -53,8 +26,6 @@ class CFPParser : public CParser void ParseMaskedDstReg(const char *token,struct nvfx_insn *insn); void ParseVectorSrc(const char *token,struct nvfx_src *reg); void ParseScalarSrc(const char *token,struct nvfx_src *reg); - void ParseTextureUnit(const char *token,u8 *texUnit); - void ParseTextureTarget(const char *token,u8 *texTarget); void ParseOutput(const char *param_str); diff --git a/tools/cgcomp/include/nvfx_shader.h b/tools/cgcomp/include/nvfx_shader.h index 540aace6..0dc0f855 100644 --- a/tools/cgcomp/include/nvfx_shader.h +++ b/tools/cgcomp/include/nvfx_shader.h @@ -237,6 +237,7 @@ #define NVFX_FP_OP_OPCODE_TXP 0x18 #define NVFX_FP_OP_OPCODE_TXD 0x19 #define NVFX_FP_OP_OPCODE_RCP 0x1A +#define NVFX_FP_OP_OPCODE_RSQ 0x1B #define NVFX_FP_OP_OPCODE_EX2 0x1C #define NVFX_FP_OP_OPCODE_LG2 0x1D #define NVFX_FP_OP_OPCODE_STR 0x20 @@ -251,10 +252,11 @@ #define NVFX_FP_OP_OPCODE_UP2US 0x2A #define NVFX_FP_OP_OPCODE_DP2A 0x2E #define NVFX_FP_OP_OPCODE_TXB 0x31 +#define NVFX_FP_OP_OPCODE_DP2 0x38 +#define NVFX_FP_OP_OPCODE_NRM 0x39 #define NVFX_FP_OP_OPCODE_DIV 0x3A /* NV30 only fragment program opcodes */ -#define NVFX_FP_OP_OPCODE_RSQ_NV30 0x1B #define NVFX_FP_OP_OPCODE_LIT_NV30 0x1E #define NVFX_FP_OP_OPCODE_LRP_NV30 0x1F #define NVFX_FP_OP_OPCODE_POW_NV30 0x26 @@ -264,6 +266,9 @@ #define NVFX_FP_OP_OPCODE_TXL_NV40 0x2F #define NVFX_FP_OP_OPCODE_LITEX2_NV40 0x3C +/* NV40 (RSX) only fragment program opcodes */ +#define NVFX_FP_OP_OPCODE_DIVRSQ_NV40RSX 0x3B + /* The use of these instructions appears to be indicated by bit 31 of DWORD 2.*/ #define NV40_FP_OP_BRA_OPCODE_BRK 0x0 #define NV40_FP_OP_BRA_OPCODE_CAL 0x1 @@ -347,6 +352,9 @@ #define NVFX_FP_OP_INDEX_INPUT (1 << 30) #define NV40_FP_OP_ADDR_INDEX_SHIFT 19 #define NV40_FP_OP_ADDR_INDEX_MASK (0xF << 19) +#define NVFX_FP_OP_SRC2_ABS (1 << 18) + +#define NV40_FP_OP_DISABLE_PC_SHIFT 31 //== Register selection == #define NVFX_FP_REG_TYPE_SHIFT 0 @@ -381,6 +389,8 @@ #define NVFXSR_CONST 5 #define NVFXSR_IMM 6 #define NVFXSR_RELOCATED 7 +#define NVFXSR_SAMPLER 8 +#define NVFXSR_ADDRESS 9 #define NVFX_COND_FL 0 #define NVFX_COND_LT 1 @@ -545,7 +555,8 @@ struct nvfx_insn { u8 op; s8 scale; - s32 unit; + s32 tex_unit; + s32 tex_target; u8 mask; u8 precision; u8 cc_swz[4]; @@ -557,17 +568,20 @@ struct nvfx_insn u8 cc_test : 1; u8 cc_test_reg : 1; + u8 disable_pc : 1; + struct nvfx_reg dst; struct nvfx_src src[3]; }; -static INLINE struct nvfx_insn nvfx_insn(boolean sat,u32 op,s32 unit,struct nvfx_reg dst,u32 mask,struct nvfx_src s0,struct nvfx_src s1,struct nvfx_src s2) +static INLINE struct nvfx_insn nvfx_insn(boolean sat,u32 op,s32 unit,s32 target,struct nvfx_reg dst,u32 mask,struct nvfx_src s0,struct nvfx_src s1,struct nvfx_src s2) { struct nvfx_insn insn; insn.op = op; insn.scale = 0; - insn.unit = unit; + insn.tex_unit = unit; + insn.tex_target = target; insn.mask = mask; insn.precision = FLOAT32; insn.cc_swz[0] = 0; insn.cc_swz[1] = 1; insn.cc_swz[2] = 2; insn.cc_swz[3] = 3; @@ -577,6 +591,7 @@ static INLINE struct nvfx_insn nvfx_insn(boolean sat,u32 op,s32 unit,struct nvfx insn.cc_cond = NVFX_COND_TR; insn.cc_test = 0; insn.cc_test_reg = 0; + insn.disable_pc = 0; insn.dst = dst; insn.src[0] = s0; insn.src[1] = s1; insn.src[2] = s2; @@ -589,7 +604,8 @@ static INLINE struct nvfx_insn nvfx_insn_ctor(struct nvfx_insn *insn,struct nvfx dest_insn.op = insn->op; dest_insn.scale = insn->scale; - dest_insn.unit = insn->unit; + dest_insn.tex_unit = insn->tex_unit; + dest_insn.tex_target = insn->tex_target; dest_insn.mask = insn->mask; dest_insn.precision = insn->precision; dest_insn.cc_swz[0] = insn->cc_swz[0]; dest_insn.cc_swz[1] = insn->cc_swz[1]; dest_insn.cc_swz[2] = insn->cc_swz[2]; dest_insn.cc_swz[3] = insn->cc_swz[3]; @@ -599,6 +615,7 @@ static INLINE struct nvfx_insn nvfx_insn_ctor(struct nvfx_insn *insn,struct nvfx dest_insn.cc_cond = insn->cc_cond; dest_insn.cc_test = insn->cc_test; dest_insn.cc_test_reg = insn->cc_test_reg; + dest_insn.disable_pc = insn->disable_pc; dest_insn.dst = dst; dest_insn.src[0] = s0; dest_insn.src[1] = s1; dest_insn.src[2] = s2; diff --git a/tools/cgcomp/include/parser.h b/tools/cgcomp/include/parser.h index 7df5ee03..c3bf0ef5 100644 --- a/tools/cgcomp/include/parser.h +++ b/tools/cgcomp/include/parser.h @@ -30,17 +30,19 @@ #define MAX_NV_FRAGMENT_PROGRAM_WRITE_ONLYS 2 /*@}*/ -enum eparams { PARAM_FLOAT = 0,PARAM_FLOAT2,PARAM_FLOAT3,PARAM_FLOAT4,PARAM_FLOAT4x4,PARAM_SAMPLER1D,PARAM_SAMPLER2D,PARAM_SAMPLER3D,PARAM_SAMPLERCUBE,PARAM_SAMPLERRECT, PARAM_NULL = 0xff }; +enum eparams { PARAM_FLOAT = 0,PARAM_FLOAT1,PARAM_FLOAT2,PARAM_FLOAT3,PARAM_FLOAT4,PARAM_FLOAT3x4,PARAM_FLOAT4x4,PARAM_FLOAT3x3,PARAM_FLOAT4x3, + PARAM_SAMPLER1D,PARAM_SAMPLER2D,PARAM_SAMPLER3D,PARAM_SAMPLERCUBE,PARAM_SAMPLERRECT, PARAM_SAMPLERSHADOW1D, PARAM_SAMPLERSHADOW2D, + PARAM_SAMPLERSHADOWRECT, PARAM_NULL = 0xff }; typedef struct _jmpdst { - char ident[64]; + std::string ident; u32 location; } jmpdst; typedef struct _paramtype { - const char *ident; + std::string ident; enum eparams type; } paramtype; @@ -69,7 +71,7 @@ typedef struct _param typedef struct _ioset { - const char *name; + std::string name; int index; } ioset; @@ -88,6 +90,8 @@ class CParser std::list GetParameters() const {return m_lParameters;} + static void InitParameter(param *p); + protected: void ParseComment(const char *line); @@ -96,19 +100,20 @@ class CParser const char* ParseMaskedDstRegExt(const char *token,struct nvfx_insn *insn); const char* ParseCond(const char *token,struct nvfx_insn *insn); const char* ParseRegSwizzle(const char *token,struct nvfx_src *reg); + void ParseTextureUnit(const char *token,s32 *texUnit); + void ParseTextureTarget(const char *token,s32 *texTarget); s32 GetParamType(const char *param_str); virtual s32 ConvertInputReg(const char *token) = 0; virtual const char* ParseOutputMask(const char *token,u8 *mask) = 0; void InitInstruction(struct nvfx_insn *insn,u8 op); - void InitParameter(param *p); bool isLetter(int c); bool isDigit(int c); bool isWhitespace(int c); - inline char* SkipSpaces(char *ptr) + inline const char* SkipSpaces(const char *ptr) { while(ptr && *ptr==' ') { ptr++; diff --git a/tools/cgcomp/include/types.h b/tools/cgcomp/include/types.h index f9012429..eab5e58c 100644 --- a/tools/cgcomp/include/types.h +++ b/tools/cgcomp/include/types.h @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef WIN32 #include @@ -77,41 +78,44 @@ typedef double f64; typedef struct rsx_vp { u16 magic; - u16 num_attrib; - u32 attrib_off; + u16 _pad0; + + u16 num_regs; + u16 num_attr; + u16 num_const; + u16 num_insn; + + u32 attr_off; + u32 const_off; + u32 ucode_off; u32 input_mask; u32 output_mask; u16 const_start; - u16 num_const; - u32 const_off; - - u16 start_insn; - u16 num_insn; - u32 ucode_off; + u16 insn_start; } rsxVertexProgram; typedef struct rsx_fp { u16 magic; - u16 num_attrib; - u32 attrib_off; + u16 _pad0; + + u16 num_regs; + u16 num_attr; + u16 num_const; + u16 num_insn; + + u32 attr_off; + u32 const_off; + u32 ucode_off; - u32 num_regs; u32 fp_control; u16 texcoords; u16 texcoord2D; u16 texcoord3D; - - u16 _pad0; - - u16 num_const; - u32 const_off; - - u16 num_insn; - u32 ucode_off; + u16 _pad1; } rsxFragmentProgram; typedef struct rsx_const diff --git a/tools/cgcomp/include/vpparser.h b/tools/cgcomp/include/vpparser.h index 06df6190..5ac94114 100644 --- a/tools/cgcomp/include/vpparser.h +++ b/tools/cgcomp/include/vpparser.h @@ -20,7 +20,7 @@ class CVPParser : public CParser void ParseMaskedDstReg(const char *token,struct nvfx_insn *insn); void ParseMaskedDstAddr(const char *token,struct nvfx_insn *insn); - void ParseSwizzledSrcReg(const char *token,struct nvfx_src *reg); + void ParseSwizzledSrcReg(const char *token,struct nvfx_insn *insn,s32 slot); const char* ParseOutputReg(const char *token,s32 *reg); const char* ParseInputReg(const char *token,s32 *reg); diff --git a/tools/cgcomp/source/compilerfp.cpp b/tools/cgcomp/source/compilerfp.cpp index 9a1d8f7b..bf0a232d 100644 --- a/tools/cgcomp/source/compilerfp.cpp +++ b/tools/cgcomp/source/compilerfp.cpp @@ -4,21 +4,13 @@ #include "compilerfp.h" #define arith(s,d,m,s0,s1,s2) \ - nvfx_insn((s),0,-1,(d),(m),(s0),(s1),(s2)) + nvfx_insn((s),0,-1,-1,(d),(m),(s0),(s1),(s2)) #define arith_ctor(ins,d,s0,s1,s2) \ nvfx_insn_ctor((ins),(d),(s0),(s1),(s2)) -static INLINE s32 ffs(u32 u) -{ - u32 i = 0; - if(!(u&0xffffffff)) return 0; - while(!(u&0x1)) { - u >>= 1; - i++; - } - return i + 1; -} +#define src_abs_flag(pos) \ + ((pos) == 2 ? NVFX_FP_OP_SRC2_ABS : ((pos) == 1 ? NVFX_FP_OP_SRC1_ABS : NVFX_FP_OP_SRC0_ABS)) CCompilerFP::CCompilerFP() { @@ -33,7 +25,6 @@ CCompilerFP::CCompilerFP() m_rTempsDiscard = 0; m_nCurInstruction = 0; m_pInstructions = NULL; - m_rTemp = NULL; } CCompilerFP::~CCompilerFP() @@ -71,8 +62,7 @@ void CCompilerFP::Prepare(CParser *pParser) } if(++high_temp) { - m_rTemp = (struct nvfx_reg*)calloc(high_temp,sizeof(struct nvfx_reg)); - for(i=0;iGetInstructionCount(); - struct nvfx_insn tmp_insn,*insns = pParser->GetInstructions(); - struct nvfx_src tmp,none = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); + struct nvfx_insn *insns = pParser->GetInstructions(); Prepare(pParser); @@ -90,108 +79,127 @@ void CCompilerFP::Compile(CParser *pParser) switch(insn->op) { case OPCODE_ADD: - emit_insn(NVFX_FP_OP_OPCODE_ADD,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_ADD); break; case OPCODE_BRK: emit_brk(insn); break; case OPCODE_COS: - emit_insn(NVFX_FP_OP_OPCODE_COS,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_COS); + break; + case OPCODE_DDX: + emit_ddx(insn); + break; + case OPCODE_DDY: + emit_ddy(insn); + break; + case OPCODE_DP2: + emit_insn(insn,NVFX_FP_OP_OPCODE_DP2); break; case OPCODE_DP3: - emit_insn(NVFX_FP_OP_OPCODE_DP3,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_DP3); break; case OPCODE_DP4: - emit_insn(NVFX_FP_OP_OPCODE_DP4,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_DP4); + break; + case OPCODE_DST: + emit_insn(insn,NVFX_FP_OP_OPCODE_DST); break; case OPCODE_EX2: - emit_insn(NVFX_FP_OP_OPCODE_EX2,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_EX2); + break; + case OPCODE_FLR: + emit_insn(insn,NVFX_FP_OP_OPCODE_FLR); + break; + case OPCODE_FRC: + emit_insn(insn,NVFX_FP_OP_OPCODE_FRC); + break; + case OPCODE_KIL_NV: + emit_insn(insn,NVFX_FP_OP_OPCODE_KIL); break; case OPCODE_LG2: - emit_insn(NVFX_FP_OP_OPCODE_LG2,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_LG2); + break; + case OPCODE_LIT: + emit_lit(insn); break; case OPCODE_LRP: - tmp = nvfx_src(temp()); - tmp_insn = arith(0,tmp.reg,insn->mask,neg(insn->src[0]),insn->src[2],insn->src[2]); - emit_insn(NVFX_FP_OP_OPCODE_MAD,&tmp_insn); - - tmp_insn = arith(insn->sat,insn->dst,insn->mask,insn->src[0],insn->src[1],tmp); - emit_insn(NVFX_FP_OP_OPCODE_MAD,&tmp_insn); + emit_lrp(insn); break; case OPCODE_MAX: - emit_insn(NVFX_FP_OP_OPCODE_MAX,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_MAX); break; case OPCODE_MIN: - emit_insn(NVFX_FP_OP_OPCODE_MIN,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_MIN); break; case OPCODE_MAD: - emit_insn(NVFX_FP_OP_OPCODE_MAD,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_MAD); break; case OPCODE_MOV: - emit_insn(NVFX_FP_OP_OPCODE_MOV,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_MOV); break; case OPCODE_MUL: - emit_insn(NVFX_FP_OP_OPCODE_MUL,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_MUL); + break; + case OPCODE_NRM3: + emit_insn(insn,NVFX_FP_OP_OPCODE_NRM); + break; + case OPCODE_PK2H: + emit_insn(insn,NVFX_FP_OP_OPCODE_PK2H); break; case OPCODE_POW: - tmp = nvfx_src(temp()); - - tmp_insn = arith(0,tmp.reg, NVFX_FP_MASK_X, insn->src[0], none, none); - emit_insn(NVFX_FP_OP_OPCODE_LG2,&tmp_insn); - - tmp_insn = arith(0,tmp.reg, NVFX_FP_MASK_X, swz(tmp, X, X, X, X),insn->src[1], none); - emit_insn(NVFX_FP_OP_OPCODE_MUL,&tmp_insn); - - tmp_insn = arith_ctor(insn,insn->dst,swz(tmp, X, X, X, X), none, none); - emit_insn(NVFX_FP_OP_OPCODE_EX2,&tmp_insn); + emit_pow(insn); break; case OPCODE_RCP: - emit_insn(NVFX_FP_OP_OPCODE_RCP,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_RCP); break; case OPCODE_RSQ: - tmp = nvfx_src(temp()); - tmp_insn = arith(0,tmp.reg,NVFX_FP_MASK_X,abs(insn->src[0]),none,none); - tmp_insn.scale = NVFX_FP_OP_DST_SCALE_INV_2X; - emit_insn(NVFX_FP_OP_OPCODE_LG2,&tmp_insn); - - tmp_insn = arith_ctor(insn,insn->dst,neg(swz(tmp,X,X,X,X)),none,none); - emit_insn(NVFX_FP_OP_OPCODE_EX2,&tmp_insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_RSQ); break; case OPCODE_SEQ: - emit_insn(NVFX_FP_OP_OPCODE_SEQ,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_SEQ); break; case OPCODE_SFL: - emit_insn(NVFX_FP_OP_OPCODE_SFL,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_SFL); break; case OPCODE_SGE: - emit_insn(NVFX_FP_OP_OPCODE_SGE,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_SGE); break; case OPCODE_SGT: - emit_insn(NVFX_FP_OP_OPCODE_SGT,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_SGT); break; case OPCODE_SIN: - emit_insn(NVFX_FP_OP_OPCODE_SIN,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_SIN); break; case OPCODE_SLE: - emit_insn(NVFX_FP_OP_OPCODE_SLE,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_SLE); break; case OPCODE_SLT: - emit_insn(NVFX_FP_OP_OPCODE_SLT,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_SLT); break; case OPCODE_SNE: - emit_insn(NVFX_FP_OP_OPCODE_SNE,insn); + emit_insn(insn,NVFX_FP_OP_OPCODE_SNE); break; case OPCODE_TEX: - emit_insn(NVFX_FP_OP_OPCODE_TEX,insn); + emit_tex(insn,NVFX_FP_OP_OPCODE_TEX); break; case OPCODE_TXB: - emit_insn(NVFX_FP_OP_OPCODE_TXB,insn); + emit_tex(insn,NVFX_FP_OP_OPCODE_TXB); break; case OPCODE_TXL: - emit_insn(NVFX_FP_OP_OPCODE_TXL_NV40,insn); + emit_txl(insn); break; case OPCODE_TXP: - emit_insn(NVFX_FP_OP_OPCODE_TXP,insn); + emit_tex(insn,NVFX_FP_OP_OPCODE_TXP); + break; + case OPCODE_UP4UB: + emit_insn(insn,NVFX_FP_OP_OPCODE_UP4UB); + break; + case OPCODE_BGNLOOP: + emit_loop(insn); + break; + case OPCODE_ENDLOOP: + fixup_loop(); break; case OPCODE_BGNREP: emit_rep(insn); @@ -211,8 +219,7 @@ void CCompilerFP::Compile(CParser *pParser) case OPCODE_END: if(m_nInstructions) m_pInstructions[m_nCurInstruction].data[0] |= NVFX_FP_OP_PROGRAM_END; else { - m_nCurInstruction = m_nInstructions; - grow_insns(1); + m_nCurInstruction = grow_insns(1); m_pInstructions[m_nCurInstruction].data[0] = 0x00000001; m_pInstructions[m_nCurInstruction].data[1] = 0x00000000; m_pInstructions[m_nCurInstruction].data[2] = 0x00000000; @@ -223,13 +230,12 @@ void CCompilerFP::Compile(CParser *pParser) } } -void CCompilerFP::emit_insn(u8 op,struct nvfx_insn *insn) +void CCompilerFP::emit_insn(struct nvfx_insn *insn,u8 op) { u32 *hw; bool have_const = false; - m_nCurInstruction = m_nInstructions; - grow_insns(1); + m_nCurInstruction = grow_insns(1); memset(&m_pInstructions[m_nCurInstruction],0,sizeof(struct fragment_program_exec)); hw = m_pInstructions[m_nCurInstruction].data; @@ -254,33 +260,28 @@ void CCompilerFP::emit_insn(u8 op,struct nvfx_insn *insn) (insn->cc_swz[2] << NVFX_FP_OP_COND_SWZ_Z_SHIFT) | (insn->cc_swz[3] << NVFX_FP_OP_COND_SWZ_W_SHIFT)); - if(insn->unit >= 0) { - hw[0] |= (insn->unit << NVFX_FP_OP_TEX_UNIT_SHIFT); - m_nSamplers |= (1<unit); - } - - emit_dst(&insn->dst,&have_const); - emit_src(0,&insn->src[0],&have_const); - emit_src(1,&insn->src[1],&have_const); - emit_src(2,&insn->src[2],&have_const); + emit_dst(insn,&have_const); + emit_src(insn,0,&have_const); + emit_src(insn,1,&have_const); + emit_src(insn,2,&have_const); } -void CCompilerFP::emit_dst(struct nvfx_reg *dst,bool *have_const) +void CCompilerFP::emit_dst(struct nvfx_insn *insn,bool *have_const) { - s32 index; + struct nvfx_reg *dst = &insn->dst; u32 *hw = m_pInstructions[m_nCurInstruction].data; - index = dst->index; + s32 index = dst->index; switch(dst->type) { case NVFXSR_TEMP: if(m_nNumRegs<(s32)(index + 1)) m_nNumRegs = (index + 1); break; case NVFXSR_OUTPUT: - if(dst->index==1) + if(dst->index==0 && !dst->is_fp16) + m_nFPControl |= 0x40; + else if(dst->index==1) m_nFPControl |= 0xe; - else - hw[0] |= NVFX_FP_OP_OUT_REG_HALF; break; case NVFXSR_NONE: hw[0] |= NV40_FP_OP_OUT_NONE; @@ -292,9 +293,10 @@ void CCompilerFP::emit_dst(struct nvfx_reg *dst,bool *have_const) hw[0] |= (index << NVFX_FP_OP_OUT_REG_SHIFT); } -void CCompilerFP::emit_src(s32 pos,struct nvfx_src *src,bool *have_const) +void CCompilerFP::emit_src(struct nvfx_insn *insn,s32 pos,bool *have_const) { u32 sr = 0; + struct nvfx_src *src = &insn->src[pos]; u32 *hw = m_pInstructions[m_nCurInstruction].data; switch(src->reg.type) { @@ -302,7 +304,7 @@ void CCompilerFP::emit_src(s32 pos,struct nvfx_src *src,bool *have_const) sr |= (NVFX_FP_REG_TYPE_INPUT << NVFX_FP_REG_TYPE_SHIFT); hw[0] |= (src->reg.index << NVFX_FP_OP_INPUT_SRC_SHIFT); - if(src->reg.index>=NVFX_FP_OP_INPUT_SRC_TC(0) && src->reg.index<=NVFX_FP_OP_INPUT_SRC_TC(8)) { + if(src->reg.index>=NVFX_FP_OP_INPUT_SRC_TC(0) && src->reg.index<=NVFX_FP_OP_INPUT_SRC_TC(7)) { param fpi = GetInputAttrib(src->reg.index); if((int)fpi.index!=-1) { @@ -321,13 +323,13 @@ void CCompilerFP::emit_src(s32 pos,struct nvfx_src *src,bool *have_const) case NVFXSR_IMM: if(!*have_const) { grow_insns(1); + memset(&m_pInstructions[m_nCurInstruction + 1], 0,4*sizeof(f32)); hw = m_pInstructions[m_nCurInstruction].data; *have_const = true; } { param fpd = GetImmData(src->reg.index); if(fpd.values!=NULL) memcpy(&m_pInstructions[m_nCurInstruction + 1],fpd.values,4*sizeof(f32)); - sr |= (NVFX_FP_REG_TYPE_CONST << NVFX_FP_REG_TYPE_SHIFT); } break; @@ -364,7 +366,7 @@ void CCompilerFP::emit_src(s32 pos,struct nvfx_src *src,bool *have_const) sr |= NVFX_FP_REG_NEGATE; if(src->abs) - hw[1] |= (1 << (29 + pos)); + hw[1] |= src_abs_flag(pos); sr |= ((src->swz[0] << NVFX_FP_REG_SWZ_X_SHIFT) | (src->swz[1] << NVFX_FP_REG_SWZ_Y_SHIFT) | @@ -379,8 +381,7 @@ void CCompilerFP::emit_rep(struct nvfx_insn *insn) u32 *hw; int count; - m_nCurInstruction = m_nInstructions; - grow_insns(1); + m_nCurInstruction = grow_insns(1); memset(&m_pInstructions[m_nCurInstruction],0,sizeof(struct fragment_program_exec)); hw = m_pInstructions[m_nCurInstruction].data; @@ -421,6 +422,17 @@ void CCompilerFP::fixup_rep() m_repStack.pop(); } +void CCompilerFP::fixup_loop() +{ + u32 *hw; + + hw = m_pInstructions[m_loopStack.top()].data; + + hw[3] |= m_nInstructions * 4; + + m_loopStack.pop(); +} + void CCompilerFP::fixup_if() { u32 *hw; @@ -445,8 +457,7 @@ void CCompilerFP::emit_brk(struct nvfx_insn *insn) { u32 *hw; - m_nCurInstruction = m_nInstructions; - grow_insns(1); + m_nCurInstruction = grow_insns(1); memset(&m_pInstructions[m_nCurInstruction],0,sizeof(struct fragment_program_exec)); hw = m_pInstructions[m_nCurInstruction].data; @@ -466,8 +477,7 @@ void CCompilerFP::emit_if(struct nvfx_insn *insn) { u32 *hw; - m_nCurInstruction = m_nInstructions; - grow_insns(1); + m_nCurInstruction = grow_insns(1); memset(&m_pInstructions[m_nCurInstruction],0,sizeof(struct fragment_program_exec)); hw = m_pInstructions[m_nCurInstruction].data; @@ -485,9 +495,188 @@ void CCompilerFP::emit_if(struct nvfx_insn *insn) m_ifStack.push(m_nCurInstruction); } +void CCompilerFP::emit_loop(struct nvfx_insn *insn) +{ + u32 *hw; + int count1,count2,count3; + + m_nCurInstruction = grow_insns(1); + memset(&m_pInstructions[m_nCurInstruction],0,sizeof(struct fragment_program_exec)); + + hw = m_pInstructions[m_nCurInstruction].data; + + param fpd = GetImmData(insn->src[0].reg.index); + + if (insn->src[0].reg.type != NVFXSR_IMM || + (*fpd.values)[0] < 0.0 || (*fpd.values)[0] > 255.0) { + fprintf(stderr,"Input to LOOP must be immediate number 0-255\n"); + exit(EXIT_FAILURE); + } + + count1 = (int)(*fpd.values)[0]; + count2 = (int)(*fpd.values)[1]; + count3 = (int)(*fpd.values)[2]; + hw[0] |= (NV40_FP_OP_BRA_OPCODE_LOOP << NVFX_FP_OP_OPCODE_SHIFT); + hw[0] |= NV40_FP_OP_OUT_NONE; + hw[0] |= NVFX_FP_PRECISION_FP16 << NVFX_FP_OP_PRECISION_SHIFT; + hw[2] |= NV40_FP_OP_OPCODE_IS_BRANCH; + hw[2] |= (count1<cc_cond << NVFX_FP_OP_COND_SHIFT); + hw[1] |= ((insn->cc_swz[0] << NVFX_FP_OP_COND_SWZ_X_SHIFT) | + (insn->cc_swz[1] << NVFX_FP_OP_COND_SWZ_Y_SHIFT) | + (insn->cc_swz[2] << NVFX_FP_OP_COND_SWZ_Z_SHIFT) | + (insn->cc_swz[3] << NVFX_FP_OP_COND_SWZ_W_SHIFT)); + + m_loopStack.push(m_nCurInstruction); +} + +void CCompilerFP::emit_lrp(struct nvfx_insn *insn) +{ + struct nvfx_insn tmp_insn; + struct nvfx_src tmp = nvfx_src(temp()); + + tmp_insn = arith(0,tmp.reg,insn->mask,neg(insn->src[0]),insn->src[2],insn->src[2]); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_MAD); + + tmp_insn = arith(insn->sat,insn->dst,insn->mask,insn->src[0],insn->src[1],tmp); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_MAD); +} + +void CCompilerFP::emit_pow(struct nvfx_insn *insn) +{ + struct nvfx_insn tmp_insn; + struct nvfx_src tmp = nvfx_src(temp()); + struct nvfx_src none = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); + + tmp_insn = arith(0,tmp.reg, NVFX_FP_MASK_X, insn->src[0], none, none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_LG2); + + tmp_insn = arith(0,tmp.reg, NVFX_FP_MASK_X, swz(tmp, X, X, X, X),insn->src[1], none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_MUL); + + tmp_insn = arith_ctor(insn,insn->dst,swz(tmp, X, X, X, X), none, none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_EX2); +} + +void CCompilerFP::emit_lit(struct nvfx_insn *insn) +{ + struct nvfx_insn tmp_insn; + struct nvfx_src tmp = nvfx_src(temp()); + struct nvfx_src none = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); + struct nvfx_src maxs = nvfx_src(imm(0.0f, FLT_MIN, 0.0f, 0.0f)); + + tmp_insn = arith(0, tmp.reg, (NVFX_FP_MASK_Y | NVFX_FP_MASK_W), swz(insn->src[0], X, X, X, Y), swz(maxs, X, X, Y, Y), none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_MAX); + + tmp_insn = arith(0, tmp.reg, NVFX_FP_MASK_W, swz(tmp, W, W, W, W), none, none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_LG2); + + tmp_insn = arith(0, tmp.reg, NVFX_FP_MASK_W, swz(tmp, W, W, W, W), swz(insn->src[0], W, W, W, W), none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_MUL); + + tmp_insn = arith_ctor(insn, insn->dst, swz(tmp, Y, Y, W, W), none, none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_LITEX2_NV40); +} + +void CCompilerFP::emit_ddx(struct nvfx_insn *insn) +{ + struct nvfx_insn tmp_insn; + struct nvfx_src tmp = nvfx_src(temp()); + struct nvfx_src none = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); + + if(insn->mask&(NVFX_FP_MASK_Z | NVFX_FP_MASK_W)) { + tmp_insn = arith(insn->sat, tmp.reg, (NVFX_FP_MASK_X | NVFX_FP_MASK_Y), swz(insn->src[0], Z, W, Z, W), none, none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_DDX); + + tmp_insn = arith(0, tmp.reg, (NVFX_FP_MASK_Z | NVFX_FP_MASK_W), swz(tmp, X, Y, X, Y), none, none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_MOV); + + tmp_insn = arith(insn->sat, tmp.reg, (NVFX_FP_MASK_X | NVFX_FP_MASK_Y), insn->src[0], none, none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_DDX); + + tmp_insn = arith(0, insn->dst, insn->mask, tmp, none, none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_MOV); + } else + emit_insn(insn,NVFX_FP_OP_OPCODE_DDX); +} + +void CCompilerFP::emit_ddy(struct nvfx_insn *insn) +{ + struct nvfx_insn tmp_insn; + struct nvfx_src tmp = nvfx_src(temp()); + struct nvfx_src none = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); + + if(insn->mask&(NVFX_FP_MASK_Z | NVFX_FP_MASK_W)) { + tmp_insn = arith(insn->sat, tmp.reg, (NVFX_FP_MASK_X | NVFX_FP_MASK_Y), swz(insn->src[0], Z, W, Z, W), none, none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_DDY); + + tmp_insn = arith(0, tmp.reg, (NVFX_FP_MASK_Z | NVFX_FP_MASK_W), swz(tmp, X, Y, X, Y), none, none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_MOV); + + tmp_insn = arith(insn->sat, tmp.reg, (NVFX_FP_MASK_X | NVFX_FP_MASK_Y), insn->src[0], none, none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_DDY); + + tmp_insn = arith(0, insn->dst, insn->mask, tmp, none, none); + emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_MOV); + } else + emit_insn(insn,NVFX_FP_OP_OPCODE_DDY); +} + +void CCompilerFP::emit_txl(struct nvfx_insn *insn) +{ + u32 *hw; + struct nvfx_insn tmp_insn; + struct nvfx_src none = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); + + if(insn->tex_unit<0 || insn->tex_unit>15) return; + + tmp_insn = arith_ctor(insn, insn->dst, insn->src[0], swz(insn->src[0], W, W, W, W), none); + emit_insn(&tmp_insn, NVFX_FP_OP_OPCODE_TXL_NV40); + + hw = m_pInstructions[m_nCurInstruction].data; + hw[0] |= (insn->tex_unit << NVFX_FP_OP_TEX_UNIT_SHIFT); + + m_nSamplers |= (1<tex_unit); +} + +void CCompilerFP::emit_tex(struct nvfx_insn *insn,u8 op) +{ + u32 *hw; + + if(insn->tex_unit<0 || insn->tex_unit>15) return; + + emit_insn(insn,op); + + hw = m_pInstructions[m_nCurInstruction].data; + hw[0] |= (insn->tex_unit << NVFX_FP_OP_TEX_UNIT_SHIFT); + + m_nSamplers |= (1<tex_unit); +} + +struct nvfx_reg CCompilerFP::imm(f32 x, f32 y, f32 z, f32 w) +{ + param p; + f32 v[4] = { x, y, z, w }; + s32 idx = m_lParameters.size(); + + p.count = 1; + p.is_const = 1; + p.is_internal = 1; + p.type = PARAM_FLOAT4; + + CParser::InitParameter(&p); + memcpy(p.values, v, sizeof(f32)*4); + + m_lParameters.push_back(p); + + return nvfx_reg(NVFXSR_IMM, idx); +} + struct nvfx_reg CCompilerFP::temp() { - s32 idx = ffs(~m_rTemps) - 1; + s32 idx = __builtin_ctzll(~m_rTemps); if(idx<0) return nvfx_reg(NVFXSR_TEMP,0); @@ -503,8 +692,12 @@ void CCompilerFP::release_temps() m_rTempsDiscard = 0; } -void CCompilerFP::grow_insns(int count) +int CCompilerFP::grow_insns(int count) { + int pos = m_nInstructions; + m_nInstructions += count; m_pInstructions = (struct fragment_program_exec*)realloc(m_pInstructions,m_nInstructions*sizeof(struct fragment_program_exec)); + + return pos; } diff --git a/tools/cgcomp/source/compiler.cpp b/tools/cgcomp/source/compilervp.cpp similarity index 58% rename from tools/cgcomp/source/compiler.cpp rename to tools/cgcomp/source/compilervp.cpp index 2ef2ebe7..d6ae5eec 100644 --- a/tools/cgcomp/source/compiler.cpp +++ b/tools/cgcomp/source/compilervp.cpp @@ -1,54 +1,46 @@ #include "types.h" #include "parser.h" -#include "compiler.h" +#include "compilervp.h" #define gen_op(o,t) \ ((NVFX_VP_INST_SLOT_##t<<7)|NVFX_VP_INST_##t##_OP_##o) +#define gen_op_nv40(o,t) \ + ((NVFX_VP_INST_SLOT_##t<<7)|NV40_VP_INST_##t##_OP_##o) + #define arith(s,d,m,s0,s1,s2) \ - nvfx_insn((s), 0, -1, (d), (m), (s0), (s1), (s2)) + nvfx_insn((s), 0, -1, -1, (d), (m), (s0), (s1), (s2)) #define arith_ctor(ins,d,s0,s1,s2) \ nvfx_insn_ctor((ins), (d), (s0), (s1), (s2)) -static INLINE s32 ffs(u32 u) -{ - u32 i = 0; - if(!(u&0xffffffff)) return 0; - while(!(u&0x1)) { - u >>= 1; - i++; - } - return i + 1; -} - -CCompiler::CCompiler() +CCompilerVP::CCompilerVP() { m_nInputMask = 0; m_nOutputMask = 0; m_nInstructions = 0; m_nConsts = 0; m_rTemps = 0; - m_nNumRegs = 1; + m_nNumRegs = 0; m_rTempsDiscard = 0; m_nCurInstruction = 0; m_pInstructions = NULL; m_pConstData = NULL; - m_rTemp = NULL; - m_rConst = NULL; } -CCompiler::~CCompiler() +CCompilerVP::~CCompilerVP() { } -void CCompiler::Prepare(CParser *pParser) +void CCompilerVP::Prepare(CParser *pParser) { s32 high_const = -1,high_temp = -1; u32 i,j,nICount = pParser->GetInstructionCount(); struct nvfx_insn *insns = pParser->GetInstructions(); + m_lParameters = pParser->GetParameters(); + for(i=0;i insns_pos; std::list label_reloc; @@ -101,25 +89,23 @@ void CCompiler::Compile(CParser *pParser) Prepare(pParser); for(i=0;iop) { case OPCODE_NOP: - tmp_insn = arith(0,none.reg,0,none,none,none); - emit_insn(gen_op(NOP,VEC),&tmp_insn); + emit_nop(); break; case OPCODE_ABS: - tmp_insn = arith_ctor(insn,insn->dst,abs(insn->src[0]),none,none); - emit_insn(gen_op(MOV,VEC),&tmp_insn); + emit_abs(insn); break; case OPCODE_ADD: - emit_insn(gen_op(ADD,VEC),insn); + emit_insn(insn,gen_op(ADD,VEC)); break; case OPCODE_ARA: break; case OPCODE_ARL: + emit_insn(insn,gen_op(ARL,VEC)); break; case OPCODE_ARR: break; @@ -128,131 +114,128 @@ void CCompiler::Compile(CParser *pParser) reloc.target = insn->dst.index; label_reloc.push_back(reloc); - tmp_insn = arith(0,none.reg,0,none,none,none); - emit_insn(gen_op(BRA,SCA),&tmp_insn); + tmp_insn = arith_ctor(insn, none.reg, none, none, none); + emit_insn(&tmp_insn,gen_op(BRA,SCA)); break; case OPCODE_CAL: reloc.location = m_nInstructions; reloc.target = insn->dst.index; label_reloc.push_back(reloc); - tmp_insn = arith(0,none.reg,0,none,none,none); - emit_insn(gen_op(CAL,SCA),&tmp_insn); + tmp_insn = arith_ctor(insn, none.reg, none, none, none); + emit_insn(&tmp_insn,gen_op(CAL,SCA)); break; case OPCODE_COS: - emit_insn(gen_op(COS,SCA),insn); + emit_insn(insn,gen_op(COS,SCA)); break; case OPCODE_DP3: - emit_insn(gen_op(DP3,VEC),insn); + emit_insn(insn,gen_op(DP3,VEC)); break; case OPCODE_DP4: - emit_insn(gen_op(DP4,VEC),insn); + emit_insn(insn,gen_op(DP4,VEC)); break; case OPCODE_DPH: - emit_insn(gen_op(DPH,VEC),insn); + emit_insn(insn,gen_op(DPH,VEC)); break; case OPCODE_DST: - emit_insn(gen_op(DST,VEC),insn); + emit_insn(insn,gen_op(DST,VEC)); break; case OPCODE_EX2: - emit_insn(gen_op(EX2,SCA),insn); + emit_insn(insn,gen_op(EX2,SCA)); break; case OPCODE_EXP: - emit_insn(gen_op(EXP,SCA),insn); + emit_insn(insn,gen_op(EXP,SCA)); break; case OPCODE_FLR: - emit_insn(gen_op(FLR,VEC),insn); + emit_insn(insn,gen_op(FLR,VEC)); break; case OPCODE_FRC: - emit_insn(gen_op(FRC,VEC),insn); + emit_insn(insn,gen_op(FRC,VEC)); break; case OPCODE_LG2: - emit_insn(gen_op(LG2,SCA),insn); + emit_insn(insn,gen_op(LG2,SCA)); break; case OPCODE_LIT: - emit_insn(gen_op(LIT,SCA),insn); + emit_insn(insn,gen_op(LIT,SCA)); break; case OPCODE_LOG: - emit_insn(gen_op(LOG,SCA),insn); + emit_insn(insn,gen_op(LOG,SCA)); + break; + case OPCODE_LRP: + emit_lrp(insn); break; case OPCODE_MAD: - emit_insn(gen_op(MAD,VEC),insn); + emit_insn(insn,gen_op(MAD,VEC)); break; case OPCODE_MAX: - emit_insn(gen_op(MAX,VEC),insn); + emit_insn(insn,gen_op(MAX,VEC)); break; case OPCODE_MIN: - emit_insn(gen_op(MIN,VEC),insn); + emit_insn(insn,gen_op(MIN,VEC)); break; case OPCODE_MOV: - emit_insn(gen_op(MOV,VEC),insn); + emit_insn(insn,gen_op(MOV,VEC)); break; case OPCODE_MUL: - emit_insn(gen_op(MUL,VEC),insn); + emit_insn(insn,gen_op(MUL,VEC)); break; case OPCODE_POW: - tmp = nvfx_src(temp()); - - tmp_insn = arith(0, tmp.reg, NVFX_VP_MASK_X, none, none, insn->src[0]); - emit_insn(gen_op(LG2,SCA),&tmp_insn); - - tmp_insn = arith(0, tmp.reg, NVFX_VP_MASK_X, swz(tmp, X, X, X, X), insn->src[1], none); - emit_insn(gen_op(MUL,VEC),&tmp_insn); - - tmp_insn = arith_ctor(insn, insn->dst, none, none, swz(tmp, X, X, X, X)); - emit_insn(gen_op(EX2,SCA),&tmp_insn); + emit_pow(insn); break; case OPCODE_RCC: - emit_insn(gen_op(RCC,SCA),insn); + emit_insn(insn,gen_op(RCC,SCA)); break; case OPCODE_RCP: - emit_insn(gen_op(RCP,SCA),insn); + emit_insn(insn,gen_op(RCP,SCA)); break; case OPCODE_RSQ: - emit_insn(gen_op(RSQ,SCA),insn); + emit_insn(insn,gen_op(RSQ,SCA)); break; case OPCODE_SEQ: - emit_insn(gen_op(SEQ,VEC),insn); + emit_insn(insn,gen_op(SEQ,VEC)); break; case OPCODE_SFL: - emit_insn(gen_op(SFL,VEC),insn); + emit_insn(insn,gen_op(SFL,VEC)); break; case OPCODE_SGE: - emit_insn(gen_op(SGE,VEC),insn); + emit_insn(insn,gen_op(SGE,VEC)); break; case OPCODE_SGT: - emit_insn(gen_op(SGT,VEC),insn); + emit_insn(insn,gen_op(SGT,VEC)); break; case OPCODE_SIN: - emit_insn(gen_op(SIN,SCA),insn); + emit_insn(insn,gen_op(SIN,SCA)); break; case OPCODE_SLE: - emit_insn(gen_op(SLE,VEC),insn); + emit_insn(insn,gen_op(SLE,VEC)); break; case OPCODE_SLT: - emit_insn(gen_op(SLT,VEC),insn); + emit_insn(insn,gen_op(SLT,VEC)); break; case OPCODE_SNE: - emit_insn(gen_op(SNE,VEC),insn); + emit_insn(insn,gen_op(SNE,VEC)); break; case OPCODE_SSG: - emit_insn(gen_op(SSG,VEC),insn); + emit_insn(insn,gen_op(SSG,VEC)); break; case OPCODE_STR: - emit_insn(gen_op(STR,VEC),insn); + emit_insn(insn,gen_op(STR,VEC)); break; case OPCODE_SUB: - tmp_insn = arith_ctor(insn,insn->dst,insn->src[0],none,neg(insn->src[2])); - emit_insn(gen_op(ADD,VEC),&tmp_insn); + emit_sub(insn); + break; + case OPCODE_TEX: + emit_tex(insn); break; case OPCODE_END: - if(m_nInstructions) m_pInstructions[m_nCurInstruction].data[3] |= NVFX_VP_INST_LAST; - else { - tmp_insn = arith(0,none.reg,0,none,none,none); - emit_insn(gen_op(NOP,VEC),&tmp_insn); - m_pInstructions[m_nCurInstruction].data[3] |= NVFX_VP_INST_LAST; + if(!m_nInstructions) { + emit_nop(); } + m_pInstructions[m_nCurInstruction].data[3] |= NVFX_VP_INST_LAST; break; + default: + fprintf(stderr, "Unknown instruction \"%d\"\n", insn->op); + exit(EXIT_FAILURE); } release_temps(); } @@ -267,23 +250,21 @@ void CCompiler::Compile(CParser *pParser) } } -void CCompiler::emit_insn(u8 opcode,struct nvfx_insn *insn) +void CCompilerVP::emit_insn(struct nvfx_insn *insn,u8 opcode) { u32 *hw; u32 slot = opcode>>7; u32 op = opcode&0x7f; - m_nCurInstruction = m_nInstructions++; - m_pInstructions = (struct vertex_program_exec*)realloc(m_pInstructions,m_nInstructions*sizeof(struct vertex_program_exec)); - + m_nCurInstruction = grow_insns(1); memset(&m_pInstructions[m_nCurInstruction],0,sizeof(struct vertex_program_exec)); hw = m_pInstructions[m_nCurInstruction].data; - emit_dst(hw,slot,insn); - emit_src(hw,0,&insn->src[0]); - emit_src(hw,1,&insn->src[1]); - emit_src(hw,2,&insn->src[2]); + emit_dst(insn,slot); + emit_src(insn,0); + emit_src(insn,1); + emit_src(insn,2); hw[0] |= (insn->cc_cond << NVFX_VP(INST_COND_SHIFT)); hw[0] |= (insn->cc_test << NVFX_VP(INST_COND_TEST_SHIFT)); @@ -295,10 +276,11 @@ void CCompiler::emit_insn(u8 opcode,struct nvfx_insn *insn) if(insn->cc_update) hw[0] |= NVFX_VP(INST_COND_UPDATE_ENABLE); + if(insn->cc_update_reg) + hw[0] |= NVFX_VP(INST_COND_REG_SELECT_1); + if(insn->sat) - { hw[0] |= NV40_VP_INST_SATURATE; - } if (slot == 0) { hw[1] |= (op << NV40_VP_INST_VEC_OPCODE_SHIFT); @@ -311,9 +293,10 @@ void CCompiler::emit_insn(u8 opcode,struct nvfx_insn *insn) } } -void CCompiler::emit_dst(u32 *hw,u8 slot,struct nvfx_insn *insn) +void CCompilerVP::emit_dst(struct nvfx_insn *insn,u8 slot) { struct nvfx_reg *dst = &insn->dst; + u32 *hw = m_pInstructions[m_nCurInstruction].data; switch(dst->type) { case NVFXSR_NONE: @@ -324,6 +307,9 @@ void CCompiler::emit_dst(u32 *hw,u8 slot,struct nvfx_insn *insn) hw[3] |= NV40_VP_INST_SCA_DEST_TEMP_MASK; break; case NVFXSR_TEMP: + if(m_nNumRegs<(s32)(dst->index + 1)) + m_nNumRegs = (dst->index + 1); + case NVFXSR_ADDRESS: hw[3] |= NV40_VP_INST_DEST_MASK; if (slot == 0) hw[0] |= (dst->index << NV40_VP_INST_VEC_DEST_TEMP_SHIFT); @@ -369,7 +355,7 @@ void CCompiler::emit_dst(u32 *hw,u8 slot,struct nvfx_insn *insn) case NV40_VP_INST_DEST_FOGC : m_nOutputMask |= (1 << 4); break; case NV40_VP_INST_DEST_PSZ : m_nOutputMask |= (1 << 5); break; default: - if(dst->index>=NV40_VP_INST_DEST_TC(0) && dst->index<=NV40_VP_INST_DEST_TC(7)) m_nOutputMask |= (1<<(dst->index - NV40_VP_INST_DEST_TC0 + 14)); + if(dst->index>=NV40_VP_INST_DEST_TC(0) && dst->index<=NV40_VP_INST_DEST_TC(7)) m_nOutputMask |= (0x4000 << (dst->index - NV40_VP_INST_DEST_TC0)); break; } hw[3] |= (dst->index << NV40_VP_INST_DEST_SHIFT); @@ -384,10 +370,12 @@ void CCompiler::emit_dst(u32 *hw,u8 slot,struct nvfx_insn *insn) } } -void CCompiler::emit_src(u32 *hw, u8 pos, struct nvfx_src *src) +void CCompilerVP::emit_src(struct nvfx_insn *insn,u8 pos) { u32 sr = 0; struct nvfx_relocation reloc; + struct nvfx_src *src = &insn->src[pos]; + u32 *hw = m_pInstructions[m_nCurInstruction].data; switch(src->reg.type) { case NVFXSR_TEMP: @@ -407,6 +395,10 @@ void CCompiler::emit_src(u32 *hw, u8 pos, struct nvfx_src *src) reloc.target = src->reg.index; m_lConstRelocation.push_back(reloc); break; + case NVFXSR_SAMPLER: + sr |= (NVFX_VP(SRC_REG_TYPE_INPUT) << NVFX_VP(SRC_REG_TYPE_SHIFT)); + sr |= (src->reg.index << NVFX_VP(SRC_TEMP_SRC_SHIFT)); + break; case NVFXSR_NONE: sr |= (NVFX_VP(SRC_REG_TYPE_INPUT) << NVFX_VP(SRC_REG_TYPE_SHIFT)); @@ -449,27 +441,119 @@ void CCompiler::emit_src(u32 *hw, u8 pos, struct nvfx_src *src) } } -struct nvfx_reg CCompiler::temp() +void CCompilerVP::emit_pow(struct nvfx_insn *insn) +{ + struct nvfx_src tmp; + struct nvfx_insn tmp_insn; + struct nvfx_src none = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); + + tmp = nvfx_src(temp()); + + tmp_insn = arith(0, tmp.reg, NVFX_VP_MASK_X, none, none, insn->src[0]); + emit_insn(&tmp_insn,gen_op(LG2,SCA)); + + tmp_insn = arith(0, tmp.reg, NVFX_VP_MASK_X, swz(tmp, X, X, X, X), insn->src[1], none); + emit_insn(&tmp_insn,gen_op(MUL,VEC)); + + tmp_insn = arith_ctor(insn, insn->dst, none, none, swz(tmp, X, X, X, X)); + emit_insn(&tmp_insn,gen_op(EX2,SCA)); +} + +void CCompilerVP::emit_abs(struct nvfx_insn *insn) +{ + struct nvfx_insn tmp_insn; + struct nvfx_src none = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); + + tmp_insn = arith_ctor(insn,insn->dst,abs(insn->src[0]),none,none); + emit_insn(&tmp_insn,gen_op(MOV,VEC)); +} + +void CCompilerVP::emit_sub(struct nvfx_insn *insn) +{ + struct nvfx_insn tmp_insn; + struct nvfx_src none = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); + + tmp_insn = arith_ctor(insn,insn->dst,insn->src[0],none,neg(insn->src[2])); + emit_insn(&tmp_insn,gen_op(ADD,VEC)); +} + +void CCompilerVP::emit_tex(struct nvfx_insn *insn) +{ + struct nvfx_insn tmp_insn; + struct nvfx_src tmp = nvfx_src(temp()); + param vpi = GetInputAttrib(insn->src[0].reg.index); + struct nvfx_src none = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); + + if(vpi.type==PARAM_FLOAT) { + tmp_insn = arith(0, tmp.reg, NVFX_VP_MASK_X, insn->src[0], none, none); + emit_insn(&tmp_insn, gen_op(MOV,VEC)); + + tmp_insn = arith(0, tmp.reg, NVFX_VP_MASK_W, swz(insn->src[0], X, X, X, X), swz(insn->src[0], X, X, X, X), none); + emit_insn(&tmp_insn,gen_op(SFL,VEC)); + + tmp_insn = arith_ctor(insn, insn->dst, swz(tmp, X, X, W, W), insn->src[1], none); + emit_insn(&tmp_insn,gen_op_nv40(TXL,VEC)); + } else if(vpi.type==PARAM_FLOAT2) { + tmp_insn = arith(0, tmp.reg, (NVFX_VP_MASK_X | NVFX_VP_MASK_Y), insn->src[0], none, none); + emit_insn(&tmp_insn, gen_op(MOV,VEC)); + + tmp_insn = arith(0, tmp.reg, NVFX_VP_MASK_W, swz(insn->src[0], X, X, X, X), swz(insn->src[0], X, X, X, X), none); + emit_insn(&tmp_insn,gen_op(SFL,VEC)); + + tmp_insn = arith_ctor(insn, insn->dst, swz(tmp, X, Y, W, W), insn->src[1], none); + emit_insn(&tmp_insn,gen_op_nv40(TXL,VEC)); + } else if(vpi.type==PARAM_FLOAT3) { + tmp_insn = arith(0, tmp.reg, (NVFX_VP_MASK_X | NVFX_VP_MASK_Y | NVFX_VP_MASK_Z), insn->src[0], none, none); + emit_insn(&tmp_insn, gen_op(MOV,VEC)); + + tmp_insn = arith(0, tmp.reg, NVFX_VP_MASK_W, swz(insn->src[0], X, X, X, X), swz(insn->src[0], X, X, X, X), none); + emit_insn(&tmp_insn,gen_op(SFL,VEC)); + + tmp_insn = arith_ctor(insn, insn->dst, swz(tmp, X, Y, Z, W), insn->src[1], none); + emit_insn(&tmp_insn,gen_op_nv40(TXL,VEC)); + } +} + +void CCompilerVP::emit_lrp(struct nvfx_insn *insn) +{ + struct nvfx_insn tmp_insn; + struct nvfx_src tmp = nvfx_src(temp()); + + tmp_insn = arith(0, tmp.reg, insn->mask, neg(insn->src[0]), insn->src[2], insn->src[2]); + emit_insn(&tmp_insn,gen_op(MAD,VEC)); + + tmp_insn = arith(insn->sat, insn->dst, insn->mask, insn->src[0], insn->src[1], tmp); + emit_insn(&tmp_insn,gen_op(MAD,VEC)); +} + +void CCompilerVP::emit_nop() +{ + struct nvfx_insn tmp_insn; + struct nvfx_src none = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); + + tmp_insn = arith(0,none.reg,0,none,none,none); + emit_insn(&tmp_insn,gen_op(NOP,VEC)); +} + +struct nvfx_reg CCompilerVP::temp() { - s32 idx = ffs(~m_rTemps) - 1; + s32 idx = __builtin_ctzll(~m_rTemps); if(idx<0) return nvfx_reg(NVFXSR_NONE,0); m_rTemps |= (1<value[3] = w; return nvfx_reg(NVFXSR_CONST,idx); } + +int CCompilerVP::grow_insns(int count) +{ + int pos = m_nInstructions; + + m_nInstructions += count; + m_pInstructions = (struct vertex_program_exec*)realloc(m_pInstructions,m_nInstructions*sizeof(struct vertex_program_exec)); + + return pos; +} diff --git a/tools/cgcomp/source/fpparser.cpp b/tools/cgcomp/source/fpparser.cpp index b859880f..57282555 100644 --- a/tools/cgcomp/source/fpparser.cpp +++ b/tools/cgcomp/source/fpparser.cpp @@ -68,6 +68,7 @@ struct _opcode { "KIL", OPCODE_KIL_NV, INPUT_CC, OUTPUT_NONE, 0 }, { "LG2", OPCODE_LG2, INPUT_1S, OUTPUT_S, _R | _H | _C | _S }, { "LIT", OPCODE_LIT, INPUT_1V, OUTPUT_V, _R | _H | _C | _S }, + { "LOOP", OPCODE_BGNLOOP, INPUT_1V, OUTPUT_NONE, 0 }, { "LRP", OPCODE_LRP, INPUT_3V, OUTPUT_V, _R | _H | _X | _C | _S }, { "MAD", OPCODE_MAD, INPUT_3V, OUTPUT_V, _R | _H | _X | _C | _S }, { "MAX", OPCODE_MAX, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S }, @@ -194,7 +195,6 @@ int CFPParser::Parse(const char *str) continue; } - char *label = NULL; char *col_ptr = NULL; char *opcode = NULL; char *ptr = line; @@ -210,7 +210,7 @@ int CFPParser::Parse(const char *str) } if(valid) { - label = strtok(ptr,":\x20"); + (void)strtok(ptr,":\x20"); ptr = col_ptr + 1; } } @@ -218,7 +218,7 @@ int CFPParser::Parse(const char *str) opcode = strtok(ptr," "); if(opcode) { - char *param_str = SkipSpaces(strtok(NULL,"\0")); + const char *param_str = SkipSpaces(strtok(NULL,"\0")); if(strcasecmp(opcode,"OPTION")==0) { if(strncasecmp(param_str,"NV_fragment_program2",20)==0) m_nOption |= NV_OPTION_FP2; @@ -252,7 +252,7 @@ int CFPParser::Parse(const char *str) void CFPParser::ParseInstruction(struct nvfx_insn *insn,opcode *opc,const char *param_str) { - char *token = SkipSpaces(strtok((char*)param_str,",")); + const char *token = SkipSpaces(strtok((char*)param_str,",")); insn->precision = opc->suffixes&(_R|_H|_X); insn->sat = ((opc->suffixes&_S) ? TRUE : FALSE); @@ -289,20 +289,14 @@ void CFPParser::ParseInstruction(struct nvfx_insn *insn,opcode *opc,const char * token = SkipSpaces(strtok(NULL,",")); ParseScalarSrc(token,&insn->src[1]); } else if(opc->inputs==INPUT_1V_T) { - u8 unit,target; - ParseVectorSrc(token,&insn->src[0]); token = SkipSpaces(strtok(NULL,",")); - ParseTextureUnit(token,&unit); + ParseTextureUnit(token,&insn->tex_unit); token = SkipSpaces(strtok(NULL,",")); - ParseTextureTarget(token,&target); - - insn->unit = unit; + ParseTextureTarget(token,&insn->tex_target); } else if(opc->inputs==INPUT_3V_T) { - u8 unit,target; - ParseVectorSrc(token,&insn->src[0]); token = SkipSpaces(strtok(NULL,",")); @@ -312,12 +306,10 @@ void CFPParser::ParseInstruction(struct nvfx_insn *insn,opcode *opc,const char * ParseVectorSrc(token,&insn->src[2]); token = SkipSpaces(strtok(NULL,",")); - ParseTextureUnit(token,&unit); + ParseTextureUnit(token,&insn->tex_unit); token = SkipSpaces(strtok(NULL,",")); - ParseTextureTarget(token,&target); - - insn->unit = unit; + ParseTextureTarget(token,&insn->tex_target); } else if(opc->inputs==INPUT_CC) { ParseCond(token,insn); } @@ -402,8 +394,8 @@ void CFPParser::ParseOutput(const char *param_str) { oparam p; s32 reg = -1; - char *token = SkipSpaces(strtok((char*)param_str," =")); - char *name = SkipSpaces(strtok(NULL,"=\0")); + const char *token = SkipSpaces(strtok((char*)param_str," =")); + const char *name = SkipSpaces(strtok(NULL,"=\0")); ParseOutputReg(name,®); @@ -415,8 +407,6 @@ void CFPParser::ParseOutput(const char *param_str) const char* CFPParser::ParseOutputReg(const char *token, s32 *reg) { - u32 i; - if(isdigit(*token)) { char *p = (char*)token; while(isdigit(*p)) p++; @@ -426,9 +416,9 @@ const char* CFPParser::ParseOutputReg(const char *token, s32 *reg) return (token + (p - token)); } - for(i=0;i::iterator it = m_lOParameters.begin(); diff --git a/tools/cgcomp/source/main.cpp b/tools/cgcomp/source/main.cpp index 6aae360d..32a12555 100644 --- a/tools/cgcomp/source/main.cpp +++ b/tools/cgcomp/source/main.cpp @@ -1,7 +1,7 @@ #include "types.h" #include "fpparser.h" #include "vpparser.h" -#include "compiler.h" +#include "compilervp.h" #include "compilerfp.h" #ifdef __CYGWIN__ @@ -33,16 +33,21 @@ struct _options const char *dst_file; const char *entry; int prog_type; + int profile; bool gen_asm; bool compile; bool strip; + bool dump_asm; + std::vector cg_args; } Options = { NULL, NULL, "main", PROG_TYPE_NONE, + -1, false, true, + false, false }; @@ -52,6 +57,8 @@ struct _options #define CG_PROFILE_FP30 6149 #define CG_PROFILE_FP40 6151 #define CG_PROFILE_VP40 7001 +#define CG_PROFILE_GP4FP 7010 +#define CG_PROFILE_GP4VP 7011 typedef void*(*_cgCreateContext)(); typedef void(*_cgDestroyContext)(void *context); @@ -65,6 +72,9 @@ _cgCreateProgramFromFile cgCreateProgramFromFile=NULL; _cgGetProgramString cgGetProgramString=NULL; _cgGetLastListing cgGetLastListing=NULL; +static const char *cgDefArgs[] = { "-O3", "-bestprecision", "-unroll", "count=4", "-ifcvt", "all", NULL }; +static size_t numCgDefArgs = sizeof(cgDefArgs)/sizeof(char*); + static bool InitCompiler() { #if defined(WIN32) @@ -102,9 +112,13 @@ static u32 endian_fp(u32 v) void usage() { - printf("cgcomp [options] input output\n"); - printf("\t-f Input is fragment program\n"); - printf("\t-v Input is vertex program\n"); + fprintf(stderr, "cgcomp [options] input output\n"); + fprintf(stderr, "\t-f Input is fragment program\n"); + fprintf(stderr, "\t-v Input is vertex program\n"); + fprintf(stderr, "\t-d Dump assembly to file (.asm)\n"); + fprintf(stderr, "\t-e Specify entry point function\n"); + fprintf(stderr, "\t-a Compile from assembly input file\n"); + fprintf(stderr, "\t-Wcg, Additional arguments passed to the cg compiler frontend\n"); } void readoptions(struct _options *options,int argc,char *argv[]) @@ -118,6 +132,15 @@ void readoptions(struct _options *options,int argc,char *argv[]) case 'v': options->prog_type = PROG_TYPE_VP; break; case 'e': options->entry = argv[++i]; break; case 'a': options->gen_asm = true; break; + case 'd': options->dump_asm = true; break; + case 'W': + { + char *cg_arg = &argv[i][2]; + + if(cg_arg[0] == 'c' && cg_arg[1] == 'g' && cg_arg[2] == ',') + options->cg_args.push_back(&cg_arg[3]); + } + break; } } else break; @@ -172,6 +195,28 @@ char* readfile(const char *filename) return prg; } +void* createProgram(void *context, int profile) +{ + int argc; + char **argv; + int numArgs; + + numArgs = numCgDefArgs + Options.cg_args.size(); + argv = new char*[numArgs]; + + memset(argv, 0, numArgs); + + argc = 0; + for(u32 i=0;i < numCgDefArgs && cgDefArgs[i];i++) { + argv[argc++] = strdup(cgDefArgs[i]); + } + for(u32 i=0;i < Options.cg_args.size();i++) { + argv[argc++] = strdup(Options.cg_args[i].c_str()); + } + + return cgCreateProgramFromFile(context, CG_SOURCE, Options.src_file, profile, Options.entry, (const char**)argv); +} + int compileVP() { char *prg; @@ -182,7 +227,7 @@ int compileVP() prg = readfile(Options.src_file); } else { context = cgCreateContext(); - program = cgCreateProgramFromFile(context,CG_SOURCE,Options.src_file,CG_PROFILE_VP40,Options.entry,NULL); + program = createProgram(context, CG_PROFILE_VP40); if(program==NULL) { const char *error = cgGetLastListing(context); fprintf(stderr,"%s\n",error); @@ -193,7 +238,17 @@ int compileVP() if(prg) { CVPParser parser; - CCompiler compiler; + CCompilerVP compiler; + + if(Options.dump_asm) { + FILE *fDump = NULL; + std::string fname = Options.dst_file; + + fname.append(".dump"); + fDump = fopen(fname.c_str(),"wb"); + fwrite(prg,strlen(prg),1,fDump); + fclose(fDump); + } parser.Parse(prg); compiler.Compile(&parser); @@ -225,8 +280,9 @@ int compileVP() rsxVertexProgram *vp = (rsxVertexProgram*)vertexprogram; vp->magic = SWAP16(magic); - vp->start_insn = SWAP16(0); + vp->insn_start = SWAP16(0); vp->const_start = SWAP16(0); + vp->num_regs = SWAP16(compiler.GetNumRegs()); vp->input_mask = SWAP32(compiler.GetInputMask()); vp->output_mask = SWAP32(compiler.GetOutputMask()); @@ -235,7 +291,7 @@ int compileVP() rsxProgramAttrib *attribs = (rsxProgramAttrib*)(vertexprogram + lastoff); - vp->attrib_off = SWAP32(lastoff); + vp->attr_off = SWAP32(lastoff); n = 0; std::list params = parser.GetParameters(); @@ -248,7 +304,7 @@ int compileVP() n++; } } - vp->num_attrib = SWAP16(n); + vp->num_attr = SWAP16(n); lastoff += (n*sizeof(rsxProgramAttrib)); while(lastoff&3) @@ -332,7 +388,7 @@ int compileFP() prg = readfile(Options.src_file); } else { context = cgCreateContext(); - program = cgCreateProgramFromFile(context,CG_SOURCE,Options.src_file,CG_PROFILE_FP40,Options.entry,NULL); + program = createProgram(context, CG_PROFILE_FP40); if(program==NULL) { const char *error = cgGetLastListing(context); fprintf(stderr,"%s\n",error); @@ -345,6 +401,16 @@ int compileFP() CFPParser parser; CCompilerFP compiler; + if(Options.dump_asm) { + FILE *fDump = NULL; + std::string fname = Options.dst_file; + + fname.append(".dump"); + fDump = fopen(fname.c_str(),"wb"); + fwrite(prg,strlen(prg),1,fDump); + fclose(fDump); + } + parser.Parse(prg); compiler.Compile(&parser); @@ -356,7 +422,7 @@ int compileFP() rsxFragmentProgram *fp = (rsxFragmentProgram*)fragmentprogram; fp->magic = SWAP16(magic); - fp->num_regs = SWAP32(compiler.GetNumRegs()); + fp->num_regs = SWAP16(compiler.GetNumRegs()); fp->fp_control = SWAP32(compiler.GetFPControl()); fp->texcoords = SWAP16(compiler.GetTexcoords()); fp->texcoord2D = SWAP16(compiler.GetTexcoord2D()); @@ -365,7 +431,7 @@ int compileFP() while(lastoff&3) fragmentprogram[lastoff++] = 0; - fp->attrib_off = SWAP32(lastoff); + fp->attr_off = SWAP32(lastoff); rsxProgramAttrib *attribs = (rsxProgramAttrib*)(fragmentprogram + lastoff); n = 0; @@ -379,7 +445,7 @@ int compileFP() n++; } } - fp->num_attrib = SWAP16(n); + fp->num_attr = SWAP16(n); lastoff += (n*sizeof(rsxProgramAttrib)); while(lastoff&3) diff --git a/tools/cgcomp/source/parser.cpp b/tools/cgcomp/source/parser.cpp index 1f6d87c8..0be1320b 100644 --- a/tools/cgcomp/source/parser.cpp +++ b/tools/cgcomp/source/parser.cpp @@ -8,17 +8,21 @@ static paramtype paramtypes[] = { { "float", PARAM_FLOAT }, + { "float1", PARAM_FLOAT1 }, { "float2", PARAM_FLOAT2 }, { "float3", PARAM_FLOAT3 }, { "float4", PARAM_FLOAT4 }, + { "float3x4", PARAM_FLOAT3x4 }, { "float4x4", PARAM_FLOAT4x4 }, + { "float3x3", PARAM_FLOAT3x3 }, + { "float4x3", PARAM_FLOAT4x3 }, { "sampler1D", PARAM_SAMPLER1D }, { "sampler2D", PARAM_SAMPLER2D }, { "sampler3D", PARAM_SAMPLER3D }, { "samplerCUBE", PARAM_SAMPLERCUBE }, { "samplerRECT", PARAM_SAMPLERRECT }, }; -static const u32 PARAM_TYPE_CNT = sizeof(paramtypes)/sizeof(struct _paramtype); +static const size_t PARAM_TYPE_CNT = sizeof(paramtypes)/sizeof(struct _paramtype); CParser::CParser() { @@ -40,37 +44,42 @@ void CParser::ParseComment(const char *line) line++; if(strncasecmp(line,"var",3)==0) { - char *token = SkipSpaces(strtok((char*)(line+3)," :")); + const char *token = SkipSpaces(strtok((char*)(line+3)," :")); p.type = GetParamType(token); p.is_const = 0; p.is_internal = 0; p.count = 1; p.name = SkipSpaces(strtok(NULL," :")); +next: token = SkipSpaces(strtok(NULL," :")); - if(strstr(token,"$vin")) { - token = SkipSpaces(strtok(NULL," :")); - if(strncasecmp(token,"ATTR",4)==0) - p.index = atoi(token+4); - else - p.index = ConvertInputReg(token); - } else if(strstr(token,"texunit")) { - token = SkipSpaces(strtok(NULL," :")); - p.index = atoi(token); - } else if(token[0]=='c') { - p.is_const = 1; - p.index = atoi(token+2); - - token = strtok(NULL," ,"); - if(isdigit(*token)) p.count = atoi(token); + if (token) { + if(strstr(token,"$vin")) { + token = SkipSpaces(strtok(NULL," :")); + if(strncasecmp(token,"ATTR",4)==0) + p.index = atoi(token+4); + else + p.index = ConvertInputReg(token); + } else if(strstr(token,"texunit")) { + token = SkipSpaces(strtok(NULL," :")); + p.index = atoi(token); + } else if(token[0]=='c') { + p.is_const = 1; + p.index = atoi(token+2); + + token = strtok(NULL," ,"); + if(isdigit(*token)) p.count = atoi(token); + } else + goto next; } else return; - InitParameter(&p); - - m_lParameters.push_back(p); + if(p.index>-1) { + InitParameter(&p); + m_lParameters.push_back(p); + } } else if(strncasecmp(line,"const",5)==0) { - char *token = SkipSpaces(strtok((char*)(line+5)," ")); + const char *token = SkipSpaces(strtok((char*)(line+5)," ")); p.is_const = 1; p.is_internal = 1; @@ -106,10 +115,13 @@ void CParser::InitParameter(param *p) switch(p->type) { case PARAM_FLOAT4x4: - p->values[0][0] = 1.0f; - p->values[1][1] = 1.0f; - p->values[2][2] = 1.0f; p->values[3][3] = 1.0f; + case PARAM_FLOAT4x3: + case PARAM_FLOAT3x3: + case PARAM_FLOAT3x4: + p->values[2][2] = 1.0f; + p->values[1][1] = 1.0f; + p->values[0][0] = 1.0f; break; default: break; @@ -150,7 +162,8 @@ void CParser::InitInstruction(struct nvfx_insn *insn,u8 op) { insn->op = op; insn->scale = 0; - insn->unit = -1; + insn->tex_unit = -1; + insn->tex_target = -1; insn->precision = FLOAT32; insn->mask = NVFX_VP_MASK_ALL; insn->cc_swz[0] = 0; insn->cc_swz[1] = 1; insn->cc_swz[2] = 2; insn->cc_swz[3] = 3; @@ -160,6 +173,7 @@ void CParser::InitInstruction(struct nvfx_insn *insn,u8 op) insn->cc_cond = NVFX_COND_TR; insn->cc_test = 0; insn->cc_test_reg = 0; + insn->disable_pc = 0; insn->dst = nvfx_reg(NVFXSR_NONE,0); insn->src[0] = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); insn->src[1] = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); insn->src[2] = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); } @@ -181,10 +195,8 @@ bool CParser::isWhitespace(int c) s32 CParser::GetParamType(const char *param_str) { - u32 i; - - for(i=0;i::iterator i=m_lIdent.begin();i!=m_lIdent.end();i++) { - if(strcmp(r->ident,i->ident)==0) { + if(strcmp(r->ident.c_str(),i->ident.c_str())==0) { found = true; m_pInstructions[r->location].dst = nvfx_reg(NVFXSR_RELOCATED,i->location); break; @@ -276,7 +278,7 @@ int CVPParser::Parse(const char *str) } if(found==false) { - fprintf(stderr,"Identifier \'%s\' not found.\n",r->ident); + fprintf(stderr,"Identifier \'%s\' not found.\n",r->ident.c_str()); exit(EXIT_FAILURE); } } @@ -287,7 +289,7 @@ int CVPParser::Parse(const char *str) void CVPParser::ParseInstruction(struct nvfx_insn *insn,opcode *opc,const char *param_str) { u32 i; - char *token = SkipSpaces(strtok((char*)param_str,",")); + const char *token = SkipSpaces(strtok((char*)param_str,",")); if(opc->is_imm) ParseMaskedDstAddr(token,insn); @@ -296,7 +298,7 @@ void CVPParser::ParseInstruction(struct nvfx_insn *insn,opcode *opc,const char * for(i=0;inr_src;i++) { token = SkipSpaces(strtok(NULL,",")); - ParseSwizzledSrcReg(token,&insn->src[opc->src_slots[i]]); + ParseSwizzledSrcReg(token,insn,opc->src_slots[i]); } } @@ -306,12 +308,13 @@ void CVPParser::ParseMaskedDstReg(const char *token,struct nvfx_insn *insn) if(!token) return; - if(token[0]=='R') { - if(token[1]=='C') return; - - token = ParseTempReg(token,&idx); - insn->dst.type = NVFXSR_TEMP; - insn->dst.index = idx; + if(token[0]=='R' || token[0]=='H') { + if(token[1]=='C') token += 2; + else { + token = ParseTempReg(token,&idx); + insn->dst.type = NVFXSR_TEMP; + insn->dst.index = idx; + } } else if(token[0]=='r' && token[1]=='e') { token = ParseOutputReg(token,&idx); insn->dst.type = NVFXSR_OUTPUT; @@ -320,17 +323,19 @@ void CVPParser::ParseMaskedDstReg(const char *token,struct nvfx_insn *insn) token = ParseOutputReg(&token[2],&idx); insn->dst.type = NVFXSR_OUTPUT; insn->dst.index = idx; + } else if(token[0]=='A' && (token[1]=='0' || token[1]=='1')) { + token = ParseOutputReg(&token[1],&idx); + insn->dst.type = NVFXSR_ADDRESS; + insn->dst.index = idx; } else if(token[0]=='C' && token[1]=='C') - return; + token += 2; ParseMaskedDstRegExt(token,insn); } opcode* CVPParser::FindOpcode(const char *mnemonic) { - u32 i; - - for(i=0;isrc[slot]; if(!token) return; @@ -380,6 +383,13 @@ void CVPParser::ParseSwizzledSrcReg(const char *token,struct nvfx_src *reg) reg->reg.type = NVFXSR_INPUT; reg->reg.index = idx; + } else if(!strncmp(token,"texture[",8)) { + ParseTextureUnit(token,&insn->tex_unit); + token = SkipSpaces(strtok(NULL,",")); + ParseTextureTarget(token,&insn->tex_target); + + reg->reg.type = NVFXSR_SAMPLER; + reg->reg.index = insn->tex_unit; } else if(token[0]=='R') { token = ParseTempReg(token,&idx); reg->reg.type = NVFXSR_TEMP; @@ -396,7 +406,7 @@ const char* CVPParser::ParseParamReg(const char *token,struct nvfx_src *reg) { if(!token) return NULL; - char *p = (char*)token; + const char *p = token; if(isdigit(*p)) { reg->reg.type = NVFXSR_CONST; @@ -429,9 +439,12 @@ const char* CVPParser::ParseParamReg(const char *token,struct nvfx_src *reg) } p += k; } + + p = SkipSpaces(p); if(*p=='-' || *p=='+') { const char sign = *p++; + p = SkipSpaces(p); if(isdigit(*p)) { const s32 k = atoi(p); if(sign=='-') { @@ -484,8 +497,6 @@ s32 CVPParser::ConvertInputReg(const char *token) const char* CVPParser::ParseOutputReg(const char *token,s32 *reg) { - u32 i; - if(isdigit(*token)) { char *p = (char*)token; while(isdigit(*p)) p++; @@ -495,12 +506,12 @@ const char* CVPParser::ParseOutputReg(const char *token,s32 *reg) return (token + (p - token)); } - for(i=0;i Date: Fri, 10 Jul 2020 12:57:14 +0200 Subject: [PATCH 05/56] - remove accidentally committed files. --- .gitignore | 2 + common/libspumars/ppu/ppu/mars_kernel.c | 724 ------------------- common/libspumars/ppu/ppu/mars_task_module.c | 475 ------------ 3 files changed, 2 insertions(+), 1199 deletions(-) delete mode 100644 common/libspumars/ppu/ppu/mars_kernel.c delete mode 100644 common/libspumars/ppu/ppu/mars_task_module.c diff --git a/.gitignore b/.gitignore index 4542cfc5..5b36bec6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,8 @@ build/ .idea/ .vscode/ +common/libspumars/ppu/ppu/ +common/libspumars/spu/spu/ ppu/sprx/libaudio/ppu/ ppu/sprx/libcamera/ppu/ ppu/sprx/libgcm_sys/ppu/ diff --git a/common/libspumars/ppu/ppu/mars_kernel.c b/common/libspumars/ppu/ppu/mars_kernel.c deleted file mode 100644 index ad40cc9e..00000000 --- a/common/libspumars/ppu/ppu/mars_kernel.c +++ /dev/null @@ -1,724 +0,0 @@ -__attribute__((aligned(128))) const unsigned char mars_kernel_entry[] = { -0x7f,0x45,0x4c,0x46,0x01,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x02,0x00,0x17,0x00,0x00,0x00,0x01,0x00,0x00,0x1e,0x68,0x00,0x00,0x00,0x34, -0x00,0x00,0x21,0x40,0x00,0x00,0x00,0x00,0x00,0x34,0x00,0x20,0x00,0x02,0x00,0x28, -0x00,0x0d,0x00,0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0xe0,0x00,0x00,0x26,0x80,0x00,0x00,0x00,0x07, -0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x04,0x00,0x00,0x20,0x80,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, -0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x7b,0x00,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81, -0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x33,0x03,0xba,0x80, -0x32,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x40,0x82,0x00,0x02,0x35,0x90,0x00,0x00, -0x00,0x20,0x00,0x00,0x12,0x00,0x08,0x2b,0x24,0x00,0x40,0x80,0x24,0xfc,0x80,0x81, -0x1c,0xc8,0x00,0x81,0x23,0x84,0xab,0x83,0x21,0xa0,0x00,0x82,0x42,0x12,0x00,0x03, -0x40,0x80,0x00,0x8a,0x00,0x20,0x00,0x00,0x04,0x00,0x01,0x89,0x3e,0xc0,0x01,0x86, -0x40,0x80,0x40,0x05,0x40,0x80,0x00,0x04,0x35,0x90,0x00,0x00,0x40,0x80,0x68,0x08, -0x40,0x80,0x5a,0x07,0x33,0x84,0xa5,0x8b,0x21,0xa0,0x08,0x09,0x3f,0xbf,0x05,0x8c, -0x3f,0xe1,0x06,0x0d,0x21,0xa0,0x08,0x8d,0x3f,0xe1,0x05,0x8e,0x21,0xa0,0x09,0x0e, -0x21,0xa0,0x09,0x85,0x21,0xa0,0x0a,0x04,0x21,0xa0,0x0a,0x88,0x01,0xa0,0x0d,0x82, -0x33,0x84,0x6a,0x0f,0xb2,0x03,0xc5,0x06,0x23,0x84,0x69,0x10,0x00,0x60,0x00,0x00, -0x33,0x84,0x9e,0x11,0x21,0xa0,0x08,0x09,0x3f,0xbf,0x08,0x92,0x3f,0xe1,0x09,0x13, -0x21,0xa0,0x08,0x93,0x3f,0xe1,0x08,0x94,0x21,0xa0,0x09,0x14,0x21,0xa0,0x09,0x85, -0x21,0xa0,0x0a,0x04,0x21,0xa0,0x0a,0x87,0x01,0xa0,0x0d,0x95,0x14,0x00,0x4a,0x96, -0x21,0x7f,0xf2,0x96,0x40,0x82,0x00,0x17,0x40,0x80,0x00,0x1b,0x04,0x00,0x0b,0x98, -0x42,0x12,0x00,0x19,0x40,0x80,0x40,0x1a,0x04,0x00,0x0d,0x9c,0x40,0x80,0x68,0x1d, -0x12,0x00,0x00,0x12,0x21,0xa0,0x01,0x18,0x33,0x84,0x93,0x1e,0x21,0xa0,0x08,0x19, -0x3f,0xbf,0x0f,0x1f,0x3f,0xe1,0x0f,0xa0,0x21,0xa0,0x08,0xa0,0x3f,0xe1,0x0f,0x21, -0x21,0xa0,0x09,0x21,0x21,0xa0,0x09,0x9a,0x21,0xa0,0x0a,0x1c,0x21,0xa0,0x0a,0x9d, -0x01,0xa0,0x0d,0x82,0x33,0x84,0x57,0xa2,0x14,0x00,0x91,0x23,0x00,0x20,0x00,0x00, -0x21,0x00,0x01,0xa3,0x01,0xa0,0x00,0x02,0x32,0x7f,0xf7,0x00,0x12,0x03,0x1e,0x8a, -0x21,0xa0,0x03,0x9b,0x21,0xa0,0x01,0x17,0x42,0x10,0x80,0x24,0x24,0x03,0x40,0xa4, -0x40,0x80,0x0f,0x86,0x33,0x84,0x53,0x84,0x40,0x80,0x40,0x05,0x42,0x11,0xc0,0x03, -0x40,0x20,0x00,0x7f,0x33,0x03,0x19,0x80,0x40,0x80,0x0f,0x83,0x33,0x03,0x2e,0x80, -0x42,0x11,0xcc,0x26,0x33,0x84,0x40,0xa5,0x3b,0x89,0x92,0xa7,0x35,0x90,0x00,0x00, -0x14,0x00,0x53,0xa8,0x21,0x00,0xac,0xa8,0x40,0x80,0x00,0x4e,0x40,0x80,0x00,0x4f, -0x40,0x80,0x00,0x09,0x40,0xff,0xff,0x8a,0x40,0x80,0x00,0x05,0x18,0x01,0x42,0xa9, -0x42,0x11,0xc0,0x2a,0x40,0x80,0x0e,0x2d,0x35,0x90,0x00,0x00,0x18,0x0a,0x94,0xab, -0x42,0x7f,0xff,0xb2,0x00,0x20,0x00,0x00,0x1c,0x0a,0x95,0xaf,0x38,0x8b,0x55,0xae, -0x0f,0x38,0x45,0x2c,0x3b,0x8b,0xd7,0x30,0x0f,0xbe,0x18,0x31,0x18,0x2c,0x98,0x33, -0x14,0x00,0x98,0x34,0x00,0x20,0x00,0x00,0x14,0x00,0x58,0x49,0x35,0x90,0x00,0x00, -0x40,0x20,0x00,0x7f,0x3f,0xbf,0x19,0xb5,0x5a,0x02,0x58,0xb6,0x00,0x20,0x00,0x00, -0x22,0x00,0x14,0x34,0x00,0x20,0x00,0x00,0x7a,0x02,0x58,0xb7,0x36,0x80,0x1b,0x38, -0x3f,0x3f,0x9a,0xba,0x36,0x80,0x1b,0xb9,0x0c,0x00,0x1c,0x3b,0x3f,0xe1,0x1d,0x3e, -0x08,0x2b,0x1d,0xbd,0x0c,0x00,0x1c,0xbc,0x56,0xc0,0x1e,0xbf,0x14,0x0f,0xdf,0x40, -0x23,0x00,0x2a,0xbf,0x59,0x13,0xe0,0x41,0x55,0xc0,0x20,0xc2,0x0c,0x00,0x21,0x43, -0x18,0x2f,0x21,0xc4,0x56,0xc0,0x22,0x45,0x7d,0x00,0x22,0xc6,0x00,0x20,0x00,0x00, -0x88,0x13,0xe0,0x46,0x36,0x80,0x23,0x47,0x89,0x02,0x82,0xc7,0x12,0x00,0xbd,0x89, -0x40,0x80,0x01,0x03,0x24,0x01,0xc0,0xc9,0x04,0x00,0x02,0x84,0x24,0x00,0x80,0x85, -0x24,0x01,0x40,0xce,0x24,0x01,0x80,0x89,0x24,0x00,0xc0,0xc0,0x24,0x01,0x00,0xc8, -0x33,0x00,0xb9,0x00,0x34,0x00,0xc0,0x8b,0x34,0x01,0x80,0x89,0x34,0x01,0x40,0xce, -0x34,0x00,0x80,0x85,0x34,0x01,0xc0,0xc9,0x34,0x01,0x00,0x8a,0x04,0x00,0x05,0xcf, -0x56,0xc0,0x24,0xca,0x22,0x00,0x0b,0x4a,0x40,0x20,0x00,0x7f,0x12,0x02,0x4e,0x89, -0x40,0x80,0x00,0x04,0x24,0x01,0x00,0x8a,0x04,0x00,0x02,0x83,0x24,0x00,0x80,0x85, -0x40,0x20,0x00,0x7f,0x24,0x01,0x40,0xce,0x24,0x01,0x80,0x89,0x24,0x01,0xc0,0xcf, -0x33,0x02,0x4a,0x00,0x12,0x7f,0xdb,0x0c,0x09,0x20,0xc1,0xcb,0x34,0x01,0x40,0xcd, -0x40,0x20,0x00,0x7f,0x34,0x01,0xc0,0xcf,0x0f,0x38,0x65,0xcc,0x34,0x01,0x80,0x89, -0x34,0x00,0x80,0x85,0x34,0x01,0x00,0x8a,0x08,0x33,0x26,0xce,0x1c,0x00,0x42,0x85, -0x7c,0x0c,0x82,0x82,0x20,0x7f,0xd5,0x02,0x7c,0xff,0xc5,0x03,0x20,0x00,0x10,0x83, -0x40,0x20,0x00,0x7f,0x21,0x7f,0xc9,0xce,0x40,0x82,0x00,0x27,0x12,0x01,0x1f,0x95, -0x04,0x00,0x13,0xa8,0x21,0xa0,0x00,0xa8,0x33,0x84,0x1b,0x05,0x42,0x13,0x00,0x29, -0x21,0xa0,0x08,0x29,0x3f,0xbf,0x02,0xaa,0x3f,0xe1,0x15,0x2b,0x21,0xa0,0x08,0xab, -0x3f,0xe1,0x02,0xac,0x21,0xa0,0x09,0x2c,0x40,0x80,0x40,0x2d,0x21,0xa0,0x09,0xad, -0x21,0xa0,0x0a,0x4e,0x40,0x80,0x68,0x2e,0x21,0xa0,0x0a,0xae,0x01,0xa0,0x0d,0x82, -0x40,0x80,0x40,0x05,0x42,0x13,0x00,0x04,0x42,0x11,0xc0,0x03,0x24,0x01,0xc0,0xa7, -0x33,0x01,0x15,0x00,0x34,0x01,0xc0,0xaf,0x20,0x00,0x6d,0x83,0x21,0xa0,0x01,0x2f, -0x32,0x7f,0xbc,0x00,0x04,0x00,0x18,0x89,0x04,0x00,0x02,0xc8,0x32,0x7f,0xda,0x00, -0x40,0x80,0x00,0x84,0x24,0x01,0x00,0x8a,0x04,0x00,0x05,0x03,0x33,0x02,0x30,0x80, -0x40,0x20,0x00,0x7f,0x12,0x00,0x76,0x89,0x4c,0xff,0xc1,0x86,0x34,0x01,0x00,0x84, -0x20,0x7f,0xb6,0x06,0x0f,0xe1,0x02,0x08,0x19,0x00,0xc4,0x03,0x04,0x00,0x01,0x87, -0x3f,0xe0,0x83,0x8c,0x23,0x84,0x39,0x8c,0x33,0x00,0x72,0x00,0x12,0x02,0xce,0x8a, -0x04,0x00,0x01,0x8d,0x34,0x03,0x40,0x8e,0x04,0x00,0x01,0x84,0x00,0x20,0x00,0x00, -0x40,0x80,0x80,0x05,0x23,0x84,0x33,0x8d,0x40,0x80,0x0f,0x86,0x42,0x10,0x80,0x03, -0x23,0x84,0x24,0x0e,0x33,0x02,0xc9,0x80,0x40,0x80,0x0f,0x83,0x33,0x02,0xde,0x80, -0x40,0x80,0x20,0x05,0x33,0x84,0x21,0x84,0x42,0x12,0x98,0x03,0x33,0x01,0x01,0x80, -0x7c,0x00,0x01,0x8f,0x0c,0x00,0x07,0x90,0x3f,0xe0,0xc8,0x11,0x23,0x84,0x1c,0x91, -0x40,0x20,0x00,0x7f,0x20,0x00,0x09,0x83,0x40,0x80,0x0c,0x17,0x33,0x84,0x1c,0x92, -0x40,0x80,0x0f,0x86,0x12,0x02,0xc1,0x8e,0x1c,0x06,0x09,0x19,0x34,0x00,0x09,0x13, -0x34,0x00,0x49,0x14,0x34,0x00,0x89,0x15,0x34,0x00,0xc9,0x16,0x34,0x00,0x09,0x04, -0x23,0x84,0x1a,0x13,0x23,0x84,0x1b,0x94,0x23,0x84,0x1d,0x15,0x23,0x84,0x1e,0x96, -0x38,0x85,0xc9,0x18,0x34,0x00,0x49,0x03,0x3b,0x86,0x4c,0x05,0x33,0x02,0xba,0x80, -0x40,0x80,0x0e,0x1b,0x12,0x02,0xb9,0x90,0x40,0x80,0x0a,0x1c,0x33,0x84,0x12,0x9a, -0x40,0x80,0x04,0x20,0x40,0x80,0x0f,0x86,0x1c,0x07,0x0d,0x1f,0x38,0x86,0xcd,0x1d, -0x1c,0x02,0x0d,0x22,0x38,0x87,0x0d,0x1e,0x1c,0x05,0x0d,0x23,0x38,0x88,0x0d,0x21, -0x24,0x00,0x80,0x9b,0x24,0x01,0xc0,0x9c,0x3b,0x87,0xce,0x85,0x3b,0x88,0xcf,0x03, -0x3b,0x88,0x90,0x84,0x33,0x02,0xb1,0x80,0x33,0x84,0x0b,0x24,0x34,0x01,0xc0,0xa5, -0x34,0x00,0x80,0xa7,0x12,0x00,0x42,0x8e,0x1c,0x05,0x12,0x2a,0x34,0x00,0x92,0x05, -0x1c,0x07,0x12,0x2b,0x38,0x89,0x52,0x26,0x40,0x80,0x00,0x25,0x38,0x89,0xd2,0x28, -0x04,0x00,0x02,0xa9,0x3b,0x8a,0x93,0x03,0x3b,0x8a,0xd4,0x2d,0x04,0x00,0x01,0xac, -0x18,0x0b,0x56,0x26,0x18,0x0a,0x53,0x24,0x58,0x09,0x92,0x2e,0x21,0x00,0x3b,0xae, -0x40,0x80,0x0f,0x83,0x33,0x02,0xbd,0x80,0x00,0x40,0x00,0x00,0x42,0x11,0x80,0x2f, -0x24,0x00,0x80,0xaf,0x33,0x03,0x2c,0x80,0x42,0x7f,0xff,0xb0,0x33,0x84,0x0e,0xb3, -0x42,0x11,0xc4,0x37,0x35,0x90,0x00,0x00,0x40,0x20,0x00,0x7f,0x33,0x83,0xc9,0xb6, -0x42,0x11,0x80,0x04,0x32,0x80,0x80,0xb1,0x33,0x83,0x33,0x3b,0x12,0x02,0xbc,0x9f, -0x40,0x20,0x00,0x7f,0x33,0x83,0x31,0xc8,0x16,0xe0,0x18,0xb2,0x3f,0x83,0x99,0xb4, -0x3b,0x8d,0xdb,0x38,0x34,0x00,0x80,0xc4,0x35,0x90,0x00,0x00,0x0f,0xbf,0x1a,0x35, -0x14,0x03,0xda,0x3a,0x18,0x4c,0x9c,0x39,0x40,0x20,0x00,0x7f,0x24,0x02,0x80,0xba, -0x18,0x2c,0x1a,0xbd,0xb7,0x8e,0x5c,0xbb,0x34,0x02,0x80,0xbe,0x24,0x02,0x40,0xbd, -0x34,0x02,0x40,0xc0,0x68,0x0c,0x9c,0x3c,0x18,0x2c,0x1f,0x3f,0x0f,0x61,0xe0,0x41, -0x0f,0x60,0xdf,0xc2,0x3f,0xbf,0x20,0xc3,0x18,0x11,0x21,0x45,0x24,0x00,0xc0,0xc2, -0x24,0x02,0xc0,0xc5,0x54,0xc0,0x21,0xc6,0x18,0x51,0x9e,0x47,0xb0,0x71,0xe3,0xc8, -0x68,0x11,0x9e,0x03,0x24,0x02,0x00,0x83,0x33,0x02,0xad,0x00,0x34,0x00,0xc0,0x8b, -0x34,0x00,0x80,0xc9,0x34,0x02,0xc0,0xcb,0x12,0x00,0x15,0x09,0x38,0x92,0x45,0xca, -0x3b,0x92,0xe5,0x4c,0x3f,0xbc,0x66,0x4d,0x3f,0xe3,0x26,0xce,0x14,0xe2,0x27,0x4f, -0x56,0xc0,0x27,0x89,0x24,0x03,0x00,0x89,0x40,0x20,0x00,0x7f,0x23,0x00,0x10,0x89, -0x40,0x80,0x0f,0x86,0x33,0x83,0xf1,0x84,0x40,0x80,0x80,0x05,0x42,0x10,0x80,0x03, -0x40,0x20,0x00,0x7f,0x33,0x02,0x92,0x80,0x40,0x80,0x0f,0x83,0x33,0x02,0x9c,0x80, -0x42,0x11,0x80,0x0a,0x33,0x83,0xeb,0x86,0x40,0x80,0x00,0x83,0x34,0x00,0xc0,0x82, -0x34,0x00,0x80,0x87,0x34,0x02,0xc0,0x90,0x12,0x00,0x44,0x11,0x32,0xbf,0xbf,0x92, -0x3f,0x83,0x43,0x0e,0x3a,0xe2,0x81,0x0d,0x38,0x81,0xc1,0x0c,0x38,0x81,0xc1,0x08, -0x14,0x3f,0xc7,0x0f,0x34,0x02,0x40,0x84,0x3f,0xbf,0x07,0x94,0x3b,0x84,0x06,0x11, -0x3f,0x82,0x0a,0x15,0x18,0x24,0x88,0x93,0x3f,0xe3,0xca,0x96,0x08,0x25,0x89,0x97, -0xb3,0x02,0x0b,0x8d,0x28,0x81,0xc1,0x18,0x40,0x20,0x00,0x7f,0x33,0x00,0x3b,0x80, -0x42,0x11,0x80,0x04,0x34,0x02,0x00,0x83,0x33,0x02,0xc9,0x00,0x12,0x7f,0x5c,0x89, -0x40,0x20,0x00,0x7f,0x34,0x03,0x00,0x84,0x40,0x20,0x00,0x7f,0x23,0x7f,0x5a,0x84, -0x33,0x83,0xdc,0x19,0x3f,0x83,0x4c,0x9a,0x4e,0xff,0xcd,0x1b,0x56,0xc0,0x0d,0x9c, -0x23,0x7f,0x58,0x1c,0x34,0x02,0x80,0x9d,0x12,0x00,0xa6,0x09,0x34,0x02,0x00,0xa1, -0x33,0x83,0x04,0x23,0x14,0x03,0xce,0x9e,0x0f,0x60,0xcf,0x1f,0x3f,0xbf,0x0f,0xa0, -0x18,0x48,0x50,0x22,0xb0,0x68,0x91,0x23,0x68,0x08,0x50,0x03,0x33,0x00,0xa1,0x80, -0x40,0x20,0x00,0x7f,0x32,0x7f,0x51,0x80,0x1c,0x04,0x13,0x26,0x24,0xff,0xd3,0x25, -0x32,0x7f,0xc3,0x00,0x01,0xa0,0x00,0x02,0x04,0x00,0x17,0xb0,0x21,0xa0,0x01,0x30, -0x40,0x20,0x00,0x7f,0x32,0x7f,0x91,0x00,0x40,0x80,0x00,0x03,0x33,0x00,0x9b,0x80, -0x1c,0x38,0x00,0x81,0x00,0x20,0x00,0x00,0x40,0x80,0x00,0x03,0x34,0x00,0x40,0x80, -0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x42,0x12,0x00,0x02,0x40,0x80,0x04,0x03, -0x1c,0x02,0x01,0x04,0x38,0x80,0xc1,0x05,0x3b,0x81,0x02,0x83,0x35,0x00,0x00,0x00, -0x33,0x83,0x9c,0x02,0x3f,0x83,0x81,0x03,0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00, -0x33,0x83,0xca,0x02,0x3f,0x83,0x81,0x03,0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00, -0x42,0x10,0x80,0x03,0x35,0x00,0x00,0x00,0x0f,0xbf,0x01,0x82,0x33,0x83,0x84,0x84, -0x42,0x7f,0xff,0x85,0x33,0x82,0xed,0x8d,0x35,0x80,0x00,0x0b,0x18,0x21,0x41,0x83, -0x18,0x21,0x41,0x06,0x08,0x00,0xc3,0x07,0x1c,0xff,0xc3,0x88,0x0f,0x62,0x04,0x09, -0x3f,0xbf,0x04,0x8a,0x54,0xc0,0x05,0x0b,0x18,0x41,0x05,0x8c,0xb0,0x63,0x06,0x0d, -0x68,0x01,0x05,0x83,0x35,0x00,0x00,0x00,0x3f,0xbe,0x41,0x83,0x78,0x01,0x01,0x82, -0x36,0x00,0x01,0x04,0x4c,0x02,0xc2,0x05,0x0c,0x00,0x02,0x83,0x35,0x00,0x00,0x00, -0x3f,0xbe,0x41,0x83,0x78,0x01,0x01,0x82,0x36,0x00,0x01,0x04,0x4c,0x02,0xc2,0x05, -0x1c,0x00,0x42,0x83,0x35,0x00,0x00,0x00,0x3f,0x82,0x02,0x04,0x32,0xbf,0xbf,0x82, -0x3f,0xe3,0xc2,0x05,0x18,0x20,0x81,0x83,0x08,0x21,0x41,0x83,0x35,0x00,0x00,0x00, -0x3f,0x82,0x02,0x04,0x32,0xf9,0xf9,0x82,0x3f,0xe2,0x82,0x05,0x18,0x20,0x81,0x83, -0x08,0x21,0x41,0x83,0x35,0x00,0x00,0x00,0x3f,0x82,0x02,0x05,0x32,0x9f,0x9f,0x82, -0x32,0xc0,0x40,0x04,0x33,0x82,0xdb,0x8a,0x40,0x20,0x00,0x7f,0x3f,0xe3,0x82,0x86, -0x18,0x20,0x81,0x83,0x33,0x82,0xdb,0x8c,0x16,0x01,0x02,0x07,0x35,0x80,0x00,0x05, -0x08,0x21,0xc1,0x88,0x08,0x21,0x84,0x09,0x18,0x22,0x84,0x8b,0x08,0x23,0x05,0x83, -0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x3f,0xbe,0x41,0x83,0x3f,0xe1,0x01,0x82, -0x14,0x1c,0x01,0x03,0x35,0x00,0x00,0x00,0x14,0x00,0x41,0x8c,0x24,0x00,0x40,0x80, -0x04,0x00,0x01,0x89,0x35,0x90,0x00,0x00,0x24,0xfe,0x00,0x81,0x04,0x00,0x02,0x06, -0x1c,0xe0,0x00,0x81,0x40,0x80,0x00,0x05,0x40,0x80,0x00,0x08,0x00,0x20,0x00,0x00, -0x40,0x80,0x00,0x07,0x20,0x00,0x0f,0x0c,0x42,0x11,0x80,0x02,0x40,0x80,0x00,0x05, -0x40,0x80,0x00,0x08,0x35,0x90,0x00,0x00,0x40,0x80,0x00,0x07,0x40,0x80,0x04,0x0d, -0x40,0x80,0x00,0x8e,0x40,0x80,0x07,0x8a,0x40,0x20,0x00,0x7f,0x38,0x83,0x41,0x03, -0x1c,0x02,0x01,0x02,0x12,0x7f,0xfe,0x91,0x3b,0x80,0x81,0x8b,0x3f,0xbe,0x45,0x84, -0x3f,0xbe,0x85,0x8f,0x3f,0xe1,0x02,0x10,0x3f,0xe1,0x07,0x91,0x14,0x04,0x08,0x12, -0x14,0x08,0x08,0x13,0x56,0xc0,0x09,0x14,0x5a,0x04,0x42,0x95,0x00,0x20,0x00,0x00, -0x56,0xc0,0x09,0x96,0x00,0x20,0x00,0x00,0x22,0x00,0x27,0x14,0x80,0xa1,0x48,0x95, -0x40,0x80,0x00,0x87,0x1c,0xff,0xc5,0x0a,0x21,0x7f,0xf6,0x0a,0x12,0x02,0x46,0x89, -0x42,0x11,0xc0,0x04,0x33,0x83,0x61,0x83,0x24,0x01,0x00,0x85,0x24,0x00,0x80,0x86, -0x24,0x01,0x80,0x87,0x24,0x01,0x40,0x88,0x24,0x00,0xc0,0x89,0x24,0x01,0xc0,0x8c, -0x33,0x02,0x42,0x00,0x34,0x01,0xc0,0x9d,0x34,0x01,0x00,0x98,0x34,0x00,0x80,0x99, -0x35,0x90,0x00,0x00,0x34,0x01,0x80,0x9a,0x34,0x01,0x40,0x9b,0x34,0x00,0xc0,0x9c, -0x40,0x20,0x00,0x7f,0x20,0x00,0x1d,0x9d,0x18,0x06,0x4c,0xbd,0x33,0x82,0xb8,0x86, -0x42,0x11,0xc0,0x3e,0x32,0xfe,0xfe,0x8b,0x40,0x80,0x0e,0x32,0x18,0x0f,0x5f,0x31, -0x35,0x90,0x00,0x00,0x14,0x3f,0xcd,0x44,0x1c,0x0a,0x98,0xc2,0x38,0x8c,0x98,0xc0, -0x42,0x7f,0xff,0xbf,0x3f,0xbf,0x22,0x46,0x14,0x3f,0xcc,0x47,0x38,0x8c,0x98,0xc1, -0x14,0x3f,0xcd,0xc5,0x3e,0xa3,0x18,0xc3,0x3f,0xbf,0x23,0xca,0x3f,0x82,0x23,0x4b, -0x3b,0x90,0xa0,0x48,0x3f,0x82,0x25,0x4f,0x3f,0xe2,0x25,0xcc,0x18,0x2f,0xe4,0x49, -0x3f,0xbf,0x24,0xce,0x3f,0x60,0x66,0x4d,0x3f,0xe2,0x67,0x89,0x18,0x21,0xa7,0x0c, -0x08,0x33,0x46,0x05,0x3f,0xe1,0x02,0x87,0x14,0xff,0x83,0x8d,0x08,0x31,0x46,0x8e, -0x18,0x2f,0xc7,0x03,0x3f,0xbf,0x01,0x82,0x18,0x22,0xc1,0x04,0x08,0x22,0x42,0x0f, -0x3f,0xe1,0x07,0x90,0xb7,0x90,0x48,0x43,0x40,0x20,0x00,0x7f,0x28,0x8c,0x98,0xbc, -0x42,0x11,0xc0,0x11,0x33,0x83,0x34,0x14,0x40,0x80,0x02,0x0a,0x12,0x02,0x5c,0x8c, -0x1c,0x01,0x08,0x92,0x33,0x83,0x32,0x17,0x1c,0x20,0x00,0x81,0x3e,0xc1,0x08,0x93, -0x04,0x00,0x08,0x84,0x33,0x83,0x41,0x83,0x3b,0x84,0x8a,0x15,0x1c,0x00,0x4a,0x96, -0xb1,0x05,0xcb,0x13,0x28,0x82,0x88,0x88,0x34,0x00,0x40,0x80,0x32,0x02,0x56,0x80, -0x7d,0x00,0x0b,0x17,0x00,0x20,0x00,0x00,0x81,0x02,0x07,0x17,0x32,0x7f,0xd9,0x00, -0x20,0x7f,0xf6,0x1c,0x18,0x06,0x4c,0x9e,0x42,0x11,0xc0,0x1f,0x40,0x80,0x0e,0x21, -0x18,0x07,0x8f,0xa0,0x42,0x7f,0xff,0xa6,0x1c,0x0a,0x90,0x23,0x38,0x88,0x50,0x22, -0x14,0x00,0x8e,0x24,0x40,0x80,0x00,0x2d,0x3b,0x88,0xd1,0x25,0x18,0x29,0x92,0xa7, -0x3f,0xbf,0x13,0xa8,0x20,0x00,0x04,0xa4,0x3f,0x3f,0x94,0x29,0x3f,0xe1,0x14,0xaa, -0x14,0x0f,0xd5,0x2d,0x7e,0x0f,0xd6,0xab,0x56,0xc0,0x15,0xac,0x23,0x00,0x01,0xac, -0x1d,0x00,0x56,0xad,0x00,0x20,0x00,0x00,0x14,0x3f,0xd6,0xae,0x33,0x82,0x92,0xb3, -0x42,0x11,0xc0,0x30,0x12,0x7f,0xe8,0x8d,0x40,0x80,0x0e,0x32,0x3f,0xbf,0x17,0x2f, -0x18,0x07,0x98,0x31,0x38,0x8c,0x98,0xb6,0x18,0x2c,0xd4,0x34,0x3f,0x82,0x17,0xb7, -0x3e,0xa3,0x18,0xb5,0x3f,0xe2,0x1b,0xb8,0x3f,0x60,0x9c,0x39,0x08,0x2e,0x5a,0x3a, -0x3f,0xe1,0x1d,0x3b,0xb7,0x8d,0x9d,0xb5,0x32,0x7f,0xe2,0x00,0x00,0x20,0x00,0x00, -0x12,0x7f,0xb1,0x0c,0x3f,0x82,0x02,0x05,0x40,0x80,0x00,0x04,0x33,0x82,0x82,0x82, -0x24,0x00,0x40,0x80,0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81,0x3f,0xe3,0x02,0x86, -0x18,0x20,0x81,0x83,0x08,0x21,0x81,0x87,0x40,0x80,0x00,0x03,0x24,0x00,0x80,0x87, -0x33,0x7f,0xab,0x00,0x34,0x00,0x80,0x83,0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80, -0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x04,0x00,0x01,0x82,0x24,0x00,0x40,0x80, -0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81,0x24,0x00,0x80,0x82,0x33,0x7f,0x87,0x80, -0x40,0x80,0x80,0x05,0x04,0x00,0x01,0x84,0x40,0x80,0x0f,0x86,0x00,0x20,0x00,0x00, -0x42,0x11,0x00,0x03,0x33,0x01,0xec,0x80,0x40,0x80,0x0f,0x83,0x33,0x01,0xf6,0x80, -0x40,0x20,0x00,0x7f,0x12,0x7f,0xa0,0x89,0x42,0x7f,0xff,0x84,0x34,0x00,0x80,0x83, -0x1c,0x0c,0x00,0x81,0x40,0x20,0x00,0x7f,0x0f,0xbf,0x01,0x85,0x34,0x00,0x40,0x80, -0x40,0x80,0x00,0x83,0x18,0x21,0x42,0x04,0x32,0x7f,0x9c,0x00,0x00,0x20,0x00,0x00, -0x04,0x00,0x01,0x82,0x24,0x00,0x40,0x80,0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81, -0x24,0x00,0x80,0x82,0x33,0x7f,0x7a,0x80,0x40,0x80,0x80,0x05,0x04,0x00,0x01,0x84, -0x40,0x80,0x0f,0x86,0x00,0x20,0x00,0x00,0x42,0x11,0x00,0x03,0x33,0x01,0xd4,0x80, -0x40,0x80,0x0f,0x83,0x33,0x01,0xe9,0x80,0x40,0x20,0x00,0x7f,0x12,0x7f,0x93,0x89, -0x42,0x7f,0xff,0x84,0x34,0x00,0x80,0x83,0x1c,0x0c,0x00,0x81,0x40,0x20,0x00,0x7f, -0x0f,0xbf,0x01,0x85,0x34,0x00,0x40,0x80,0x40,0x80,0x00,0x83,0x18,0x21,0x42,0x04, -0x32,0x7f,0x8f,0x00,0x00,0x20,0x00,0x00,0x04,0x00,0x01,0x85,0x40,0x80,0x1f,0x83, -0x3f,0xbf,0x02,0x84,0x3f,0xe1,0x02,0x85,0x3f,0xe1,0x02,0x04,0x32,0x02,0x42,0x80, -0x35,0x80,0x00,0x09,0x42,0x12,0x00,0x04,0x40,0x80,0x02,0x02,0x1c,0x01,0x02,0x03, -0x40,0x20,0x00,0x7f,0x38,0x80,0x82,0x05,0x3b,0x80,0xc2,0x86,0x01,0xa0,0x04,0x07, -0x08,0x01,0x83,0x83,0x35,0x00,0x00,0x00,0x14,0x03,0xc2,0x02,0x12,0x00,0x04,0x98, -0x41,0x81,0x81,0x86,0x33,0x82,0x5f,0x8a,0x40,0x80,0x08,0x08,0x34,0x00,0x02,0x0b, -0xb0,0xe0,0x81,0x06,0x34,0x00,0x01,0x8c,0x33,0x82,0x5d,0x0e,0x18,0x01,0xc5,0x0f, -0x1c,0xfc,0x02,0x85,0x38,0x82,0x01,0x90,0x38,0x82,0x02,0x11,0x4c,0x00,0x02,0x8d, -0x1c,0x04,0x04,0x08,0xb1,0x24,0x06,0x0e,0x04,0x00,0x08,0x0c,0xb2,0x44,0x45,0x8f, -0x04,0x00,0x08,0x8b,0x7a,0x04,0x84,0x93,0x09,0x24,0xc9,0x94,0x36,0x40,0x0a,0x15, -0x7c,0x00,0x0a,0x96,0x18,0x23,0x4b,0x17,0x40,0x20,0x00,0x7f,0x21,0x7f,0xf8,0x97, -0x0c,0x00,0x02,0x83,0x35,0x80,0x00,0x12,0x4c,0xff,0xc2,0x85,0x42,0x81,0x01,0x99, -0x58,0x21,0x41,0x84,0x5a,0x04,0x84,0x98,0x5a,0x02,0x49,0x1b,0xb3,0x41,0x02,0x19, -0x42,0x7f,0xff,0x9c,0x36,0x40,0x0c,0x1d,0x36,0x40,0x0d,0x9e,0x0b,0x66,0x8e,0x1f, -0x58,0x07,0x8e,0xa0,0x18,0x27,0x0f,0xa1,0x0f,0x60,0x50,0x22,0x18,0x28,0x4a,0xa3, -0x7c,0x00,0x11,0xa4,0x0c,0xff,0xd1,0x25,0x58,0x29,0x12,0x83,0x35,0x00,0x00,0x00, -0x40,0x20,0x00,0x7f,0x12,0x7f,0x50,0x89,0x40,0x20,0x00,0x7f,0x33,0x83,0x16,0x84, -0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x3f,0x83,0x82,0x02, -0x79,0x00,0xc1,0x05,0x23,0x00,0x07,0x85,0x33,0x7f,0x4c,0x00,0x40,0x80,0x0f,0x86, -0x04,0x00,0x01,0x84,0x40,0x80,0x80,0x05,0x42,0x10,0x00,0x03,0x33,0x01,0xa6,0x80, -0x40,0x80,0x0f,0x83,0x33,0x01,0xbb,0x80,0x42,0x10,0x00,0x03,0x00,0x20,0x00,0x00, -0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x40,0x20,0x00,0x7f,0x35,0x00,0x00,0x00, -0x42,0x10,0x80,0x03,0x32,0x7f,0xfd,0x80,0x42,0x10,0x80,0x05,0x12,0x00,0x06,0x93, -0x33,0x82,0x84,0x02,0x3e,0xe0,0x02,0x86,0x24,0x00,0x40,0x80,0x24,0xff,0x40,0x81, -0x1c,0xf4,0x00,0x81,0xb0,0x60,0x81,0x86,0x23,0x82,0x81,0x03,0x20,0x00,0x06,0x04, -0x42,0x10,0x80,0x0a,0x40,0x80,0x00,0x0c,0x1c,0x10,0x05,0x0b,0x1c,0x0c,0x05,0x87, -0x18,0x02,0x86,0x08,0x38,0x83,0x02,0x09,0x1c,0x04,0x06,0x0c,0x18,0x03,0x05,0x8d, -0x58,0x03,0x43,0x8e,0x24,0x01,0x04,0x09,0x21,0x7f,0xfd,0x0e,0x12,0x01,0xb2,0x89, -0x42,0x12,0x0c,0x0f,0x33,0x82,0xce,0x90,0x42,0x13,0x00,0x04,0x40,0x20,0x00,0x7f, -0x40,0x20,0x00,0x7f,0x3b,0x83,0xc8,0x11,0x04,0x00,0x08,0x83,0x24,0x00,0x80,0x91, -0x33,0x01,0xae,0x00,0x12,0x00,0x09,0x8a,0x42,0x13,0x00,0x04,0x33,0x83,0x07,0x93, -0x40,0x20,0x00,0x7f,0x34,0x00,0x80,0x96,0x1c,0x02,0x02,0x12,0x40,0x20,0x00,0x7f, -0x3b,0x84,0x89,0x94,0x7c,0x0d,0x8a,0x15,0x40,0x20,0x00,0x7f,0x20,0x00,0x04,0x95, -0x04,0x00,0x0b,0x03,0x33,0x01,0xdb,0x80,0x40,0x80,0x03,0x03,0x00,0x20,0x00,0x00, -0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80,0x40,0x20,0x00,0x7f,0x35,0x00,0x00,0x00, -0x40,0x80,0x01,0x1c,0x33,0x82,0xfe,0x1d,0x40,0x80,0x02,0x21,0x3e,0xc1,0x02,0x17, -0x40,0x80,0x0a,0x19,0x33,0x82,0xef,0x9f,0x1c,0x00,0x4a,0x1b,0x3e,0xc2,0x02,0x18, -0x40,0x80,0x04,0x2c,0x12,0x00,0x0b,0x93,0x3e,0xc0,0x02,0x1a,0xb3,0xc7,0x4e,0x17, -0x3f,0x83,0x8f,0xa0,0x28,0x88,0x42,0x1e,0x33,0x82,0xf9,0x22,0x18,0x08,0x91,0x23, -0x1c,0x00,0x51,0x24,0x18,0x08,0xc2,0x25,0x7c,0x0d,0x92,0x26,0x38,0x86,0x52,0xa7, -0x3e,0xa1,0x12,0xa8,0xb5,0x29,0xd0,0x28,0x28,0x86,0x52,0xa9,0x33,0x82,0xf3,0xaa, -0xb5,0x6a,0x8d,0x98,0x28,0x8b,0x02,0x2b,0x33,0x82,0xf3,0x04,0xb5,0xc1,0x12,0x1a, -0x20,0x00,0x02,0x26,0x40,0x80,0x00,0x2d,0x40,0x20,0x00,0x7f,0xb5,0xc1,0x16,0x9a, -0x42,0x13,0x00,0x04,0x23,0x82,0xef,0xae,0x04,0x00,0x0b,0x03,0x33,0x01,0xc6,0x80, -0x42,0x12,0x0c,0x32,0x33,0x82,0xae,0xb1,0x12,0x7f,0xa8,0x09,0x32,0x80,0x80,0xaf, -0x40,0x20,0x00,0x7f,0x33,0x82,0x05,0xb5,0x16,0x01,0x17,0xb0,0x3b,0x8c,0x98,0xb3, -0x18,0x4c,0x19,0xb4,0xb0,0x6d,0x1a,0x35,0x68,0x0c,0x19,0x83,0x33,0x7f,0xa3,0x80, -0x40,0x80,0x00,0x03,0x32,0x7f,0xe5,0x80,0x40,0x20,0x00,0x7f,0x21,0x00,0x02,0x83, -0x42,0x10,0xbc,0x06,0x33,0x82,0x52,0x83,0x3b,0x81,0x81,0x83,0x35,0x00,0x00,0x00, -0x40,0x20,0x00,0x7f,0x12,0x7f,0xfd,0x89,0x40,0x20,0x00,0x7f,0x33,0x82,0x48,0x82, -0x40,0x20,0x00,0x7f,0x24,0x00,0x01,0x82,0x33,0x82,0x49,0x04,0x24,0x00,0x41,0x84, -0x33,0x82,0x4a,0x05,0x24,0x00,0x81,0x85,0x32,0x7f,0xf9,0x00,0x00,0x20,0x00,0x00, -0x40,0x81,0x8f,0x82,0x24,0x00,0x40,0x80,0x04,0x00,0x02,0x09,0x35,0x90,0x00,0x00, -0x24,0xfd,0x80,0x81,0x1c,0xd8,0x00,0x81,0x59,0x00,0x81,0x84,0x24,0x01,0x40,0x83, -0x24,0x02,0x00,0x86,0x24,0x02,0x40,0x87,0x24,0x01,0xc0,0x88,0x23,0x00,0x28,0x84, -0x14,0x03,0xc1,0x8b,0x00,0x20,0x00,0x00,0x22,0x00,0x27,0x0b,0x35,0x90,0x00,0x00, -0x0f,0xbf,0x01,0x83,0x33,0x82,0x84,0x87,0x42,0x7f,0xff,0x8c,0x32,0x80,0x80,0x88, -0x42,0x11,0xc4,0x06,0x33,0x81,0xed,0x8a,0x18,0x23,0x05,0x92,0x12,0x01,0x76,0x93, -0x18,0x23,0x01,0x8d,0x24,0x00,0xc0,0x85,0x16,0xe0,0x04,0x0e,0x24,0x01,0x00,0x89, -0x0f,0x61,0xc6,0x90,0x3b,0x81,0x83,0x8f,0x0f,0x60,0xc9,0x14,0x42,0x11,0x80,0x04, -0x18,0x43,0x87,0x91,0x3f,0xbf,0x08,0x15,0x24,0x00,0x80,0x94,0xb2,0x64,0x48,0x8a, -0x54,0xc0,0x0a,0x96,0x68,0x03,0x87,0x93,0x18,0x44,0xcb,0x17,0xb0,0x65,0xcb,0x8a, -0x68,0x04,0xcb,0x03,0x24,0x01,0x80,0x83,0x33,0x01,0x6d,0x00,0x34,0x01,0x00,0x98, -0x35,0x80,0x0c,0x09,0x34,0x00,0x80,0x9c,0x34,0x00,0xc0,0x85,0x20,0x00,0x08,0x98, -0x42,0x11,0x80,0x19,0x04,0x00,0x02,0x84,0x18,0x07,0x0c,0x9b,0x38,0x87,0x0c,0x9a, -0x3b,0x86,0xcd,0x03,0x35,0x20,0x0c,0x00,0x34,0x00,0x80,0x9c,0x21,0x00,0x04,0x83, -0x42,0x11,0x80,0x04,0x34,0x01,0x80,0x83,0x33,0x01,0x99,0x00,0x40,0x80,0x03,0x83, -0x1c,0x28,0x00,0x81,0x34,0x00,0x40,0x80,0x40,0x20,0x00,0x7f,0x35,0x00,0x00,0x00, -0x42,0x11,0x80,0x1d,0x34,0x02,0x40,0x84,0x40,0x20,0x00,0x7f,0x34,0x02,0x00,0xa0, -0x18,0x07,0x0e,0x9f,0x38,0x87,0x0e,0x9e,0x24,0x00,0x80,0x9c,0x24,0x00,0xc0,0x9d, -0x3b,0x87,0xcf,0x03,0x35,0x20,0x10,0x00,0x42,0x11,0x80,0x24,0x34,0x00,0x80,0xa1, -0x34,0x00,0xc0,0xa2,0x3a,0xe9,0x10,0xa5,0x38,0x88,0x51,0x23,0xb4,0xc8,0xc1,0xa5, -0x28,0x88,0x51,0x26,0x34,0x01,0xc0,0xa7,0x20,0x00,0x02,0x27,0x34,0x01,0x40,0x83, -0x40,0x20,0x00,0x7f,0x35,0x20,0x13,0x80,0x42,0x11,0x80,0x04,0x34,0x01,0x80,0x83, -0x40,0x20,0x00,0x7f,0x33,0x01,0x89,0x80,0x40,0x80,0x00,0x03,0x32,0x7f,0xf0,0x80, -0x40,0x80,0x01,0x03,0x32,0x7f,0xef,0x80,0x04,0x00,0x02,0x02,0x12,0x7f,0xd0,0x8b, -0x42,0x05,0xc0,0x08,0x32,0x80,0x80,0x87,0x42,0x03,0xec,0x06,0x24,0x00,0x40,0x80, -0x40,0x80,0x00,0x05,0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81,0x16,0x02,0x03,0x87, -0x42,0x04,0x24,0x04,0x24,0x00,0x80,0x82,0x33,0x7f,0xcb,0x00,0x34,0x00,0x80,0x88, -0x21,0x00,0x04,0x03,0x20,0x00,0x03,0x88,0x42,0x11,0x00,0x04,0x34,0x00,0x04,0x05, -0x3e,0xc0,0x04,0x06,0xb1,0x21,0x42,0x06,0x40,0x20,0x00,0x7f,0x24,0x00,0x04,0x09, -0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00, -0x04,0x00,0x02,0x82,0x12,0x7f,0xc3,0x8d,0x14,0x3f,0xc2,0x04,0x32,0x80,0x80,0x85, -0x42,0x05,0xc0,0x08,0x24,0x00,0x40,0x80,0x42,0x04,0x04,0x06,0x24,0xff,0x40,0x81, -0x1c,0xf4,0x00,0x81,0x3f,0xbf,0x02,0x07,0x16,0xe0,0x02,0x85,0x00,0x20,0x00,0x00, -0x42,0x03,0xd4,0x04,0x24,0x00,0x80,0x82,0x33,0x7f,0xbd,0x00,0x34,0x00,0x80,0x87, -0x21,0x00,0x04,0x03,0x20,0x00,0x03,0x87,0x42,0x11,0x00,0x09,0x34,0x00,0x03,0x88, -0x3e,0xc0,0x03,0x86,0xb1,0x42,0x04,0x86,0x40,0x20,0x00,0x7f,0x24,0x00,0x03,0x8a, -0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00, -0x40,0x20,0x00,0x7f,0x12,0x7f,0xb5,0x89,0x40,0x80,0x00,0x08,0x33,0x82,0x84,0x82, -0x40,0x80,0x00,0x07,0x42,0x05,0x68,0x06,0x40,0x80,0x00,0x05,0x40,0x80,0x00,0x04, -0x40,0x20,0x00,0x7f,0x3f,0x83,0x81,0x03,0x32,0x7f,0xb1,0x00,0x00,0x20,0x00,0x00, -0x40,0x20,0x00,0x7f,0x12,0x7f,0xaf,0x89,0x40,0x80,0x00,0x08,0x32,0x80,0x80,0x87, -0x42,0x05,0x68,0x06,0x40,0x80,0x00,0x05,0x42,0x03,0xe0,0x04,0x15,0x40,0x43,0x87, -0x40,0x20,0x00,0x7f,0x40,0x20,0x00,0x7f,0x32,0x7f,0xab,0x00,0x00,0x20,0x00,0x00, -0x40,0x20,0x00,0x7f,0x12,0x7f,0xa9,0x89,0x40,0x80,0x00,0x08,0x33,0x82,0x78,0x82, -0x42,0x03,0xf8,0x06,0x32,0x81,0x81,0x87,0x40,0x80,0x00,0x05,0x40,0x80,0x00,0x04, -0x40,0x20,0x00,0x7f,0x3f,0x83,0x81,0x03,0x32,0x7f,0xa5,0x00,0x00,0x20,0x00,0x00, -0x42,0x7f,0xff,0x87,0x12,0x7f,0xa3,0x8b,0x40,0x80,0x00,0x08,0x00,0x20,0x00,0x00, -0x18,0x21,0xc1,0x82,0x33,0x82,0x71,0x83,0x42,0x03,0xf8,0x06,0x00,0x20,0x00,0x00, -0x40,0x80,0x00,0x05,0x3f,0xbf,0x01,0x07,0x42,0x03,0xe0,0x04,0x3f,0x83,0x81,0x83, -0x32,0x7f,0x9e,0x00,0x00,0x20,0x00,0x00,0x04,0x00,0x01,0x82,0x12,0x7f,0x9c,0x8d, -0x42,0x05,0x8c,0x08,0x32,0x80,0x80,0x87,0x42,0x03,0xec,0x06,0x32,0x80,0x80,0x85, -0x42,0x03,0xd4,0x04,0x24,0x00,0x40,0x80,0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81, -0x16,0xe0,0x03,0x87,0x00,0x20,0x00,0x00,0x16,0x02,0x02,0x85,0x24,0x00,0x80,0x82, -0x33,0x7f,0x96,0x00,0x34,0x00,0x80,0x8a,0x04,0x00,0x01,0x89,0x21,0x00,0x0f,0x83, -0x0f,0xbf,0x05,0x04,0x33,0x82,0x20,0x8d,0x42,0x7f,0xff,0x83,0x32,0x80,0x80,0x86, -0x42,0x11,0xc4,0x0e,0x33,0x81,0x89,0x88,0x14,0x03,0xc5,0x12,0x12,0x7f,0x29,0x95, -0x18,0x20,0xc2,0x0b,0x24,0x00,0x80,0x89,0x16,0xe0,0x03,0x0c,0x00,0x20,0x00,0x00, -0x0f,0x61,0xc5,0x90,0x3b,0x83,0x86,0x8f,0x18,0x20,0xc9,0x14,0x0f,0x60,0xca,0x16, -0x18,0x43,0x07,0x91,0x3f,0xbf,0x08,0x15,0xb2,0x64,0x48,0x88,0x3f,0xbf,0x0b,0x19, -0x54,0xc0,0x0a,0x97,0x68,0x03,0x07,0x93,0x18,0x45,0xc9,0x98,0xb0,0x86,0x0c,0x08, -0x68,0x05,0xc9,0x84,0x18,0x46,0x42,0x1b,0xb0,0x66,0xcd,0x88,0x68,0x06,0x42,0x03, -0x33,0x7f,0x1f,0x00,0x34,0x00,0x80,0x89,0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80, -0x04,0x00,0x04,0x83,0x35,0x00,0x00,0x00,0x7c,0x00,0x02,0x04,0x12,0x7f,0x82,0x8a, -0x40,0x80,0x40,0x07,0x32,0x80,0x80,0x85,0x40,0x80,0x08,0x02,0x42,0x05,0x8c,0x08, -0x81,0x20,0x83,0x84,0x42,0x03,0xec,0x06,0x16,0x01,0x02,0x85,0x3f,0xbf,0x04,0x87, -0x42,0x03,0xd4,0x04,0x32,0x7f,0x7d,0x80,0x40,0x81,0x8f,0x82,0x12,0x00,0x26,0x8a, -0x04,0x00,0x01,0x87,0x24,0x00,0x40,0x80,0x59,0x00,0x81,0x83,0x24,0xfe,0x40,0x81, -0x04,0x00,0x02,0x09,0x1c,0xe4,0x00,0x81,0x23,0x00,0x23,0x03,0x14,0x03,0xc3,0x8d, -0x40,0x20,0x00,0x7f,0x22,0x00,0x21,0x8d,0x0f,0xbf,0x03,0x85,0x33,0x82,0x03,0x88, -0x42,0x7f,0xff,0x8b,0x32,0x80,0x80,0x8a,0x42,0x11,0xc4,0x0e,0x33,0x81,0x6c,0x8c, -0x40,0x20,0x00,0x7f,0x12,0x00,0xf5,0x96,0x18,0x22,0xc2,0x84,0x24,0x00,0xc0,0x87, -0x16,0xe0,0x05,0x06,0x24,0x00,0x80,0x89,0x0f,0x61,0xc2,0x10,0x3b,0x83,0x84,0x0f, -0x42,0x11,0x80,0x04,0x24,0x01,0x00,0x8d,0x40,0x20,0x00,0x7f,0x24,0x01,0x40,0x8b, -0x18,0x41,0x87,0x91,0x3f,0xbf,0x08,0x13,0xb2,0x44,0x48,0x8c,0x54,0xc0,0x09,0x94, -0x68,0x01,0x87,0x92,0x18,0x45,0x09,0x15,0xb2,0xc5,0x4a,0x8c,0x68,0x05,0x09,0x16, -0x04,0x00,0x0b,0x03,0x24,0x01,0x80,0x96,0x40,0x20,0x00,0x7f,0x33,0x00,0xea,0x80, -0x42,0x11,0x80,0x1a,0x34,0x01,0x00,0x97,0x40,0x20,0x00,0x7f,0x34,0x01,0x40,0x98, -0x04,0x00,0x0d,0x04,0x12,0x01,0x1b,0x89,0x34,0x01,0x80,0x83,0x18,0x26,0x0b,0x99, -0x0f,0x60,0xcc,0x9b,0x00,0x20,0x00,0x00,0x18,0x06,0x8d,0x9d,0x38,0x86,0x8d,0x9c, -0x3b,0x87,0x4e,0x1e,0x24,0x01,0x00,0x9e,0x33,0x01,0x17,0x00,0x34,0x01,0x00,0x83, -0x34,0x00,0xc0,0x87,0x34,0x00,0x80,0x89,0x04,0x00,0x01,0x9f,0x0f,0x60,0x84,0xa0, -0x42,0x0a,0x26,0x21,0x5c,0x01,0xc4,0xa2,0x18,0x08,0x10,0xa4,0x38,0x88,0x10,0xa3, -0x3b,0x89,0x11,0xa5,0x21,0x00,0x1c,0xa2,0x35,0x00,0x12,0x80,0x00,0x00,0x14,0x78, -0x00,0x00,0x14,0x94,0x00,0x00,0x14,0xcc,0x00,0x00,0x14,0xd8,0x00,0x00,0x14,0xe8, -0x00,0x00,0x14,0xf8,0x00,0x00,0x15,0x08,0x00,0x00,0x15,0x18,0x00,0x20,0x00,0x00, -0x40,0x80,0x00,0x1f,0x32,0x7f,0xf7,0x00,0x33,0x82,0x13,0x37,0x3f,0x83,0x5b,0xb8, -0x14,0x3f,0xdc,0x03,0x00,0x20,0x00,0x00,0x1c,0x1c,0x00,0x81,0x34,0x00,0x40,0x80, -0x35,0x00,0x00,0x00,0x33,0x82,0x21,0xaf,0x3f,0x83,0x97,0xb0,0x79,0x0c,0x03,0xb1, -0x22,0x00,0x11,0x31,0x12,0x7f,0xfc,0x89,0x40,0x20,0x00,0x7f,0x33,0x82,0x18,0xb2, -0x40,0x20,0x00,0x7f,0x3f,0x83,0x59,0x33,0x7e,0x00,0x19,0xb4,0x09,0x2d,0x1a,0x35, -0x36,0x80,0x1a,0xb6,0x0c,0x00,0x1b,0x03,0x32,0x7f,0xf8,0x00,0x3f,0xbe,0x4f,0xae, -0x3f,0xe1,0x17,0x03,0x32,0x7f,0xf6,0x80,0x3f,0xbe,0x4f,0xac,0x3f,0xe1,0x16,0x2d, -0x14,0x04,0x16,0x83,0x32,0x7f,0xf4,0x80,0x3f,0xbe,0x4f,0xaa,0x3f,0xe1,0x15,0x2b, -0x14,0x08,0x15,0x83,0x32,0x7f,0xf2,0x80,0x3f,0xbe,0x4f,0xa8,0x3f,0xe1,0x14,0x29, -0x14,0x10,0x14,0x83,0x32,0x7f,0xf0,0x80,0x3f,0xbe,0x4f,0xa6,0x3f,0xe1,0x13,0x27, -0x14,0x20,0x13,0x83,0x32,0x7f,0xee,0x80,0x3f,0xbf,0x0f,0xb9,0x3f,0xe1,0x1c,0xba, -0x14,0x00,0x5d,0x03,0x32,0x7f,0xec,0x80,0x40,0x80,0x00,0x03,0x32,0x7f,0xeb,0x80, -0x42,0x11,0xc4,0x08,0x33,0x81,0xca,0x85,0x04,0x00,0x01,0x8a,0x35,0x90,0x00,0x00, -0x40,0x20,0x00,0x7f,0x32,0x80,0x80,0x86,0x0f,0x61,0xc1,0x83,0x33,0x81,0x32,0x87, -0x40,0x80,0x00,0x0c,0x12,0x00,0xbb,0x9b,0x40,0x80,0x00,0x0d,0x24,0x00,0x40,0x80, -0x16,0xe0,0x03,0x0b,0x24,0xfd,0x40,0x81,0x1c,0xd4,0x00,0x81,0x35,0x90,0x00,0x00, -0x40,0x20,0x00,0x7f,0x3b,0x82,0x02,0x8e,0x40,0xff,0xff,0x91,0x3f,0xbf,0x01,0x89, -0x40,0x80,0x00,0x92,0x24,0x02,0x80,0x84,0x42,0x11,0x80,0x04,0x24,0x00,0xc0,0x8a, -0x18,0x42,0xc7,0x02,0x24,0x00,0x80,0x91,0x54,0xc0,0x04,0x90,0x24,0x01,0x00,0x92, -0xb1,0xe0,0x81,0x07,0x24,0x01,0x40,0x8c,0x24,0x01,0x80,0x8d,0x68,0x02,0xc7,0x0f, -0x18,0x44,0x07,0x93,0xb0,0x64,0xc9,0x87,0x68,0x04,0x07,0x83,0x24,0x02,0x40,0x83, -0x33,0x00,0xae,0x00,0x34,0x01,0x80,0xc2,0x34,0x01,0x40,0xc1,0x34,0x01,0x00,0x8b, -0x35,0x90,0x00,0x00,0x34,0x00,0xc0,0x8a,0x40,0x20,0x00,0x7f,0x34,0x00,0x80,0x89, -0x0f,0x60,0xc5,0xc0,0x34,0x02,0x80,0x99,0x42,0x11,0x80,0x04,0x35,0x90,0x00,0x00, -0x18,0x10,0x02,0x15,0x38,0x90,0x02,0x14,0x3b,0x85,0x4a,0x16,0x3f,0xbe,0x4b,0x17, -0x3f,0xe1,0x0b,0x98,0x35,0x90,0x00,0x00,0x20,0x00,0x3f,0x19,0x14,0x04,0x0c,0x0c, -0x56,0xc0,0x06,0x0f,0x00,0x20,0x00,0x00,0x22,0x00,0x18,0x8f,0x00,0x20,0x00,0x00, -0x0f,0x38,0x44,0x8d,0x3f,0xbe,0x8b,0x10,0x40,0xbf,0xff,0x95,0x3f,0x3f,0xcb,0x11, -0x3f,0xe1,0x08,0x12,0x3f,0xbf,0x08,0x93,0x5a,0x10,0x89,0x03,0x3f,0xe1,0x09,0x84, -0x36,0x80,0x01,0x94,0x18,0x25,0x42,0x17,0x0c,0x00,0x0a,0x18,0x08,0x23,0x4c,0x19, -0x56,0xc0,0x0c,0x9a,0x00,0x20,0x00,0x00,0x23,0x00,0x33,0x1a,0x7a,0x10,0x89,0x1b, -0x59,0x10,0x4b,0x9c,0x36,0x80,0x0d,0x9d,0x55,0xc0,0x0e,0x1e,0x0c,0x00,0x0f,0x1f, -0x0c,0x00,0x0e,0xa0,0x18,0x28,0x0f,0xa1,0x56,0xc0,0x10,0xa2,0x7d,0x00,0x11,0x23, -0x88,0x30,0x4b,0xa3,0x36,0x80,0x11,0xa4,0x81,0x22,0x45,0xa4,0x40,0xbf,0xff,0xa5, -0x79,0x09,0x4b,0xa6,0x23,0x00,0x09,0x26,0x42,0x7f,0xff,0xa8,0x12,0x7f,0xe8,0x92, -0x42,0x11,0x80,0x27,0x33,0x81,0x13,0xae,0x18,0x2a,0x0b,0xa9,0x38,0x90,0x13,0xaa, -0x1c,0x00,0x54,0xab,0x3a,0xf0,0x13,0xac,0x3f,0xbf,0x15,0xad,0x18,0x2b,0x8b,0x16, -0x54,0xc0,0x16,0xaf,0x3f,0x82,0x17,0xb0,0x3f,0xe3,0x18,0x31,0x3f,0x60,0x58,0xb2, -0x08,0x25,0x99,0x33,0xb6,0x8a,0x99,0xac,0x28,0x90,0x13,0xb4,0x1c,0x00,0x45,0x8b, -0x7c,0x04,0x05,0xb5,0x20,0x7f,0xdf,0xb5,0x7c,0xff,0xc4,0xb6,0x21,0x00,0x1c,0xb6, -0x34,0x02,0x80,0xb7,0x20,0x00,0x18,0xb7,0x0f,0x60,0xc4,0xb8,0x12,0x00,0x52,0x91, -0x42,0x11,0x80,0x39,0x33,0x81,0xa3,0xc1,0x42,0x7f,0xff,0xbb,0x33,0x81,0xd2,0xbe, -0x0f,0x61,0x05,0x3a,0x00,0x20,0x00,0x00,0x18,0x0e,0x5c,0x3d,0x38,0x8e,0x5c,0x3c, -0x18,0x02,0x5d,0x40,0x3f,0x83,0xa0,0xc2,0x3f,0x83,0x9f,0x3f,0x3b,0x8f,0x5e,0x46, -0x18,0x2e,0xe1,0x44,0x18,0x2e,0xdf,0xc3,0x3f,0xbf,0x22,0x47,0x78,0x10,0x21,0xc5, -0x20,0x00,0x4a,0x45,0x32,0x81,0x81,0xc9,0x18,0x32,0x63,0x4a,0x78,0x11,0xe5,0x4b, -0x36,0x00,0x25,0xcc,0x4c,0x02,0xe6,0x4d,0x0c,0x00,0x26,0xc8,0x00,0x20,0x00,0x00, -0x42,0x11,0x80,0x4e,0x12,0x7e,0x1e,0x91,0x04,0x00,0x05,0x04,0x32,0xc0,0x40,0x4f, -0x40,0x80,0x02,0x03,0x33,0x80,0xff,0x87,0x38,0x8e,0x27,0x06,0x3a,0xf3,0x9c,0x08, -0x16,0x10,0x27,0x85,0x3f,0xe0,0xe4,0x02,0x08,0x21,0x63,0x8c,0x18,0x21,0xe3,0x0e, -0x23,0x81,0xbc,0x02,0x08,0x23,0x07,0x0f,0xb1,0xa1,0x87,0x88,0x28,0x8e,0x27,0x0d, -0x24,0x00,0x80,0x89,0x24,0x00,0xc0,0x8a,0x33,0x7e,0x16,0x00,0x34,0x00,0xc0,0x8a, -0x40,0x20,0x00,0x7f,0x34,0x00,0x80,0x89,0x04,0x00,0x05,0x04,0x24,0x00,0x80,0x89, -0x40,0x80,0x00,0x83,0x33,0x7e,0x12,0x80,0x40,0x20,0x00,0x7f,0x34,0x00,0x80,0x89, -0x42,0x11,0x80,0x04,0x34,0x02,0x40,0x83,0x24,0x00,0x80,0x89,0x33,0x00,0x9e,0x80, -0x34,0x00,0x80,0x83,0x1c,0x2c,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, -0x04,0x00,0x0b,0xc1,0x04,0x00,0x09,0x42,0x04,0x00,0x05,0x89,0x32,0x7f,0xd2,0x00, -0x14,0x08,0x0c,0x1a,0x56,0xc0,0x0d,0x1b,0x22,0x7f,0xda,0x9b,0x3f,0xbf,0x8b,0x1c, -0x3f,0xe1,0x0e,0x1d,0x7d,0xff,0xce,0x9e,0x23,0x00,0x24,0x1e,0x0f,0xbf,0x0e,0xae, -0x42,0x7f,0xff,0xaf,0x14,0x03,0xce,0xbf,0x42,0x11,0x80,0x3e,0x18,0x2b,0xd7,0x30, -0x78,0x0c,0x05,0x31,0x21,0x00,0x14,0x31,0x42,0x11,0xc4,0x37,0x33,0x81,0x69,0xb6, -0x0f,0x61,0xd8,0x32,0x32,0x80,0x80,0xb3,0x40,0x80,0x0f,0x86,0x33,0x80,0xd2,0xbb, -0x40,0x80,0x40,0x05,0x12,0x00,0x3f,0x94,0x42,0x13,0x00,0x03,0x24,0x02,0x00,0xbf, -0x16,0xe0,0x19,0xb4,0x3f,0xbf,0x19,0x35,0x3b,0x8d,0xdb,0x38,0x24,0x01,0xc0,0xc0, -0x40,0x20,0x00,0x7f,0x24,0x00,0x80,0x89,0x54,0xc0,0x1a,0xba,0x24,0x00,0xc0,0x8a, -0x18,0x4d,0x1c,0x39,0x24,0x01,0x00,0x8b,0x24,0x01,0x40,0xc1,0xb7,0x8e,0x5c,0xbb, -0x24,0x01,0x80,0xc2,0x68,0x0d,0x1c,0x3c,0x18,0x4e,0x9e,0x3d,0xb0,0x8f,0x5e,0xbb, -0x68,0x0e,0x9e,0x04,0x33,0x00,0x35,0x80,0x40,0x80,0x0f,0x83,0x33,0x00,0x4a,0x80, -0x42,0x13,0x00,0x3e,0x12,0x7f,0xc5,0x10,0x34,0x02,0x00,0xbf,0x34,0x01,0xc0,0xc0, -0x34,0x00,0x80,0x89,0x34,0x00,0xc0,0x8a,0x34,0x01,0x00,0x8b,0x34,0x01,0x40,0xc1, -0x34,0x01,0x80,0xc2,0x0f,0x60,0xdf,0xc3,0x18,0x0f,0xa1,0xc5,0x38,0x8f,0xa1,0xc4, -0x3b,0x91,0x62,0x46,0x3f,0xbc,0x63,0x47,0x3f,0xe3,0x23,0xc8,0x4e,0xff,0xe4,0x49, -0x56,0xc0,0x24,0xca,0x23,0x7f,0xbd,0x4a,0x42,0x11,0x80,0x4b,0x12,0x7f,0xbc,0x0e, -0x04,0x00,0x05,0x89,0x32,0xbf,0xbf,0x86,0x18,0x10,0x25,0xce,0x38,0x90,0x25,0xcc, -0x33,0x80,0xc8,0x05,0x38,0x90,0x25,0xcd,0x3a,0xf0,0x25,0xcf,0x3b,0x93,0xa6,0x08, -0x18,0x21,0x84,0x07,0x08,0x21,0x43,0x8e,0xb0,0x53,0x47,0x4f,0x28,0x90,0x25,0x82, -0x40,0x20,0x00,0x7f,0x32,0x7f,0xb5,0x00,0x40,0x80,0x00,0x22,0x32,0x88,0x08,0x1f, -0x12,0x7f,0xb3,0x93,0x15,0x40,0x4f,0xa0,0x18,0x28,0x0b,0x21,0x78,0x08,0x90,0xa3, -0x36,0x00,0x11,0xa4,0x4c,0x02,0xd2,0x25,0x40,0x20,0x00,0x7f,0x21,0x7f,0xb0,0x25, -0x04,0x00,0x05,0x89,0x33,0x80,0xbf,0xa9,0x32,0xc0,0x40,0x27,0x38,0x90,0x02,0x26, -0x3a,0xf0,0x02,0x28,0x16,0x04,0x13,0xab,0x18,0x2a,0x4b,0x2a,0x08,0x2a,0xd5,0x2c, -0xb5,0xa9,0x96,0x28,0x28,0x90,0x02,0x2d,0x40,0x20,0x00,0x7f,0x32,0x7f,0xaa,0x00, -0x40,0x80,0x00,0x48,0x32,0x7f,0xb9,0x80,0x40,0x80,0x12,0x02,0x33,0x81,0x70,0x83, -0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x00,0x20,0x00,0x00, -0x1c,0x09,0x01,0x84,0x38,0x80,0x81,0x85,0x42,0x0f,0xc0,0x03,0x3b,0x81,0x02,0x86, -0x35,0x20,0x03,0x00,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, -0x3f,0xe0,0xc1,0x83,0x23,0x81,0x75,0x83,0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00, -0x40,0xa0,0x00,0x09,0x33,0x80,0x9f,0x8a,0x04,0x00,0x01,0x88,0x00,0x20,0x00,0x00, -0x58,0x02,0x42,0x82,0x21,0x00,0x01,0x85,0x40,0x80,0x00,0x03,0x35,0x00,0x00,0x00, -0x81,0x62,0x42,0x82,0x12,0x7f,0xfc,0x92,0x21,0xa0,0x08,0x08,0x3f,0xbf,0x02,0x0c, -0x3f,0xe1,0x06,0x0d,0x21,0xa0,0x08,0x8d,0x3f,0xe1,0x02,0x0e,0x21,0xa0,0x09,0x0e, -0x04,0x00,0x05,0x8f,0x21,0xa0,0x09,0x8f,0x21,0xa0,0x0a,0x06,0x21,0xa0,0x0a,0x87, -0x18,0x02,0xc1,0x83,0x3f,0xbf,0x05,0x90,0x08,0x01,0x45,0x85,0x18,0x41,0x08,0x11, -0xb2,0x44,0x48,0x8a,0x68,0x01,0x08,0x12,0x04,0x00,0x09,0x04,0x32,0x7f,0xf3,0x80, -0x5c,0x07,0xc3,0x07,0x12,0x7f,0xf1,0x8f,0x40,0x20,0x00,0x7f,0x21,0x00,0x07,0x07, -0x40,0x80,0x00,0x0e,0x3f,0xbf,0x01,0x89,0x32,0x80,0x80,0x8a,0x08,0x21,0x04,0x8b, -0x16,0x03,0xc5,0x0c,0x18,0x23,0x05,0x8d,0x78,0x03,0x86,0x8f,0x36,0x00,0x07,0x90, -0x4c,0x02,0xc8,0x11,0x00,0x20,0x00,0x00,0x20,0x00,0x03,0x11,0x40,0x80,0x20,0x07, -0x32,0x7f,0xea,0x00,0x40,0x80,0x01,0x03,0x40,0x20,0x00,0x7f,0x35,0x00,0x00,0x00, -0x40,0x80,0x02,0x83,0x32,0x7f,0xfe,0x80,0x5c,0x07,0xc3,0x07,0x12,0x7f,0xe6,0x8f, -0x40,0x20,0x00,0x7f,0x21,0x00,0x07,0x07,0x40,0x80,0x00,0x0e,0x3f,0xbf,0x01,0x89, -0x32,0x80,0x80,0x8a,0x08,0x21,0x04,0x8b,0x16,0x03,0xc5,0x0c,0x18,0x23,0x05,0x8d, -0x78,0x03,0x86,0x8f,0x36,0x00,0x07,0x90,0x4c,0x02,0xc8,0x11,0x00,0x20,0x00,0x00, -0x20,0x00,0x03,0x11,0x40,0x80,0x10,0x07,0x32,0x7f,0xdf,0x00,0x40,0x80,0x01,0x03, -0x40,0x20,0x00,0x7f,0x35,0x00,0x00,0x00,0x40,0x80,0x02,0x83,0x32,0x7f,0xfe,0x80, -0x5c,0x07,0xc1,0x82,0x21,0x00,0x04,0x82,0x40,0x80,0x00,0x84,0x0b,0x60,0xc2,0x03, -0x21,0xa0,0x0b,0x03,0x40,0x80,0x01,0x05,0x21,0xa0,0x0b,0x85,0x01,0xa0,0x0c,0x02, -0x40,0x80,0x00,0x03,0x35,0x00,0x00,0x00,0x40,0x80,0x01,0x03,0x32,0x7f,0xff,0x00, -0x40,0x80,0x00,0x07,0x12,0x00,0x31,0x89,0x7c,0x00,0x02,0x05,0x78,0x01,0xc1,0x86, -0x0c,0x00,0x02,0x89,0x36,0x00,0x03,0x02,0x4c,0x02,0xc1,0x08,0x0c,0x00,0x04,0x0a, -0x08,0x22,0x84,0x8b,0x56,0xc0,0x05,0x8c,0x23,0x00,0x2d,0x0c,0x32,0x80,0x80,0x8d, -0x16,0x1f,0xc6,0x8e,0x18,0x23,0x81,0x8f,0x78,0x01,0xc7,0x90,0x36,0x00,0x08,0x11, -0x4c,0x02,0xc8,0x92,0x20,0x00,0x2a,0x92,0x14,0x1f,0xc2,0x25,0x21,0x00,0x29,0xa5, -0x40,0x82,0x00,0x07,0x21,0xa0,0x00,0x87,0x40,0x80,0x00,0x37,0x40,0x80,0x40,0x1a, -0x04,0x00,0x02,0x18,0x3f,0xbf,0x01,0x94,0x04,0x00,0x1b,0x99,0x3f,0xe1,0x01,0x83, -0x40,0x80,0x68,0x1b,0x3e,0x80,0xc2,0x13,0x40,0x80,0x5a,0x1c,0x3e,0x80,0x02,0x16, -0x40,0x80,0x01,0x9d,0x3f,0xe1,0x0a,0x1f,0x1c,0x04,0x02,0x1e,0x3e,0x80,0x82,0x17, -0x04,0x00,0x03,0xa0,0x40,0x80,0x01,0x21,0x1c,0x03,0xc2,0x22,0x40,0x80,0x00,0x95, -0x12,0x00,0x00,0x17,0x21,0xa0,0x08,0x18,0x21,0xa0,0x08,0x9f,0x21,0xa0,0x09,0x03, -0x21,0xa0,0x09,0x9a,0x21,0xa0,0x0a,0x19,0x21,0xa0,0x0a,0x9b,0x01,0xa0,0x0d,0x85, -0x20,0x00,0x14,0xb7,0x34,0x00,0x02,0x28,0x3f,0x83,0x54,0x29,0x7e,0x00,0x54,0xaa, -0x56,0xc0,0x15,0x2b,0x00,0x20,0x00,0x00,0x23,0x00,0x03,0xab,0x38,0x88,0x42,0x2c, -0x3b,0x88,0x96,0x2d,0x7a,0x09,0x56,0xae,0x56,0xc0,0x17,0x2f,0x00,0x20,0x00,0x00, -0x23,0x00,0x02,0x2f,0x01,0xa0,0x00,0x05,0x21,0xa0,0x01,0x20,0x32,0x7f,0xf4,0x80, -0x1d,0x00,0x52,0xb1,0x34,0x00,0x02,0x30,0x04,0x00,0x16,0xa5,0xb6,0x4c,0x0a,0x96, -0x24,0x00,0x02,0x32,0x38,0x88,0x42,0x33,0xb6,0x8c,0xd8,0x97,0x28,0x88,0x42,0x34, -0x00,0x60,0x00,0x00,0x21,0xa0,0x08,0x18,0x21,0xa0,0x08,0x9f,0x21,0xa0,0x09,0x03, -0x21,0xa0,0x09,0x9a,0x21,0xa0,0x0a,0x19,0x21,0xa0,0x0a,0x9c,0x01,0xa0,0x0d,0xb5, -0x14,0x00,0x5a,0xb6,0x21,0x7f,0xeb,0xb6,0x1c,0x00,0x5b,0xb7,0x7c,0x00,0x9b,0xb8, -0x20,0x7f,0xea,0x38,0x40,0x82,0x00,0x04,0x21,0xa0,0x01,0x04,0x40,0x80,0x00,0x03, -0x35,0x00,0x00,0x00,0x12,0x7f,0xf7,0x8a,0x40,0x20,0x00,0x7f,0x38,0x87,0x42,0x23, -0x40,0x20,0x00,0x7f,0x38,0x87,0x42,0x24,0x3b,0x87,0x91,0xa5,0x1d,0x00,0x52,0xa6, -0xb4,0xe9,0x13,0x13,0x28,0x87,0x42,0x27,0x40,0x20,0x00,0x7f,0x32,0x7f,0xf2,0x80, -0x40,0x80,0x00,0x83,0x32,0x7f,0xf9,0x80,0x40,0x80,0x02,0x83,0x32,0x7f,0xf8,0x80, -0x40,0x80,0x00,0x07,0x12,0x00,0x22,0x89,0x7c,0x00,0x02,0x06,0x78,0x01,0xc1,0x85, -0x0c,0x00,0x03,0x09,0x36,0x00,0x02,0x82,0x4c,0x02,0xc1,0x08,0x0c,0x00,0x04,0x0a, -0x08,0x22,0x45,0x0b,0x56,0xc0,0x05,0x8c,0x23,0x00,0x1e,0x0c,0x32,0x80,0x80,0x8d, -0x16,0x1f,0xc6,0x8e,0x18,0x23,0x81,0x8f,0x78,0x01,0xc7,0x90,0x36,0x00,0x08,0x11, -0x4c,0x02,0xc8,0x92,0x20,0x00,0x1b,0x92,0x14,0x1f,0xc2,0x13,0x21,0x00,0x1a,0x93, -0x34,0x00,0x02,0x14,0x3f,0x83,0x4a,0x15,0x7e,0x00,0x4a,0x96,0x56,0xc0,0x0b,0x17, -0x22,0x00,0x19,0x17,0x12,0x00,0x0a,0x29,0x21,0xa0,0x08,0x13,0x21,0xa0,0x09,0x13, -0x21,0xa0,0x09,0x93,0x21,0xa0,0x0a,0x13,0x40,0x80,0x66,0x18,0x21,0xa0,0x0a,0x98, -0x40,0x82,0x00,0x19,0x21,0xa0,0x00,0x99,0x40,0x80,0x00,0x1b,0x42,0x12,0x40,0x1c, -0x04,0x00,0x02,0x1a,0x3f,0xbf,0x01,0x9d,0x40,0x80,0x40,0x1e,0x3e,0xc0,0x02,0x1f, -0x40,0x80,0x68,0x20,0x3e,0x80,0x02,0x21,0x40,0x80,0x5a,0x22,0x3f,0xe1,0x01,0xa3, -0x3f,0xe1,0x0e,0xa4,0x21,0xa0,0x08,0x1c,0x21,0xa0,0x08,0xa4,0x21,0xa0,0x09,0x23, -0x21,0xa0,0x09,0x9e,0x21,0xa0,0x0a,0x13,0x21,0xa0,0x0a,0xa0,0x01,0xa0,0x0d,0x83, -0x33,0x80,0xdc,0x03,0x34,0x00,0x02,0x25,0xb4,0xc9,0x41,0x9f,0xb4,0xe9,0x8d,0xa1, -0x24,0x00,0x02,0x27,0x00,0x60,0x00,0x00,0x21,0xa0,0x08,0x1a,0x21,0xa0,0x08,0xa4, -0x21,0xa0,0x09,0x23,0x21,0xa0,0x09,0x9e,0x21,0xa0,0x0a,0x13,0x21,0xa0,0x0a,0xa2, -0x01,0xa0,0x0d,0xa8,0x14,0x00,0x54,0x03,0x21,0x7f,0xf5,0x83,0x40,0x82,0x00,0x04, -0x21,0xa0,0x01,0x04,0x35,0x00,0x00,0x00,0x40,0x80,0x00,0x83,0x32,0x7f,0xff,0x00, -0x40,0x80,0x02,0x83,0x32,0x7f,0xfe,0x00,0x40,0x80,0x03,0x83,0x32,0x7f,0xfd,0x00, -0x21,0xa0,0x0e,0x03,0x00,0x00,0x01,0x02,0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00, -0x0f,0x66,0x01,0x82,0x35,0x80,0x00,0x0c,0x5e,0x0f,0xc1,0x83,0x32,0xbb,0xbb,0x86, -0x56,0xc0,0x01,0x87,0x18,0x21,0x82,0x04,0x08,0x21,0x01,0x08,0x23,0x00,0x03,0x87, -0x01,0xe0,0x0e,0x89,0x21,0x00,0x04,0x89,0x21,0xa0,0x0e,0x05,0x21,0xa0,0x0f,0x08, -0x01,0xa0,0x0e,0x83,0x35,0x00,0x00,0x00,0x41,0x40,0x00,0x8a,0x00,0x20,0x00,0x00, -0x04,0x00,0x85,0x03,0x35,0x00,0x00,0x00,0x41,0x40,0x00,0x85,0x00,0x20,0x00,0x00, -0x04,0x02,0x82,0x83,0x35,0x00,0x00,0x00,0x40,0x80,0x00,0x00,0x42,0x14,0xf8,0x01, -0x24,0x00,0x00,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x33,0x7c,0x31,0x00, -0x33,0x7c,0x37,0x00,0x32,0x7c,0x33,0x80,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81, -0x1c,0xf8,0x00,0x81,0x20,0x84,0xa0,0x01,0x33,0x7f,0x60,0x00,0x1c,0x08,0x00,0x81, -0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81, -0x1c,0xf8,0x00,0x81,0x33,0x7f,0x63,0x80,0x30,0x84,0xa0,0x01,0x1c,0x08,0x00,0x81, -0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81, -0x1c,0xf8,0x00,0x81,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, -0x04,0x05,0x06,0x07,0x80,0x80,0x80,0x80,0x0c,0x0d,0x0e,0x0f,0x80,0x80,0x80,0x80, -0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0xff,0xfe,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, -0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x10,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0xff,0x00,0x01,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x0c,0x00,0x00,0x00,0x07,0x28,0x00,0x00,0x07,0x40,0x00,0x00,0x07,0x50, -0x00,0x00,0x07,0x60,0x00,0x00,0x0c,0xe0,0x00,0x00,0x1e,0xa8,0x00,0x00,0x13,0x38, -0x00,0x00,0x12,0x00,0x00,0x00,0x11,0xd0,0x00,0x00,0x11,0xa0,0x00,0x00,0x11,0x70, -0x00,0x00,0x11,0x00,0x00,0x00,0x13,0x08,0x00,0x00,0x10,0x98,0x00,0x00,0x12,0x38, -0x00,0x00,0x0b,0xe8,0x00,0x00,0x0d,0x48,0x00,0x00,0x0e,0xd8,0x00,0x00,0x1b,0x30, -0x00,0x00,0x1c,0xd0,0x00,0x00,0x1a,0x50,0x00,0x00,0x1a,0xa8,0x00,0x00,0x1b,0x00, -0x47,0x43,0x43,0x3a,0x20,0x28,0x47,0x4e,0x55,0x29,0x20,0x37,0x2e,0x32,0x2e,0x30, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x01,0x53,0x50,0x55,0x4e, -0x41,0x4d,0x45,0x00,0x2f,0x68,0x6f,0x6d,0x65,0x2f,0x6d,0x69,0x6b,0x65,0x2f,0x70, -0x72,0x69,0x76,0x61,0x74,0x65,0x2f,0x70,0x73,0x6c,0x31,0x67,0x68,0x74,0x2f,0x63, -0x6f,0x6d,0x6d,0x6f,0x6e,0x2f,0x6c,0x69,0x62,0x73,0x70,0x75,0x6d,0x61,0x72,0x73, -0x2f,0x73,0x70,0x75,0x2f,0x6c,0x69,0x62,0x2f,0x6d,0x61,0x72,0x73,0x5f,0x6b,0x65, -0x72,0x6e,0x65,0x6c,0x2e,0x65,0x6c,0x66,0x00,0x00,0x00,0x00,0x00,0x2e,0x73,0x79, -0x6d,0x74,0x61,0x62,0x00,0x2e,0x73,0x74,0x72,0x74,0x61,0x62,0x00,0x2e,0x73,0x68, -0x73,0x74,0x72,0x74,0x61,0x62,0x00,0x2e,0x69,0x6e,0x74,0x65,0x72,0x72,0x75,0x70, -0x74,0x00,0x2e,0x69,0x6e,0x69,0x74,0x00,0x2e,0x74,0x65,0x78,0x74,0x00,0x2e,0x66, -0x69,0x6e,0x69,0x00,0x2e,0x72,0x6f,0x64,0x61,0x74,0x61,0x00,0x2e,0x64,0x61,0x74, -0x61,0x00,0x2e,0x62,0x73,0x73,0x00,0x2e,0x63,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x00, -0x2e,0x6e,0x6f,0x74,0x65,0x2e,0x73,0x70,0x75,0x5f,0x6e,0x61,0x6d,0x65,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x01, -0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x04, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x04, -0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x01, -0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0xa0,0x00,0x00,0x1e,0xa8, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x1e,0xc8, -0x00,0x00,0x1f,0x48,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x01, -0x00,0x00,0x00,0x12,0x00,0x00,0x1e,0xe0,0x00,0x00,0x1f,0x60,0x00,0x00,0x00,0xa0, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10, -0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x1f,0x80, -0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x00,0x00,0x00,0x08, -0x00,0x00,0x00,0x03,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x60,0x00,0x00,0x06,0x80, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x4b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00, -0x00,0x00,0x20,0x60,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x54,0x00,0x00,0x00,0x07, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x80,0x00,0x00,0x00,0x5c, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x20,0xdc,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x48,0x00,0x00,0x05,0x60, -0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x10, -0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x28,0xa8,0x00,0x00,0x04,0x76,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, -0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20, -0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x1e,0xc8, -0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x1e,0xe0, -0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x80, -0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00, -0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x09,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x00,0x0a,0x00,0x00,0x07,0x28, -0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x1e,0x00,0x00,0x24,0x00, -0x00,0x00,0x00,0x80,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x2c,0x00,0x00,0x07,0x40, -0x00,0x00,0x00,0x0c,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x3a,0x00,0x00,0x07,0x50, -0x00,0x00,0x00,0x0c,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x4a,0x00,0x00,0x25,0xa0, -0x00,0x00,0x00,0x02,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x56,0x00,0x00,0x07,0x60, -0x00,0x00,0x00,0x08,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x63,0x00,0x00,0x21,0x00, -0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x6c,0x00,0x00,0x07,0x68, -0x00,0x00,0x00,0x40,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x7c,0x00,0x00,0x23,0x80, -0x00,0x00,0x00,0x80,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x89,0x00,0x00,0x07,0xa8, -0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x9a,0x00,0x00,0x07,0xc0, -0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xaf,0x00,0x00,0x07,0xd8, -0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xbe,0x00,0x00,0x07,0xf0, -0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xcf,0x00,0x00,0x08,0x08, -0x00,0x00,0x00,0x3c,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xe1,0x00,0x00,0x08,0x48, -0x00,0x00,0x00,0x10,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xf9,0x00,0x00,0x08,0x58, -0x00,0x00,0x02,0x74,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x0c,0x00,0x00,0x23,0x00, -0x00,0x00,0x00,0x80,0x01,0x00,0x00,0x07,0x00,0x00,0x01,0x18,0x00,0x00,0x0a,0xd0, -0x00,0x00,0x00,0x44,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x28,0x00,0x00,0x0b,0x18, -0x00,0x00,0x00,0x64,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x35,0x00,0x00,0x22,0x00, -0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x07,0x00,0x00,0x01,0x42,0x00,0x00,0x0b,0x80, -0x00,0x00,0x00,0x64,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x51,0x00,0x00,0x0b,0xe8, -0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x62,0x00,0x00,0x0c,0x00, -0x00,0x00,0x00,0x28,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x6c,0x00,0x00,0x0c,0x28, -0x00,0x00,0x00,0xb8,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x7a,0x00,0x00,0x0c,0xe0, -0x00,0x00,0x00,0x68,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x8d,0x00,0x00,0x20,0x00, -0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x07,0x00,0x00,0x01,0x9f,0x00,0x00,0x0d,0x48, -0x00,0x00,0x01,0x90,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xb1,0x00,0x00,0x0e,0xd8, -0x00,0x00,0x00,0x44,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xc5,0x00,0x00,0x0f,0x20, -0x00,0x00,0x01,0x78,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xd1,0x00,0x00,0x10,0x98, -0x00,0x00,0x00,0x64,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xeb,0x00,0x00,0x11,0x00, -0x00,0x00,0x00,0x6c,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x03,0x00,0x00,0x11,0x70, -0x00,0x00,0x00,0x2c,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x19,0x00,0x00,0x11,0xa0, -0x00,0x00,0x00,0x2c,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x2d,0x00,0x00,0x11,0xd0, -0x00,0x00,0x00,0x2c,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x41,0x00,0x00,0x12,0x00, -0x00,0x00,0x00,0x34,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x53,0x00,0x00,0x12,0x38, -0x00,0x00,0x00,0xd0,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x6b,0x00,0x00,0x13,0x08, -0x00,0x00,0x00,0x30,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x81,0x00,0x00,0x13,0x38, -0x00,0x00,0x01,0xf8,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x90,0x00,0x00,0x25,0x10, -0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x07,0x00,0x00,0x02,0xaa,0x00,0x00,0x25,0x70, -0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x07,0x00,0x00,0x02,0xbd,0x00,0x00,0x15,0x30, -0x00,0x00,0x04,0x68,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0xca,0x00,0x00,0x25,0x20, -0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x07,0x00,0x00,0x02,0xda,0x00,0x00,0x1f,0x80, -0x00,0x00,0x00,0x60,0x01,0x00,0x00,0x06,0x00,0x00,0x02,0xea,0x00,0x00,0x25,0x80, -0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x07,0x00,0x00,0x02,0xf9,0x00,0x00,0x25,0xb0, -0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x07,0x00,0x00,0x03,0x0a,0x00,0x00,0x25,0x90, -0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x07,0x00,0x00,0x03,0x16,0x00,0x00,0x25,0x30, -0x00,0x00,0x00,0x40,0x01,0x00,0x00,0x07,0x00,0x00,0x03,0x2d,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x03,0x33,0x00,0x00,0x19,0xe0, -0x00,0x00,0x00,0x70,0x02,0x00,0x00,0x03,0x00,0x00,0x03,0x44,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x03,0x4c,0x00,0x00,0x24,0x80, -0x00,0x00,0x00,0x80,0x01,0x00,0x00,0x07,0x00,0x00,0x03,0x59,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x03,0x6b,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x03,0x83,0x00,0x00,0x19,0xd0, -0x00,0x00,0x00,0x0c,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0x93,0x00,0x00,0x1c,0xd0, -0x00,0x00,0x01,0x30,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xa4,0x00,0x00,0x26,0x00, -0x00,0x00,0x00,0x80,0x11,0x00,0x00,0x07,0x00,0x00,0x03,0xb2,0x00,0x00,0x1e,0x10, -0x00,0x00,0x00,0x58,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xc8,0x00,0x00,0x19,0x98, -0x00,0x00,0x00,0x38,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xd7,0x00,0x00,0x1e,0xa8, -0x00,0x00,0x00,0x20,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xe5,0x00,0x00,0x00,0x04, -0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x02,0x00,0x00,0x03,0xeb,0x00,0x00,0x1b,0x00, -0x00,0x00,0x00,0x30,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xf4,0x00,0x00,0x1a,0x50, -0x00,0x00,0x00,0x58,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xfc,0x00,0x00,0x1e,0x68, -0x00,0x00,0x00,0x20,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x03,0x00,0x00,0x1e,0x88, -0x00,0x00,0x00,0x20,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x10,0x00,0x00,0x1f,0xe0, -0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x04,0x1c,0x00,0x00,0x00,0x38, -0x00,0x00,0x06,0xec,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x21,0x00,0x00,0x1e,0xc8, -0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x04,0x00,0x00,0x04,0x27,0x00,0x00,0x1b,0x30, -0x00,0x00,0x01,0xa0,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x36,0x00,0x00,0x29,0xf0, -0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x04,0x3e,0x00,0x00,0x1f,0xe0, -0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x04,0x45,0x00,0x00,0x25,0x00, -0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x07,0x00,0x00,0x04,0x54,0x00,0x00,0x26,0x80, -0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x04,0x59,0x00,0x00,0x00,0x20, -0x00,0x00,0x00,0x14,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x5e,0x00,0x00,0x1e,0x00, -0x00,0x00,0x00,0x0c,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x6e,0x00,0x00,0x1a,0xa8, -0x00,0x00,0x00,0x58,0x12,0x00,0x00,0x03,0x00,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x2e, -0x63,0x00,0x67,0x65,0x74,0x5f,0x6d,0x61,0x72,0x73,0x5f,0x63,0x6f,0x6e,0x74,0x65, -0x78,0x74,0x5f,0x65,0x61,0x00,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x70,0x61,0x72, -0x61,0x6d,0x73,0x00,0x67,0x65,0x74,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x69, -0x64,0x00,0x67,0x65,0x74,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x69, -0x64,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x69,0x64,0x00,0x67,0x65, -0x74,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x00,0x77,0x6f,0x72,0x6b,0x6c, -0x6f,0x61,0x64,0x00,0x67,0x65,0x74,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64, -0x5f,0x65,0x61,0x00,0x71,0x75,0x65,0x75,0x65,0x5f,0x68,0x65,0x61,0x64,0x65,0x72, -0x00,0x63,0x68,0x65,0x63,0x6b,0x5f,0x73,0x74,0x61,0x74,0x65,0x5f,0x62,0x69,0x74, -0x73,0x00,0x63,0x68,0x65,0x63,0x6b,0x5f,0x73,0x74,0x61,0x74,0x65,0x5f,0x62,0x69, -0x74,0x73,0x5f,0x6e,0x6f,0x74,0x00,0x73,0x65,0x74,0x5f,0x73,0x74,0x61,0x74,0x65, -0x5f,0x62,0x69,0x74,0x73,0x00,0x73,0x65,0x74,0x5f,0x77,0x61,0x69,0x74,0x5f,0x69, -0x64,0x5f,0x62,0x69,0x74,0x73,0x00,0x73,0x65,0x74,0x5f,0x73,0x63,0x68,0x65,0x64, -0x75,0x6c,0x65,0x5f,0x62,0x69,0x74,0x73,0x00,0x75,0x6e,0x73,0x63,0x68,0x65,0x64, -0x75,0x6c,0x69,0x6e,0x67,0x5f,0x73,0x74,0x61,0x74,0x65,0x5f,0x62,0x69,0x74,0x73, -0x00,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x68,0x65,0x61,0x64,0x65,0x72,0x5f,0x62, -0x69,0x74,0x73,0x00,0x71,0x75,0x65,0x75,0x65,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x00, -0x73,0x65,0x74,0x5f,0x73,0x69,0x67,0x6e,0x61,0x6c,0x5f,0x62,0x69,0x74,0x73,0x00, -0x65,0x6e,0x64,0x5f,0x63,0x61,0x6c,0x6c,0x62,0x61,0x63,0x6b,0x00,0x77,0x6f,0x72, -0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x62,0x75,0x66,0x00,0x62,0x65,0x67,0x69,0x6e,0x5f, -0x63,0x61,0x6c,0x6c,0x62,0x61,0x63,0x6b,0x00,0x68,0x6f,0x73,0x74,0x5f,0x73,0x69, -0x67,0x6e,0x61,0x6c,0x5f,0x73,0x65,0x6e,0x64,0x00,0x67,0x65,0x74,0x5f,0x74,0x69, -0x63,0x6b,0x73,0x00,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x6d,0x65,0x6d,0x63,0x6d, -0x70,0x00,0x67,0x65,0x74,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x62, -0x79,0x5f,0x69,0x64,0x00,0x72,0x65,0x74,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61, -0x64,0x2e,0x32,0x38,0x39,0x37,0x00,0x68,0x6f,0x73,0x74,0x5f,0x63,0x61,0x6c,0x6c, -0x62,0x61,0x63,0x6b,0x5f,0x73,0x65,0x74,0x00,0x68,0x6f,0x73,0x74,0x5f,0x63,0x61, -0x6c,0x6c,0x62,0x61,0x63,0x6b,0x5f,0x72,0x65,0x73,0x65,0x74,0x00,0x63,0x68,0x61, -0x6e,0x67,0x65,0x5f,0x62,0x69,0x74,0x73,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61, -0x64,0x5f,0x75,0x6e,0x73,0x63,0x68,0x65,0x64,0x75,0x6c,0x65,0x5f,0x62,0x65,0x67, -0x69,0x6e,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x73,0x63,0x68,0x65, -0x64,0x75,0x6c,0x65,0x5f,0x62,0x65,0x67,0x69,0x6e,0x00,0x77,0x6f,0x72,0x6b,0x6c, -0x6f,0x61,0x64,0x5f,0x73,0x69,0x67,0x6e,0x61,0x6c,0x5f,0x72,0x65,0x73,0x65,0x74, -0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x73,0x69,0x67,0x6e,0x61,0x6c, -0x5f,0x73,0x65,0x74,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x77,0x61, -0x69,0x74,0x5f,0x72,0x65,0x73,0x65,0x74,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61, -0x64,0x5f,0x77,0x61,0x69,0x74,0x5f,0x73,0x65,0x74,0x00,0x77,0x6f,0x72,0x6b,0x6c, -0x6f,0x61,0x64,0x5f,0x75,0x6e,0x73,0x63,0x68,0x65,0x64,0x75,0x6c,0x65,0x5f,0x65, -0x6e,0x64,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x73,0x63,0x68,0x65, -0x64,0x75,0x6c,0x65,0x5f,0x65,0x6e,0x64,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61, -0x64,0x5f,0x71,0x75,0x65,0x72,0x79,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64, -0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x69,0x73,0x5f,0x63,0x61,0x63,0x68,0x65, -0x64,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x69,0x73,0x5f,0x63,0x61, -0x63,0x68,0x65,0x64,0x00,0x73,0x65,0x61,0x72,0x63,0x68,0x5f,0x62,0x6c,0x6f,0x63, -0x6b,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x6d,0x6f,0x64,0x75,0x6c, -0x65,0x00,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x79,0x73,0x63,0x61,0x6c,0x6c, -0x73,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x73,0x74,0x61,0x74,0x65, -0x00,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5f,0x65, -0x61,0x00,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x65,0x61,0x00,0x63,0x61, -0x63,0x68,0x65,0x64,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x6d,0x6f, -0x64,0x75,0x6c,0x65,0x00,0x64,0x6d,0x61,0x2e,0x63,0x00,0x64,0x6d,0x61,0x5f,0x6c, -0x61,0x72,0x67,0x65,0x2e,0x70,0x61,0x72,0x74,0x2e,0x30,0x00,0x6d,0x75,0x74,0x65, -0x78,0x2e,0x63,0x00,0x6d,0x75,0x74,0x65,0x78,0x5f,0x62,0x75,0x66,0x66,0x65,0x72, -0x00,0x73,0x70,0x75,0x5f,0x74,0x68,0x72,0x65,0x61,0x64,0x5f,0x65,0x78,0x69,0x74, -0x2e,0x63,0x00,0x73,0x70,0x75,0x5f,0x74,0x68,0x72,0x65,0x61,0x64,0x5f,0x73,0x65, -0x6e,0x64,0x5f,0x65,0x76,0x65,0x6e,0x74,0x2e,0x63,0x00,0x5f,0x5f,0x77,0x6f,0x72, -0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x65,0x78,0x69,0x74,0x00,0x6d,0x75,0x74,0x65,0x78, -0x5f,0x75,0x6e,0x6c,0x6f,0x63,0x6b,0x5f,0x70,0x75,0x74,0x00,0x6b,0x65,0x72,0x6e, -0x65,0x6c,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x00,0x73,0x70,0x75,0x5f,0x74,0x68, -0x72,0x65,0x61,0x64,0x5f,0x73,0x65,0x6e,0x64,0x5f,0x65,0x76,0x65,0x6e,0x74,0x00, -0x5f,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x72,0x75,0x6e,0x00,0x77, -0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x65,0x78,0x69,0x74,0x00,0x5f,0x69,0x6e, -0x69,0x74,0x00,0x64,0x6d,0x61,0x5f,0x77,0x61,0x69,0x74,0x00,0x64,0x6d,0x61,0x5f, -0x67,0x65,0x74,0x00,0x5f,0x73,0x74,0x61,0x72,0x74,0x00,0x77,0x6f,0x72,0x6b,0x6c, -0x6f,0x61,0x64,0x5f,0x72,0x75,0x6e,0x00,0x5f,0x5f,0x62,0x73,0x73,0x5f,0x73,0x74, -0x61,0x72,0x74,0x00,0x6d,0x61,0x69,0x6e,0x00,0x5f,0x66,0x69,0x6e,0x69,0x00,0x6d, -0x75,0x74,0x65,0x78,0x5f,0x6c,0x6f,0x63,0x6b,0x5f,0x67,0x65,0x74,0x00,0x5f,0x5f, -0x73,0x74,0x61,0x63,0x6b,0x00,0x5f,0x65,0x64,0x61,0x74,0x61,0x00,0x5f,0x5f,0x6b, -0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x63,0x6b,0x00,0x5f,0x65,0x6e,0x64, -0x00,0x65,0x78,0x69,0x74,0x00,0x73,0x70,0x75,0x5f,0x74,0x68,0x72,0x65,0x61,0x64, -0x5f,0x65,0x78,0x69,0x74,0x00,0x64,0x6d,0x61,0x5f,0x70,0x75,0x74,0x00, -}; diff --git a/common/libspumars/ppu/ppu/mars_task_module.c b/common/libspumars/ppu/ppu/mars_task_module.c deleted file mode 100644 index 60cec3a7..00000000 --- a/common/libspumars/ppu/ppu/mars_task_module.c +++ /dev/null @@ -1,475 +0,0 @@ -__attribute__((aligned(128))) const unsigned char mars_task_module_entry[] = { -0x7f,0x45,0x4c,0x46,0x01,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x02,0x00,0x17,0x00,0x00,0x00,0x01,0x00,0x00,0x33,0xe0,0x00,0x00,0x00,0x34, -0x00,0x00,0x0d,0xd4,0x00,0x00,0x00,0x00,0x00,0x34,0x00,0x20,0x00,0x03,0x00,0x28, -0x00,0x0f,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2a,0x00, -0x00,0x00,0x2a,0x00,0x00,0x00,0x0b,0x30,0x00,0x00,0x0b,0x30,0x00,0x00,0x00,0x05, -0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x01,0x00,0x00,0x0c,0x80,0x00,0x00,0x35,0x80, -0x00,0x00,0x35,0x80,0x00,0x00,0x00,0x60,0x00,0x00,0x03,0xf0,0x00,0x00,0x00,0x06, -0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x04,0x00,0x00,0x0d,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, -0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x7b,0x00,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81, -0x33,0x00,0x54,0x00,0x33,0x01,0x2c,0x80,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80, -0x35,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x20,0x86,0xc0,0x01, -0x33,0x00,0xf1,0x00,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, -0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x30,0x86,0xc0,0x01, -0x32,0x01,0x46,0x80,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81, -0x20,0x86,0xc4,0x04,0x20,0x86,0xc2,0x01,0x30,0x86,0xc0,0x01,0x33,0x00,0xca,0x80, -0x33,0x00,0x08,0x00,0x30,0x86,0xc4,0x02,0x20,0x01,0x40,0x02,0x32,0x01,0x3e,0x00, -0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x20,0x86,0xc4,0x03, -0x33,0x00,0x06,0x80,0x30,0x86,0xc4,0x03,0x33,0x00,0xd5,0x00,0x30,0x86,0xc2,0x01, -0x00,0x40,0x00,0x00,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, -0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x40,0x80,0x00,0x4f, -0x32,0x00,0x04,0x80,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81, -0x42,0x1b,0x28,0x03,0x40,0x80,0x00,0x04,0x33,0x00,0xb5,0x00,0x40,0x80,0x08,0x4f, -0x3f,0xe3,0xe7,0xcf,0x42,0x1b,0x18,0x4a,0x42,0x1b,0x28,0x4b,0x33,0x80,0x08,0xcd, -0x24,0x00,0x25,0x4d,0x33,0x80,0x09,0xce,0x08,0x33,0xe7,0x4e,0x40,0x80,0x00,0xcc, -0x3f,0xe3,0x26,0x4c,0x40,0x80,0x17,0xcd,0x00,0x40,0x00,0x00,0x35,0x20,0x25,0x00, -0x21,0x00,0x02,0x4f,0x42,0x1b,0x28,0x03,0x40,0x80,0x00,0x84,0x33,0x00,0xac,0x80, -0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x24,0x00,0x65,0x4e,0x18,0x13,0x27,0x4e,0x1c,0xff,0xe6,0xcd,0x00,0x40,0x00,0x00, -0x24,0x00,0x25,0xd0,0x1c,0x04,0x25,0xcb,0x25,0x00,0x00,0x4d,0x32,0x7f,0xfc,0x80, -0x42,0x1a,0xf0,0x03,0x24,0x00,0x40,0x80,0x42,0x1a,0xf0,0x02,0x24,0xff,0x80,0x81, -0x78,0x00,0xc1,0x04,0x1c,0xf8,0x00,0x81,0x21,0x00,0x02,0x04,0x42,0x00,0x00,0x05, -0x20,0x00,0x01,0x05,0x35,0x20,0x02,0x80,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80, -0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x42,0x1a,0xf0,0x02,0x24,0x00,0x40,0x80, -0x42,0x1a,0xf0,0x04,0x24,0xff,0x80,0x81,0x08,0x01,0x01,0x03,0x1c,0xf8,0x00,0x81, -0x0f,0x5f,0x81,0x85,0x0f,0x38,0x42,0x86,0x18,0x01,0x43,0x07,0x0f,0x5f,0xc3,0x84, -0x20,0x00,0x04,0x04,0x42,0x00,0x00,0x08,0x20,0x00,0x03,0x08,0x00,0x20,0x00,0x00, -0x42,0x1a,0xf0,0x03,0x00,0x20,0x00,0x00,0x35,0x20,0x04,0x00,0x00,0x20,0x00,0x00, -0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00, -0x33,0x81,0x3e,0x02,0x24,0x00,0x40,0x80,0x24,0xff,0xc0,0xd0,0x24,0xff,0x80,0xd1, -0x24,0xff,0x00,0x81,0x1c,0xf0,0x00,0x81,0x3f,0x83,0x41,0x03,0x56,0xc0,0x01,0x84, -0x40,0x20,0x00,0x7f,0x23,0x00,0x0d,0x84,0x42,0x1a,0xc6,0x50,0x33,0x81,0x3a,0x8f, -0x42,0x1a,0xc4,0x05,0x42,0x1a,0xc4,0x51,0x08,0x14,0x02,0x86,0x0f,0x5f,0x83,0x07, -0x1c,0xff,0xc3,0xd0,0x58,0x03,0xe8,0x08,0x20,0x00,0x07,0x08,0x1c,0x00,0x47,0x89, -0x0f,0x60,0x84,0x8b,0x04,0x00,0x04,0x8a,0x40,0x20,0x00,0x7f,0x23,0x81,0x34,0x8a, -0x18,0x14,0x45,0x8d,0x38,0x94,0x45,0x8c,0x40,0x20,0x00,0x7f,0x3b,0x83,0x46,0x0e, -0x35,0x20,0x07,0x00,0x33,0x81,0x31,0x8f,0x58,0x03,0xe8,0x10,0x21,0x7f,0xfa,0x10, -0x33,0x7f,0xde,0x00,0x40,0x80,0x00,0x91,0x3f,0xe0,0xc8,0x92,0x23,0x81,0x2c,0x92, -0x1c,0x10,0x00,0x81,0x34,0x00,0x40,0x80,0x34,0xff,0xc0,0xd0,0x34,0xff,0x80,0xd1, -0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81, -0x1c,0xf8,0x00,0x81,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, -0x32,0x7f,0xdd,0x00,0x00,0x20,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81, -0x1c,0xf8,0x00,0x81,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, -0x40,0x20,0x00,0x7f,0x20,0x00,0x01,0x86,0x40,0x80,0x0f,0x86,0x32,0x00,0xfe,0x80, -0x40,0x80,0x0f,0x86,0x32,0x00,0xfc,0x80,0x32,0x00,0xe7,0x80,0x00,0x20,0x00,0x00, -0x40,0x80,0x00,0x04,0x32,0x7f,0xae,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81, -0x1c,0xf8,0x00,0x81,0x33,0x00,0xe3,0x00,0x40,0x80,0x03,0x84,0x33,0x00,0xe5,0x00, -0x40,0x20,0x00,0x7f,0x20,0x00,0x02,0x83,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80, -0x40,0x20,0x00,0x7f,0x32,0x00,0xe6,0x00,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80, -0x40,0x80,0x04,0x83,0x35,0x00,0x00,0x00,0x40,0x80,0x00,0x84,0x24,0x00,0x40,0x80, -0x40,0x20,0x00,0x7f,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x33,0x7f,0xa3,0x00, -0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x32,0x00,0xdf,0x80,0x00,0x20,0x00,0x00, -0x32,0x00,0xdd,0x80,0x00,0x20,0x00,0x00,0x32,0x00,0xe7,0x00,0x00,0x20,0x00,0x00, -0x40,0x20,0x00,0x7f,0x12,0x00,0xdf,0x0a,0x04,0x00,0x02,0x05,0x24,0x00,0x40,0x80, -0x04,0x00,0x01,0x86,0x24,0xfe,0xc0,0x81,0x1c,0xec,0x00,0x81,0x00,0x20,0x00,0x00, -0x1c,0x10,0x00,0x84,0x24,0x00,0x80,0x85,0x24,0x00,0xc0,0x86,0x33,0x00,0xda,0x00, -0x34,0x00,0x80,0x88,0x34,0x00,0xc0,0x89,0x21,0x00,0x06,0x03,0x12,0x00,0xd9,0x0a, -0x40,0x80,0x56,0x04,0x34,0x01,0x00,0x82,0x04,0x00,0x04,0x83,0x40,0x20,0x00,0x7f, -0x38,0x81,0x01,0x0a,0x3e,0xc3,0x01,0x07,0xb1,0x62,0x84,0x07,0x28,0x81,0x01,0x0b, -0x40,0x20,0x00,0x7f,0x33,0x00,0xd4,0x00,0x1c,0x14,0x00,0x81,0x34,0x00,0x40,0x80, -0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x40,0x80,0x04,0x04,0x1c,0x05,0x81,0x82, -0x38,0x81,0x01,0x83,0x3b,0x80,0x81,0x83,0x32,0x00,0xc6,0x80,0x00,0x20,0x00,0x00, -0x32,0x00,0xc2,0x80,0x00,0x20,0x00,0x00,0x32,0x00,0xbf,0x80,0x00,0x20,0x00,0x00, -0x24,0x00,0x40,0x80,0x24,0xff,0x00,0x81,0x1c,0xf0,0x00,0x81,0x24,0x00,0x80,0x86, -0x24,0x00,0xc0,0x85,0x33,0x00,0xd0,0x80,0x34,0x00,0x80,0x86,0x21,0x00,0x05,0x83, -0x40,0x80,0x00,0x84,0x00,0x20,0x00,0x00,0x04,0x00,0x03,0x03,0x33,0x7f,0x85,0x00, -0x33,0x00,0xc2,0x80,0x34,0x00,0xc0,0x83,0x1c,0x10,0x00,0x81,0x34,0x00,0x40,0x80, -0x40,0x20,0x00,0x7f,0x32,0x00,0xcb,0x80,0x1c,0x10,0x00,0x81,0x34,0x00,0x40,0x80, -0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x40,0x20,0x00,0x7f,0x12,0x00,0xb9,0x09, -0x04,0x00,0x01,0x82,0x24,0x00,0x40,0x80,0x40,0x80,0x01,0x04,0x24,0xff,0x40,0x81, -0x1c,0xf4,0x00,0x81,0x40,0x20,0x00,0x7f,0x40,0x20,0x00,0x7f,0x24,0x00,0x80,0x82, -0x33,0x00,0xb4,0x80,0x34,0x00,0x80,0x85,0x20,0x00,0x04,0x83,0x40,0x80,0x03,0x04, -0x04,0x00,0x02,0x83,0x33,0x00,0xb2,0x00,0x40,0x20,0x00,0x7f,0x20,0x00,0x04,0x83, -0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80,0x32,0x00,0xb1,0x80,0x40,0x80,0x03,0x83, -0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80,0x40,0x20,0x00,0x7f,0x35,0x00,0x00,0x00, -0x40,0x80,0x04,0x83,0x32,0x7f,0xfd,0x80,0x04,0x00,0x02,0x02,0x24,0x00,0x40,0x80, -0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81,0x24,0x00,0x80,0x82,0x33,0x00,0xaa,0x00, -0x34,0x00,0x80,0x85,0x21,0x00,0x04,0x83,0x40,0x80,0x00,0x84,0x00,0x20,0x00,0x00, -0x04,0x00,0x02,0x83,0x33,0x7f,0x6c,0x00,0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80, -0x40,0x20,0x00,0x7f,0x32,0x00,0xa6,0x00,0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80, -0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x04,0x00,0x02,0x82,0x12,0x00,0xa6,0x0a, -0x04,0x00,0x02,0x06,0x24,0x00,0x40,0x80,0x04,0x00,0x01,0x87,0x24,0xfe,0xc0,0x81, -0x1c,0xec,0x00,0x81,0x04,0x00,0x01,0x04,0x1c,0x10,0x00,0x85,0x24,0x00,0x80,0x86, -0x24,0x00,0xc0,0x87,0x33,0x00,0xa1,0x00,0x34,0x00,0x80,0x89,0x34,0x00,0xc0,0x8a, -0x40,0x20,0x00,0x7f,0x21,0x00,0x07,0x83,0x40,0x80,0x54,0x04,0x34,0x01,0x00,0x8b, -0x40,0x80,0x00,0x03,0x38,0x81,0x05,0x85,0x3e,0xe2,0x05,0x88,0xb1,0x81,0x41,0x88, -0x28,0x81,0x05,0x8c,0x21,0x00,0x05,0x09,0x40,0x80,0x00,0x04,0x00,0x20,0x00,0x00, -0x04,0x00,0x05,0x03,0x00,0x20,0x00,0x00,0x33,0x00,0x99,0x80,0x00,0x20,0x00,0x00, -0x1c,0x14,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00,0x34,0x00,0x04,0x8d, -0x24,0x03,0x85,0x8d,0x32,0x7f,0xfa,0x80,0x42,0x1c,0xa8,0x0f,0x12,0x7f,0xa2,0x9c, -0x40,0x80,0x4a,0x06,0x33,0x80,0xa9,0x88,0x04,0x00,0x01,0x87,0x34,0x00,0x07,0x82, -0x04,0x00,0x02,0x0d,0x33,0x80,0xa9,0x8b,0x04,0x00,0x02,0x8e,0x24,0x00,0x40,0x80, -0x24,0xfe,0x00,0x81,0x1c,0xe0,0x00,0x81,0x1c,0x25,0x01,0x03,0x34,0x02,0xc1,0x09, -0x40,0x20,0x00,0x7f,0x38,0x81,0x81,0x0c,0x04,0x00,0x02,0x86,0x24,0x01,0xc0,0x87, -0x04,0x00,0x03,0x85,0x24,0x00,0xc0,0x88,0x24,0x00,0x80,0x8b,0x24,0x01,0x80,0x8d, -0x18,0x42,0x04,0x8a,0x24,0x01,0x40,0x8e,0x3b,0x80,0xc6,0x03,0xb2,0x02,0x85,0x0b, -0x24,0x01,0x00,0x8f,0x04,0x00,0x08,0x04,0x68,0x02,0x04,0x84,0x33,0x7f,0x94,0x80, -0x41,0x00,0x02,0x18,0x34,0x01,0x00,0x84,0x34,0x00,0xc0,0x92,0x34,0x00,0x80,0x96, -0x34,0x01,0xc0,0x94,0x12,0x7f,0x91,0x8d,0x34,0x01,0x40,0x86,0x34,0x00,0x02,0x05, -0x3f,0xbf,0x0a,0x15,0x34,0x02,0xc2,0x91,0x34,0x01,0x80,0x85,0x18,0x44,0x88,0x93, -0x08,0x06,0x02,0x83,0xb2,0xe4,0xc9,0x96,0x68,0x04,0x88,0x97,0x18,0x45,0x4b,0x99, -0xb0,0x86,0x4c,0x96,0x68,0x05,0x4b,0x84,0x33,0x7f,0x8b,0x00,0x1c,0x20,0x00,0x81, -0x40,0x80,0x0f,0x83,0x34,0x00,0x40,0x80,0x32,0x00,0x8a,0x00,0x00,0x20,0x00,0x00, -0x40,0x20,0x00,0x7f,0x12,0x7f,0x87,0x89,0x04,0x00,0x02,0x06,0x33,0x81,0x16,0x82, -0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x34,0x02,0xc1,0x05, -0x04,0x00,0x02,0x84,0x40,0x81,0x78,0x05,0x33,0x7f,0x83,0x00,0x1c,0x08,0x00,0x81, -0x40,0x80,0x0f,0x83,0x34,0x00,0x40,0x80,0x32,0x00,0x82,0x00,0x00,0x20,0x00,0x00, -0x40,0x80,0x4a,0x05,0x33,0x81,0x0f,0x82,0x40,0x80,0x54,0x0b,0x33,0x80,0xa6,0x84, -0x40,0x80,0x5c,0x0a,0x12,0x7f,0xda,0x99,0x40,0x80,0x5e,0x09,0x41,0x00,0x02,0x11, -0x1c,0x25,0x01,0x06,0x38,0x81,0x41,0x0e,0x04,0x1f,0xc8,0x92,0x38,0x82,0xc1,0x07, -0x40,0x80,0x00,0x85,0x38,0x82,0x81,0x08,0x08,0x04,0x82,0x13,0x3e,0xc2,0x01,0x0c, -0x3e,0xc3,0x01,0x0d,0x14,0xe0,0x09,0x95,0x3b,0x81,0x87,0x0f,0xb2,0x81,0xc2,0x0c, -0x04,0x00,0x0a,0x84,0x0c,0x1f,0xc7,0x90,0x28,0x82,0xc1,0x14,0x18,0x04,0x01,0x83, -0x14,0xe0,0x01,0x83,0xb2,0xc2,0x01,0x8c,0x28,0x82,0x81,0x16,0x38,0x82,0x41,0x17, -0xb3,0x05,0xca,0x8d,0x28,0x82,0x41,0x18,0x32,0x7f,0xce,0x00,0x00,0x20,0x00,0x00, -0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81,0x21,0x00,0x06,0x83, -0x40,0x80,0x5e,0x03,0x12,0x7f,0xca,0x8a,0x40,0x80,0x5c,0x05,0x33,0x80,0xfc,0x82, -0x1c,0x2f,0x01,0x06,0x38,0x80,0xc1,0x04,0x1c,0x2e,0x01,0x08,0x38,0x81,0x41,0x07, -0x40,0x80,0x00,0x05,0x3b,0x81,0x82,0x04,0x3b,0x82,0x03,0x83,0x33,0x7f,0xc5,0x80, -0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x40,0x20,0x00,0x7f,0x35,0x80,0x00,0x09, -0x40,0x80,0x54,0x0b,0x33,0x80,0xf5,0x89,0x40,0x20,0x00,0x7f,0x40,0x20,0x00,0x7f, -0x1c,0x2a,0x04,0x8a,0x38,0x82,0xc4,0x8c,0x3b,0x82,0x86,0x0d,0x23,0x80,0x8a,0x8d, -0x35,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x00,0x81, -0x1c,0xf0,0x00,0x81,0x33,0x00,0x4a,0x00,0x23,0x80,0xef,0x03,0x33,0x00,0x48,0x00, -0x40,0x80,0x00,0x04,0x33,0x00,0x4a,0x00,0x40,0x20,0x00,0x7f,0x21,0x00,0x26,0x83, -0x40,0x9f,0xf8,0x0d,0x33,0x80,0xeb,0x8a,0x40,0x80,0x4c,0x11,0x30,0x87,0xfe,0x0e, -0x40,0x80,0x0f,0x86,0x3e,0xe0,0x06,0x8f,0x40,0xa0,0x00,0x03,0x12,0x00,0x57,0x88, -0x1c,0x26,0x05,0x0c,0x34,0x02,0x05,0x0b,0xb2,0x03,0x85,0x8f,0x20,0x87,0xfe,0x10, -0x38,0x84,0x45,0x12,0x34,0x02,0x05,0x04,0x3b,0x83,0x09,0x05,0x33,0x00,0x53,0x80, -0x40,0x80,0x00,0x03,0x00,0x20,0x00,0x00,0x40,0x80,0x54,0x15,0x33,0x80,0xe2,0x93, -0x1c,0x2a,0x09,0x94,0x38,0x85,0x49,0x96,0x3b,0x85,0x0b,0x17,0x21,0x00,0x23,0x97, -0x40,0x80,0x4e,0x18,0x12,0x00,0x4e,0x90,0x40,0x80,0x4a,0x19,0x00,0x20,0x00,0x00, -0x40,0x80,0x44,0x1c,0x38,0x86,0x09,0x9a,0x1c,0x27,0x09,0x9b,0x38,0x86,0x49,0x83, -0x1c,0x22,0x09,0x9e,0x38,0x87,0x09,0x9d,0x1c,0x25,0x09,0x9f,0x24,0x00,0xc0,0x98, -0x40,0x80,0x0f,0x86,0x24,0x00,0x80,0x99,0x3b,0x86,0xcd,0x05,0x3b,0x87,0xc1,0x83, -0x3b,0x87,0x8e,0x84,0x33,0x00,0x46,0x80,0x40,0x80,0x00,0x31,0x33,0x80,0xd6,0xa0, -0x34,0x00,0x80,0xa1,0x34,0x00,0xc0,0xa3,0x40,0x20,0x00,0x7f,0x12,0x00,0x15,0x8e, -0x1c,0x25,0x10,0x26,0x34,0x02,0x90,0x05,0x1c,0x27,0x10,0x27,0x38,0x88,0x50,0x22, -0x38,0x88,0xd0,0x24,0x04,0x00,0x02,0xa5,0x3b,0x89,0x91,0x03,0x3b,0x89,0xd2,0x29, -0x04,0x00,0x01,0xa8,0x18,0x0a,0x54,0x32,0x18,0x09,0x59,0x30,0x58,0x0c,0x98,0x2a, -0x40,0x20,0x00,0x7f,0x21,0x00,0x0e,0xaa,0x40,0x80,0x0f,0x83,0x33,0x00,0x3d,0x80, -0x00,0x40,0x00,0x00,0x33,0x80,0xcb,0xab,0x40,0x80,0x52,0x2d,0x42,0x1a,0xc8,0x04, -0x1c,0x29,0x15,0xac,0x38,0x8b,0x55,0xae,0x1c,0x38,0x15,0x83,0x3b,0x8b,0x17,0x2f, -0x35,0x20,0x17,0x80,0x1c,0x10,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, -0x33,0x00,0x1f,0x80,0x40,0x80,0x00,0x84,0x33,0x00,0x21,0x80,0x12,0x7f,0xd8,0x89, -0x40,0x20,0x00,0x7f,0x21,0x7f,0xe0,0x83,0x33,0x80,0xc3,0x05,0x30,0x87,0xfe,0x04, -0x34,0x02,0x02,0x82,0x78,0x00,0x82,0x07,0x36,0x00,0x03,0x88,0x4c,0x02,0xc4,0x09, -0x20,0x7f,0xd4,0x09,0x32,0x7f,0xdc,0x80,0x1c,0x04,0x19,0x32,0x24,0xff,0xd9,0x31, -0x40,0x20,0x00,0x7f,0x32,0x7f,0xef,0x00,0x1c,0x10,0x00,0x81,0x34,0x00,0x40,0x80, -0x32,0x7e,0xe4,0x00,0x00,0x20,0x00,0x00,0x33,0x80,0x41,0x02,0x24,0xff,0xc0,0xd0, -0x42,0x1a,0xc0,0x50,0x24,0x00,0x40,0x80,0x24,0xff,0x40,0x81,0x1c,0xf4,0x00,0x81, -0x3b,0x94,0x01,0x05,0x7c,0xff,0xc2,0x83,0x40,0x20,0x00,0x7f,0x21,0x00,0x03,0x83, -0x1c,0xff,0x28,0x50,0x35,0x20,0x02,0x80,0x34,0x00,0x28,0x04,0x3b,0x94,0x02,0x05, -0x7c,0xff,0xc2,0x86,0x20,0x7f,0xfd,0x86,0x1c,0x0c,0x00,0x81,0x34,0x00,0x40,0x80, -0x34,0xff,0xc0,0xd0,0x35,0x00,0x00,0x00,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81, -0x1c,0xf8,0x00,0x81,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, -0x42,0x1f,0xf0,0x01,0x40,0x80,0x00,0x00,0x24,0x00,0x00,0x80,0x24,0xff,0x80,0x81, -0x1c,0xf8,0x00,0x81,0x23,0x80,0xad,0x83,0x33,0x7e,0xc1,0x80,0x33,0x7e,0xc6,0x80, -0x32,0x00,0x12,0x80,0x40,0x80,0x00,0x02,0x32,0x00,0x1b,0x00,0x40,0x80,0x02,0x02, -0x32,0x00,0x1a,0x00,0x40,0x80,0x04,0x02,0x32,0x00,0x19,0x00,0x40,0x80,0x06,0x02, -0x32,0x00,0x18,0x00,0x40,0x80,0x08,0x02,0x32,0x00,0x17,0x00,0x40,0x80,0x0a,0x02, -0x32,0x00,0x16,0x00,0x40,0x80,0x0e,0x02,0x32,0x00,0x15,0x00,0x40,0x80,0x10,0x02, -0x32,0x00,0x14,0x00,0x40,0x80,0x12,0x02,0x32,0x00,0x13,0x00,0x40,0x80,0x14,0x02, -0x32,0x00,0x12,0x00,0x40,0x80,0x16,0x02,0x32,0x00,0x11,0x00,0x40,0x80,0x18,0x02, -0x32,0x00,0x10,0x00,0x40,0x80,0x1a,0x02,0x32,0x00,0x0f,0x00,0x40,0x80,0x1c,0x02, -0x32,0x00,0x0e,0x00,0x40,0x80,0x1e,0x02,0x32,0x00,0x0d,0x00,0x40,0x80,0x10,0x03, -0x40,0x80,0x0c,0x02,0x32,0x00,0x0b,0x80,0x40,0x80,0x08,0x03,0x40,0x80,0x0c,0x02, -0x32,0x00,0x0a,0x00,0x40,0x80,0x40,0x03,0x40,0x80,0x0c,0x02,0x32,0x00,0x08,0x80, -0x40,0x80,0x20,0x02,0x32,0x00,0x07,0x80,0x40,0x80,0x22,0x02,0x32,0x00,0x06,0x80, -0x40,0x80,0x24,0x02,0x32,0x00,0x05,0x80,0x40,0x80,0x26,0x02,0x32,0x00,0x04,0x80, -0x40,0x80,0x28,0x02,0x32,0x00,0x03,0x80,0x40,0x80,0x2a,0x02,0x32,0x00,0x02,0x80, -0x40,0x80,0x2c,0x02,0x32,0x00,0x01,0x80,0x40,0x80,0x2e,0x02,0x32,0x00,0x00,0x80, -0x33,0x80,0x90,0x4c,0x38,0x80,0xa6,0x4d,0x18,0x00,0xa6,0x4e,0x3b,0x93,0xa6,0xcf, -0x35,0x00,0x27,0x80,0x24,0x00,0x40,0x80,0x24,0xff,0x80,0x81,0x1c,0xf8,0x00,0x81, -0x33,0x7e,0xde,0x00,0x1c,0x08,0x00,0x81,0x34,0x00,0x40,0x80,0x35,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x02,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x04,0x05,0x06,0x07,0x80,0x80,0x80,0x80,0x0c,0x0d,0x0e,0x0f,0x80,0x80,0x80,0x80, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, -0x00,0x00,0x2e,0x08,0x00,0x00,0x2e,0x00,0x00,0x00,0x2c,0xe8,0x00,0x00,0x2d,0xe8, -0x00,0x00,0x2a,0x50,0x00,0x00,0x2c,0xf0,0x00,0x00,0x2f,0x28,0x00,0x00,0x2d,0x70, -0x00,0x00,0x2e,0xd8,0x00,0x00,0x2e,0x68,0x00,0x00,0x2d,0x68,0x00,0x00,0x2d,0x60, -0x00,0x00,0x2d,0x38,0x00,0x00,0x2c,0xf8,0x00,0x00,0x2e,0x10,0x00,0x00,0x34,0xb8, -0x00,0x00,0x34,0xc0,0x00,0x00,0x34,0xc8,0x00,0x00,0x34,0xd0,0x00,0x00,0x34,0xd8, -0x47,0x43,0x43,0x3a,0x20,0x28,0x47,0x4e,0x55,0x29,0x20,0x37,0x2e,0x32,0x2e,0x30, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x4a,0x00,0x00,0x00,0x01,0x53,0x50,0x55,0x4e, -0x41,0x4d,0x45,0x00,0x2f,0x68,0x6f,0x6d,0x65,0x2f,0x6d,0x69,0x6b,0x65,0x2f,0x70, -0x72,0x69,0x76,0x61,0x74,0x65,0x2f,0x70,0x73,0x6c,0x31,0x67,0x68,0x74,0x2f,0x63, -0x6f,0x6d,0x6d,0x6f,0x6e,0x2f,0x6c,0x69,0x62,0x73,0x70,0x75,0x6d,0x61,0x72,0x73, -0x2f,0x73,0x70,0x75,0x2f,0x6c,0x69,0x62,0x2f,0x6d,0x61,0x72,0x73,0x5f,0x74,0x61, -0x73,0x6b,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x2e,0x65,0x6c,0x66,0x00,0x00,0x00, -0x00,0x2e,0x73,0x79,0x6d,0x74,0x61,0x62,0x00,0x2e,0x73,0x74,0x72,0x74,0x61,0x62, -0x00,0x2e,0x73,0x68,0x73,0x74,0x72,0x74,0x61,0x62,0x00,0x2e,0x69,0x6e,0x74,0x65, -0x72,0x72,0x75,0x70,0x74,0x00,0x2e,0x69,0x6e,0x69,0x74,0x00,0x2e,0x74,0x65,0x78, -0x74,0x00,0x2e,0x66,0x69,0x6e,0x69,0x00,0x2e,0x72,0x6f,0x64,0x61,0x74,0x61,0x00, -0x2e,0x63,0x74,0x6f,0x72,0x73,0x00,0x2e,0x64,0x74,0x6f,0x72,0x73,0x00,0x2e,0x64, -0x61,0x74,0x61,0x00,0x2e,0x62,0x73,0x73,0x00,0x2e,0x63,0x6f,0x6d,0x6d,0x65,0x6e, -0x74,0x00,0x2e,0x6e,0x6f,0x74,0x65,0x2e,0x73,0x70,0x75,0x5f,0x6e,0x61,0x6d,0x65, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1b, -0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x2a,0x00,0x00,0x00,0x01,0x00, -0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06, -0x00,0x00,0x2a,0x04,0x00,0x00,0x01,0x04,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2c, -0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x2a,0x30,0x00,0x00,0x01,0x30, -0x00,0x00,0x0a,0xc4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06, -0x00,0x00,0x34,0xf4,0x00,0x00,0x0b,0xf4,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38, -0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x12,0x00,0x00,0x35,0x10,0x00,0x00,0x0c,0x10, -0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10, -0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03, -0x00,0x00,0x35,0x80,0x00,0x00,0x0c,0x80,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47, -0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x35,0x88,0x00,0x00,0x0c,0x88, -0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03, -0x00,0x00,0x35,0x90,0x00,0x00,0x0c,0x90,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54, -0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x03,0x00,0x00,0x35,0xe0,0x00,0x00,0x0c,0xe0, -0x00,0x00,0x03,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x30, -0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0xe0,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x62, -0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x00, -0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x60,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, -0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x2c, -0x00,0x00,0x06,0xc0,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x04, -0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x16,0xec,0x00,0x00,0x06,0x95,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x2a,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x00, -0x00,0x00,0x2a,0x04,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x00, -0x00,0x00,0x2a,0x30,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x00,0x00, -0x00,0x00,0x34,0xf4,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x00,0x00, -0x00,0x00,0x35,0x10,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x05,0x00,0x00,0x00,0x00, -0x00,0x00,0x35,0x80,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x00, -0x00,0x00,0x35,0x88,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x00, -0x00,0x00,0x35,0x90,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x08,0x00,0x00,0x00,0x00, -0x00,0x00,0x35,0xe0,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x09,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x0a,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x0b,0x00,0x00,0x00,0x01, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x00,0x08, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x00,0x0f, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x00,0x1a, -0x00,0x00,0x35,0x80,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0x28, -0x00,0x00,0x35,0x88,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x36, -0x00,0x00,0x2b,0x60,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x4b, -0x00,0x00,0x2b,0x98,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x5e, -0x00,0x00,0x2b,0xf0,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x74, -0x00,0x00,0x35,0xe0,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x09,0x00,0x00,0x00,0x83, -0x00,0x00,0x35,0xf0,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x09,0x00,0x00,0x00,0x91, -0x00,0x00,0x2c,0x98,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xac, -0x00,0x00,0x2c,0xb0,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xb8, -0x00,0x00,0x2c,0xb8,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0x0f, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x00,0xc9, -0x00,0x00,0x35,0x84,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0xd6, -0x00,0x00,0x33,0x78,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x00,0xec, -0x00,0x00,0x33,0xc8,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x07, -0x00,0x00,0x2a,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x18, -0x00,0x00,0x2a,0xd4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x2c, -0x00,0x00,0x2a,0xf4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x31, -0x00,0x00,0x2b,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x36, -0x00,0x00,0x2b,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x3b, -0x00,0x00,0x2b,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x01,0x40, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xff,0xf1,0x00,0x00,0x01,0x4e, -0x00,0x00,0x2c,0xd0,0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x52, -0x00,0x00,0x2c,0xe8,0x00,0x00,0x00,0x04,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x5b, -0x00,0x00,0x2c,0xf0,0x00,0x00,0x00,0x08,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x66, -0x00,0x00,0x2c,0xf8,0x00,0x00,0x00,0x40,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x7b, -0x00,0x00,0x2d,0x38,0x00,0x00,0x00,0x24,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x8c, -0x00,0x00,0x2d,0x60,0x00,0x00,0x00,0x04,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0x9d, -0x00,0x00,0x2d,0x68,0x00,0x00,0x00,0x04,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xae, -0x00,0x00,0x2d,0x70,0x00,0x00,0x00,0x74,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xbe, -0x00,0x00,0x2d,0xe8,0x00,0x00,0x00,0x14,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xcd, -0x00,0x00,0x2e,0x00,0x00,0x00,0x00,0x04,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xdb, -0x00,0x00,0x2e,0x08,0x00,0x00,0x00,0x04,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xe5, -0x00,0x00,0x2e,0x10,0x00,0x00,0x00,0x54,0x02,0x00,0x00,0x03,0x00,0x00,0x01,0xf4, -0x00,0x00,0x2e,0x68,0x00,0x00,0x00,0x70,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x02, -0x00,0x00,0x2e,0xd8,0x00,0x00,0x00,0x4c,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x0c, -0x00,0x00,0x2f,0x28,0x00,0x00,0x00,0x90,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x1a, -0x00,0x00,0x2f,0xb8,0x00,0x00,0x00,0xd4,0x02,0x00,0x00,0x03,0x00,0x00,0x02,0x26, -0x00,0x00,0x39,0x50,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x09,0x00,0x00,0x02,0x2b, -0x00,0x00,0x35,0x90,0x00,0x00,0x00,0x50,0x01,0x00,0x00,0x08,0x00,0x00,0x02,0x40, -0x00,0x00,0x34,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x02,0x54, -0x00,0x00,0x31,0xc8,0x00,0x00,0x01,0xac,0x12,0x00,0x00,0x03,0x00,0x00,0x02,0x62, -0x00,0x00,0x2a,0x30,0x00,0x00,0x00,0x20,0x12,0x00,0x00,0x03,0x00,0x00,0x02,0x73, -0x00,0x00,0x34,0x24,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x02,0x8c, -0x00,0x00,0x34,0x44,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x02,0xac, -0x00,0x00,0x31,0x50,0x00,0x00,0x00,0x74,0x12,0x00,0x00,0x03,0x00,0x00,0x02,0xbb, -0x00,0x00,0x34,0xb8,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x02,0xd6, -0x00,0x00,0x34,0x5c,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x02,0xfa, -0x00,0x00,0x34,0x3c,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0x18, -0x00,0x00,0x2a,0x90,0x00,0x00,0x00,0x30,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0x25, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x03,0x34, -0x00,0x00,0x35,0xe0,0x00,0x00,0x00,0x00,0x11,0x02,0x00,0x08,0x00,0x00,0x03,0x40, -0x00,0x00,0x36,0x20,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x09,0x00,0x00,0x03,0x4d, -0x00,0x00,0x35,0x8c,0x00,0x00,0x00,0x00,0x11,0x02,0x00,0x07,0x00,0x00,0x03,0x5a, -0x00,0x00,0x30,0xd0,0x00,0x00,0x00,0x7c,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0x66, -0x00,0x00,0x34,0x54,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0x88, -0x00,0x00,0x39,0x60,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x09,0x00,0x00,0x03,0x98, -0x00,0x00,0x34,0x34,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xb3, -0x00,0x00,0x2a,0x04,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x02,0x00,0x00,0x03,0xb9, -0x00,0x00,0x34,0x88,0x00,0x00,0x00,0x0c,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xd4, -0x00,0x00,0x30,0x90,0x00,0x00,0x00,0x3c,0x12,0x00,0x00,0x03,0x00,0x00,0x03,0xe4, -0x00,0x00,0x34,0x74,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x08, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x04,0x22, -0x00,0x00,0x34,0x14,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x3c, -0x00,0x00,0x34,0x04,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x52, -0x00,0x00,0x34,0x2c,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0x71, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x04,0x8d, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x04,0x92, -0x00,0x00,0x34,0xa0,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0xaf, -0x00,0x00,0x34,0xc0,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0xcc, -0x00,0x00,0x34,0x0c,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x04,0xec, -0x00,0x00,0x35,0xe0,0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x04,0xf8, -0x00,0x00,0x36,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x09,0x00,0x00,0x05,0x07, -0x00,0x00,0x33,0xe0,0x00,0x00,0x00,0x24,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0x19, -0x00,0x00,0x34,0xc8,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0x2d, -0x00,0x00,0x34,0x94,0x00,0x00,0x00,0x0c,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0x49, -0x00,0x00,0x34,0x1c,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0x65, -0x00,0x00,0x34,0xf4,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x04,0x00,0x00,0x05,0x6b, -0x00,0x00,0x2a,0x50,0x00,0x00,0x00,0x14,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0x75, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x05,0x7c, -0x00,0x00,0x34,0xb0,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0x9c, -0x00,0x00,0x34,0x6c,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0xc2, -0x00,0x00,0x34,0x4c,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x05,0xe2, -0x00,0x00,0x34,0x64,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x06,0x04, -0x00,0x00,0x34,0x7c,0x00,0x00,0x00,0x0c,0x12,0x00,0x00,0x03,0x00,0x00,0x06,0x1e, -0x00,0x00,0x3f,0xe0,0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x06,0x26, -0x00,0x00,0x35,0xe0,0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x06,0x2d, -0x00,0x00,0x39,0x70,0x00,0x00,0x00,0x00,0x10,0x00,0xff,0xf1,0x00,0x00,0x06,0x32, -0x00,0x00,0x34,0xd8,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x06,0x47, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x06,0x4c, -0x00,0x00,0x2a,0x64,0x00,0x00,0x00,0x2c,0x12,0x00,0x00,0x03,0x00,0x00,0x06,0x56, -0x00,0x00,0x34,0xd0,0x00,0x00,0x00,0x10,0x12,0x00,0x00,0x03,0x00,0x00,0x06,0x6a, -0x00,0x00,0x34,0xa8,0x00,0x00,0x00,0x08,0x12,0x00,0x00,0x03,0x00,0x00,0x06,0x88, -0x00,0x00,0x36,0x10,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x09,0x00,0x63,0x72,0x74, -0x6e,0x2e,0x53,0x00,0x63,0x72,0x74,0x69,0x2e,0x53,0x00,0x63,0x72,0x74,0x73,0x74, -0x75,0x66,0x66,0x2e,0x63,0x00,0x5f,0x5f,0x43,0x54,0x4f,0x52,0x5f,0x4c,0x49,0x53, -0x54,0x5f,0x5f,0x00,0x5f,0x5f,0x44,0x54,0x4f,0x52,0x5f,0x4c,0x49,0x53,0x54,0x5f, -0x5f,0x00,0x64,0x65,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x5f,0x74,0x6d,0x5f, -0x63,0x6c,0x6f,0x6e,0x65,0x73,0x00,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x5f, -0x74,0x6d,0x5f,0x63,0x6c,0x6f,0x6e,0x65,0x73,0x00,0x5f,0x5f,0x64,0x6f,0x5f,0x67, -0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x74,0x6f,0x72,0x73,0x5f,0x61,0x75,0x78,0x00, -0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x65,0x64,0x2e,0x33,0x38,0x37,0x39,0x00,0x64, -0x74,0x6f,0x72,0x5f,0x69,0x64,0x78,0x2e,0x33,0x38,0x38,0x31,0x00,0x63,0x61,0x6c, -0x6c,0x5f,0x5f,0x5f,0x64,0x6f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x74, -0x6f,0x72,0x73,0x5f,0x61,0x75,0x78,0x00,0x66,0x72,0x61,0x6d,0x65,0x5f,0x64,0x75, -0x6d,0x6d,0x79,0x00,0x63,0x61,0x6c,0x6c,0x5f,0x66,0x72,0x61,0x6d,0x65,0x5f,0x64, -0x75,0x6d,0x6d,0x79,0x00,0x5f,0x5f,0x43,0x54,0x4f,0x52,0x5f,0x45,0x4e,0x44,0x5f, -0x5f,0x00,0x5f,0x5f,0x64,0x6f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x63,0x74, -0x6f,0x72,0x73,0x5f,0x61,0x75,0x78,0x00,0x63,0x61,0x6c,0x6c,0x5f,0x5f,0x5f,0x64, -0x6f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x63,0x74,0x6f,0x72,0x73,0x5f,0x61, -0x75,0x78,0x00,0x5f,0x5f,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x73,0x5f,0x73, -0x61,0x76,0x65,0x00,0x5f,0x5f,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x73,0x5f, -0x72,0x65,0x73,0x74,0x6f,0x72,0x65,0x00,0x62,0x6f,0x64,0x79,0x00,0x6c,0x6f,0x6f, -0x70,0x00,0x69,0x6e,0x73,0x74,0x00,0x64,0x6f,0x6e,0x65,0x00,0x74,0x61,0x73,0x6b, -0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x2e,0x63,0x00,0x64,0x6d,0x61,0x00,0x67,0x65, -0x74,0x5f,0x74,0x61,0x73,0x6b,0x00,0x74,0x61,0x73,0x6b,0x5f,0x79,0x69,0x65,0x6c, -0x64,0x00,0x74,0x61,0x73,0x6b,0x5f,0x73,0x69,0x67,0x6e,0x61,0x6c,0x5f,0x74,0x72, -0x79,0x5f,0x77,0x61,0x69,0x74,0x00,0x74,0x61,0x73,0x6b,0x5f,0x73,0x69,0x67,0x6e, -0x61,0x6c,0x5f,0x77,0x61,0x69,0x74,0x00,0x74,0x61,0x73,0x6b,0x5f,0x73,0x69,0x67, -0x6e,0x61,0x6c,0x5f,0x73,0x65,0x6e,0x64,0x00,0x74,0x61,0x73,0x6b,0x5f,0x73,0x69, -0x67,0x6e,0x61,0x6c,0x5f,0x68,0x6f,0x73,0x74,0x00,0x74,0x61,0x73,0x6b,0x5f,0x75, -0x6e,0x73,0x63,0x68,0x65,0x64,0x75,0x6c,0x65,0x00,0x67,0x65,0x74,0x5f,0x74,0x61, -0x73,0x6b,0x5f,0x62,0x79,0x5f,0x69,0x64,0x00,0x67,0x65,0x74,0x5f,0x6b,0x65,0x72, -0x6e,0x65,0x6c,0x5f,0x69,0x64,0x00,0x67,0x65,0x74,0x5f,0x74,0x69,0x63,0x6b,0x73, -0x00,0x74,0x61,0x73,0x6b,0x5f,0x63,0x61,0x6c,0x6c,0x5f,0x68,0x6f,0x73,0x74,0x00, -0x74,0x61,0x73,0x6b,0x5f,0x74,0x72,0x79,0x5f,0x77,0x61,0x69,0x74,0x00,0x74,0x61, -0x73,0x6b,0x5f,0x77,0x61,0x69,0x74,0x00,0x74,0x61,0x73,0x6b,0x5f,0x73,0x63,0x68, -0x65,0x64,0x75,0x6c,0x65,0x00,0x64,0x6d,0x61,0x5f,0x63,0x6f,0x6e,0x74,0x65,0x78, -0x74,0x00,0x74,0x61,0x73,0x6b,0x00,0x74,0x61,0x73,0x6b,0x5f,0x6d,0x6f,0x64,0x75, -0x6c,0x65,0x5f,0x73,0x79,0x73,0x63,0x61,0x6c,0x6c,0x73,0x00,0x63,0x61,0x6c,0x6c, -0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x79,0x73,0x63,0x61,0x6c,0x6c,0x00, -0x5f,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x6d,0x61,0x69,0x6e,0x00,0x6d,0x61, -0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x6d,0x61,0x69,0x6e,0x00,0x6d, -0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x67,0x65,0x74,0x5f,0x77, -0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64, -0x75,0x6c,0x65,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x77,0x61,0x69, -0x74,0x5f,0x72,0x65,0x73,0x65,0x74,0x00,0x5f,0x5f,0x74,0x61,0x73,0x6b,0x5f,0x72, -0x65,0x73,0x74,0x6f,0x72,0x65,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75, -0x6c,0x65,0x5f,0x6d,0x75,0x74,0x65,0x78,0x5f,0x6c,0x6f,0x63,0x6b,0x5f,0x67,0x65, -0x74,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x77,0x6f, -0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x73,0x63,0x68,0x65,0x64,0x75,0x6c,0x65,0x5f, -0x62,0x65,0x67,0x69,0x6e,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c, -0x65,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x77,0x61,0x69,0x74,0x5f, -0x73,0x65,0x74,0x00,0x74,0x61,0x73,0x6b,0x5f,0x72,0x65,0x73,0x74,0x6f,0x72,0x65, -0x00,0x6d,0x61,0x72,0x73,0x5f,0x74,0x61,0x73,0x6b,0x5f,0x6d,0x61,0x69,0x6e,0x00, -0x5f,0x5f,0x54,0x4d,0x43,0x5f,0x45,0x4e,0x44,0x5f,0x5f,0x00,0x5f,0x5f,0x77,0x6f, -0x72,0x6b,0x5f,0x73,0x74,0x61,0x63,0x6b,0x00,0x5f,0x5f,0x44,0x54,0x4f,0x52,0x5f, -0x45,0x4e,0x44,0x5f,0x5f,0x00,0x5f,0x5f,0x74,0x61,0x73,0x6b,0x5f,0x73,0x61,0x76, -0x65,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x77,0x6f, -0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x73,0x69,0x67,0x6e,0x61,0x6c,0x5f,0x72,0x65, -0x73,0x65,0x74,0x00,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x79,0x73,0x63,0x61, -0x6c,0x6c,0x73,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f, -0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x71,0x75,0x65,0x72,0x79,0x00,0x5f, -0x69,0x6e,0x69,0x74,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65, -0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x79,0x69,0x65,0x6c,0x64,0x00, -0x5f,0x5f,0x64,0x6d,0x61,0x5f,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x73,0x00, -0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x77,0x6f,0x72,0x6b, -0x6c,0x6f,0x61,0x64,0x5f,0x75,0x6e,0x73,0x63,0x68,0x65,0x64,0x75,0x6c,0x65,0x5f, -0x65,0x6e,0x64,0x00,0x5f,0x49,0x54,0x4d,0x5f,0x72,0x65,0x67,0x69,0x73,0x74,0x65, -0x72,0x54,0x4d,0x43,0x6c,0x6f,0x6e,0x65,0x54,0x61,0x62,0x6c,0x65,0x00,0x6d,0x61, -0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x67,0x65,0x74,0x5f,0x6b,0x65, -0x72,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64, -0x75,0x6c,0x65,0x5f,0x67,0x65,0x74,0x5f,0x74,0x69,0x63,0x6b,0x73,0x00,0x6d,0x61, -0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x67,0x65,0x74,0x5f,0x77,0x6f, -0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x62,0x79,0x5f,0x69,0x64,0x00,0x5f,0x49,0x54, -0x4d,0x5f,0x64,0x65,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x54,0x4d,0x43,0x6c, -0x6f,0x6e,0x65,0x54,0x61,0x62,0x6c,0x65,0x00,0x73,0x62,0x72,0x6b,0x00,0x6d,0x61, -0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x68,0x6f,0x73,0x74,0x5f,0x73, -0x69,0x67,0x6e,0x61,0x6c,0x5f,0x73,0x65,0x6e,0x64,0x00,0x6d,0x61,0x72,0x73,0x5f, -0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x6d,0x75,0x74,0x65,0x78,0x5f,0x75,0x6e,0x6c, -0x6f,0x63,0x6b,0x5f,0x70,0x75,0x74,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64, -0x75,0x6c,0x65,0x5f,0x67,0x65,0x74,0x5f,0x6d,0x61,0x72,0x73,0x5f,0x63,0x6f,0x6e, -0x74,0x65,0x78,0x74,0x5f,0x65,0x61,0x00,0x5f,0x5f,0x62,0x73,0x73,0x5f,0x73,0x74, -0x61,0x72,0x74,0x00,0x5f,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x73,0x74,0x61, -0x63,0x6b,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x65, -0x6e,0x74,0x72,0x79,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65, -0x5f,0x64,0x6d,0x61,0x5f,0x67,0x65,0x74,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f, -0x64,0x75,0x6c,0x65,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x66,0x69, -0x6e,0x69,0x73,0x68,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65, -0x5f,0x67,0x65,0x74,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x69,0x64, -0x00,0x5f,0x66,0x69,0x6e,0x69,0x00,0x74,0x61,0x73,0x6b,0x5f,0x65,0x78,0x69,0x74, -0x00,0x61,0x74,0x65,0x78,0x69,0x74,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64, -0x75,0x6c,0x65,0x5f,0x68,0x6f,0x73,0x74,0x5f,0x63,0x61,0x6c,0x6c,0x62,0x61,0x63, -0x6b,0x5f,0x72,0x65,0x73,0x65,0x74,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64, -0x75,0x6c,0x65,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f,0x61,0x64,0x5f,0x75,0x6e,0x73, -0x63,0x68,0x65,0x64,0x75,0x6c,0x65,0x5f,0x62,0x65,0x67,0x69,0x6e,0x00,0x6d,0x61, -0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f, -0x61,0x64,0x5f,0x73,0x69,0x67,0x6e,0x61,0x6c,0x5f,0x73,0x65,0x74,0x00,0x6d,0x61, -0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x77,0x6f,0x72,0x6b,0x6c,0x6f, -0x61,0x64,0x5f,0x73,0x63,0x68,0x65,0x64,0x75,0x6c,0x65,0x5f,0x65,0x6e,0x64,0x00, -0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x77,0x6f,0x72,0x6b, -0x6c,0x6f,0x61,0x64,0x5f,0x77,0x61,0x69,0x74,0x00,0x5f,0x5f,0x73,0x74,0x61,0x63, -0x6b,0x00,0x5f,0x65,0x64,0x61,0x74,0x61,0x00,0x5f,0x65,0x6e,0x64,0x00,0x6d,0x61, -0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x64,0x6d,0x61,0x5f,0x77,0x61, -0x69,0x74,0x00,0x65,0x78,0x69,0x74,0x00,0x74,0x61,0x73,0x6b,0x5f,0x73,0x61,0x76, -0x65,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c,0x65,0x5f,0x64,0x6d, -0x61,0x5f,0x70,0x75,0x74,0x00,0x6d,0x61,0x72,0x73,0x5f,0x6d,0x6f,0x64,0x75,0x6c, -0x65,0x5f,0x68,0x6f,0x73,0x74,0x5f,0x63,0x61,0x6c,0x6c,0x62,0x61,0x63,0x6b,0x5f, -0x73,0x65,0x74,0x00,0x5f,0x5f,0x74,0x61,0x73,0x6b,0x5f,0x73,0x74,0x61,0x63,0x6b, -0x00, -}; From 9cfc2425cdb1445ed5ed910ed928e4144bb8d02c Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Mon, 13 Jul 2020 13:17:34 +0200 Subject: [PATCH 06/56] - refactor blitting sample (to catchup psl1ght changes) --- samples/graphics/blitting/Makefile | 13 +- samples/graphics/blitting/include/bitmap.h | 8 +- samples/graphics/blitting/include/psl1ght.xpm | 2 +- samples/graphics/blitting/include/rsxutil.h | 27 ++ .../blitting/source/{bitmap.c => bitmap.cpp} | 19 +- samples/graphics/blitting/source/main.c | 313 ------------------ samples/graphics/blitting/source/main.cpp | 180 ++++++++++ samples/graphics/blitting/source/rsxutil.cpp | 153 +++++++++ 8 files changed, 385 insertions(+), 330 deletions(-) create mode 100644 samples/graphics/blitting/include/rsxutil.h rename samples/graphics/blitting/source/{bitmap.c => bitmap.cpp} (85%) delete mode 100644 samples/graphics/blitting/source/main.c create mode 100644 samples/graphics/blitting/source/main.cpp create mode 100644 samples/graphics/blitting/source/rsxutil.cpp diff --git a/samples/graphics/blitting/Makefile b/samples/graphics/blitting/Makefile index 6eb1b48f..106e4b0b 100644 --- a/samples/graphics/blitting/Makefile +++ b/samples/graphics/blitting/Makefile @@ -18,14 +18,17 @@ include $(PSL1GHT)/ppu_rules TARGET := $(notdir $(CURDIR)) BUILD := build SOURCES := source -DATA := data INCLUDES := include +TITLE := Blitting Test - PSL1GHT +APPID := BLT00001 +CONTENTID := UP0001-$(APPID)_00-0000000000000000 + #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- -CFLAGS = -O2 -Wall -mcpu=cell $(MACHDEP) $(INCLUDE) +CFLAGS = -Wall -mcpu=cell $(MACHDEP) $(INCLUDE) CXXFLAGS = $(CFLAGS) LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map @@ -110,17 +113,19 @@ clean: run: ps3load $(OUTPUT).self +#--------------------------------------------------------------------------------- +pkg: $(BUILD) $(OUTPUT).pkg #--------------------------------------------------------------------------------- else -DEPENDS := $(OFILES:.o=.d) +DEPENDS := $(OFILES:.o=.d) #--------------------------------------------------------------------------------- # main targets #--------------------------------------------------------------------------------- $(OUTPUT).self: $(OUTPUT).elf -$(OUTPUT).elf: $(OFILES) +$(OUTPUT).elf: $(OFILES) #--------------------------------------------------------------------------------- # This rule links in binary data with the .bin extension diff --git a/samples/graphics/blitting/include/bitmap.h b/samples/graphics/blitting/include/bitmap.h index 08f1f6b3..3f857107 100644 --- a/samples/graphics/blitting/include/bitmap.h +++ b/samples/graphics/blitting/include/bitmap.h @@ -1,5 +1,7 @@ +#ifndef __BITMAP_H__ +#define __BITMAP_H__ - +#include typedef struct { u32 width; @@ -12,4 +14,6 @@ void bitmapInit(Bitmap *bitmap, u32 width, u32 height); void bitmapDestroy(Bitmap *bitmap); -void bitmapSetXpm(Bitmap *bitmap, char * xpm[]); +void bitmapSetXpm(Bitmap *bitmap, const char* xpm[]); + +#endif // __BITMAP_H__ \ No newline at end of file diff --git a/samples/graphics/blitting/include/psl1ght.xpm b/samples/graphics/blitting/include/psl1ght.xpm index aca4641f..b033e161 100644 --- a/samples/graphics/blitting/include/psl1ght.xpm +++ b/samples/graphics/blitting/include/psl1ght.xpm @@ -1,5 +1,5 @@ /* XPM */ -static char * psl1ght_xpm[] = { +static const char * psl1ght_xpm[] = { "224 40 16 1", " c #000000", ". c #210000", diff --git a/samples/graphics/blitting/include/rsxutil.h b/samples/graphics/blitting/include/rsxutil.h new file mode 100644 index 00000000..2b13486c --- /dev/null +++ b/samples/graphics/blitting/include/rsxutil.h @@ -0,0 +1,27 @@ +#ifndef __RSXUTIL_H__ +#define __RSXUTIL_H__ + +#include + +#include + +#define CB_SIZE 0x100000 +#define HOST_SIZE (32*1024*1024) + +extern gcmContextData *context; +extern u32 display_width; +extern u32 display_height; +extern u32 curr_fb; + +extern u32 color_pitch; +extern u32 color_offset[2]; + +extern u32 depth_pitch; +extern u32 depth_offset; + +void setRenderTarget(u32 index); +void init_screen(void *host_addr,u32 size); +void waitflip(); +void flip(); + +#endif diff --git a/samples/graphics/blitting/source/bitmap.c b/samples/graphics/blitting/source/bitmap.cpp similarity index 85% rename from samples/graphics/blitting/source/bitmap.c rename to samples/graphics/blitting/source/bitmap.cpp index eaefff37..d288935b 100644 --- a/samples/graphics/blitting/source/bitmap.c +++ b/samples/graphics/blitting/source/bitmap.cpp @@ -1,19 +1,18 @@ - -#include -#include -#include -#include - #include -#include #include +#include +#include +#include #include +#include + +#include #include "bitmap.h" void bitmapInit(Bitmap *bitmap, u32 width, u32 height) { s32 status; - bitmap->pixels = rsxMemalign(64, width * height * 4); + bitmap->pixels = (u32*)rsxMemalign(64, width * height * 4); status = rsxAddressToOffset(bitmap->pixels, &bitmap->offset); assert(status==0); bitmap->width = width; @@ -25,11 +24,11 @@ void bitmapDestroy(Bitmap *bitmap) { bitmap->pixels = NULL; } -void bitmapSetXpm(Bitmap *bitmap, char * xpm[]) { +void bitmapSetXpm(Bitmap *bitmap, const char* xpm[]) { u32 palette[256]; u32 width, height, ncolors, depth; char *p; - int ln; + u32 ln; u32 x, y, *pix; width = atoi(xpm[0]); diff --git a/samples/graphics/blitting/source/main.c b/samples/graphics/blitting/source/main.c deleted file mode 100644 index 34a1f934..00000000 --- a/samples/graphics/blitting/source/main.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Usage of the blit functions. - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "bitmap.h" -#include "psl1ght.xpm" - -typedef struct { - gcmContextData *context; - u32 curr_fb; - u32 framecnt; - u32 pitch; - u32 *buffer[2]; - u32 offset[2]; - u32 depth_pitch; - u16 *depth_buffer; - u32 depth_offset; - videoResolution res; -} displayData; - -void setRenderTarget(displayData *vdat) -{ - gcmSurface sf; - - sf.colorFormat = GCM_TF_COLOR_X8R8G8B8; - sf.colorTarget = GCM_TF_TARGET_0; - sf.colorLocation[0] = GCM_LOCATION_RSX; - sf.colorOffset[0] = vdat->offset[vdat->curr_fb]; - sf.colorPitch[0] = vdat->pitch; - - sf.colorLocation[1] = GCM_LOCATION_RSX; - sf.colorLocation[2] = GCM_LOCATION_RSX; - sf.colorLocation[3] = GCM_LOCATION_RSX; - sf.colorOffset[1] = 0; - sf.colorOffset[2] = 0; - sf.colorOffset[3] = 0; - sf.colorPitch[1] = 64; - sf.colorPitch[2] = 64; - sf.colorPitch[3] = 64; - - sf.depthFormat = GCM_TF_ZETA_Z16; - sf.depthLocation = GCM_LOCATION_RSX; - sf.depthOffset = vdat->depth_offset; - sf.depthPitch = vdat->depth_pitch; - - sf.type = GCM_TF_TYPE_LINEAR; - sf.antiAlias = GCM_TF_CENTER_1; - - sf.width = vdat->res.width; - sf.height = vdat->res.height; - sf.x = 0; - sf.y = 0; - - rsxSetSurface(vdat->context, &sf); -} - -static void eventHandle(u64 status, u64 param, void * userdata) { - (void)param; - (void)userdata; - if(status == SYSUTIL_EXIT_GAME){ - printf("Quit game requested\n"); - exit(0); - }else if(status == SYSUTIL_MENU_OPEN){ - //xmb opened, should prob pause game or something :P - printf("XMB opened\n"); - }else if(status == SYSUTIL_MENU_CLOSE){ - //xmb closed, and then resume - printf("XMB closed\n"); - }else if(status == SYSUTIL_DRAW_BEGIN){ - }else if(status == SYSUTIL_DRAW_END){ - }else{ - printf("Unhandled event: %08llX\n", (unsigned long long int)status); - } -} - -void appCleanup(){ - sysUtilUnregisterCallback(SYSUTIL_EVENT_SLOT0); - printf("Exiting for real.\n"); -} - -/* Block the PPU thread untill the previous flip operation has finished. */ -void waitFlip() { - while(gcmGetFlipStatus() != 0) - usleep(200); - gcmResetFlipStatus(); -} - -/* Enqueue a flip command in RSX command buffer. - Setup next screen to be drawn to. */ -void flip(displayData *vdat) { - s32 status = gcmSetFlip(vdat->context, vdat->curr_fb); - assert(status == 0); - rsxFlushBuffer(vdat->context); - gcmSetWaitFlip(vdat->context); - vdat->curr_fb = !vdat->curr_fb; - ++vdat->framecnt; - setRenderTarget(vdat); -} - -/* Initilize everything. */ -void init_screen(displayData *vdat) { - int i; - - /* Allocate a 1Mb buffer, alligned to a 1Mb boundary to be our shared IO memory with the RSX. */ - void *host_addr = memalign(1024*1024, 1024*1024); - assert(host_addr != NULL); - - /* Initilise libRSX, which sets up the command buffer and shared IO memory */ - vdat->context = rsxInit(0x10000, 1024*1024, host_addr); - assert(vdat->context != NULL); - - videoState state; - s32 status = videoGetState(0, 0, &state); // Get the state of the display - assert(status == 0); - assert(state.state == 0); // Make sure display is enabled - - /* Get the current resolution */ - status = videoGetResolution(state.displayMode.resolution, &vdat->res); - assert(status == 0); - - /* Configure the buffer format to xRGB */ - videoConfiguration vconfig; - memset(&vconfig, 0, sizeof(videoConfiguration)); - vconfig.resolution = state.displayMode.resolution; - vconfig.format = VIDEO_BUFFER_FORMAT_XRGB; - vconfig.pitch = vdat->res.width * 4; - vconfig.aspect=state.displayMode.aspect; - - status = videoConfigure(0, &vconfig, NULL, 0); - assert(status == 0); - status = videoGetState(0, 0, &state); - assert(status == 0); - - gcmSetFlipMode(GCM_FLIP_VSYNC); /* Wait for VSYNC to flip */ - - /* Allocate and setup two buffers for the RSX to draw to the screen (double buffering) */ - vdat->pitch = vdat->res.width*sizeof(u32); - for (i=0; i<2; ++i) { - vdat->buffer[i] = (u32*)rsxMemalign(64, vdat->pitch * vdat->res.height); - assert(vdat->buffer[i] != NULL); - status = rsxAddressToOffset(vdat->buffer[i], &vdat->offset[i]); - assert(status==0); - status = gcmSetDisplayBuffer(i, vdat->offset[i], vdat->pitch, vdat->res.width, vdat->res.height); - assert(status==0); - } - - /* Allocate the depth buffer */ - vdat->depth_pitch = vdat->res.width * sizeof(u16); - vdat->depth_buffer = (u16*)rsxMemalign(64, vdat->depth_pitch * vdat->res.height); - assert(vdat->depth_buffer != NULL); - status = rsxAddressToOffset(vdat->depth_buffer, &vdat->depth_offset); - assert(status==0); - - gcmResetFlipStatus(); - vdat->curr_fb = 0; - vdat->framecnt = 0; - flip(vdat); -} - -void blit_simple(displayData *vdat, Bitmap *bitmap, - u32 dstX, u32 dstY, u32 srcX, u32 srcY, u32 w, u32 h) -{ - rsxSetTransferImage(vdat->context, GCM_TRANSFER_LOCAL_TO_LOCAL, - vdat->offset[vdat->curr_fb], vdat->pitch, dstX-w/2, dstY-h/2, - bitmap->offset, bitmap->width*4, rsxGetFixedUint16((float)srcX), - rsxGetFixedUint16((float)srcY), w, h, 4); -} - -void blit_data(displayData *vdat, Bitmap *bitmap, - u32 dstX, u32 dstY, u32 srcX, u32 srcY, u32 w, u32 h) -{ - rsxSetTransferData(vdat->context, GCM_TRANSFER_LOCAL_TO_LOCAL, - vdat->offset[vdat->curr_fb], vdat->pitch, bitmap->offset, bitmap->width*4, - w*4, h); -} - -void blit_scale(displayData *vdat, Bitmap *bitmap, - u32 dstX, u32 dstY, u32 srcX, u32 srcY, u32 w, u32 h, float zoom) -{ - gcmTransferScale scale; - gcmTransferSurface surface; - - scale.conversion = GCM_TRANSFER_CONVERSION_TRUNCATE; - scale.format = GCM_TRANSFER_SCALE_FORMAT_A8R8G8B8; - scale.origin = GCM_TRANSFER_ORIGIN_CORNER; - scale.operation = GCM_TRANSFER_OPERATION_SRCCOPY_AND; - scale.interp = GCM_TRANSFER_INTERPOLATOR_NEAREST; - scale.clipX = 0; - scale.clipY = 0; - scale.clipW = vdat->res.width; - scale.clipH = vdat->res.height; - scale.outX = dstX - w*zoom*.5f; - scale.outY = dstY - h*zoom*.5f; - scale.outW = w * zoom; - scale.outH = h * zoom; - scale.ratioX = rsxGetFixedSint32(1.f / zoom); - scale.ratioY = rsxGetFixedSint32(1.f / zoom); - scale.inX = rsxGetFixedUint16(srcX); - scale.inY = rsxGetFixedUint16(srcY); - scale.inW = bitmap->width; - scale.inH = bitmap->height; - scale.offset = bitmap->offset; - scale.pitch = sizeof(u32) * bitmap->width; - - surface.format = GCM_TRANSFER_SURFACE_FORMAT_A8R8G8B8; - surface.pitch = vdat->pitch; - surface.offset = vdat->offset[vdat->curr_fb]; - - rsxSetTransferScaleMode(vdat->context, GCM_TRANSFER_LOCAL_TO_LOCAL, - GCM_TRANSFER_SURFACE); - - rsxSetTransferScaleSurface(vdat->context, &scale, &surface); -} - -int main(int argc, const char* argv[]) -{ - padInfo padinfo; - padData paddata; - Bitmap bitmap; - - displayData vdat; - int quit = 0; - int i; - int k = 0; - - atexit(appCleanup); - sysUtilRegisterCallback(SYSUTIL_EVENT_SLOT0, eventHandle, NULL); - - init_screen(&vdat); - printf("screen res: %dx%d buffers: %p %p\n", - vdat.res.width, vdat.res.height, vdat.buffer[0], vdat.buffer[1]); - ioPadInit(7); - - bitmapSetXpm(&bitmap, psl1ght_xpm); - - while (!quit) { - /* Check the pads. */ - ioPadGetInfo(&padinfo); - for (i=0; i=0; --i) { - int x = vdat.res.width * (0.1f + 0.8f * (0.5f+0.5f*sinf((vdat.framecnt - 10*i) * .01f))); - int y = 150 + 400 * (0.5f+0.5f*sinf((vdat.framecnt - 10*i) * .02f)); - blit_simple(&vdat, &bitmap, x, y, i*32, 0, 32, bitmap.height); - } - - /* Animate all letters, with zoom */ - for (i=6; i>=0; --i) { - int x = vdat.res.width * (0.1f + 0.8f * (0.5f+0.5f*sinf(M_PI + (vdat.framecnt - 10*i) * .01f))); - int y = 150 + 400 * (0.5f+0.5f*sinf((vdat.framecnt - 10*i) * .02f)); - blit_scale(&vdat, &bitmap, x, y, i*32, 0, 32, bitmap.height, 2.f); - } - - /* Flip buffer onto screen */ - flip(&vdat); - - sysUtilCheckCallback(); - } - waitFlip(); - bitmapDestroy(&bitmap); - - return 0; -} diff --git a/samples/graphics/blitting/source/main.cpp b/samples/graphics/blitting/source/main.cpp new file mode 100644 index 00000000..49544f04 --- /dev/null +++ b/samples/graphics/blitting/source/main.cpp @@ -0,0 +1,180 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "rsxutil.h" +#include "bitmap.h" +#include "psl1ght.xpm" + +u32 running = 0; +u32 frame_cnt = 0; + +SYS_PROCESS_PARAM(1001, 0x100000); + +extern "C" { +static void program_exit_callback() +{ + sysUtilUnregisterCallback(SYSUTIL_EVENT_SLOT0); + gcmSetWaitFlip(context); + rsxFinish(context,1); +} + +static void sysutil_exit_callback(u64 status,u64 param,void *usrdata) +{ + switch(status) { + case SYSUTIL_EXIT_GAME: + running = 0; + break; + case SYSUTIL_DRAW_BEGIN: + case SYSUTIL_DRAW_END: + break; + default: + break; + } +} +} + +void blit_simple(Bitmap *bitmap, u32 dstX, u32 dstY, u32 srcX, u32 srcY, u32 w, u32 h) +{ + rsxSetTransferImage(context, GCM_TRANSFER_LOCAL_TO_LOCAL, + color_offset[curr_fb], color_pitch, dstX-w/2, dstY-h/2, + bitmap->offset, bitmap->width*4, rsxGetFixedUint16((float)srcX), + rsxGetFixedUint16((float)srcY), w, h, 4); +} + +void blit_data(Bitmap *bitmap, u32 dstX, u32 dstY, u32 srcX, u32 srcY, u32 w, u32 h) +{ + rsxSetTransferData(context, GCM_TRANSFER_LOCAL_TO_LOCAL, + color_offset[curr_fb], color_pitch, bitmap->offset, bitmap->width*4, + w*4, h); +} + +void blit_scale(Bitmap *bitmap, u32 dstX, u32 dstY, u32 srcX, u32 srcY, u32 w, u32 h, float zoom) +{ + gcmTransferScale scale; + gcmTransferSurface surface; + + scale.conversion = GCM_TRANSFER_CONVERSION_TRUNCATE; + scale.format = GCM_TRANSFER_SCALE_FORMAT_A8R8G8B8; + scale.origin = GCM_TRANSFER_ORIGIN_CORNER; + scale.operation = GCM_TRANSFER_OPERATION_SRCCOPY_AND; + scale.interp = GCM_TRANSFER_INTERPOLATOR_NEAREST; + scale.clipX = 0; + scale.clipY = 0; + scale.clipW = display_width; + scale.clipH = display_height; + scale.outX = dstX - w*zoom*.5f; + scale.outY = dstY - h*zoom*.5f; + scale.outW = w * zoom; + scale.outH = h * zoom; + scale.ratioX = rsxGetFixedSint32(1.f / zoom); + scale.ratioY = rsxGetFixedSint32(1.f / zoom); + scale.inX = rsxGetFixedUint16(srcX); + scale.inY = rsxGetFixedUint16(srcY); + scale.inW = bitmap->width; + scale.inH = bitmap->height; + scale.offset = bitmap->offset; + scale.pitch = sizeof(u32) * bitmap->width; + + surface.format = GCM_TRANSFER_SURFACE_FORMAT_A8R8G8B8; + surface.pitch = color_pitch; + surface.offset = color_offset[curr_fb]; + + rsxSetTransferScaleMode(context, GCM_TRANSFER_LOCAL_TO_LOCAL, GCM_TRANSFER_SURFACE); + rsxSetTransferScaleSurface(context, &scale, &surface); +} + +int main(int argc,const char *argv[]) +{ + int k = 0; + padInfo padinfo; + padData paddata; + Bitmap bitmap; + void *host_addr = memalign(1024*1024,HOST_SIZE); + + printf("blitting started...\n"); + + init_screen(host_addr,HOST_SIZE); + ioPadInit(7); + + atexit(program_exit_callback); + sysUtilRegisterCallback(0,sysutil_exit_callback,NULL); + + bitmapSetXpm(&bitmap, psl1ght_xpm); + setRenderTarget(curr_fb); + + running = 1; + while(running) { + sysUtilCheckCallback(); + + ioPadGetInfo(&padinfo); + for(int i=0; i < MAX_PADS; i++){ + if(padinfo.status[i]){ + ioPadGetData(i, &paddata); + + if(paddata.BTN_CROSS) + goto done; + } + + } + + /* Display some stuff on the screen */ + rsxSetClearColor(context, 0x200030); + rsxSetClearDepthStencil(context, 0xffff); + rsxClearSurface(context,GCM_CLEAR_R | + GCM_CLEAR_G | + GCM_CLEAR_B | + GCM_CLEAR_A | + GCM_CLEAR_S | + GCM_CLEAR_Z); + + /* Enable blending (for rsxSetTransferScaleSurface) */ + rsxSetBlendFunc(context, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA); + rsxSetBlendEquation(context, GCM_FUNC_ADD, GCM_FUNC_ADD); + rsxSetBlendEnable(context, GCM_TRUE); + + /* Display the whole PSL1GHT image */ + blit_simple(&bitmap, display_width/4, 100, 0, 0, bitmap.width, bitmap.height); + + /* Distort the PSL1GHT image by displaying lines with different X coords */ + for (u32 i=0; i=0; --i) { + int x = display_width * (0.1f + 0.8f * (0.5f+0.5f*sinf((frame_cnt - 10*i) * .01f))); + int y = 150 + 400 * (0.5f+0.5f*sinf((frame_cnt - 10*i) * .02f)); + blit_simple(&bitmap, x, y, i*32, 0, 32, bitmap.height); + } + + /* Animate all letters, with zoom */ + for (int i=6; i>=0; --i) { + int x = display_width * (0.1f + 0.8f * (0.5f+0.5f*sinf(M_PI + (frame_cnt - 10*i) * .01f))); + int y = 150 + 400 * (0.5f+0.5f*sinf((frame_cnt - 10*i) * .02f)); + blit_scale(&bitmap, x, y, i*32, 0, 32, bitmap.height, 2.f); + } + + flip(); + frame_cnt++; + } + +done: + printf("blitting finished...\n"); + + bitmapDestroy(&bitmap); + return 0; +} diff --git a/samples/graphics/blitting/source/rsxutil.cpp b/samples/graphics/blitting/source/rsxutil.cpp new file mode 100644 index 00000000..e61327a7 --- /dev/null +++ b/samples/graphics/blitting/source/rsxutil.cpp @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include "rsxutil.h" + +#define GCM_LABEL_INDEX 255 + +videoResolution res; +gcmContextData *context = NULL; + +u32 curr_fb = 0; +u32 first_fb = 1; + +u32 display_width; +u32 display_height; + +u32 depth_pitch; +u32 depth_offset; +u32 *depth_buffer; + +u32 color_pitch; +u32 color_offset[2]; +u32 *color_buffer[2]; + +static u32 sLabelVal = 1; + +static void waitFinish() +{ + rsxSetWriteBackendLabel(context,GCM_LABEL_INDEX,sLabelVal); + + rsxFlushBuffer(context); + + while(*(vu32*)gcmGetLabelAddress(GCM_LABEL_INDEX)!=sLabelVal) + usleep(30); + + ++sLabelVal; +} + +static void waitRSXIdle() +{ + rsxSetWriteBackendLabel(context,GCM_LABEL_INDEX,sLabelVal); + rsxSetWaitLabel(context,GCM_LABEL_INDEX,sLabelVal); + + ++sLabelVal; + + waitFinish(); +} + +void setRenderTarget(u32 index) +{ + gcmSurface sf; + + sf.colorFormat = GCM_SURFACE_X8R8G8B8; + sf.colorTarget = GCM_SURFACE_TARGET_0; + sf.colorLocation[0] = GCM_LOCATION_RSX; + sf.colorOffset[0] = color_offset[index]; + sf.colorPitch[0] = color_pitch; + + sf.colorLocation[1] = GCM_LOCATION_RSX; + sf.colorLocation[2] = GCM_LOCATION_RSX; + sf.colorLocation[3] = GCM_LOCATION_RSX; + sf.colorOffset[1] = 0; + sf.colorOffset[2] = 0; + sf.colorOffset[3] = 0; + sf.colorPitch[1] = 64; + sf.colorPitch[2] = 64; + sf.colorPitch[3] = 64; + + sf.depthFormat = GCM_SURFACE_ZETA_Z16; + sf.depthLocation = GCM_LOCATION_RSX; + sf.depthOffset = depth_offset; + sf.depthPitch = depth_pitch; + + sf.type = GCM_SURFACE_TYPE_LINEAR; + sf.antiAlias = GCM_SURFACE_CENTER_1; + + sf.width = display_width; + sf.height = display_height; + sf.x = 0; + sf.y = 0; + + rsxSetSurface(context,&sf); +} + +void init_screen(void *host_addr,u32 size) +{ + rsxInit(&context,CB_SIZE,size,host_addr); + + videoState state; + videoGetState(0,0,&state); + + videoGetResolution(state.displayMode.resolution,&res); + + videoConfiguration vconfig; + memset(&vconfig,0,sizeof(videoConfiguration)); + + vconfig.resolution = state.displayMode.resolution; + vconfig.format = VIDEO_BUFFER_FORMAT_XRGB; + vconfig.pitch = res.width*sizeof(u32); + + waitRSXIdle(); + + videoConfigure(0,&vconfig,NULL,0); + videoGetState(0,0,&state); + + gcmSetFlipMode(GCM_FLIP_VSYNC); + + display_width = res.width; + display_height = res.height; + + color_pitch = display_width*sizeof(u32); + color_buffer[0] = (u32*)rsxMemalign(64,(display_height*color_pitch)); + color_buffer[1] = (u32*)rsxMemalign(64,(display_height*color_pitch)); + + rsxAddressToOffset(color_buffer[0],&color_offset[0]); + rsxAddressToOffset(color_buffer[1],&color_offset[1]); + + gcmSetDisplayBuffer(0,color_offset[0],color_pitch,display_width,display_height); + gcmSetDisplayBuffer(1,color_offset[1],color_pitch,display_width,display_height); + + depth_pitch = display_width*sizeof(u32); + depth_buffer = (u32*)rsxMemalign(64,(display_height*depth_pitch)*2); + rsxAddressToOffset(depth_buffer,&depth_offset); +} + +void waitflip() +{ + while(gcmGetFlipStatus()!=0) + usleep(200); + gcmResetFlipStatus(); +} + +void flip() +{ + if(!first_fb) waitflip(); + else gcmResetFlipStatus(); + + gcmSetFlip(context,curr_fb); + rsxFlushBuffer(context); + + gcmSetWaitFlip(context); + + curr_fb ^= 1; + setRenderTarget(curr_fb); + + first_fb = 0; +} From 48a1ed7fea6af7a3372d7340a37e66a919512887 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Mon, 13 Jul 2020 13:27:37 +0200 Subject: [PATCH 07/56] - fix API changes --- samples/graphics/videoTest/source/rsxutil.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/graphics/videoTest/source/rsxutil.c b/samples/graphics/videoTest/source/rsxutil.c index bce58746..ec417867 100644 --- a/samples/graphics/videoTest/source/rsxutil.c +++ b/samples/graphics/videoTest/source/rsxutil.c @@ -108,7 +108,7 @@ initScreen (void *host_addr, u32 size) videoResolution res; /* Screen Resolution */ /* Initilise Reality, which sets up the command buffer and shared IO memory */ - context = rsxInit (CB_SIZE, size, host_addr); + rsxInit (&context, CB_SIZE, size, host_addr); if (context == NULL) goto error; @@ -191,8 +191,8 @@ setRenderTarget(gcmContextData *context, rsxBuffer *buffer) { gcmSurface sf; - sf.colorFormat = GCM_TF_COLOR_X8R8G8B8; - sf.colorTarget = GCM_TF_TARGET_0; + sf.colorFormat = GCM_SURFACE_X8R8G8B8; + sf.colorTarget = GCM_SURFACE_TARGET_0; sf.colorLocation[0] = GCM_LOCATION_RSX; sf.colorOffset[0] = buffer->offset; sf.colorPitch[0] = depth_pitch; @@ -207,13 +207,13 @@ setRenderTarget(gcmContextData *context, rsxBuffer *buffer) sf.colorPitch[2] = 64; sf.colorPitch[3] = 64; - sf.depthFormat = GCM_TF_ZETA_Z16; + sf.depthFormat = GCM_SURFACE_ZETA_Z16; sf.depthLocation = GCM_LOCATION_RSX; sf.depthOffset = depth_offset; sf.depthPitch = depth_pitch; - sf.type = GCM_TF_TYPE_LINEAR; - sf.antiAlias = GCM_TF_CENTER_1; + sf.type = GCM_TEXTURE_LINEAR; + sf.antiAlias = GCM_SURFACE_CENTER_1; sf.width = buffer->width; sf.height = buffer->height; From 98a4d1d2a6ed8cc0fc38386f7a6b40074975a78e Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Mon, 13 Jul 2020 13:36:55 +0200 Subject: [PATCH 08/56] - fix API changes --- samples/network/ps3load/Makefile | 3 +++ samples/network/ps3load/source/main.c | 4 +--- samples/network/ps3load/source/rsxutil.c | 12 ++++++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/samples/network/ps3load/Makefile b/samples/network/ps3load/Makefile index 5690d720..b40b9189 100644 --- a/samples/network/ps3load/Makefile +++ b/samples/network/ps3load/Makefile @@ -114,6 +114,9 @@ clean: run: ps3load $(OUTPUT).self +#--------------------------------------------------------------------------------- +pkg: $(BUILD) $(OUTPUT).pkg + #--------------------------------------------------------------------------------- else diff --git a/samples/network/ps3load/source/main.c b/samples/network/ps3load/source/main.c index 7ed6c9af..ad0bfa55 100644 --- a/samples/network/ps3load/source/main.c +++ b/samples/network/ps3load/source/main.c @@ -79,12 +79,10 @@ static void empty_directory(const char *path) static void control_thread(void *arg) { - u32 btns; - printf("PS3Load Control thread\n"); while(running) { - btns = pad_read(); + (void)pad_read(); flip(); } diff --git a/samples/network/ps3load/source/rsxutil.c b/samples/network/ps3load/source/rsxutil.c index b7803e6f..df37acfd 100644 --- a/samples/network/ps3load/source/rsxutil.c +++ b/samples/network/ps3load/source/rsxutil.c @@ -53,8 +53,8 @@ void set_render_target(u32 index) { gcmSurface sf; - sf.colorFormat = GCM_TF_COLOR_X8R8G8B8; - sf.colorTarget = GCM_TF_TARGET_0; + sf.colorFormat = GCM_SURFACE_X8R8G8B8; + sf.colorTarget = GCM_SURFACE_TARGET_0; sf.colorLocation[0] = GCM_LOCATION_RSX; sf.colorOffset[0] = color_offset[index]; sf.colorPitch[0] = color_pitch; @@ -69,13 +69,13 @@ void set_render_target(u32 index) sf.colorPitch[2] = 64; sf.colorPitch[3] = 64; - sf.depthFormat = GCM_TF_ZETA_Z16; + sf.depthFormat = GCM_SURFACE_ZETA_Z16; sf.depthLocation = GCM_LOCATION_RSX; sf.depthOffset = depth_offset; sf.depthPitch = depth_pitch; - sf.type = GCM_TF_TYPE_LINEAR; - sf.antiAlias = GCM_TF_CENTER_1; + sf.type = GCM_TEXTURE_LINEAR; + sf.antiAlias = GCM_SURFACE_CENTER_1; sf.width = display_width; sf.height = display_height; @@ -87,7 +87,7 @@ void set_render_target(u32 index) void init_screen(void *host_addr,u32 size) { - context = rsxInit(CB_SIZE,size,host_addr); + rsxInit(&context, CB_SIZE,size,host_addr); videoState state; videoGetState(0,0,&state); From c155a3375781f6814a171f785590fb5df27f3af4 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Mon, 13 Jul 2020 14:55:01 +0200 Subject: [PATCH 09/56] - refactor and fix API changes --- samples/sys/msgdialog/include/rsxutil.h | 13 ++----- .../sys/msgdialog/source/{main.c => main.cpp} | 38 ++++++++++--------- .../source/{rsxutil.c => rsxutil.cpp} | 28 ++++++++------ 3 files changed, 39 insertions(+), 40 deletions(-) rename samples/sys/msgdialog/source/{main.c => main.cpp} (72%) rename samples/sys/msgdialog/source/{rsxutil.c => rsxutil.cpp} (85%) diff --git a/samples/sys/msgdialog/include/rsxutil.h b/samples/sys/msgdialog/include/rsxutil.h index cbf41557..5bc22475 100644 --- a/samples/sys/msgdialog/include/rsxutil.h +++ b/samples/sys/msgdialog/include/rsxutil.h @@ -1,28 +1,21 @@ #ifndef __RSXUTIL_H__ #define __RSXUTIL_H__ +#include + #include -#include #define CB_SIZE 0x100000 #define HOST_SIZE (32*1024*1024) -#ifdef __cplusplus -extern "C" { -#endif - extern gcmContextData *context; extern u32 display_width; extern u32 display_height; extern u32 curr_fb; -void set_render_target(u32 index); +void setRenderTarget(u32 index); void init_screen(void *host_addr,u32 size); void waitflip(); void flip(); -#ifdef __cplusplus - } -#endif - #endif diff --git a/samples/sys/msgdialog/source/main.c b/samples/sys/msgdialog/source/main.cpp similarity index 72% rename from samples/sys/msgdialog/source/main.c rename to samples/sys/msgdialog/source/main.cpp index 0722da5e..9acffda6 100644 --- a/samples/sys/msgdialog/source/main.c +++ b/samples/sys/msgdialog/source/main.cpp @@ -13,12 +13,7 @@ static vs32 dialog_action = 0; -static void do_flip() -{ - sysUtilCheckCallback(); - flip(); -} - +extern "C" { static void dialog_handler(msgButton button,void *usrData) { switch(button) { @@ -37,13 +32,13 @@ static void dialog_handler(msgButton button,void *usrData) } } -void program_exit_callback() +static void program_exit_callback() { gcmSetWaitFlip(context); rsxFinish(context,1); } -void sysutil_exit_callback(u64 status,u64 param,void *usrdata) +static void sysutil_exit_callback(u64 status,u64 param,void *usrdata) { switch(status) { case SYSUTIL_EXIT_GAME: @@ -55,20 +50,26 @@ void sysutil_exit_callback(u64 status,u64 param,void *usrdata) break; } } +} + +static void do_flip() +{ + sysUtilCheckCallback(); + flip(); +} int main(int argc,char *argv[]) { - s32 ret; - msgType dialogType; - void *host_addr = memalign(1024*1024,HOST_SIZE); + msgType dialogType; + void *host_addr = memalign(1024*1024,HOST_SIZE); - printf("msgdialog test...\n"); + printf("msgdialog test...\n"); init_screen(host_addr,HOST_SIZE); ioPadInit(7); - ret = atexit(program_exit_callback); - ret = sysUtilRegisterCallback(SYSUTIL_EVENT_SLOT0,sysutil_exit_callback,NULL); + atexit(program_exit_callback); + sysUtilRegisterCallback(SYSUTIL_EVENT_SLOT0,sysutil_exit_callback,NULL); msgDialogOpenErrorCode(0xBADC0FFE,dialog_handler,NULL,NULL); msgDialogClose(3000.0f); @@ -78,9 +79,9 @@ int main(int argc,char *argv[]) do_flip(); msgDialogAbort(); - + // yes/no dialog type - dialogType = (MSG_DIALOG_NORMAL | MSG_DIALOG_BTN_TYPE_YESNO | MSG_DIALOG_DISABLE_CANCEL_ON | MSG_DIALOG_DEFAULT_CURSOR_NO); + dialogType = (msgType)(MSG_DIALOG_NORMAL | MSG_DIALOG_BTN_TYPE_YESNO | MSG_DIALOG_DISABLE_CANCEL_ON | MSG_DIALOG_DEFAULT_CURSOR_NO); msgDialogOpen2(dialogType,"Do you want to continue?",dialog_handler,NULL,NULL); dialog_action = 0; @@ -90,7 +91,7 @@ int main(int argc,char *argv[]) msgDialogAbort(); // OK dialog type - dialogType = (MSG_DIALOG_NORMAL | MSG_DIALOG_BTN_OK); + dialogType = (msgType)(MSG_DIALOG_NORMAL | MSG_DIALOG_BTN_OK); if(dialog_action==1) msgDialogOpen2(dialogType,"Your answer was YES",dialog_handler,NULL,NULL); else @@ -102,5 +103,6 @@ int main(int argc,char *argv[]) msgDialogAbort(); - return 0; + printf("msgdialog test done...\n"); + return 0; } diff --git a/samples/sys/msgdialog/source/rsxutil.c b/samples/sys/msgdialog/source/rsxutil.cpp similarity index 85% rename from samples/sys/msgdialog/source/rsxutil.c rename to samples/sys/msgdialog/source/rsxutil.cpp index b7803e6f..01e6a667 100644 --- a/samples/sys/msgdialog/source/rsxutil.c +++ b/samples/sys/msgdialog/source/rsxutil.cpp @@ -3,6 +3,10 @@ #include #include #include +#include + +#include +#include #include "rsxutil.h" @@ -27,7 +31,7 @@ u32 *color_buffer[2]; static u32 sLabelVal = 1; -static void wait_finish() +static void waitFinish() { rsxSetWriteBackendLabel(context,GCM_LABEL_INDEX,sLabelVal); @@ -39,22 +43,22 @@ static void wait_finish() ++sLabelVal; } -static void wait_rsx_idle() +static void waitRSXIdle() { rsxSetWriteBackendLabel(context,GCM_LABEL_INDEX,sLabelVal); rsxSetWaitLabel(context,GCM_LABEL_INDEX,sLabelVal); ++sLabelVal; - wait_finish(); + waitFinish(); } -void set_render_target(u32 index) +void setRenderTarget(u32 index) { gcmSurface sf; - sf.colorFormat = GCM_TF_COLOR_X8R8G8B8; - sf.colorTarget = GCM_TF_TARGET_0; + sf.colorFormat = GCM_SURFACE_X8R8G8B8; + sf.colorTarget = GCM_SURFACE_TARGET_0; sf.colorLocation[0] = GCM_LOCATION_RSX; sf.colorOffset[0] = color_offset[index]; sf.colorPitch[0] = color_pitch; @@ -69,13 +73,13 @@ void set_render_target(u32 index) sf.colorPitch[2] = 64; sf.colorPitch[3] = 64; - sf.depthFormat = GCM_TF_ZETA_Z16; + sf.depthFormat = GCM_SURFACE_ZETA_Z16; sf.depthLocation = GCM_LOCATION_RSX; sf.depthOffset = depth_offset; sf.depthPitch = depth_pitch; - sf.type = GCM_TF_TYPE_LINEAR; - sf.antiAlias = GCM_TF_CENTER_1; + sf.type = GCM_SURFACE_TYPE_LINEAR; + sf.antiAlias = GCM_SURFACE_CENTER_1; sf.width = display_width; sf.height = display_height; @@ -87,7 +91,7 @@ void set_render_target(u32 index) void init_screen(void *host_addr,u32 size) { - context = rsxInit(CB_SIZE,size,host_addr); + rsxInit(&context,CB_SIZE,size,host_addr); videoState state; videoGetState(0,0,&state); @@ -101,7 +105,7 @@ void init_screen(void *host_addr,u32 size) vconfig.format = VIDEO_BUFFER_FORMAT_XRGB; vconfig.pitch = res.width*sizeof(u32); - wait_rsx_idle(); + waitRSXIdle(); videoConfigure(0,&vconfig,NULL,0); videoGetState(0,0,&state); @@ -144,7 +148,7 @@ void flip() gcmSetWaitFlip(context); curr_fb ^= 1; - set_render_target(curr_fb); + setRenderTarget(curr_fb); first_fb = 0; } From 665c6918b7381e9d1556bd7215ff35dca406c9ec Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Mon, 13 Jul 2020 15:13:16 +0200 Subject: [PATCH 10/56] - refactor and fix API changes --- samples/sys/osk/include/rsxutil.h | 13 +- samples/sys/osk/source/{main.c => main.cpp} | 113 +++++++++--------- .../sys/osk/source/{rsxutil.c => rsxutil.cpp} | 28 +++-- 3 files changed, 73 insertions(+), 81 deletions(-) rename samples/sys/osk/source/{main.c => main.cpp} (94%) rename samples/sys/osk/source/{rsxutil.c => rsxutil.cpp} (85%) diff --git a/samples/sys/osk/include/rsxutil.h b/samples/sys/osk/include/rsxutil.h index cbf41557..5bc22475 100644 --- a/samples/sys/osk/include/rsxutil.h +++ b/samples/sys/osk/include/rsxutil.h @@ -1,28 +1,21 @@ #ifndef __RSXUTIL_H__ #define __RSXUTIL_H__ +#include + #include -#include #define CB_SIZE 0x100000 #define HOST_SIZE (32*1024*1024) -#ifdef __cplusplus -extern "C" { -#endif - extern gcmContextData *context; extern u32 display_width; extern u32 display_height; extern u32 curr_fb; -void set_render_target(u32 index); +void setRenderTarget(u32 index); void init_screen(void *host_addr,u32 size); void waitflip(); void flip(); -#ifdef __cplusplus - } -#endif - #endif diff --git a/samples/sys/osk/source/main.c b/samples/sys/osk/source/main.cpp similarity index 94% rename from samples/sys/osk/source/main.c rename to samples/sys/osk/source/main.cpp index 7108bd83..1b65bee6 100644 --- a/samples/sys/osk/source/main.c +++ b/samples/sys/osk/source/main.cpp @@ -13,6 +13,8 @@ #include "rsxutil.h" +#define TEXT_BUFFER_LENGTH 256 + static vs32 dialog_action = 0; uint8_t isRunningOSK = 0; @@ -20,6 +22,55 @@ oskInputFieldInfo inputFieldInfo; oskParam parameters; oskCallbackReturnParam outputParam; +extern "C" { +static void program_exit_callback() +{ + gcmSetWaitFlip(context); + rsxFinish(context, 1); +} + +static void sysutil_exit_callback(u64 status, u64 param, void *usrdata) +{ + switch(status) { + case SYSUTIL_EXIT_GAME: + break; + case SYSUTIL_DRAW_BEGIN: + case SYSUTIL_DRAW_END: + break; + case SYSUTIL_OSK_LOADED: + printf("OSK loaded\n"); + break; + case SYSUTIL_OSK_INPUT_CANCELED: + printf("OSK input canceled\n"); + oskAbort(); + // fall-through + case SYSUTIL_OSK_DONE: + if (status == SYSUTIL_OSK_DONE) + { + printf("OSK done\n"); + } + oskUnloadAsync(&outputParam); + + if (outputParam.res == OSK_OK) + { + printf("OSK result OK\n"); + } + else + { + printf("OKS result: %d\n", outputParam.res); + } + + break; + case SYSUTIL_OSK_UNLOADED: + printf("OSK unloaded\n"); + isRunningOSK = 0; + break; + default: + break; + } +} +} + static void utf16_to_utf8(const uint16_t *src, uint8_t *dst) { int i; @@ -84,63 +135,15 @@ static void do_flip() flip(); } -void program_exit_callback() -{ - gcmSetWaitFlip(context); - rsxFinish(context, 1); -} - -void sysutil_exit_callback(u64 status, u64 param, void *usrdata) -{ - switch(status) { - case SYSUTIL_EXIT_GAME: - break; - case SYSUTIL_DRAW_BEGIN: - case SYSUTIL_DRAW_END: - break; - case SYSUTIL_OSK_LOADED: - printf("OSK loaded\n"); - break; - case SYSUTIL_OSK_INPUT_CANCELED: - printf("OSK input canceled\n"); - oskAbort(); - // fall-through - case SYSUTIL_OSK_DONE: - if (status == SYSUTIL_OSK_DONE) - { - printf("OSK done\n"); - } - oskUnloadAsync(&outputParam); - - if (outputParam.res == OSK_OK) - { - printf("OSK result OK\n"); - } - else - { - printf("OKS result: %d\n", outputParam.res); - } - - break; - case SYSUTIL_OSK_UNLOADED: - printf("OSK unloaded\n"); - isRunningOSK = 0; - break; - default: - break; - } -} - -#define TEXT_BUFFER_LENGTH 256 - int main(int argc,char *argv[]) { - void *host_addr = memalign(1024*1024, HOST_SIZE); static uint16_t title_utf16[TEXT_BUFFER_LENGTH]; static uint16_t input_text_utf16[TEXT_BUFFER_LENGTH]; static uint16_t initial_text_utf16[TEXT_BUFFER_LENGTH]; static uint8_t input_text_utf8[TEXT_BUFFER_LENGTH]; + void *host_addr = memalign(1024*1024, HOST_SIZE); + // Convert UTF8 to UTF16 memset(title_utf16, 0, sizeof(title_utf16)); memset(initial_text_utf16, 0, sizeof(initial_text_utf16)); @@ -169,9 +172,9 @@ int main(int argc,char *argv[]) outputParam.str = input_text_utf16; atexit(program_exit_callback); + sysUtilRegisterCallback(SYSUTIL_EVENT_SLOT0, sysutil_exit_callback, NULL); s32 res = 0; - sys_mem_container_t containerid; res = sysMemContainerCreate(&containerid, 4 * 1024 * 1024); if (res != 0) @@ -180,14 +183,6 @@ int main(int argc,char *argv[]) return 0; } - res = sysUtilRegisterCallback(SYSUTIL_EVENT_SLOT0, sysutil_exit_callback, NULL); - if (res != 0) - { - printf("Error sysUtilRegisterCallback: %08x\n", res); - sysMemContainerDestroy(containerid); - return 0; - } - oskSetInitialInputDevice(OSK_DEVICE_PAD); oskSetKeyLayoutOption(OSK_FULLKEY_PANEL); oskSetLayoutMode(OSK_LAYOUTMODE_HORIZONTAL_ALIGN_CENTER | OSK_LAYOUTMODE_VERTICAL_ALIGN_CENTER); diff --git a/samples/sys/osk/source/rsxutil.c b/samples/sys/osk/source/rsxutil.cpp similarity index 85% rename from samples/sys/osk/source/rsxutil.c rename to samples/sys/osk/source/rsxutil.cpp index b7803e6f..01e6a667 100644 --- a/samples/sys/osk/source/rsxutil.c +++ b/samples/sys/osk/source/rsxutil.cpp @@ -3,6 +3,10 @@ #include #include #include +#include + +#include +#include #include "rsxutil.h" @@ -27,7 +31,7 @@ u32 *color_buffer[2]; static u32 sLabelVal = 1; -static void wait_finish() +static void waitFinish() { rsxSetWriteBackendLabel(context,GCM_LABEL_INDEX,sLabelVal); @@ -39,22 +43,22 @@ static void wait_finish() ++sLabelVal; } -static void wait_rsx_idle() +static void waitRSXIdle() { rsxSetWriteBackendLabel(context,GCM_LABEL_INDEX,sLabelVal); rsxSetWaitLabel(context,GCM_LABEL_INDEX,sLabelVal); ++sLabelVal; - wait_finish(); + waitFinish(); } -void set_render_target(u32 index) +void setRenderTarget(u32 index) { gcmSurface sf; - sf.colorFormat = GCM_TF_COLOR_X8R8G8B8; - sf.colorTarget = GCM_TF_TARGET_0; + sf.colorFormat = GCM_SURFACE_X8R8G8B8; + sf.colorTarget = GCM_SURFACE_TARGET_0; sf.colorLocation[0] = GCM_LOCATION_RSX; sf.colorOffset[0] = color_offset[index]; sf.colorPitch[0] = color_pitch; @@ -69,13 +73,13 @@ void set_render_target(u32 index) sf.colorPitch[2] = 64; sf.colorPitch[3] = 64; - sf.depthFormat = GCM_TF_ZETA_Z16; + sf.depthFormat = GCM_SURFACE_ZETA_Z16; sf.depthLocation = GCM_LOCATION_RSX; sf.depthOffset = depth_offset; sf.depthPitch = depth_pitch; - sf.type = GCM_TF_TYPE_LINEAR; - sf.antiAlias = GCM_TF_CENTER_1; + sf.type = GCM_SURFACE_TYPE_LINEAR; + sf.antiAlias = GCM_SURFACE_CENTER_1; sf.width = display_width; sf.height = display_height; @@ -87,7 +91,7 @@ void set_render_target(u32 index) void init_screen(void *host_addr,u32 size) { - context = rsxInit(CB_SIZE,size,host_addr); + rsxInit(&context,CB_SIZE,size,host_addr); videoState state; videoGetState(0,0,&state); @@ -101,7 +105,7 @@ void init_screen(void *host_addr,u32 size) vconfig.format = VIDEO_BUFFER_FORMAT_XRGB; vconfig.pitch = res.width*sizeof(u32); - wait_rsx_idle(); + waitRSXIdle(); videoConfigure(0,&vconfig,NULL,0); videoGetState(0,0,&state); @@ -144,7 +148,7 @@ void flip() gcmSetWaitFlip(context); curr_fb ^= 1; - set_render_target(curr_fb); + setRenderTarget(curr_fb); first_fb = 0; } From 216c381170de63bfeff25d0059570fd083eddfcc Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Tue, 14 Jul 2020 08:08:59 +0200 Subject: [PATCH 11/56] - fix sample --- samples/spu/spurs/Makefile | 2 +- samples/spu/spurs/source/main.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/samples/spu/spurs/Makefile b/samples/spu/spurs/Makefile index a7d93395..003195c1 100644 --- a/samples/spu/spurs/Makefile +++ b/samples/spu/spurs/Makefile @@ -30,7 +30,7 @@ CONTENTID := UP0001-$(APPID)_00-0000000000000000 # options for code generation #--------------------------------------------------------------------------------- -CFLAGS = -Os -Wall -mcpu=cell $(MACHDEP) $(INCLUDE) +CFLAGS = -O2 -Wall -mcpu=cell $(MACHDEP) $(INCLUDE) CXXFLAGS = $(CFLAGS) LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map diff --git a/samples/spu/spurs/source/main.c b/samples/spu/spurs/source/main.c index 36f37042..e5266bd1 100644 --- a/samples/spu/spurs/source/main.c +++ b/samples/spu/spurs/source/main.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -27,7 +28,7 @@ int main(void) printf("sysSpuInitialize return %d\n",ret); ret= sysThreadGetId(&ppu_thread_id); - printf("sysThreadGetId return %d ppu_thread_id %x\n",ret,ppu_thread_id); + printf("sysThreadGetId return %d ppu_thread_id %lx\n",ret,ppu_thread_id); ret = sysThreadGetPriority(ppu_thread_id, &ppu_prio); printf("sysThreadGetPriority return %d ppu_prio %d\n",ret,ppu_prio); From e656d7dd305236772f36418b190c291d234299c5 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Tue, 14 Jul 2020 08:11:26 +0200 Subject: [PATCH 12/56] - add .gitignore into data folder (hence "folder" is committed to git) --- samples/spu/spumars/data/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 samples/spu/spumars/data/.gitignore diff --git a/samples/spu/spumars/data/.gitignore b/samples/spu/spumars/data/.gitignore new file mode 100644 index 00000000..900ec39a --- /dev/null +++ b/samples/spu/spumars/data/.gitignore @@ -0,0 +1 @@ +spu.bin From 26ce40205c0dcb584b9e8008025df9a4468fed95 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Tue, 14 Jul 2020 08:14:01 +0200 Subject: [PATCH 13/56] - explicitly add spu.bin to BINFILES --- samples/spu/spumars/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samples/spu/spumars/Makefile b/samples/spu/spumars/Makefile index e06a7101..e517ccab 100644 --- a/samples/spu/spumars/Makefile +++ b/samples/spu/spumars/Makefile @@ -67,7 +67,8 @@ CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) -BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) \ + spu.bin #--------------------------------------------------------------------------------- # use CXX for linking C++ projects, CC for standard C From a9d15cd96281519cdf913e1709d576b363395204 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Tue, 14 Jul 2020 10:16:01 +0200 Subject: [PATCH 14/56] - update vectormath library --- common/vectormath/ppu/c/mat_aos.h | 3666 ++++++++-------- common/vectormath/ppu/c/quat_aos.h | 758 ++-- common/vectormath/ppu/c/vec_aos.h | 2250 +++++----- common/vectormath/ppu/c/vectormath_aos.h | 3920 ++++++++--------- common/vectormath/ppu/cpp/boolInVec.h | 522 +-- common/vectormath/ppu/cpp/floatInVec.h | 722 +-- common/vectormath/ppu/cpp/mat_aos.h | 4389 +++++++++---------- common/vectormath/ppu/cpp/quat_aos.h | 1092 ++--- common/vectormath/ppu/cpp/vec_aos.h | 3144 ++++++------- common/vectormath/ppu/cpp/vecidx_aos.h | 160 +- common/vectormath/ppu/cpp/vectormath_aos.h | 4613 ++++++++++---------- common/vectormath/spu/c/mat_aos.h | 3666 ++++++++-------- common/vectormath/spu/c/quat_aos.h | 742 ++-- common/vectormath/spu/c/vec_aos.h | 2058 ++++----- common/vectormath/spu/c/vectormath_aos.h | 3902 ++++++++--------- common/vectormath/spu/cpp/boolInVec.h | 492 +-- common/vectormath/spu/cpp/floatInVec.h | 678 +-- common/vectormath/spu/cpp/mat_aos.h | 4054 ++++++++--------- common/vectormath/spu/cpp/quat_aos.h | 866 ++-- common/vectormath/spu/cpp/vec_aos.h | 2430 ++++++----- common/vectormath/spu/cpp/vecidx_aos.h | 128 +- common/vectormath/spu/cpp/vectormath_aos.h | 3752 ++++++++-------- 22 files changed, 24250 insertions(+), 23754 deletions(-) diff --git a/common/vectormath/ppu/c/mat_aos.h b/common/vectormath/ppu/c/mat_aos.h index dbf1ab87..c0ce539e 100644 --- a/common/vectormath/ppu/c/mat_aos.h +++ b/common/vectormath/ppu/c/mat_aos.h @@ -1,1833 +1,1833 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_MAT_AOS_C_H -#define _VECTORMATH_MAT_AOS_C_H - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/*----------------------------------------------------------------------------- - * Constants - * for shuffles, words are labeled [x,y,z,w] [a,b,c,d] - */ -#define _VECTORMATH_PERM_ZBWX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_B, _VECTORMATH_PERM_W, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_XCYX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_C, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_XYAB ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A, _VECTORMATH_PERM_B }) -#define _VECTORMATH_PERM_ZWCD ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_W, _VECTORMATH_PERM_C, _VECTORMATH_PERM_D }) -#define _VECTORMATH_PERM_XZBX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_B, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_CXXX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_C, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_YAXX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_XAZC ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_A, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_C }) -#define _VECTORMATH_PERM_YXWZ ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X, _VECTORMATH_PERM_W, _VECTORMATH_PERM_Z }) -#define _VECTORMATH_PERM_YBWD ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_B, _VECTORMATH_PERM_W, _VECTORMATH_PERM_D }) -#define _VECTORMATH_PERM_XYCX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_C, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_YCXY ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_C, _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y }) -#define _VECTORMATH_PERM_CXYC ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_C, _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_C }) -#define _VECTORMATH_PERM_ZAYX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_BZXX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_B, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_XZYA ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A }) -#define _VECTORMATH_PERM_ZXXB ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X, _VECTORMATH_PERM_B }) -#define _VECTORMATH_PERM_YXXC ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X, _VECTORMATH_PERM_C }) -#define _VECTORMATH_PERM_BBYX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_B, _VECTORMATH_PERM_B, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PI_OVER_2 1.570796327f - -/*----------------------------------------------------------------------------- - * Definitions - */ -static inline void vmathM3Copy( VmathMatrix3 *result, const VmathMatrix3 *mat ) -{ - vmathV3Copy( &result->col0, &mat->col0 ); - vmathV3Copy( &result->col1, &mat->col1 ); - vmathV3Copy( &result->col2, &mat->col2 ); -} - -static inline void vmathM3MakeFromScalar( VmathMatrix3 *result, float scalar ) -{ - vmathV3MakeFromScalar( &result->col0, scalar ); - vmathV3MakeFromScalar( &result->col1, scalar ); - vmathV3MakeFromScalar( &result->col2, scalar ); -} - -static inline void vmathM3MakeFromQ( VmathMatrix3 *result, const VmathQuat *unitQuat ) -{ - vec_float4 xyzw_2, wwww, yzxw, zxyw, yzxw_2, zxyw_2; - vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; - vec_uint4 select_x = _VECTORMATH_MASK_0xF000; - vec_uint4 select_z = _VECTORMATH_MASK_0x00F0; - xyzw_2 = vec_add( unitQuat->vec128, unitQuat->vec128 ); - wwww = vec_splat( unitQuat->vec128, 3 ); - yzxw = vec_perm( unitQuat->vec128, unitQuat->vec128, _VECTORMATH_PERM_YZXW ); - zxyw = vec_perm( unitQuat->vec128, unitQuat->vec128, _VECTORMATH_PERM_ZXYW ); - yzxw_2 = vec_perm( xyzw_2, xyzw_2, _VECTORMATH_PERM_YZXW ); - zxyw_2 = vec_perm( xyzw_2, xyzw_2, _VECTORMATH_PERM_ZXYW ); - tmp0 = vec_madd( yzxw_2, wwww, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - tmp1 = vec_nmsub( yzxw, yzxw_2, ((vec_float4){1.0f,1.0f,1.0f,1.0f}) ); - tmp2 = vec_madd( yzxw, xyzw_2, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - tmp0 = vec_madd( zxyw, xyzw_2, tmp0 ); - tmp1 = vec_nmsub( zxyw, zxyw_2, tmp1 ); - tmp2 = vec_nmsub( zxyw_2, wwww, tmp2 ); - tmp3 = vec_sel( tmp0, tmp1, select_x ); - tmp4 = vec_sel( tmp1, tmp2, select_x ); - tmp5 = vec_sel( tmp2, tmp0, select_x ); - result->col0.vec128 = vec_sel( tmp3, tmp2, select_z ); - result->col1.vec128 = vec_sel( tmp4, tmp0, select_z ); - result->col2.vec128 = vec_sel( tmp5, tmp1, select_z ); -} - -static inline void vmathM3MakeFromCols( VmathMatrix3 *result, const VmathVector3 *_col0, const VmathVector3 *_col1, const VmathVector3 *_col2 ) -{ - vmathV3Copy( &result->col0, _col0 ); - vmathV3Copy( &result->col1, _col1 ); - vmathV3Copy( &result->col2, _col2 ); -} - -static inline void vmathM3SetCol0( VmathMatrix3 *result, const VmathVector3 *_col0 ) -{ - vmathV3Copy( &result->col0, _col0 ); -} - -static inline void vmathM3SetCol1( VmathMatrix3 *result, const VmathVector3 *_col1 ) -{ - vmathV3Copy( &result->col1, _col1 ); -} - -static inline void vmathM3SetCol2( VmathMatrix3 *result, const VmathVector3 *_col2 ) -{ - vmathV3Copy( &result->col2, _col2 ); -} - -static inline void vmathM3SetCol( VmathMatrix3 *result, int col, const VmathVector3 *vec ) -{ - vmathV3Copy( (&result->col0 + col), vec ); -} - -static inline void vmathM3SetRow( VmathMatrix3 *result, int row, const VmathVector3 *vec ) -{ - vmathV3SetElem( &result->col0, row, vmathV3GetElem( vec, 0 ) ); - vmathV3SetElem( &result->col1, row, vmathV3GetElem( vec, 1 ) ); - vmathV3SetElem( &result->col2, row, vmathV3GetElem( vec, 2 ) ); -} - -static inline void vmathM3SetElem( VmathMatrix3 *result, int col, int row, float val ) -{ - VmathVector3 tmpV3_0; - vmathM3GetCol( &tmpV3_0, result, col ); - vmathV3SetElem( &tmpV3_0, row, val ); - vmathM3SetCol( result, col, &tmpV3_0 ); -} - -static inline float vmathM3GetElem( const VmathMatrix3 *mat, int col, int row ) -{ - VmathVector3 tmpV3_0; - vmathM3GetCol( &tmpV3_0, mat, col ); - return vmathV3GetElem( &tmpV3_0, row ); -} - -static inline void vmathM3GetCol0( VmathVector3 *result, const VmathMatrix3 *mat ) -{ - vmathV3Copy( result, &mat->col0 ); -} - -static inline void vmathM3GetCol1( VmathVector3 *result, const VmathMatrix3 *mat ) -{ - vmathV3Copy( result, &mat->col1 ); -} - -static inline void vmathM3GetCol2( VmathVector3 *result, const VmathMatrix3 *mat ) -{ - vmathV3Copy( result, &mat->col2 ); -} - -static inline void vmathM3GetCol( VmathVector3 *result, const VmathMatrix3 *mat, int col ) -{ - vmathV3Copy( result, (&mat->col0 + col) ); -} - -static inline void vmathM3GetRow( VmathVector3 *result, const VmathMatrix3 *mat, int row ) -{ - vmathV3MakeFromElems( result, vmathV3GetElem( &mat->col0, row ), vmathV3GetElem( &mat->col1, row ), vmathV3GetElem( &mat->col2, row ) ); -} - -static inline void vmathM3Transpose( VmathMatrix3 *result, const VmathMatrix3 *mat ) -{ - vec_float4 tmp0, tmp1, res0, res1, res2; - tmp0 = vec_mergeh( mat->col0.vec128, mat->col2.vec128 ); - tmp1 = vec_mergel( mat->col0.vec128, mat->col2.vec128 ); - res0 = vec_mergeh( tmp0, mat->col1.vec128 ); - res1 = vec_perm( tmp0, mat->col1.vec128, _VECTORMATH_PERM_ZBWX ); - res2 = vec_perm( tmp1, mat->col1.vec128, _VECTORMATH_PERM_XCYX ); - result->col0.vec128 = res0; - result->col1.vec128 = res1; - result->col2.vec128 = res2; -} - -static inline void vmathM3Inverse( VmathMatrix3 *result, const VmathMatrix3 *mat ) -{ - vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet, inv0, inv1, inv2; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - tmp2 = _vmathVfCross( mat->col0.vec128, mat->col1.vec128 ); - tmp0 = _vmathVfCross( mat->col1.vec128, mat->col2.vec128 ); - tmp1 = _vmathVfCross( mat->col2.vec128, mat->col0.vec128 ); - dot = _vmathVfDot3( tmp2, mat->col2.vec128 ); - dot = vec_splat( dot, 0 ); - invdet = recipf4( dot ); - tmp3 = vec_mergeh( tmp0, tmp2 ); - tmp4 = vec_mergel( tmp0, tmp2 ); - inv0 = vec_mergeh( tmp3, tmp1 ); - inv1 = vec_perm( tmp3, tmp1, _VECTORMATH_PERM_ZBWX ); - inv2 = vec_perm( tmp4, tmp1, _VECTORMATH_PERM_XCYX ); - inv0 = vec_madd( inv0, invdet, zero ); - inv1 = vec_madd( inv1, invdet, zero ); - inv2 = vec_madd( inv2, invdet, zero ); - result->col0.vec128 = inv0; - result->col1.vec128 = inv1; - result->col2.vec128 = inv2; -} - -static inline float vmathM3Determinant( const VmathMatrix3 *mat ) -{ - VmathVector3 tmpV3_0; - vmathV3Cross( &tmpV3_0, &mat->col0, &mat->col1 ); - return vmathV3Dot( &mat->col2, &tmpV3_0 ); -} - -static inline void vmathM3Add( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) -{ - vmathV3Add( &result->col0, &mat0->col0, &mat1->col0 ); - vmathV3Add( &result->col1, &mat0->col1, &mat1->col1 ); - vmathV3Add( &result->col2, &mat0->col2, &mat1->col2 ); -} - -static inline void vmathM3Sub( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) -{ - vmathV3Sub( &result->col0, &mat0->col0, &mat1->col0 ); - vmathV3Sub( &result->col1, &mat0->col1, &mat1->col1 ); - vmathV3Sub( &result->col2, &mat0->col2, &mat1->col2 ); -} - -static inline void vmathM3Neg( VmathMatrix3 *result, const VmathMatrix3 *mat ) -{ - vmathV3Neg( &result->col0, &mat->col0 ); - vmathV3Neg( &result->col1, &mat->col1 ); - vmathV3Neg( &result->col2, &mat->col2 ); -} - -static inline void vmathM3AbsPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat ) -{ - vmathV3AbsPerElem( &result->col0, &mat->col0 ); - vmathV3AbsPerElem( &result->col1, &mat->col1 ); - vmathV3AbsPerElem( &result->col2, &mat->col2 ); -} - -static inline void vmathM3ScalarMul( VmathMatrix3 *result, const VmathMatrix3 *mat, float scalar ) -{ - vmathV3ScalarMul( &result->col0, &mat->col0, scalar ); - vmathV3ScalarMul( &result->col1, &mat->col1, scalar ); - vmathV3ScalarMul( &result->col2, &mat->col2, scalar ); -} - -static inline void vmathM3MulV3( VmathVector3 *result, const VmathMatrix3 *mat, const VmathVector3 *vec ) -{ - vec_float4 res; - vec_float4 xxxx, yyyy, zzzz; - xxxx = vec_splat( vec->vec128, 0 ); - yyyy = vec_splat( vec->vec128, 1 ); - zzzz = vec_splat( vec->vec128, 2 ); - res = vec_madd( mat->col0.vec128, xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - res = vec_madd( mat->col1.vec128, yyyy, res ); - res = vec_madd( mat->col2.vec128, zzzz, res ); - result->vec128 = res; -} - -static inline void vmathM3Mul( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) -{ - VmathMatrix3 tmpResult; - vmathM3MulV3( &tmpResult.col0, mat0, &mat1->col0 ); - vmathM3MulV3( &tmpResult.col1, mat0, &mat1->col1 ); - vmathM3MulV3( &tmpResult.col2, mat0, &mat1->col2 ); - vmathM3Copy( result, &tmpResult ); -} - -static inline void vmathM3MulPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) -{ - vmathV3MulPerElem( &result->col0, &mat0->col0, &mat1->col0 ); - vmathV3MulPerElem( &result->col1, &mat0->col1, &mat1->col1 ); - vmathV3MulPerElem( &result->col2, &mat0->col2, &mat1->col2 ); -} - -static inline void vmathM3MakeIdentity( VmathMatrix3 *result ) -{ - vmathV3MakeXAxis( &result->col0 ); - vmathV3MakeYAxis( &result->col1 ); - vmathV3MakeZAxis( &result->col2 ); -} - -static inline void vmathM3MakeRotationX( VmathMatrix3 *result, float radians ) -{ - vec_float4 s, c, res1, res2; - vec_uint4 select_y, select_z; - vec_float4 zero; - select_y = _VECTORMATH_MASK_0x0F00; - select_z = _VECTORMATH_MASK_0x00F0; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( _vmathVfSplatScalar(radians), &s, &c ); - res1 = vec_sel( zero, c, select_y ); - res1 = vec_sel( res1, s, select_z ); - res2 = vec_sel( zero, negatef4(s), select_y ); - res2 = vec_sel( res2, c, select_z ); - vmathV3MakeXAxis( &result->col0 ); - result->col1.vec128 = res1; - result->col2.vec128 = res2; -} - -static inline void vmathM3MakeRotationY( VmathMatrix3 *result, float radians ) -{ - vec_float4 s, c, res0, res2; - vec_uint4 select_x, select_z; - vec_float4 zero; - select_x = _VECTORMATH_MASK_0xF000; - select_z = _VECTORMATH_MASK_0x00F0; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( _vmathVfSplatScalar(radians), &s, &c ); - res0 = vec_sel( zero, c, select_x ); - res0 = vec_sel( res0, negatef4(s), select_z ); - res2 = vec_sel( zero, s, select_x ); - res2 = vec_sel( res2, c, select_z ); - result->col0.vec128 = res0; - vmathV3MakeYAxis( &result->col1 ); - result->col2.vec128 = res2; -} - -static inline void vmathM3MakeRotationZ( VmathMatrix3 *result, float radians ) -{ - vec_float4 s, c, res0, res1; - vec_uint4 select_x, select_y; - vec_float4 zero; - select_x = _VECTORMATH_MASK_0xF000; - select_y = _VECTORMATH_MASK_0x0F00; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( _vmathVfSplatScalar(radians), &s, &c ); - res0 = vec_sel( zero, c, select_x ); - res0 = vec_sel( res0, s, select_y ); - res1 = vec_sel( zero, negatef4(s), select_x ); - res1 = vec_sel( res1, c, select_y ); - result->col0.vec128 = res0; - result->col1.vec128 = res1; - vmathV3MakeZAxis( &result->col2 ); -} - -static inline void vmathM3MakeRotationZYX( VmathMatrix3 *result, const VmathVector3 *radiansXYZ ) -{ - VmathVector4 tmpV4_0; - vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - vmathV4MakeFromV3Scalar( &tmpV4_0, radiansXYZ, 0.0f ); - angles = tmpV4_0.vec128; - sincosf4( angles, &s, &c ); - negS = negatef4( s ); - Z0 = vec_mergel( c, s ); - Z1 = vec_mergel( negS, c ); - Z1 = vec_andc( Z1, (vec_float4)_VECTORMATH_MASK_0x000F ); - Y0 = vec_perm( negS, c, _VECTORMATH_PERM_BBYX ); - Y1 = vec_perm( c, s, _VECTORMATH_PERM_BBYX ); - X0 = vec_splat( s, 0 ); - X1 = vec_splat( c, 0 ); - tmp = vec_madd( Z0, Y1, zero ); - result->col0.vec128 = vec_madd( Z0, Y0, zero ); - result->col1.vec128 = vec_madd( Z1, X1, vec_madd( tmp, X0, zero ) ); - result->col2.vec128 = vec_nmsub( Z1, X0, vec_madd( tmp, X1, zero ) ); -} - -static inline void vmathM3MakeRotationAxis( VmathMatrix3 *result, float radians, const VmathVector3 *unitVec ) -{ - vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - axis = unitVec->vec128; - sincosf4( (vec_float4){radians,radians,radians,radians}, &s, &c ); - xxxx = vec_splat( axis, 0 ); - yyyy = vec_splat( axis, 1 ); - zzzz = vec_splat( axis, 2 ); - oneMinusC = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), c ); - axisS = vec_madd( axis, s, zero ); - negAxisS = negatef4( axisS ); - tmp0 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_XZBX ); - tmp1 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_CXXX ); - tmp2 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_YAXX ); - tmp0 = vec_sel( tmp0, c, _VECTORMATH_MASK_0xF000 ); - tmp1 = vec_sel( tmp1, c, _VECTORMATH_MASK_0x0F00 ); - tmp2 = vec_sel( tmp2, c, _VECTORMATH_MASK_0x00F0 ); - result->col0.vec128 = vec_madd( vec_madd( axis, xxxx, zero ), oneMinusC, tmp0 ); - result->col1.vec128 = vec_madd( vec_madd( axis, yyyy, zero ), oneMinusC, tmp1 ); - result->col2.vec128 = vec_madd( vec_madd( axis, zzzz, zero ), oneMinusC, tmp2 ); -} - -static inline void vmathM3MakeRotationQ( VmathMatrix3 *result, const VmathQuat *unitQuat ) -{ - vmathM3MakeFromQ( result, unitQuat ); -} - -static inline void vmathM3MakeScale( VmathMatrix3 *result, const VmathVector3 *scaleVec ) -{ - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - result->col0.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0xF000 ); - result->col1.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0x0F00 ); - result->col2.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0x00F0 ); -} - -static inline void vmathM3AppendScale( VmathMatrix3 *result, const VmathMatrix3 *mat, const VmathVector3 *scaleVec ) -{ - vmathV3ScalarMul( &result->col0, &mat->col0, vmathV3GetX( scaleVec ) ); - vmathV3ScalarMul( &result->col1, &mat->col1, vmathV3GetY( scaleVec ) ); - vmathV3ScalarMul( &result->col2, &mat->col2, vmathV3GetZ( scaleVec ) ); -} - -static inline void vmathM3PrependScale( VmathMatrix3 *result, const VmathVector3 *scaleVec, const VmathMatrix3 *mat ) -{ - vmathV3MulPerElem( &result->col0, &mat->col0, scaleVec ); - vmathV3MulPerElem( &result->col1, &mat->col1, scaleVec ); - vmathV3MulPerElem( &result->col2, &mat->col2, scaleVec ); -} - -static inline void vmathM3Select( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1, unsigned int select1 ) -{ - vmathV3Select( &result->col0, &mat0->col0, &mat1->col0, select1 ); - vmathV3Select( &result->col1, &mat0->col1, &mat1->col1, select1 ); - vmathV3Select( &result->col2, &mat0->col2, &mat1->col2, select1 ); -} - -#ifdef _VECTORMATH_DEBUG - -static inline void vmathM3Print( const VmathMatrix3 *mat ) -{ - VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2; - vmathM3GetRow( &tmpV3_0, mat, 0 ); - vmathV3Print( &tmpV3_0 ); - vmathM3GetRow( &tmpV3_1, mat, 1 ); - vmathV3Print( &tmpV3_1 ); - vmathM3GetRow( &tmpV3_2, mat, 2 ); - vmathV3Print( &tmpV3_2 ); -} - -static inline void vmathM3Prints( const VmathMatrix3 *mat, const char *name ) -{ - printf("%s:\n", name); - vmathM3Print( mat ); -} - -#endif - -static inline void vmathM4Copy( VmathMatrix4 *result, const VmathMatrix4 *mat ) -{ - vmathV4Copy( &result->col0, &mat->col0 ); - vmathV4Copy( &result->col1, &mat->col1 ); - vmathV4Copy( &result->col2, &mat->col2 ); - vmathV4Copy( &result->col3, &mat->col3 ); -} - -static inline void vmathM4MakeFromScalar( VmathMatrix4 *result, float scalar ) -{ - vmathV4MakeFromScalar( &result->col0, scalar ); - vmathV4MakeFromScalar( &result->col1, scalar ); - vmathV4MakeFromScalar( &result->col2, scalar ); - vmathV4MakeFromScalar( &result->col3, scalar ); -} - -static inline void vmathM4MakeFromT3( VmathMatrix4 *result, const VmathTransform3 *mat ) -{ - vmathV4MakeFromV3Scalar( &result->col0, &mat->col0, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col1, &mat->col1, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col2, &mat->col2, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col3, &mat->col3, 1.0f ); -} - -static inline void vmathM4MakeFromCols( VmathMatrix4 *result, const VmathVector4 *_col0, const VmathVector4 *_col1, const VmathVector4 *_col2, const VmathVector4 *_col3 ) -{ - vmathV4Copy( &result->col0, _col0 ); - vmathV4Copy( &result->col1, _col1 ); - vmathV4Copy( &result->col2, _col2 ); - vmathV4Copy( &result->col3, _col3 ); -} - -static inline void vmathM4MakeFromM3V3( VmathMatrix4 *result, const VmathMatrix3 *mat, const VmathVector3 *translateVec ) -{ - vmathV4MakeFromV3Scalar( &result->col0, &mat->col0, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col1, &mat->col1, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col2, &mat->col2, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col3, translateVec, 1.0f ); -} - -static inline void vmathM4MakeFromQV3( VmathMatrix4 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ) -{ - VmathMatrix3 mat; - vmathM3MakeFromQ( &mat, unitQuat ); - vmathV4MakeFromV3Scalar( &result->col0, &mat.col0, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col1, &mat.col1, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col2, &mat.col2, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col3, translateVec, 1.0f ); -} - -static inline void vmathM4SetCol0( VmathMatrix4 *result, const VmathVector4 *_col0 ) -{ - vmathV4Copy( &result->col0, _col0 ); -} - -static inline void vmathM4SetCol1( VmathMatrix4 *result, const VmathVector4 *_col1 ) -{ - vmathV4Copy( &result->col1, _col1 ); -} - -static inline void vmathM4SetCol2( VmathMatrix4 *result, const VmathVector4 *_col2 ) -{ - vmathV4Copy( &result->col2, _col2 ); -} - -static inline void vmathM4SetCol3( VmathMatrix4 *result, const VmathVector4 *_col3 ) -{ - vmathV4Copy( &result->col3, _col3 ); -} - -static inline void vmathM4SetCol( VmathMatrix4 *result, int col, const VmathVector4 *vec ) -{ - vmathV4Copy( (&result->col0 + col), vec ); -} - -static inline void vmathM4SetRow( VmathMatrix4 *result, int row, const VmathVector4 *vec ) -{ - vmathV4SetElem( &result->col0, row, vmathV4GetElem( vec, 0 ) ); - vmathV4SetElem( &result->col1, row, vmathV4GetElem( vec, 1 ) ); - vmathV4SetElem( &result->col2, row, vmathV4GetElem( vec, 2 ) ); - vmathV4SetElem( &result->col3, row, vmathV4GetElem( vec, 3 ) ); -} - -static inline void vmathM4SetElem( VmathMatrix4 *result, int col, int row, float val ) -{ - VmathVector4 tmpV3_0; - vmathM4GetCol( &tmpV3_0, result, col ); - vmathV4SetElem( &tmpV3_0, row, val ); - vmathM4SetCol( result, col, &tmpV3_0 ); -} - -static inline float vmathM4GetElem( const VmathMatrix4 *mat, int col, int row ) -{ - VmathVector4 tmpV4_0; - vmathM4GetCol( &tmpV4_0, mat, col ); - return vmathV4GetElem( &tmpV4_0, row ); -} - -static inline void vmathM4GetCol0( VmathVector4 *result, const VmathMatrix4 *mat ) -{ - vmathV4Copy( result, &mat->col0 ); -} - -static inline void vmathM4GetCol1( VmathVector4 *result, const VmathMatrix4 *mat ) -{ - vmathV4Copy( result, &mat->col1 ); -} - -static inline void vmathM4GetCol2( VmathVector4 *result, const VmathMatrix4 *mat ) -{ - vmathV4Copy( result, &mat->col2 ); -} - -static inline void vmathM4GetCol3( VmathVector4 *result, const VmathMatrix4 *mat ) -{ - vmathV4Copy( result, &mat->col3 ); -} - -static inline void vmathM4GetCol( VmathVector4 *result, const VmathMatrix4 *mat, int col ) -{ - vmathV4Copy( result, (&mat->col0 + col) ); -} - -static inline void vmathM4GetRow( VmathVector4 *result, const VmathMatrix4 *mat, int row ) -{ - vmathV4MakeFromElems( result, vmathV4GetElem( &mat->col0, row ), vmathV4GetElem( &mat->col1, row ), vmathV4GetElem( &mat->col2, row ), vmathV4GetElem( &mat->col3, row ) ); -} - -static inline void vmathM4Transpose( VmathMatrix4 *result, const VmathMatrix4 *mat ) -{ - vec_float4 tmp0, tmp1, tmp2, tmp3, res0, res1, res2, res3; - tmp0 = vec_mergeh( mat->col0.vec128, mat->col2.vec128 ); - tmp1 = vec_mergeh( mat->col1.vec128, mat->col3.vec128 ); - tmp2 = vec_mergel( mat->col0.vec128, mat->col2.vec128 ); - tmp3 = vec_mergel( mat->col1.vec128, mat->col3.vec128 ); - res0 = vec_mergeh( tmp0, tmp1 ); - res1 = vec_mergel( tmp0, tmp1 ); - res2 = vec_mergeh( tmp2, tmp3 ); - res3 = vec_mergel( tmp2, tmp3 ); - result->col0.vec128 = res0; - result->col1.vec128 = res1; - result->col2.vec128 = res2; - result->col3.vec128 = res3; -} - -static inline void vmathM4Inverse( VmathMatrix4 *result, const VmathMatrix4 *mat ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vector float in0, in1, in2, in3; - vector float tmp0, tmp1, tmp2, tmp3; - vector float cof0, cof1, cof2, cof3; - vector float t0, t1, t2, t3; - vector float t01, t02, t03, t12, t23; - vector float t1r, t2r; - vector float t01r, t02r, t03r, t12r, t23r; - vector float t1r3, t1r3r; - vector float det, det0, det1, det2, det3, invdet; - vector float vzero = (vector float){0.0}; - in0 = mat->col0.vec128; - in1 = mat->col1.vec128; - in2 = mat->col2.vec128; - in3 = mat->col3.vec128; - /* Perform transform of the input matrix of the form: - * A B C D - * E F G H - * I J K L - * M N O P - * - * The pseudo transpose of the input matrix is trans: - * A E I M - * J N B F - * C G K O - * L P D H - */ - tmp0 = vec_perm(in0, in1, _VECTORMATH_PERM_XAZC); /* A E C G */ - tmp1 = vec_perm(in2, in3, _VECTORMATH_PERM_XAZC); /* I M K O */ - tmp2 = vec_perm(in0, in1, _VECTORMATH_PERM_YBWD); /* B F D H */ - tmp3 = vec_perm(in2, in3, _VECTORMATH_PERM_YBWD); /* J N L P */ - t0 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_XYAB); /* A E I M */ - t1 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_XYAB); /* J N B F */ - t2 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_ZWCD); /* C G K O */ - t3 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_ZWCD); /* L P D H */ - /* Generate a cofactor matrix. The computed cofactors reside in - * cof0, cof1, cof2, cof3. - */ - t23 = vec_madd(t2, t3, vzero); /* CL GP KD OH */ - t23 = vec_perm(t23, t23, _VECTORMATH_PERM_YXWZ); /* GP CL OH KD */ - cof0 = vec_nmsub(t1, t23, vzero); /* -(JGP NCL FOH BKD) */ - cof1 = vec_nmsub(t0, t23, vzero); /* -(AGP ECL IOH MKD) */ - t23r = vec_sld(t23, t23, 8); /* OH KD GP CL */ - cof0 = vec_madd(t1, t23r, cof0); /* JOH NKD BGP FCL + cof0 */ - cof1 = vec_madd(t0, t23r, cof1); /* AOH EKD IGP MCL + cof1 */ - cof1 = vec_sld(cof1, cof1, 8); /* IGP MCL AOH EKD - IOH MKD AGP ECL */ - t12 = vec_madd(t1, t2, vzero); /* JC NG BK FO */ - t12 = vec_perm(t12, t12, _VECTORMATH_PERM_YXWZ); /* NG JC FO BK */ - cof0 = vec_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ - cof3 = vec_madd(t0, t12, vzero); /* ANG EJC IFO MBK */ - t12r = vec_sld(t12, t12, 8); /* FO BK NG JC */ - cof0 = vec_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ - cof3 = vec_nmsub(t0, t12r, cof3); /* cof3 - AFO EBK ING MJC */ - cof3 = vec_sld(cof3, cof3, 8); /* ING MJC AFO EBK - IFO MBK ANG EJC */ - t1r = vec_sld(t1, t1, 8); /* B F J N */ - t2r = vec_sld(t2, t2, 8); /* K O C G */ - t1r3 = vec_madd(t1r, t3, vzero); /* BL FP JD NH */ - t1r3 = vec_perm(t1r3, t1r3, _VECTORMATH_PERM_YXWZ); /* FP BL NH JD */ - cof0 = vec_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ - cof2 = vec_madd(t0, t1r3, vzero); /* AFP EBL INH MJD */ - t1r3r = vec_sld(t1r3, t1r3, 8); /* NH JD FP BL */ - cof0 = vec_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ - cof2 = vec_nmsub(t0, t1r3r, cof2); /* cof2 - ANH EJD IFP MBL */ - cof2 = vec_sld(cof2, cof2, 8); /* IFP MBL ANH EJD - INH MJD AFP EBL */ - t01 = vec_madd(t0, t1, vzero); /* AJ EN IB MF */ - t01 = vec_perm(t01, t01, _VECTORMATH_PERM_YXWZ); /* EN AJ MF IB */ - cof2 = vec_nmsub(t3, t01, cof2); /* cof2 - LEN PAJ DMF HIB */ - cof3 = vec_madd(t2r, t01, cof3); /* KEN OAJ CMF GIB + cof3 */ - t01r = vec_sld(t01, t01, 8); /* MF IB EN AJ */ - cof2 = vec_madd(t3, t01r, cof2); /* LMF PIB DEN HAJ + cof2 */ - cof3 = vec_nmsub(t2r, t01r, cof3); /* cof3 - KMF OIB CEN GAJ */ - t03 = vec_madd(t0, t3, vzero); /* AL EP ID MH */ - t03 = vec_perm(t03, t03, _VECTORMATH_PERM_YXWZ); /* EP AL MH ID */ - cof1 = vec_nmsub(t2r, t03, cof1); /* cof1 - KEP OAL CMH GID */ - cof2 = vec_madd(t1, t03, cof2); /* JEP NAL BMH FID + cof2 */ - t03r = vec_sld(t03, t03, 8); /* MH ID EP AL */ - cof1 = vec_madd(t2r, t03r, cof1); /* KMH OID CEP GAL + cof1 */ - cof2 = vec_nmsub(t1, t03r, cof2); /* cof2 - JMH NID BEP FAL */ - t02 = vec_madd(t0, t2r, vzero); /* AK EO IC MG */ - t02 = vec_perm(t02, t02, _VECTORMATH_PERM_YXWZ); /* E0 AK MG IC */ - cof1 = vec_madd(t3, t02, cof1); /* LEO PAK DMG HIC + cof1 */ - cof3 = vec_nmsub(t1, t02, cof3); /* cof3 - JEO NAK BMG FIC */ - t02r = vec_sld(t02, t02, 8); /* MG IC EO AK */ - cof1 = vec_nmsub(t3, t02r, cof1); /* cof1 - LMG PIC DEO HAK */ - cof3 = vec_madd(t1, t02r, cof3); /* JMG NIC BEO FAK + cof3 */ - /* Compute the determinant of the matrix - * - * det = sum_across(t0 * cof0); - * - * We perform a sum across the entire vector so that - * we don't have to splat the result when multiplying the - * cofactors by the inverse of the determinant. - */ - det = vec_madd(t0, cof0, vzero); - det0 = vec_splat(det, 0); - det1 = vec_splat(det, 1); - det2 = vec_splat(det, 2); - det3 = vec_splat(det, 3); - det = vec_add(det0, det1); - det2 = vec_add(det2, det3); - det = vec_add(det, det2); - /* Compute the reciprocal of the determinant. - */ - invdet = recipf4(det); - /* Multiply the cofactors by the reciprocal of the determinant. - */ - result->col0.vec128 = vec_madd(cof0, invdet, vzero); - result->col1.vec128 = vec_madd(cof1, invdet, vzero); - result->col2.vec128 = vec_madd(cof2, invdet, vzero); - result->col3.vec128 = vec_madd(cof3, invdet, vzero); -} - -static inline void vmathM4AffineInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ) -{ - VmathTransform3 affineMat, tmpT3_0; - VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2, tmpV3_3; - vmathV4GetXYZ( &tmpV3_0, &mat->col0 ); - vmathT3SetCol0( &affineMat, &tmpV3_0 ); - vmathV4GetXYZ( &tmpV3_1, &mat->col1 ); - vmathT3SetCol1( &affineMat, &tmpV3_1 ); - vmathV4GetXYZ( &tmpV3_2, &mat->col2 ); - vmathT3SetCol2( &affineMat, &tmpV3_2 ); - vmathV4GetXYZ( &tmpV3_3, &mat->col3 ); - vmathT3SetCol3( &affineMat, &tmpV3_3 ); - vmathT3Inverse( &tmpT3_0, &affineMat ); - vmathM4MakeFromT3( result, &tmpT3_0 ); -} - -static inline void vmathM4OrthoInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ) -{ - VmathTransform3 affineMat, tmpT3_0; - VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2, tmpV3_3; - vmathV4GetXYZ( &tmpV3_0, &mat->col0 ); - vmathT3SetCol0( &affineMat, &tmpV3_0 ); - vmathV4GetXYZ( &tmpV3_1, &mat->col1 ); - vmathT3SetCol1( &affineMat, &tmpV3_1 ); - vmathV4GetXYZ( &tmpV3_2, &mat->col2 ); - vmathT3SetCol2( &affineMat, &tmpV3_2 ); - vmathV4GetXYZ( &tmpV3_3, &mat->col3 ); - vmathT3SetCol3( &affineMat, &tmpV3_3 ); - vmathT3OrthoInverse( &tmpT3_0, &affineMat ); - vmathM4MakeFromT3( result, &tmpT3_0 ); -} - -static inline float vmathM4Determinant( const VmathMatrix4 *mat ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vector float in0, in1, in2, in3; - vector float tmp0, tmp1, tmp2, tmp3; - vector float cof0; - vector float t0, t1, t2, t3; - vector float t12, t23; - vector float t1r, t2r; - vector float t12r, t23r; - vector float t1r3, t1r3r; - vector float vzero = (vector float){0.0}; - union { vec_float4 v; float s[4]; } tmp; - in0 = mat->col0.vec128; - in1 = mat->col1.vec128; - in2 = mat->col2.vec128; - in3 = mat->col3.vec128; - /* Perform transform of the input matrix of the form: - * A B C D - * E F G H - * I J K L - * M N O P - * - * The pseudo transpose of the input matrix is trans: - * A E I M - * J N B F - * C G K O - * L P D H - */ - tmp0 = vec_perm(in0, in1, _VECTORMATH_PERM_XAZC); /* A E C G */ - tmp1 = vec_perm(in2, in3, _VECTORMATH_PERM_XAZC); /* I M K O */ - tmp2 = vec_perm(in0, in1, _VECTORMATH_PERM_YBWD); /* B F D H */ - tmp3 = vec_perm(in2, in3, _VECTORMATH_PERM_YBWD); /* J N L P */ - t0 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_XYAB); /* A E I M */ - t1 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_XYAB); /* J N B F */ - t2 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_ZWCD); /* C G K O */ - t3 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_ZWCD); /* L P D H */ - /* Generate a cofactor matrix. The computed cofactors reside in - * cof0, cof1, cof2, cof3. - */ - t23 = vec_madd(t2, t3, vzero); /* CL GP KD OH */ - t23 = vec_perm(t23, t23, _VECTORMATH_PERM_YXWZ); /* GP CL OH KD */ - cof0 = vec_nmsub(t1, t23, vzero); /* -(JGP NCL FOH BKD) */ - t23r = vec_sld(t23, t23, 8); /* OH KD GP CL */ - cof0 = vec_madd(t1, t23r, cof0); /* JOH NKD BGP FCL + cof0 */ - t12 = vec_madd(t1, t2, vzero); /* JC NG BK FO */ - t12 = vec_perm(t12, t12, _VECTORMATH_PERM_YXWZ); /* NG JC FO BK */ - cof0 = vec_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ - t12r = vec_sld(t12, t12, 8); /* FO BK NG JC */ - cof0 = vec_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ - t1r = vec_sld(t1, t1, 8); /* B F J N */ - t2r = vec_sld(t2, t2, 8); /* K O C G */ - t1r3 = vec_madd(t1r, t3, vzero); /* BL FP JD NH */ - t1r3 = vec_perm(t1r3, t1r3, _VECTORMATH_PERM_YXWZ); /* FP BL NH JD */ - cof0 = vec_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ - t1r3r = vec_sld(t1r3, t1r3, 8); /* NH JD FP BL */ - cof0 = vec_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ - tmp.v = _vmathVfDot4(t0,cof0); - return tmp.s[0]; -} - -static inline void vmathM4Add( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) -{ - vmathV4Add( &result->col0, &mat0->col0, &mat1->col0 ); - vmathV4Add( &result->col1, &mat0->col1, &mat1->col1 ); - vmathV4Add( &result->col2, &mat0->col2, &mat1->col2 ); - vmathV4Add( &result->col3, &mat0->col3, &mat1->col3 ); -} - -static inline void vmathM4Sub( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) -{ - vmathV4Sub( &result->col0, &mat0->col0, &mat1->col0 ); - vmathV4Sub( &result->col1, &mat0->col1, &mat1->col1 ); - vmathV4Sub( &result->col2, &mat0->col2, &mat1->col2 ); - vmathV4Sub( &result->col3, &mat0->col3, &mat1->col3 ); -} - -static inline void vmathM4Neg( VmathMatrix4 *result, const VmathMatrix4 *mat ) -{ - vmathV4Neg( &result->col0, &mat->col0 ); - vmathV4Neg( &result->col1, &mat->col1 ); - vmathV4Neg( &result->col2, &mat->col2 ); - vmathV4Neg( &result->col3, &mat->col3 ); -} - -static inline void vmathM4AbsPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat ) -{ - vmathV4AbsPerElem( &result->col0, &mat->col0 ); - vmathV4AbsPerElem( &result->col1, &mat->col1 ); - vmathV4AbsPerElem( &result->col2, &mat->col2 ); - vmathV4AbsPerElem( &result->col3, &mat->col3 ); -} - -static inline void vmathM4ScalarMul( VmathMatrix4 *result, const VmathMatrix4 *mat, float scalar ) -{ - vmathV4ScalarMul( &result->col0, &mat->col0, scalar ); - vmathV4ScalarMul( &result->col1, &mat->col1, scalar ); - vmathV4ScalarMul( &result->col2, &mat->col2, scalar ); - vmathV4ScalarMul( &result->col3, &mat->col3, scalar ); -} - -static inline void vmathM4MulV4( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector4 *vec ) -{ - vec_float4 tmp0, tmp1, res; - vec_float4 xxxx, yyyy, zzzz, wwww; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - xxxx = vec_splat( vec->vec128, 0 ); - yyyy = vec_splat( vec->vec128, 1 ); - zzzz = vec_splat( vec->vec128, 2 ); - wwww = vec_splat( vec->vec128, 3 ); - tmp0 = vec_madd( mat->col0.vec128, xxxx, zero ); - tmp1 = vec_madd( mat->col1.vec128, yyyy, zero ); - tmp0 = vec_madd( mat->col2.vec128, zzzz, tmp0 ); - tmp1 = vec_madd( mat->col3.vec128, wwww, tmp1 ); - res = vec_add( tmp0, tmp1 ); - result->vec128 = res; -} - -static inline void vmathM4MulV3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector3 *vec ) -{ - vec_float4 res; - vec_float4 xxxx, yyyy, zzzz; - xxxx = vec_splat( vec->vec128, 0 ); - yyyy = vec_splat( vec->vec128, 1 ); - zzzz = vec_splat( vec->vec128, 2 ); - res = vec_madd( mat->col0.vec128, xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - res = vec_madd( mat->col1.vec128, yyyy, res ); - res = vec_madd( mat->col2.vec128, zzzz, res ); - result->vec128 = res; -} - -static inline void vmathM4MulP3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathPoint3 *pnt ) -{ - vec_float4 tmp0, tmp1, res; - vec_float4 xxxx, yyyy, zzzz; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - xxxx = vec_splat( pnt->vec128, 0 ); - yyyy = vec_splat( pnt->vec128, 1 ); - zzzz = vec_splat( pnt->vec128, 2 ); - tmp0 = vec_madd( mat->col0.vec128, xxxx, zero ); - tmp1 = vec_madd( mat->col1.vec128, yyyy, zero ); - tmp0 = vec_madd( mat->col2.vec128, zzzz, tmp0 ); - tmp1 = vec_add( mat->col3.vec128, tmp1 ); - res = vec_add( tmp0, tmp1 ); - result->vec128 = res; -} - -static inline void vmathM4Mul( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) -{ - VmathMatrix4 tmpResult; - vmathM4MulV4( &tmpResult.col0, mat0, &mat1->col0 ); - vmathM4MulV4( &tmpResult.col1, mat0, &mat1->col1 ); - vmathM4MulV4( &tmpResult.col2, mat0, &mat1->col2 ); - vmathM4MulV4( &tmpResult.col3, mat0, &mat1->col3 ); - vmathM4Copy( result, &tmpResult ); -} - -static inline void vmathM4MulT3( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathTransform3 *tfrm1 ) -{ - VmathMatrix4 tmpResult; - VmathPoint3 tmpP3_0; - vmathM4MulV3( &tmpResult.col0, mat, &tfrm1->col0 ); - vmathM4MulV3( &tmpResult.col1, mat, &tfrm1->col1 ); - vmathM4MulV3( &tmpResult.col2, mat, &tfrm1->col2 ); - vmathP3MakeFromV3( &tmpP3_0, &tfrm1->col3 ); - vmathM4MulP3( &tmpResult.col3, mat, &tmpP3_0 ); - vmathM4Copy( result, &tmpResult ); -} - -static inline void vmathM4MulPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) -{ - vmathV4MulPerElem( &result->col0, &mat0->col0, &mat1->col0 ); - vmathV4MulPerElem( &result->col1, &mat0->col1, &mat1->col1 ); - vmathV4MulPerElem( &result->col2, &mat0->col2, &mat1->col2 ); - vmathV4MulPerElem( &result->col3, &mat0->col3, &mat1->col3 ); -} - -static inline void vmathM4MakeIdentity( VmathMatrix4 *result ) -{ - vmathV4MakeXAxis( &result->col0 ); - vmathV4MakeYAxis( &result->col1 ); - vmathV4MakeZAxis( &result->col2 ); - vmathV4MakeWAxis( &result->col3 ); -} - -static inline void vmathM4SetUpper3x3( VmathMatrix4 *result, const VmathMatrix3 *mat3 ) -{ - vmathV4SetXYZ( &result->col0, &mat3->col0 ); - vmathV4SetXYZ( &result->col1, &mat3->col1 ); - vmathV4SetXYZ( &result->col2, &mat3->col2 ); -} - -static inline void vmathM4GetUpper3x3( VmathMatrix3 *result, const VmathMatrix4 *mat ) -{ - vmathV4GetXYZ( &result->col0, &mat->col0 ); - vmathV4GetXYZ( &result->col1, &mat->col1 ); - vmathV4GetXYZ( &result->col2, &mat->col2 ); -} - -static inline void vmathM4SetTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ) -{ - vmathV4SetXYZ( &result->col3, translateVec ); -} - -static inline void vmathM4GetTranslation( VmathVector3 *result, const VmathMatrix4 *mat ) -{ - vmathV4GetXYZ( result, &mat->col3 ); -} - -static inline void vmathM4MakeRotationX( VmathMatrix4 *result, float radians ) -{ - vec_float4 s, c, res1, res2; - vec_uint4 select_y, select_z; - vec_float4 zero; - select_y = _VECTORMATH_MASK_0x0F00; - select_z = _VECTORMATH_MASK_0x00F0; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( _vmathVfSplatScalar(radians), &s, &c ); - res1 = vec_sel( zero, c, select_y ); - res1 = vec_sel( res1, s, select_z ); - res2 = vec_sel( zero, negatef4(s), select_y ); - res2 = vec_sel( res2, c, select_z ); - vmathV4MakeXAxis( &result->col0 ); - result->col1.vec128 = res1; - result->col2.vec128 = res2; - vmathV4MakeWAxis( &result->col3 ); -} - -static inline void vmathM4MakeRotationY( VmathMatrix4 *result, float radians ) -{ - vec_float4 s, c, res0, res2; - vec_uint4 select_x, select_z; - vec_float4 zero; - select_x = _VECTORMATH_MASK_0xF000; - select_z = _VECTORMATH_MASK_0x00F0; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( _vmathVfSplatScalar(radians), &s, &c ); - res0 = vec_sel( zero, c, select_x ); - res0 = vec_sel( res0, negatef4(s), select_z ); - res2 = vec_sel( zero, s, select_x ); - res2 = vec_sel( res2, c, select_z ); - result->col0.vec128 = res0; - vmathV4MakeYAxis( &result->col1 ); - result->col2.vec128 = res2; - vmathV4MakeWAxis( &result->col3 ); -} - -static inline void vmathM4MakeRotationZ( VmathMatrix4 *result, float radians ) -{ - vec_float4 s, c, res0, res1; - vec_uint4 select_x, select_y; - vec_float4 zero; - select_x = _VECTORMATH_MASK_0xF000; - select_y = _VECTORMATH_MASK_0x0F00; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( _vmathVfSplatScalar(radians), &s, &c ); - res0 = vec_sel( zero, c, select_x ); - res0 = vec_sel( res0, s, select_y ); - res1 = vec_sel( zero, negatef4(s), select_x ); - res1 = vec_sel( res1, c, select_y ); - result->col0.vec128 = res0; - result->col1.vec128 = res1; - vmathV4MakeZAxis( &result->col2 ); - vmathV4MakeWAxis( &result->col3 ); -} - -static inline void vmathM4MakeRotationZYX( VmathMatrix4 *result, const VmathVector3 *radiansXYZ ) -{ - VmathVector4 tmpV4_0; - vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - vmathV4MakeFromV3Scalar( &tmpV4_0, radiansXYZ, 0.0f ); - angles = tmpV4_0.vec128; - sincosf4( angles, &s, &c ); - negS = negatef4( s ); - Z0 = vec_mergel( c, s ); - Z1 = vec_mergel( negS, c ); - Z1 = vec_andc( Z1, (vec_float4)_VECTORMATH_MASK_0x000F ); - Y0 = vec_perm( negS, c, _VECTORMATH_PERM_BBYX ); - Y1 = vec_perm( c, s, _VECTORMATH_PERM_BBYX ); - X0 = vec_splat( s, 0 ); - X1 = vec_splat( c, 0 ); - tmp = vec_madd( Z0, Y1, zero ); - result->col0.vec128 = vec_madd( Z0, Y0, zero ); - result->col1.vec128 = vec_madd( Z1, X1, vec_madd( tmp, X0, zero ) ); - result->col2.vec128 = vec_nmsub( Z1, X0, vec_madd( tmp, X1, zero ) ); - vmathV4MakeWAxis( &result->col3 ); -} - -static inline void vmathM4MakeRotationAxis( VmathMatrix4 *result, float radians, const VmathVector3 *unitVec ) -{ - vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2, zeroW; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - axis = unitVec->vec128; - sincosf4( (vec_float4){radians,radians,radians,radians}, &s, &c ); - xxxx = vec_splat( axis, 0 ); - yyyy = vec_splat( axis, 1 ); - zzzz = vec_splat( axis, 2 ); - oneMinusC = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), c ); - axisS = vec_madd( axis, s, zero ); - negAxisS = negatef4( axisS ); - tmp0 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_XZBX ); - tmp1 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_CXXX ); - tmp2 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_YAXX ); - tmp0 = vec_sel( tmp0, c, _VECTORMATH_MASK_0xF000 ); - tmp1 = vec_sel( tmp1, c, _VECTORMATH_MASK_0x0F00 ); - tmp2 = vec_sel( tmp2, c, _VECTORMATH_MASK_0x00F0 ); - zeroW = (vec_float4)_VECTORMATH_MASK_0x000F; - axis = vec_andc( axis, zeroW ); - tmp0 = vec_andc( tmp0, zeroW ); - tmp1 = vec_andc( tmp1, zeroW ); - tmp2 = vec_andc( tmp2, zeroW ); - result->col0.vec128 = vec_madd( vec_madd( axis, xxxx, zero ), oneMinusC, tmp0 ); - result->col1.vec128 = vec_madd( vec_madd( axis, yyyy, zero ), oneMinusC, tmp1 ); - result->col2.vec128 = vec_madd( vec_madd( axis, zzzz, zero ), oneMinusC, tmp2 ); - vmathV4MakeWAxis( &result->col3 ); -} - -static inline void vmathM4MakeRotationQ( VmathMatrix4 *result, const VmathQuat *unitQuat ) -{ - VmathTransform3 tmpT3_0; - vmathT3MakeRotationQ( &tmpT3_0, unitQuat ); - vmathM4MakeFromT3( result, &tmpT3_0 ); -} - -static inline void vmathM4MakeScale( VmathMatrix4 *result, const VmathVector3 *scaleVec ) -{ - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - result->col0.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0xF000 ); - result->col1.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0x0F00 ); - result->col2.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0x00F0 ); - vmathV4MakeWAxis( &result->col3 ); -} - -static inline void vmathM4AppendScale( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathVector3 *scaleVec ) -{ - vmathV4ScalarMul( &result->col0, &mat->col0, vmathV3GetX( scaleVec ) ); - vmathV4ScalarMul( &result->col1, &mat->col1, vmathV3GetY( scaleVec ) ); - vmathV4ScalarMul( &result->col2, &mat->col2, vmathV3GetZ( scaleVec ) ); - vmathV4Copy( &result->col3, &mat->col3 ); -} - -static inline void vmathM4PrependScale( VmathMatrix4 *result, const VmathVector3 *scaleVec, const VmathMatrix4 *mat ) -{ - VmathVector4 scale4; - vmathV4MakeFromV3Scalar( &scale4, scaleVec, 1.0f ); - vmathV4MulPerElem( &result->col0, &mat->col0, &scale4 ); - vmathV4MulPerElem( &result->col1, &mat->col1, &scale4 ); - vmathV4MulPerElem( &result->col2, &mat->col2, &scale4 ); - vmathV4MulPerElem( &result->col3, &mat->col3, &scale4 ); -} - -static inline void vmathM4MakeTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ) -{ - vmathV4MakeXAxis( &result->col0 ); - vmathV4MakeYAxis( &result->col1 ); - vmathV4MakeZAxis( &result->col2 ); - vmathV4MakeFromV3Scalar( &result->col3, translateVec, 1.0f ); -} - -static inline void vmathM4MakeLookAt( VmathMatrix4 *result, const VmathPoint3 *eyePos, const VmathPoint3 *lookAtPos, const VmathVector3 *upVec ) -{ - VmathMatrix4 m4EyeFrame; - VmathVector3 v3X, v3Y, v3Z, tmpV3_0, tmpV3_1; - VmathVector4 tmpV4_0, tmpV4_1, tmpV4_2, tmpV4_3; - vmathV3Normalize( &v3Y, upVec ); - vmathP3Sub( &tmpV3_0, eyePos, lookAtPos ); - vmathV3Normalize( &v3Z, &tmpV3_0 ); - vmathV3Cross( &tmpV3_1, &v3Y, &v3Z ); - vmathV3Normalize( &v3X, &tmpV3_1 ); - vmathV3Cross( &v3Y, &v3Z, &v3X ); - vmathV4MakeFromV3( &tmpV4_0, &v3X ); - vmathV4MakeFromV3( &tmpV4_1, &v3Y ); - vmathV4MakeFromV3( &tmpV4_2, &v3Z ); - vmathV4MakeFromP3( &tmpV4_3, eyePos ); - vmathM4MakeFromCols( &m4EyeFrame, &tmpV4_0, &tmpV4_1, &tmpV4_2, &tmpV4_3 ); - vmathM4OrthoInverse( result, &m4EyeFrame ); -} - -static inline void vmathM4MakePerspective( VmathMatrix4 *result, float fovyRadians, float aspect, float zNear, float zFar ) -{ - float f, rangeInv; - vec_float4 zero, col0, col1, col2, col3; - union { vec_float4 v; float s[4]; } tmp; - f = tanf( _VECTORMATH_PI_OVER_2 - fovyRadians * 0.5f ); - rangeInv = 1.0f / ( zNear - zFar ); - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - tmp.v = zero; - tmp.s[0] = f / aspect; - col0 = tmp.v; - tmp.v = zero; - tmp.s[1] = f; - col1 = tmp.v; - tmp.v = zero; - tmp.s[2] = ( zNear + zFar ) * rangeInv; - tmp.s[3] = -1.0f; - col2 = tmp.v; - tmp.v = zero; - tmp.s[2] = zNear * zFar * rangeInv * 2.0f; - col3 = tmp.v; - result->col0.vec128 = col0; - result->col1.vec128 = col1; - result->col2.vec128 = col2; - result->col3.vec128 = col3; -} - -static inline void vmathM4MakeFrustum( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vec_float4 lbf, rtn; - vec_float4 diff, sum, inv_diff; - vec_float4 diagonal, column, near2; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - union { vec_float4 v; float s[4]; } l, f, r, n, b, t; - l.s[0] = left; - f.s[0] = zFar; - r.s[0] = right; - n.s[0] = zNear; - b.s[0] = bottom; - t.s[0] = top; - lbf = vec_mergeh( l.v, f.v ); - rtn = vec_mergeh( r.v, n.v ); - lbf = vec_mergeh( lbf, b.v ); - rtn = vec_mergeh( rtn, t.v ); - diff = vec_sub( rtn, lbf ); - sum = vec_add( rtn, lbf ); - inv_diff = recipf4( diff ); - near2 = vec_splat( n.v, 0 ); - near2 = vec_add( near2, near2 ); - diagonal = vec_madd( near2, inv_diff, zero ); - column = vec_madd( sum, inv_diff, zero ); - result->col0.vec128 = vec_sel( zero, diagonal, _VECTORMATH_MASK_0xF000 ); - result->col1.vec128 = vec_sel( zero, diagonal, _VECTORMATH_MASK_0x0F00 ); - result->col2.vec128 = vec_sel( column, ((vec_float4){-1.0f,-1.0f,-1.0f,-1.0f}), _VECTORMATH_MASK_0x000F ); - result->col3.vec128 = vec_sel( zero, vec_madd( diagonal, vec_splat( f.v, 0 ), zero ), _VECTORMATH_MASK_0x00F0 ); -} - -static inline void vmathM4MakeOrthographic( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vec_float4 lbf, rtn; - vec_float4 diff, sum, inv_diff, neg_inv_diff; - vec_float4 diagonal, column; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - union { vec_float4 v; float s[4]; } l, f, r, n, b, t; - l.s[0] = left; - f.s[0] = zFar; - r.s[0] = right; - n.s[0] = zNear; - b.s[0] = bottom; - t.s[0] = top; - lbf = vec_mergeh( l.v, f.v ); - rtn = vec_mergeh( r.v, n.v ); - lbf = vec_mergeh( lbf, b.v ); - rtn = vec_mergeh( rtn, t.v ); - diff = vec_sub( rtn, lbf ); - sum = vec_add( rtn, lbf ); - inv_diff = recipf4( diff ); - neg_inv_diff = negatef4( inv_diff ); - diagonal = vec_add( inv_diff, inv_diff ); - column = vec_madd( sum, vec_sel( neg_inv_diff, inv_diff, _VECTORMATH_MASK_0x00F0 ), zero ); - result->col0.vec128 = vec_sel( zero, diagonal, _VECTORMATH_MASK_0xF000 ); - result->col1.vec128 = vec_sel( zero, diagonal, _VECTORMATH_MASK_0x0F00 ); - result->col2.vec128 = vec_sel( zero, diagonal, _VECTORMATH_MASK_0x00F0 ); - result->col3.vec128 = vec_sel( column, ((vec_float4){1.0f,1.0f,1.0f,1.0f}), _VECTORMATH_MASK_0x000F ); -} - -static inline void vmathM4Select( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1, unsigned int select1 ) -{ - vmathV4Select( &result->col0, &mat0->col0, &mat1->col0, select1 ); - vmathV4Select( &result->col1, &mat0->col1, &mat1->col1, select1 ); - vmathV4Select( &result->col2, &mat0->col2, &mat1->col2, select1 ); - vmathV4Select( &result->col3, &mat0->col3, &mat1->col3, select1 ); -} - -#ifdef _VECTORMATH_DEBUG - -static inline void vmathM4Print( const VmathMatrix4 *mat ) -{ - VmathVector4 tmpV4_0, tmpV4_1, tmpV4_2, tmpV4_3; - vmathM4GetRow( &tmpV4_0, mat, 0 ); - vmathV4Print( &tmpV4_0 ); - vmathM4GetRow( &tmpV4_1, mat, 1 ); - vmathV4Print( &tmpV4_1 ); - vmathM4GetRow( &tmpV4_2, mat, 2 ); - vmathV4Print( &tmpV4_2 ); - vmathM4GetRow( &tmpV4_3, mat, 3 ); - vmathV4Print( &tmpV4_3 ); -} - -static inline void vmathM4Prints( const VmathMatrix4 *mat, const char *name ) -{ - printf("%s:\n", name); - vmathM4Print( mat ); -} - -#endif - -static inline void vmathT3Copy( VmathTransform3 *result, const VmathTransform3 *tfrm ) -{ - vmathV3Copy( &result->col0, &tfrm->col0 ); - vmathV3Copy( &result->col1, &tfrm->col1 ); - vmathV3Copy( &result->col2, &tfrm->col2 ); - vmathV3Copy( &result->col3, &tfrm->col3 ); -} - -static inline void vmathT3MakeFromScalar( VmathTransform3 *result, float scalar ) -{ - vmathV3MakeFromScalar( &result->col0, scalar ); - vmathV3MakeFromScalar( &result->col1, scalar ); - vmathV3MakeFromScalar( &result->col2, scalar ); - vmathV3MakeFromScalar( &result->col3, scalar ); -} - -static inline void vmathT3MakeFromCols( VmathTransform3 *result, const VmathVector3 *_col0, const VmathVector3 *_col1, const VmathVector3 *_col2, const VmathVector3 *_col3 ) -{ - vmathV3Copy( &result->col0, _col0 ); - vmathV3Copy( &result->col1, _col1 ); - vmathV3Copy( &result->col2, _col2 ); - vmathV3Copy( &result->col3, _col3 ); -} - -static inline void vmathT3MakeFromM3V3( VmathTransform3 *result, const VmathMatrix3 *tfrm, const VmathVector3 *translateVec ) -{ - vmathT3SetUpper3x3( result, tfrm ); - vmathT3SetTranslation( result, translateVec ); -} - -static inline void vmathT3MakeFromQV3( VmathTransform3 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ) -{ - VmathMatrix3 tmpM3_0; - vmathM3MakeFromQ( &tmpM3_0, unitQuat ); - vmathT3SetUpper3x3( result, &tmpM3_0 ); - vmathT3SetTranslation( result, translateVec ); -} - -static inline void vmathT3SetCol0( VmathTransform3 *result, const VmathVector3 *_col0 ) -{ - vmathV3Copy( &result->col0, _col0 ); -} - -static inline void vmathT3SetCol1( VmathTransform3 *result, const VmathVector3 *_col1 ) -{ - vmathV3Copy( &result->col1, _col1 ); -} - -static inline void vmathT3SetCol2( VmathTransform3 *result, const VmathVector3 *_col2 ) -{ - vmathV3Copy( &result->col2, _col2 ); -} - -static inline void vmathT3SetCol3( VmathTransform3 *result, const VmathVector3 *_col3 ) -{ - vmathV3Copy( &result->col3, _col3 ); -} - -static inline void vmathT3SetCol( VmathTransform3 *result, int col, const VmathVector3 *vec ) -{ - vmathV3Copy( (&result->col0 + col), vec ); -} - -static inline void vmathT3SetRow( VmathTransform3 *result, int row, const VmathVector4 *vec ) -{ - vmathV3SetElem( &result->col0, row, vmathV4GetElem( vec, 0 ) ); - vmathV3SetElem( &result->col1, row, vmathV4GetElem( vec, 1 ) ); - vmathV3SetElem( &result->col2, row, vmathV4GetElem( vec, 2 ) ); - vmathV3SetElem( &result->col3, row, vmathV4GetElem( vec, 3 ) ); -} - -static inline void vmathT3SetElem( VmathTransform3 *result, int col, int row, float val ) -{ - VmathVector3 tmpV3_0; - vmathT3GetCol( &tmpV3_0, result, col ); - vmathV3SetElem( &tmpV3_0, row, val ); - vmathT3SetCol( result, col, &tmpV3_0 ); -} - -static inline float vmathT3GetElem( const VmathTransform3 *tfrm, int col, int row ) -{ - VmathVector3 tmpV3_0; - vmathT3GetCol( &tmpV3_0, tfrm, col ); - return vmathV3GetElem( &tmpV3_0, row ); -} - -static inline void vmathT3GetCol0( VmathVector3 *result, const VmathTransform3 *tfrm ) -{ - vmathV3Copy( result, &tfrm->col0 ); -} - -static inline void vmathT3GetCol1( VmathVector3 *result, const VmathTransform3 *tfrm ) -{ - vmathV3Copy( result, &tfrm->col1 ); -} - -static inline void vmathT3GetCol2( VmathVector3 *result, const VmathTransform3 *tfrm ) -{ - vmathV3Copy( result, &tfrm->col2 ); -} - -static inline void vmathT3GetCol3( VmathVector3 *result, const VmathTransform3 *tfrm ) -{ - vmathV3Copy( result, &tfrm->col3 ); -} - -static inline void vmathT3GetCol( VmathVector3 *result, const VmathTransform3 *tfrm, int col ) -{ - vmathV3Copy( result, (&tfrm->col0 + col) ); -} - -static inline void vmathT3GetRow( VmathVector4 *result, const VmathTransform3 *tfrm, int row ) -{ - vmathV4MakeFromElems( result, vmathV3GetElem( &tfrm->col0, row ), vmathV3GetElem( &tfrm->col1, row ), vmathV3GetElem( &tfrm->col2, row ), vmathV3GetElem( &tfrm->col3, row ) ); -} - -static inline void vmathT3Inverse( VmathTransform3 *result, const VmathTransform3 *tfrm ) -{ - vec_float4 inv0, inv1, inv2, inv3; - vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet; - vec_float4 xxxx, yyyy, zzzz; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - tmp2 = _vmathVfCross( tfrm->col0.vec128, tfrm->col1.vec128 ); - tmp0 = _vmathVfCross( tfrm->col1.vec128, tfrm->col2.vec128 ); - tmp1 = _vmathVfCross( tfrm->col2.vec128, tfrm->col0.vec128 ); - inv3 = negatef4( tfrm->col3.vec128 ); - dot = _vmathVfDot3( tmp2, tfrm->col2.vec128 ); - dot = vec_splat( dot, 0 ); - invdet = recipf4( dot ); - tmp3 = vec_mergeh( tmp0, tmp2 ); - tmp4 = vec_mergel( tmp0, tmp2 ); - inv0 = vec_mergeh( tmp3, tmp1 ); - xxxx = vec_splat( inv3, 0 ); - inv1 = vec_perm( tmp3, tmp1, _VECTORMATH_PERM_ZBWX ); - inv2 = vec_perm( tmp4, tmp1, _VECTORMATH_PERM_XCYX ); - yyyy = vec_splat( inv3, 1 ); - zzzz = vec_splat( inv3, 2 ); - inv3 = vec_madd( inv0, xxxx, zero ); - inv3 = vec_madd( inv1, yyyy, inv3 ); - inv3 = vec_madd( inv2, zzzz, inv3 ); - inv0 = vec_madd( inv0, invdet, zero ); - inv1 = vec_madd( inv1, invdet, zero ); - inv2 = vec_madd( inv2, invdet, zero ); - inv3 = vec_madd( inv3, invdet, zero ); - result->col0.vec128 = inv0; - result->col1.vec128 = inv1; - result->col2.vec128 = inv2; - result->col3.vec128 = inv3; -} - -static inline void vmathT3OrthoInverse( VmathTransform3 *result, const VmathTransform3 *tfrm ) -{ - vec_float4 inv0, inv1, inv2, inv3; - vec_float4 tmp0, tmp1; - vec_float4 xxxx, yyyy, zzzz; - tmp0 = vec_mergeh( tfrm->col0.vec128, tfrm->col2.vec128 ); - tmp1 = vec_mergel( tfrm->col0.vec128, tfrm->col2.vec128 ); - inv3 = negatef4( tfrm->col3.vec128 ); - inv0 = vec_mergeh( tmp0, tfrm->col1.vec128 ); - xxxx = vec_splat( inv3, 0 ); - inv1 = vec_perm( tmp0, tfrm->col1.vec128, _VECTORMATH_PERM_ZBWX ); - inv2 = vec_perm( tmp1, tfrm->col1.vec128, _VECTORMATH_PERM_XCYX ); - yyyy = vec_splat( inv3, 1 ); - zzzz = vec_splat( inv3, 2 ); - inv3 = vec_madd( inv0, xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - inv3 = vec_madd( inv1, yyyy, inv3 ); - inv3 = vec_madd( inv2, zzzz, inv3 ); - result->col0.vec128 = inv0; - result->col1.vec128 = inv1; - result->col2.vec128 = inv2; - result->col3.vec128 = inv3; -} - -static inline void vmathT3AbsPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm ) -{ - vmathV3AbsPerElem( &result->col0, &tfrm->col0 ); - vmathV3AbsPerElem( &result->col1, &tfrm->col1 ); - vmathV3AbsPerElem( &result->col2, &tfrm->col2 ); - vmathV3AbsPerElem( &result->col3, &tfrm->col3 ); -} - -static inline void vmathT3MulV3( VmathVector3 *result, const VmathTransform3 *tfrm, const VmathVector3 *vec ) -{ - vec_float4 res; - vec_float4 xxxx, yyyy, zzzz; - xxxx = vec_splat( vec->vec128, 0 ); - yyyy = vec_splat( vec->vec128, 1 ); - zzzz = vec_splat( vec->vec128, 2 ); - res = vec_madd( tfrm->col0.vec128, xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - res = vec_madd( tfrm->col1.vec128, yyyy, res ); - res = vec_madd( tfrm->col2.vec128, zzzz, res ); - result->vec128 = res; -} - -static inline void vmathT3MulP3( VmathPoint3 *result, const VmathTransform3 *tfrm, const VmathPoint3 *pnt ) -{ - vec_float4 tmp0, tmp1, res; - vec_float4 xxxx, yyyy, zzzz; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - xxxx = vec_splat( pnt->vec128, 0 ); - yyyy = vec_splat( pnt->vec128, 1 ); - zzzz = vec_splat( pnt->vec128, 2 ); - tmp0 = vec_madd( tfrm->col0.vec128, xxxx, zero ); - tmp1 = vec_madd( tfrm->col1.vec128, yyyy, zero ); - tmp0 = vec_madd( tfrm->col2.vec128, zzzz, tmp0 ); - tmp1 = vec_add( tfrm->col3.vec128, tmp1 ); - res = vec_add( tmp0, tmp1 ); - result->vec128 = res; -} - -static inline void vmathT3Mul( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ) -{ - VmathTransform3 tmpResult; - VmathPoint3 tmpP3_0, tmpP3_1; - vmathT3MulV3( &tmpResult.col0, tfrm0, &tfrm1->col0 ); - vmathT3MulV3( &tmpResult.col1, tfrm0, &tfrm1->col1 ); - vmathT3MulV3( &tmpResult.col2, tfrm0, &tfrm1->col2 ); - vmathP3MakeFromV3( &tmpP3_0, &tfrm1->col3 ); - vmathT3MulP3( &tmpP3_1, tfrm0, &tmpP3_0 ); - vmathV3MakeFromP3( &tmpResult.col3, &tmpP3_1 ); - vmathT3Copy( result, &tmpResult ); -} - -static inline void vmathT3MulPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ) -{ - vmathV3MulPerElem( &result->col0, &tfrm0->col0, &tfrm1->col0 ); - vmathV3MulPerElem( &result->col1, &tfrm0->col1, &tfrm1->col1 ); - vmathV3MulPerElem( &result->col2, &tfrm0->col2, &tfrm1->col2 ); - vmathV3MulPerElem( &result->col3, &tfrm0->col3, &tfrm1->col3 ); -} - -static inline void vmathT3MakeIdentity( VmathTransform3 *result ) -{ - vmathV3MakeXAxis( &result->col0 ); - vmathV3MakeYAxis( &result->col1 ); - vmathV3MakeZAxis( &result->col2 ); - vmathV3MakeFromScalar( &result->col3, 0.0f ); -} - -static inline void vmathT3SetUpper3x3( VmathTransform3 *result, const VmathMatrix3 *tfrm ) -{ - vmathV3Copy( &result->col0, &tfrm->col0 ); - vmathV3Copy( &result->col1, &tfrm->col1 ); - vmathV3Copy( &result->col2, &tfrm->col2 ); -} - -static inline void vmathT3GetUpper3x3( VmathMatrix3 *result, const VmathTransform3 *tfrm ) -{ - vmathM3MakeFromCols( result, &tfrm->col0, &tfrm->col1, &tfrm->col2 ); -} - -static inline void vmathT3SetTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ) -{ - vmathV3Copy( &result->col3, translateVec ); -} - -static inline void vmathT3GetTranslation( VmathVector3 *result, const VmathTransform3 *tfrm ) -{ - vmathV3Copy( result, &tfrm->col3 ); -} - -static inline void vmathT3MakeRotationX( VmathTransform3 *result, float radians ) -{ - vec_float4 s, c, res1, res2; - vec_uint4 select_y, select_z; - vec_float4 zero; - select_y = _VECTORMATH_MASK_0x0F00; - select_z = _VECTORMATH_MASK_0x00F0; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( _vmathVfSplatScalar(radians), &s, &c ); - res1 = vec_sel( zero, c, select_y ); - res1 = vec_sel( res1, s, select_z ); - res2 = vec_sel( zero, negatef4(s), select_y ); - res2 = vec_sel( res2, c, select_z ); - vmathV3MakeXAxis( &result->col0 ); - result->col1.vec128 = res1; - result->col2.vec128 = res2; - vmathV3MakeFromScalar( &result->col3, 0.0f ); -} - -static inline void vmathT3MakeRotationY( VmathTransform3 *result, float radians ) -{ - vec_float4 s, c, res0, res2; - vec_uint4 select_x, select_z; - vec_float4 zero; - select_x = _VECTORMATH_MASK_0xF000; - select_z = _VECTORMATH_MASK_0x00F0; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( _vmathVfSplatScalar(radians), &s, &c ); - res0 = vec_sel( zero, c, select_x ); - res0 = vec_sel( res0, negatef4(s), select_z ); - res2 = vec_sel( zero, s, select_x ); - res2 = vec_sel( res2, c, select_z ); - result->col0.vec128 = res0; - vmathV3MakeYAxis( &result->col1 ); - result->col2.vec128 = res2; - vmathV3MakeFromScalar( &result->col3, 0.0f ); -} - -static inline void vmathT3MakeRotationZ( VmathTransform3 *result, float radians ) -{ - vec_float4 s, c, res0, res1; - vec_uint4 select_x, select_y; - vec_float4 zero; - select_x = _VECTORMATH_MASK_0xF000; - select_y = _VECTORMATH_MASK_0x0F00; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( _vmathVfSplatScalar(radians), &s, &c ); - res0 = vec_sel( zero, c, select_x ); - res0 = vec_sel( res0, s, select_y ); - res1 = vec_sel( zero, negatef4(s), select_x ); - res1 = vec_sel( res1, c, select_y ); - result->col0.vec128 = res0; - result->col1.vec128 = res1; - vmathV3MakeZAxis( &result->col2 ); - vmathV3MakeFromScalar( &result->col3, 0.0f ); -} - -static inline void vmathT3MakeRotationZYX( VmathTransform3 *result, const VmathVector3 *radiansXYZ ) -{ - VmathVector4 tmpV4_0; - vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - vmathV4MakeFromV3Scalar( &tmpV4_0, radiansXYZ, 0.0f ); - angles = tmpV4_0.vec128; - sincosf4( angles, &s, &c ); - negS = negatef4( s ); - Z0 = vec_mergel( c, s ); - Z1 = vec_mergel( negS, c ); - Z1 = vec_andc( Z1, (vec_float4)_VECTORMATH_MASK_0x000F ); - Y0 = vec_perm( negS, c, _VECTORMATH_PERM_BBYX ); - Y1 = vec_perm( c, s, _VECTORMATH_PERM_BBYX ); - X0 = vec_splat( s, 0 ); - X1 = vec_splat( c, 0 ); - tmp = vec_madd( Z0, Y1, zero ); - result->col0.vec128 = vec_madd( Z0, Y0, zero ); - result->col1.vec128 = vec_madd( Z1, X1, vec_madd( tmp, X0, zero ) ); - result->col2.vec128 = vec_nmsub( Z1, X0, vec_madd( tmp, X1, zero ) ); - vmathV3MakeFromScalar( &result->col3, 0.0f ); -} - -static inline void vmathT3MakeRotationAxis( VmathTransform3 *result, float radians, const VmathVector3 *unitVec ) -{ - VmathMatrix3 tmpM3_0; - VmathVector3 tmpV3_0; - vmathM3MakeRotationAxis( &tmpM3_0, radians, unitVec ); - vmathV3MakeFromScalar( &tmpV3_0, 0.0f ); - vmathT3MakeFromM3V3( result, &tmpM3_0, &tmpV3_0 ); -} - -static inline void vmathT3MakeRotationQ( VmathTransform3 *result, const VmathQuat *unitQuat ) -{ - VmathMatrix3 tmpM3_0; - VmathVector3 tmpV3_0; - vmathM3MakeFromQ( &tmpM3_0, unitQuat ); - vmathV3MakeFromScalar( &tmpV3_0, 0.0f ); - vmathT3MakeFromM3V3( result, &tmpM3_0, &tmpV3_0 ); -} - -static inline void vmathT3MakeScale( VmathTransform3 *result, const VmathVector3 *scaleVec ) -{ - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - result->col0.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0xF000 ); - result->col1.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0x0F00 ); - result->col2.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0x00F0 ); - vmathV3MakeFromScalar( &result->col3, 0.0f ); -} - -static inline void vmathT3AppendScale( VmathTransform3 *result, const VmathTransform3 *tfrm, const VmathVector3 *scaleVec ) -{ - vmathV3ScalarMul( &result->col0, &tfrm->col0, vmathV3GetX( scaleVec ) ); - vmathV3ScalarMul( &result->col1, &tfrm->col1, vmathV3GetY( scaleVec ) ); - vmathV3ScalarMul( &result->col2, &tfrm->col2, vmathV3GetZ( scaleVec ) ); - vmathV3Copy( &result->col3, &tfrm->col3 ); -} - -static inline void vmathT3PrependScale( VmathTransform3 *result, const VmathVector3 *scaleVec, const VmathTransform3 *tfrm ) -{ - vmathV3MulPerElem( &result->col0, &tfrm->col0, scaleVec ); - vmathV3MulPerElem( &result->col1, &tfrm->col1, scaleVec ); - vmathV3MulPerElem( &result->col2, &tfrm->col2, scaleVec ); - vmathV3MulPerElem( &result->col3, &tfrm->col3, scaleVec ); -} - -static inline void vmathT3MakeTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ) -{ - vmathV3MakeXAxis( &result->col0 ); - vmathV3MakeYAxis( &result->col1 ); - vmathV3MakeZAxis( &result->col2 ); - vmathV3Copy( &result->col3, translateVec ); -} - -static inline void vmathT3Select( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1, unsigned int select1 ) -{ - vmathV3Select( &result->col0, &tfrm0->col0, &tfrm1->col0, select1 ); - vmathV3Select( &result->col1, &tfrm0->col1, &tfrm1->col1, select1 ); - vmathV3Select( &result->col2, &tfrm0->col2, &tfrm1->col2, select1 ); - vmathV3Select( &result->col3, &tfrm0->col3, &tfrm1->col3, select1 ); -} - -#ifdef _VECTORMATH_DEBUG - -static inline void vmathT3Print( const VmathTransform3 *tfrm ) -{ - VmathVector4 tmpV4_0, tmpV4_1, tmpV4_2; - vmathT3GetRow( &tmpV4_0, tfrm, 0 ); - vmathV4Print( &tmpV4_0 ); - vmathT3GetRow( &tmpV4_1, tfrm, 1 ); - vmathV4Print( &tmpV4_1 ); - vmathT3GetRow( &tmpV4_2, tfrm, 2 ); - vmathV4Print( &tmpV4_2 ); -} - -static inline void vmathT3Prints( const VmathTransform3 *tfrm, const char *name ) -{ - printf("%s:\n", name); - vmathT3Print( tfrm ); -} - -#endif - -static inline void vmathQMakeFromM3( VmathQuat *result, const VmathMatrix3 *tfrm ) -{ - vec_float4 res; - vec_float4 col0, col1, col2; - vec_float4 xx_yy, xx_yy_zz_xx, yy_zz_xx_yy, zz_xx_yy_zz, diagSum, diagDiff; - vec_float4 zy_xz_yx, yz_zx_xy, sum, diff; - vec_float4 radicand, invSqrt, scale; - vec_float4 res0, res1, res2, res3; - vec_float4 xx, yy, zz; - vec_uint4 select_x = _VECTORMATH_MASK_0xF000; - vec_uint4 select_y = _VECTORMATH_MASK_0x0F00; - vec_uint4 select_z = _VECTORMATH_MASK_0x00F0; - vec_uint4 select_w = _VECTORMATH_MASK_0x000F; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - - col0 = tfrm->col0.vec128; - col1 = tfrm->col1.vec128; - col2 = tfrm->col2.vec128; - - /* four cases: */ - /* trace > 0 */ - /* else */ - /* xx largest diagonal element */ - /* yy largest diagonal element */ - /* zz largest diagonal element */ - - /* compute quaternion for each case */ - - xx_yy = vec_sel( col0, col1, select_y ); - xx_yy_zz_xx = vec_perm( xx_yy, col2, _VECTORMATH_PERM_XYCX ); - yy_zz_xx_yy = vec_perm( xx_yy, col2, _VECTORMATH_PERM_YCXY ); - zz_xx_yy_zz = vec_perm( xx_yy, col2, _VECTORMATH_PERM_CXYC ); - - diagSum = vec_add( vec_add( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); - diagDiff = vec_sub( vec_sub( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); - radicand = vec_add( vec_sel( diagDiff, diagSum, select_w ), ((vec_float4){1.0f,1.0f,1.0f,1.0f}) ); - invSqrt = rsqrtf4( radicand ); - - zy_xz_yx = vec_sel( col0, col1, select_z ); - zy_xz_yx = vec_perm( zy_xz_yx, col2, _VECTORMATH_PERM_ZAYX ); - yz_zx_xy = vec_sel( col0, col1, select_x ); - yz_zx_xy = vec_perm( yz_zx_xy, col2, _VECTORMATH_PERM_BZXX ); - - sum = vec_add( zy_xz_yx, yz_zx_xy ); - diff = vec_sub( zy_xz_yx, yz_zx_xy ); - - scale = vec_madd( invSqrt, ((vec_float4){0.5f,0.5f,0.5f,0.5f}), zero ); - res0 = vec_perm( sum, diff, _VECTORMATH_PERM_XZYA ); - res1 = vec_perm( sum, diff, _VECTORMATH_PERM_ZXXB ); - res2 = vec_perm( sum, diff, _VECTORMATH_PERM_YXXC ); - res3 = diff; - res0 = vec_sel( res0, radicand, select_x ); - res1 = vec_sel( res1, radicand, select_y ); - res2 = vec_sel( res2, radicand, select_z ); - res3 = vec_sel( res3, radicand, select_w ); - res0 = vec_madd( res0, vec_splat( scale, 0 ), zero ); - res1 = vec_madd( res1, vec_splat( scale, 1 ), zero ); - res2 = vec_madd( res2, vec_splat( scale, 2 ), zero ); - res3 = vec_madd( res3, vec_splat( scale, 3 ), zero ); - - /* determine case and select answer */ - - xx = vec_splat( col0, 0 ); - yy = vec_splat( col1, 1 ); - zz = vec_splat( col2, 2 ); - res = vec_sel( res0, res1, vec_cmpgt( yy, xx ) ); - res = vec_sel( res, res2, vec_and( vec_cmpgt( zz, xx ), vec_cmpgt( zz, yy ) ) ); - res = vec_sel( res, res3, vec_cmpgt( vec_splat( diagSum, 0 ), zero ) ); - result->vec128 = res; -} - -static inline void vmathV3Outer( VmathMatrix3 *result, const VmathVector3 *tfrm0, const VmathVector3 *tfrm1 ) -{ - vmathV3ScalarMul( &result->col0, tfrm0, vmathV3GetX( tfrm1 ) ); - vmathV3ScalarMul( &result->col1, tfrm0, vmathV3GetY( tfrm1 ) ); - vmathV3ScalarMul( &result->col2, tfrm0, vmathV3GetZ( tfrm1 ) ); -} - -static inline void vmathV4Outer( VmathMatrix4 *result, const VmathVector4 *tfrm0, const VmathVector4 *tfrm1 ) -{ - vmathV4ScalarMul( &result->col0, tfrm0, vmathV4GetX( tfrm1 ) ); - vmathV4ScalarMul( &result->col1, tfrm0, vmathV4GetY( tfrm1 ) ); - vmathV4ScalarMul( &result->col2, tfrm0, vmathV4GetZ( tfrm1 ) ); - vmathV4ScalarMul( &result->col3, tfrm0, vmathV4GetW( tfrm1 ) ); -} - -static inline void vmathV3RowMul( VmathVector3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ) -{ - vec_float4 tmp0, tmp1, mcol0, mcol1, mcol2, res; - vec_float4 xxxx, yyyy, zzzz; - tmp0 = vec_mergeh( mat->col0.vec128, mat->col2.vec128 ); - tmp1 = vec_mergel( mat->col0.vec128, mat->col2.vec128 ); - xxxx = vec_splat( vec->vec128, 0 ); - mcol0 = vec_mergeh( tmp0, mat->col1.vec128 ); - mcol1 = vec_perm( tmp0, mat->col1.vec128, _VECTORMATH_PERM_ZBWX ); - mcol2 = vec_perm( tmp1, mat->col1.vec128, _VECTORMATH_PERM_XCYX ); - yyyy = vec_splat( vec->vec128, 1 ); - res = vec_madd( mcol0, xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - zzzz = vec_splat( vec->vec128, 2 ); - res = vec_madd( mcol1, yyyy, res ); - res = vec_madd( mcol2, zzzz, res ); - result->vec128 = res; -} - -static inline void vmathV3CrossMatrix( VmathMatrix3 *result, const VmathVector3 *vec ) -{ - vec_float4 neg, res0, res1, res2; - neg = negatef4( vec->vec128 ); - res0 = vec_perm( vec->vec128, neg, _VECTORMATH_PERM_XZBX ); - res1 = vec_perm( vec->vec128, neg, _VECTORMATH_PERM_CXXX ); - res2 = vec_perm( vec->vec128, neg, _VECTORMATH_PERM_YAXX ); - res0 = vec_andc( res0, (vec_float4)_VECTORMATH_MASK_0xF000 ); - res1 = vec_andc( res1, (vec_float4)_VECTORMATH_MASK_0x0F00 ); - res2 = vec_andc( res2, (vec_float4)_VECTORMATH_MASK_0x00F0 ); - result->col0.vec128 = res0; - result->col1.vec128 = res1; - result->col2.vec128 = res2; -} - -static inline void vmathV3CrossMatrixMul( VmathMatrix3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ) -{ - VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2; - vmathV3Cross( &tmpV3_0, vec, &mat->col0 ); - vmathV3Cross( &tmpV3_1, vec, &mat->col1 ); - vmathV3Cross( &tmpV3_2, vec, &mat->col2 ); - vmathM3MakeFromCols( result, &tmpV3_0, &tmpV3_1, &tmpV3_2 ); -} - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_MAT_AOS_C_H +#define _VECTORMATH_MAT_AOS_C_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*----------------------------------------------------------------------------- + * Constants + * for shuffles, words are labeled [x,y,z,w] [a,b,c,d] + */ +#define _VECTORMATH_PERM_ZBWX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_B, _VECTORMATH_PERM_W, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_XCYX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_C, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_XYAB ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A, _VECTORMATH_PERM_B }) +#define _VECTORMATH_PERM_ZWCD ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_W, _VECTORMATH_PERM_C, _VECTORMATH_PERM_D }) +#define _VECTORMATH_PERM_XZBX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_B, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_CXXX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_C, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_YAXX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_XAZC ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_A, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_C }) +#define _VECTORMATH_PERM_YXWZ ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X, _VECTORMATH_PERM_W, _VECTORMATH_PERM_Z }) +#define _VECTORMATH_PERM_YBWD ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_B, _VECTORMATH_PERM_W, _VECTORMATH_PERM_D }) +#define _VECTORMATH_PERM_XYCX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_C, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_YCXY ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_C, _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y }) +#define _VECTORMATH_PERM_CXYC ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_C, _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_C }) +#define _VECTORMATH_PERM_ZAYX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_BZXX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_B, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_XZYA ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A }) +#define _VECTORMATH_PERM_ZXXB ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X, _VECTORMATH_PERM_B }) +#define _VECTORMATH_PERM_YXXC ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X, _VECTORMATH_PERM_C }) +#define _VECTORMATH_PERM_BBYX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_B, _VECTORMATH_PERM_B, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PI_OVER_2 1.570796327f + +/*----------------------------------------------------------------------------- + * Definitions + */ +static inline void vmathM3Copy( VmathMatrix3 *result, const VmathMatrix3 *mat ) +{ + vmathV3Copy( &result->col0, &mat->col0 ); + vmathV3Copy( &result->col1, &mat->col1 ); + vmathV3Copy( &result->col2, &mat->col2 ); +} + +static inline void vmathM3MakeFromScalar( VmathMatrix3 *result, float scalar ) +{ + vmathV3MakeFromScalar( &result->col0, scalar ); + vmathV3MakeFromScalar( &result->col1, scalar ); + vmathV3MakeFromScalar( &result->col2, scalar ); +} + +static inline void vmathM3MakeFromQ( VmathMatrix3 *result, const VmathQuat *unitQuat ) +{ + vec_float4 xyzw_2, wwww, yzxw, zxyw, yzxw_2, zxyw_2; + vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + vec_uint4 select_x = _VECTORMATH_MASK_0xF000; + vec_uint4 select_z = _VECTORMATH_MASK_0x00F0; + xyzw_2 = vec_add( unitQuat->vec128, unitQuat->vec128 ); + wwww = vec_splat( unitQuat->vec128, 3 ); + yzxw = vec_perm( unitQuat->vec128, unitQuat->vec128, _VECTORMATH_PERM_YZXW ); + zxyw = vec_perm( unitQuat->vec128, unitQuat->vec128, _VECTORMATH_PERM_ZXYW ); + yzxw_2 = vec_perm( xyzw_2, xyzw_2, _VECTORMATH_PERM_YZXW ); + zxyw_2 = vec_perm( xyzw_2, xyzw_2, _VECTORMATH_PERM_ZXYW ); + tmp0 = vec_madd( yzxw_2, wwww, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + tmp1 = vec_nmsub( yzxw, yzxw_2, ((vec_float4){1.0f,1.0f,1.0f,1.0f}) ); + tmp2 = vec_madd( yzxw, xyzw_2, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + tmp0 = vec_madd( zxyw, xyzw_2, tmp0 ); + tmp1 = vec_nmsub( zxyw, zxyw_2, tmp1 ); + tmp2 = vec_nmsub( zxyw_2, wwww, tmp2 ); + tmp3 = vec_sel( tmp0, tmp1, select_x ); + tmp4 = vec_sel( tmp1, tmp2, select_x ); + tmp5 = vec_sel( tmp2, tmp0, select_x ); + result->col0.vec128 = vec_sel( tmp3, tmp2, select_z ); + result->col1.vec128 = vec_sel( tmp4, tmp0, select_z ); + result->col2.vec128 = vec_sel( tmp5, tmp1, select_z ); +} + +static inline void vmathM3MakeFromCols( VmathMatrix3 *result, const VmathVector3 *_col0, const VmathVector3 *_col1, const VmathVector3 *_col2 ) +{ + vmathV3Copy( &result->col0, _col0 ); + vmathV3Copy( &result->col1, _col1 ); + vmathV3Copy( &result->col2, _col2 ); +} + +static inline void vmathM3SetCol0( VmathMatrix3 *result, const VmathVector3 *_col0 ) +{ + vmathV3Copy( &result->col0, _col0 ); +} + +static inline void vmathM3SetCol1( VmathMatrix3 *result, const VmathVector3 *_col1 ) +{ + vmathV3Copy( &result->col1, _col1 ); +} + +static inline void vmathM3SetCol2( VmathMatrix3 *result, const VmathVector3 *_col2 ) +{ + vmathV3Copy( &result->col2, _col2 ); +} + +static inline void vmathM3SetCol( VmathMatrix3 *result, int col, const VmathVector3 *vec ) +{ + vmathV3Copy( (&result->col0 + col), vec ); +} + +static inline void vmathM3SetRow( VmathMatrix3 *result, int row, const VmathVector3 *vec ) +{ + vmathV3SetElem( &result->col0, row, vmathV3GetElem( vec, 0 ) ); + vmathV3SetElem( &result->col1, row, vmathV3GetElem( vec, 1 ) ); + vmathV3SetElem( &result->col2, row, vmathV3GetElem( vec, 2 ) ); +} + +static inline void vmathM3SetElem( VmathMatrix3 *result, int col, int row, float val ) +{ + VmathVector3 tmpV3_0; + vmathM3GetCol( &tmpV3_0, result, col ); + vmathV3SetElem( &tmpV3_0, row, val ); + vmathM3SetCol( result, col, &tmpV3_0 ); +} + +static inline float vmathM3GetElem( const VmathMatrix3 *mat, int col, int row ) +{ + VmathVector3 tmpV3_0; + vmathM3GetCol( &tmpV3_0, mat, col ); + return vmathV3GetElem( &tmpV3_0, row ); +} + +static inline void vmathM3GetCol0( VmathVector3 *result, const VmathMatrix3 *mat ) +{ + vmathV3Copy( result, &mat->col0 ); +} + +static inline void vmathM3GetCol1( VmathVector3 *result, const VmathMatrix3 *mat ) +{ + vmathV3Copy( result, &mat->col1 ); +} + +static inline void vmathM3GetCol2( VmathVector3 *result, const VmathMatrix3 *mat ) +{ + vmathV3Copy( result, &mat->col2 ); +} + +static inline void vmathM3GetCol( VmathVector3 *result, const VmathMatrix3 *mat, int col ) +{ + vmathV3Copy( result, (&mat->col0 + col) ); +} + +static inline void vmathM3GetRow( VmathVector3 *result, const VmathMatrix3 *mat, int row ) +{ + vmathV3MakeFromElems( result, vmathV3GetElem( &mat->col0, row ), vmathV3GetElem( &mat->col1, row ), vmathV3GetElem( &mat->col2, row ) ); +} + +static inline void vmathM3Transpose( VmathMatrix3 *result, const VmathMatrix3 *mat ) +{ + vec_float4 tmp0, tmp1, res0, res1, res2; + tmp0 = vec_mergeh( mat->col0.vec128, mat->col2.vec128 ); + tmp1 = vec_mergel( mat->col0.vec128, mat->col2.vec128 ); + res0 = vec_mergeh( tmp0, mat->col1.vec128 ); + res1 = vec_perm( tmp0, mat->col1.vec128, _VECTORMATH_PERM_ZBWX ); + res2 = vec_perm( tmp1, mat->col1.vec128, _VECTORMATH_PERM_XCYX ); + result->col0.vec128 = res0; + result->col1.vec128 = res1; + result->col2.vec128 = res2; +} + +static inline void vmathM3Inverse( VmathMatrix3 *result, const VmathMatrix3 *mat ) +{ + vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet, inv0, inv1, inv2; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + tmp2 = _vmathVfCross( mat->col0.vec128, mat->col1.vec128 ); + tmp0 = _vmathVfCross( mat->col1.vec128, mat->col2.vec128 ); + tmp1 = _vmathVfCross( mat->col2.vec128, mat->col0.vec128 ); + dot = _vmathVfDot3( tmp2, mat->col2.vec128 ); + dot = vec_splat( dot, 0 ); + invdet = recipf4( dot ); + tmp3 = vec_mergeh( tmp0, tmp2 ); + tmp4 = vec_mergel( tmp0, tmp2 ); + inv0 = vec_mergeh( tmp3, tmp1 ); + inv1 = vec_perm( tmp3, tmp1, _VECTORMATH_PERM_ZBWX ); + inv2 = vec_perm( tmp4, tmp1, _VECTORMATH_PERM_XCYX ); + inv0 = vec_madd( inv0, invdet, zero ); + inv1 = vec_madd( inv1, invdet, zero ); + inv2 = vec_madd( inv2, invdet, zero ); + result->col0.vec128 = inv0; + result->col1.vec128 = inv1; + result->col2.vec128 = inv2; +} + +static inline float vmathM3Determinant( const VmathMatrix3 *mat ) +{ + VmathVector3 tmpV3_0; + vmathV3Cross( &tmpV3_0, &mat->col0, &mat->col1 ); + return vmathV3Dot( &mat->col2, &tmpV3_0 ); +} + +static inline void vmathM3Add( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) +{ + vmathV3Add( &result->col0, &mat0->col0, &mat1->col0 ); + vmathV3Add( &result->col1, &mat0->col1, &mat1->col1 ); + vmathV3Add( &result->col2, &mat0->col2, &mat1->col2 ); +} + +static inline void vmathM3Sub( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) +{ + vmathV3Sub( &result->col0, &mat0->col0, &mat1->col0 ); + vmathV3Sub( &result->col1, &mat0->col1, &mat1->col1 ); + vmathV3Sub( &result->col2, &mat0->col2, &mat1->col2 ); +} + +static inline void vmathM3Neg( VmathMatrix3 *result, const VmathMatrix3 *mat ) +{ + vmathV3Neg( &result->col0, &mat->col0 ); + vmathV3Neg( &result->col1, &mat->col1 ); + vmathV3Neg( &result->col2, &mat->col2 ); +} + +static inline void vmathM3AbsPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat ) +{ + vmathV3AbsPerElem( &result->col0, &mat->col0 ); + vmathV3AbsPerElem( &result->col1, &mat->col1 ); + vmathV3AbsPerElem( &result->col2, &mat->col2 ); +} + +static inline void vmathM3ScalarMul( VmathMatrix3 *result, const VmathMatrix3 *mat, float scalar ) +{ + vmathV3ScalarMul( &result->col0, &mat->col0, scalar ); + vmathV3ScalarMul( &result->col1, &mat->col1, scalar ); + vmathV3ScalarMul( &result->col2, &mat->col2, scalar ); +} + +static inline void vmathM3MulV3( VmathVector3 *result, const VmathMatrix3 *mat, const VmathVector3 *vec ) +{ + vec_float4 res; + vec_float4 xxxx, yyyy, zzzz; + xxxx = vec_splat( vec->vec128, 0 ); + yyyy = vec_splat( vec->vec128, 1 ); + zzzz = vec_splat( vec->vec128, 2 ); + res = vec_madd( mat->col0.vec128, xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + res = vec_madd( mat->col1.vec128, yyyy, res ); + res = vec_madd( mat->col2.vec128, zzzz, res ); + result->vec128 = res; +} + +static inline void vmathM3Mul( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) +{ + VmathMatrix3 tmpResult; + vmathM3MulV3( &tmpResult.col0, mat0, &mat1->col0 ); + vmathM3MulV3( &tmpResult.col1, mat0, &mat1->col1 ); + vmathM3MulV3( &tmpResult.col2, mat0, &mat1->col2 ); + vmathM3Copy( result, &tmpResult ); +} + +static inline void vmathM3MulPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) +{ + vmathV3MulPerElem( &result->col0, &mat0->col0, &mat1->col0 ); + vmathV3MulPerElem( &result->col1, &mat0->col1, &mat1->col1 ); + vmathV3MulPerElem( &result->col2, &mat0->col2, &mat1->col2 ); +} + +static inline void vmathM3MakeIdentity( VmathMatrix3 *result ) +{ + vmathV3MakeXAxis( &result->col0 ); + vmathV3MakeYAxis( &result->col1 ); + vmathV3MakeZAxis( &result->col2 ); +} + +static inline void vmathM3MakeRotationX( VmathMatrix3 *result, float radians ) +{ + vec_float4 s, c, res1, res2; + vec_uint4 select_y, select_z; + vec_float4 zero; + select_y = _VECTORMATH_MASK_0x0F00; + select_z = _VECTORMATH_MASK_0x00F0; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( _vmathVfSplatScalar(radians), &s, &c ); + res1 = vec_sel( zero, c, select_y ); + res1 = vec_sel( res1, s, select_z ); + res2 = vec_sel( zero, negatef4(s), select_y ); + res2 = vec_sel( res2, c, select_z ); + vmathV3MakeXAxis( &result->col0 ); + result->col1.vec128 = res1; + result->col2.vec128 = res2; +} + +static inline void vmathM3MakeRotationY( VmathMatrix3 *result, float radians ) +{ + vec_float4 s, c, res0, res2; + vec_uint4 select_x, select_z; + vec_float4 zero; + select_x = _VECTORMATH_MASK_0xF000; + select_z = _VECTORMATH_MASK_0x00F0; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( _vmathVfSplatScalar(radians), &s, &c ); + res0 = vec_sel( zero, c, select_x ); + res0 = vec_sel( res0, negatef4(s), select_z ); + res2 = vec_sel( zero, s, select_x ); + res2 = vec_sel( res2, c, select_z ); + result->col0.vec128 = res0; + vmathV3MakeYAxis( &result->col1 ); + result->col2.vec128 = res2; +} + +static inline void vmathM3MakeRotationZ( VmathMatrix3 *result, float radians ) +{ + vec_float4 s, c, res0, res1; + vec_uint4 select_x, select_y; + vec_float4 zero; + select_x = _VECTORMATH_MASK_0xF000; + select_y = _VECTORMATH_MASK_0x0F00; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( _vmathVfSplatScalar(radians), &s, &c ); + res0 = vec_sel( zero, c, select_x ); + res0 = vec_sel( res0, s, select_y ); + res1 = vec_sel( zero, negatef4(s), select_x ); + res1 = vec_sel( res1, c, select_y ); + result->col0.vec128 = res0; + result->col1.vec128 = res1; + vmathV3MakeZAxis( &result->col2 ); +} + +static inline void vmathM3MakeRotationZYX( VmathMatrix3 *result, const VmathVector3 *radiansXYZ ) +{ + VmathVector4 tmpV4_0; + vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + vmathV4MakeFromV3Scalar( &tmpV4_0, radiansXYZ, 0.0f ); + angles = tmpV4_0.vec128; + sincosf4( angles, &s, &c ); + negS = negatef4( s ); + Z0 = vec_mergel( c, s ); + Z1 = vec_mergel( negS, c ); + Z1 = vec_andc( Z1, (vec_float4)_VECTORMATH_MASK_0x000F ); + Y0 = vec_perm( negS, c, _VECTORMATH_PERM_BBYX ); + Y1 = vec_perm( c, s, _VECTORMATH_PERM_BBYX ); + X0 = vec_splat( s, 0 ); + X1 = vec_splat( c, 0 ); + tmp = vec_madd( Z0, Y1, zero ); + result->col0.vec128 = vec_madd( Z0, Y0, zero ); + result->col1.vec128 = vec_madd( Z1, X1, vec_madd( tmp, X0, zero ) ); + result->col2.vec128 = vec_nmsub( Z1, X0, vec_madd( tmp, X1, zero ) ); +} + +static inline void vmathM3MakeRotationAxis( VmathMatrix3 *result, float radians, const VmathVector3 *unitVec ) +{ + vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + axis = unitVec->vec128; + sincosf4( (vec_float4){radians,radians,radians,radians}, &s, &c ); + xxxx = vec_splat( axis, 0 ); + yyyy = vec_splat( axis, 1 ); + zzzz = vec_splat( axis, 2 ); + oneMinusC = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), c ); + axisS = vec_madd( axis, s, zero ); + negAxisS = negatef4( axisS ); + tmp0 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_XZBX ); + tmp1 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_CXXX ); + tmp2 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_YAXX ); + tmp0 = vec_sel( tmp0, c, _VECTORMATH_MASK_0xF000 ); + tmp1 = vec_sel( tmp1, c, _VECTORMATH_MASK_0x0F00 ); + tmp2 = vec_sel( tmp2, c, _VECTORMATH_MASK_0x00F0 ); + result->col0.vec128 = vec_madd( vec_madd( axis, xxxx, zero ), oneMinusC, tmp0 ); + result->col1.vec128 = vec_madd( vec_madd( axis, yyyy, zero ), oneMinusC, tmp1 ); + result->col2.vec128 = vec_madd( vec_madd( axis, zzzz, zero ), oneMinusC, tmp2 ); +} + +static inline void vmathM3MakeRotationQ( VmathMatrix3 *result, const VmathQuat *unitQuat ) +{ + vmathM3MakeFromQ( result, unitQuat ); +} + +static inline void vmathM3MakeScale( VmathMatrix3 *result, const VmathVector3 *scaleVec ) +{ + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + result->col0.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0xF000 ); + result->col1.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0x0F00 ); + result->col2.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0x00F0 ); +} + +static inline void vmathM3AppendScale( VmathMatrix3 *result, const VmathMatrix3 *mat, const VmathVector3 *scaleVec ) +{ + vmathV3ScalarMul( &result->col0, &mat->col0, vmathV3GetX( scaleVec ) ); + vmathV3ScalarMul( &result->col1, &mat->col1, vmathV3GetY( scaleVec ) ); + vmathV3ScalarMul( &result->col2, &mat->col2, vmathV3GetZ( scaleVec ) ); +} + +static inline void vmathM3PrependScale( VmathMatrix3 *result, const VmathVector3 *scaleVec, const VmathMatrix3 *mat ) +{ + vmathV3MulPerElem( &result->col0, &mat->col0, scaleVec ); + vmathV3MulPerElem( &result->col1, &mat->col1, scaleVec ); + vmathV3MulPerElem( &result->col2, &mat->col2, scaleVec ); +} + +static inline void vmathM3Select( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1, unsigned int select1 ) +{ + vmathV3Select( &result->col0, &mat0->col0, &mat1->col0, select1 ); + vmathV3Select( &result->col1, &mat0->col1, &mat1->col1, select1 ); + vmathV3Select( &result->col2, &mat0->col2, &mat1->col2, select1 ); +} + +#ifdef _VECTORMATH_DEBUG + +static inline void vmathM3Print( const VmathMatrix3 *mat ) +{ + VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2; + vmathM3GetRow( &tmpV3_0, mat, 0 ); + vmathV3Print( &tmpV3_0 ); + vmathM3GetRow( &tmpV3_1, mat, 1 ); + vmathV3Print( &tmpV3_1 ); + vmathM3GetRow( &tmpV3_2, mat, 2 ); + vmathV3Print( &tmpV3_2 ); +} + +static inline void vmathM3Prints( const VmathMatrix3 *mat, const char *name ) +{ + printf("%s:\n", name); + vmathM3Print( mat ); +} + +#endif + +static inline void vmathM4Copy( VmathMatrix4 *result, const VmathMatrix4 *mat ) +{ + vmathV4Copy( &result->col0, &mat->col0 ); + vmathV4Copy( &result->col1, &mat->col1 ); + vmathV4Copy( &result->col2, &mat->col2 ); + vmathV4Copy( &result->col3, &mat->col3 ); +} + +static inline void vmathM4MakeFromScalar( VmathMatrix4 *result, float scalar ) +{ + vmathV4MakeFromScalar( &result->col0, scalar ); + vmathV4MakeFromScalar( &result->col1, scalar ); + vmathV4MakeFromScalar( &result->col2, scalar ); + vmathV4MakeFromScalar( &result->col3, scalar ); +} + +static inline void vmathM4MakeFromT3( VmathMatrix4 *result, const VmathTransform3 *mat ) +{ + vmathV4MakeFromV3Scalar( &result->col0, &mat->col0, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col1, &mat->col1, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col2, &mat->col2, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col3, &mat->col3, 1.0f ); +} + +static inline void vmathM4MakeFromCols( VmathMatrix4 *result, const VmathVector4 *_col0, const VmathVector4 *_col1, const VmathVector4 *_col2, const VmathVector4 *_col3 ) +{ + vmathV4Copy( &result->col0, _col0 ); + vmathV4Copy( &result->col1, _col1 ); + vmathV4Copy( &result->col2, _col2 ); + vmathV4Copy( &result->col3, _col3 ); +} + +static inline void vmathM4MakeFromM3V3( VmathMatrix4 *result, const VmathMatrix3 *mat, const VmathVector3 *translateVec ) +{ + vmathV4MakeFromV3Scalar( &result->col0, &mat->col0, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col1, &mat->col1, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col2, &mat->col2, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col3, translateVec, 1.0f ); +} + +static inline void vmathM4MakeFromQV3( VmathMatrix4 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ) +{ + VmathMatrix3 mat; + vmathM3MakeFromQ( &mat, unitQuat ); + vmathV4MakeFromV3Scalar( &result->col0, &mat.col0, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col1, &mat.col1, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col2, &mat.col2, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col3, translateVec, 1.0f ); +} + +static inline void vmathM4SetCol0( VmathMatrix4 *result, const VmathVector4 *_col0 ) +{ + vmathV4Copy( &result->col0, _col0 ); +} + +static inline void vmathM4SetCol1( VmathMatrix4 *result, const VmathVector4 *_col1 ) +{ + vmathV4Copy( &result->col1, _col1 ); +} + +static inline void vmathM4SetCol2( VmathMatrix4 *result, const VmathVector4 *_col2 ) +{ + vmathV4Copy( &result->col2, _col2 ); +} + +static inline void vmathM4SetCol3( VmathMatrix4 *result, const VmathVector4 *_col3 ) +{ + vmathV4Copy( &result->col3, _col3 ); +} + +static inline void vmathM4SetCol( VmathMatrix4 *result, int col, const VmathVector4 *vec ) +{ + vmathV4Copy( (&result->col0 + col), vec ); +} + +static inline void vmathM4SetRow( VmathMatrix4 *result, int row, const VmathVector4 *vec ) +{ + vmathV4SetElem( &result->col0, row, vmathV4GetElem( vec, 0 ) ); + vmathV4SetElem( &result->col1, row, vmathV4GetElem( vec, 1 ) ); + vmathV4SetElem( &result->col2, row, vmathV4GetElem( vec, 2 ) ); + vmathV4SetElem( &result->col3, row, vmathV4GetElem( vec, 3 ) ); +} + +static inline void vmathM4SetElem( VmathMatrix4 *result, int col, int row, float val ) +{ + VmathVector4 tmpV3_0; + vmathM4GetCol( &tmpV3_0, result, col ); + vmathV4SetElem( &tmpV3_0, row, val ); + vmathM4SetCol( result, col, &tmpV3_0 ); +} + +static inline float vmathM4GetElem( const VmathMatrix4 *mat, int col, int row ) +{ + VmathVector4 tmpV4_0; + vmathM4GetCol( &tmpV4_0, mat, col ); + return vmathV4GetElem( &tmpV4_0, row ); +} + +static inline void vmathM4GetCol0( VmathVector4 *result, const VmathMatrix4 *mat ) +{ + vmathV4Copy( result, &mat->col0 ); +} + +static inline void vmathM4GetCol1( VmathVector4 *result, const VmathMatrix4 *mat ) +{ + vmathV4Copy( result, &mat->col1 ); +} + +static inline void vmathM4GetCol2( VmathVector4 *result, const VmathMatrix4 *mat ) +{ + vmathV4Copy( result, &mat->col2 ); +} + +static inline void vmathM4GetCol3( VmathVector4 *result, const VmathMatrix4 *mat ) +{ + vmathV4Copy( result, &mat->col3 ); +} + +static inline void vmathM4GetCol( VmathVector4 *result, const VmathMatrix4 *mat, int col ) +{ + vmathV4Copy( result, (&mat->col0 + col) ); +} + +static inline void vmathM4GetRow( VmathVector4 *result, const VmathMatrix4 *mat, int row ) +{ + vmathV4MakeFromElems( result, vmathV4GetElem( &mat->col0, row ), vmathV4GetElem( &mat->col1, row ), vmathV4GetElem( &mat->col2, row ), vmathV4GetElem( &mat->col3, row ) ); +} + +static inline void vmathM4Transpose( VmathMatrix4 *result, const VmathMatrix4 *mat ) +{ + vec_float4 tmp0, tmp1, tmp2, tmp3, res0, res1, res2, res3; + tmp0 = vec_mergeh( mat->col0.vec128, mat->col2.vec128 ); + tmp1 = vec_mergeh( mat->col1.vec128, mat->col3.vec128 ); + tmp2 = vec_mergel( mat->col0.vec128, mat->col2.vec128 ); + tmp3 = vec_mergel( mat->col1.vec128, mat->col3.vec128 ); + res0 = vec_mergeh( tmp0, tmp1 ); + res1 = vec_mergel( tmp0, tmp1 ); + res2 = vec_mergeh( tmp2, tmp3 ); + res3 = vec_mergel( tmp2, tmp3 ); + result->col0.vec128 = res0; + result->col1.vec128 = res1; + result->col2.vec128 = res2; + result->col3.vec128 = res3; +} + +static inline void vmathM4Inverse( VmathMatrix4 *result, const VmathMatrix4 *mat ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vector float in0, in1, in2, in3; + vector float tmp0, tmp1, tmp2, tmp3; + vector float cof0, cof1, cof2, cof3; + vector float t0, t1, t2, t3; + vector float t01, t02, t03, t12, t23; + vector float t1r, t2r; + vector float t01r, t02r, t03r, t12r, t23r; + vector float t1r3, t1r3r; + vector float det, det0, det1, det2, det3, invdet; + vector float vzero = (vector float){0.0}; + in0 = mat->col0.vec128; + in1 = mat->col1.vec128; + in2 = mat->col2.vec128; + in3 = mat->col3.vec128; + /* Perform transform of the input matrix of the form: + * A B C D + * E F G H + * I J K L + * M N O P + * + * The pseudo transpose of the input matrix is trans: + * A E I M + * J N B F + * C G K O + * L P D H + */ + tmp0 = vec_perm(in0, in1, _VECTORMATH_PERM_XAZC); /* A E C G */ + tmp1 = vec_perm(in2, in3, _VECTORMATH_PERM_XAZC); /* I M K O */ + tmp2 = vec_perm(in0, in1, _VECTORMATH_PERM_YBWD); /* B F D H */ + tmp3 = vec_perm(in2, in3, _VECTORMATH_PERM_YBWD); /* J N L P */ + t0 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_XYAB); /* A E I M */ + t1 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_XYAB); /* J N B F */ + t2 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_ZWCD); /* C G K O */ + t3 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_ZWCD); /* L P D H */ + /* Generate a cofactor matrix. The computed cofactors reside in + * cof0, cof1, cof2, cof3. + */ + t23 = vec_madd(t2, t3, vzero); /* CL GP KD OH */ + t23 = vec_perm(t23, t23, _VECTORMATH_PERM_YXWZ); /* GP CL OH KD */ + cof0 = vec_nmsub(t1, t23, vzero); /* -(JGP NCL FOH BKD) */ + cof1 = vec_nmsub(t0, t23, vzero); /* -(AGP ECL IOH MKD) */ + t23r = vec_sld(t23, t23, 8); /* OH KD GP CL */ + cof0 = vec_madd(t1, t23r, cof0); /* JOH NKD BGP FCL + cof0 */ + cof1 = vec_madd(t0, t23r, cof1); /* AOH EKD IGP MCL + cof1 */ + cof1 = vec_sld(cof1, cof1, 8); /* IGP MCL AOH EKD - IOH MKD AGP ECL */ + t12 = vec_madd(t1, t2, vzero); /* JC NG BK FO */ + t12 = vec_perm(t12, t12, _VECTORMATH_PERM_YXWZ); /* NG JC FO BK */ + cof0 = vec_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ + cof3 = vec_madd(t0, t12, vzero); /* ANG EJC IFO MBK */ + t12r = vec_sld(t12, t12, 8); /* FO BK NG JC */ + cof0 = vec_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ + cof3 = vec_nmsub(t0, t12r, cof3); /* cof3 - AFO EBK ING MJC */ + cof3 = vec_sld(cof3, cof3, 8); /* ING MJC AFO EBK - IFO MBK ANG EJC */ + t1r = vec_sld(t1, t1, 8); /* B F J N */ + t2r = vec_sld(t2, t2, 8); /* K O C G */ + t1r3 = vec_madd(t1r, t3, vzero); /* BL FP JD NH */ + t1r3 = vec_perm(t1r3, t1r3, _VECTORMATH_PERM_YXWZ); /* FP BL NH JD */ + cof0 = vec_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ + cof2 = vec_madd(t0, t1r3, vzero); /* AFP EBL INH MJD */ + t1r3r = vec_sld(t1r3, t1r3, 8); /* NH JD FP BL */ + cof0 = vec_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ + cof2 = vec_nmsub(t0, t1r3r, cof2); /* cof2 - ANH EJD IFP MBL */ + cof2 = vec_sld(cof2, cof2, 8); /* IFP MBL ANH EJD - INH MJD AFP EBL */ + t01 = vec_madd(t0, t1, vzero); /* AJ EN IB MF */ + t01 = vec_perm(t01, t01, _VECTORMATH_PERM_YXWZ); /* EN AJ MF IB */ + cof2 = vec_nmsub(t3, t01, cof2); /* cof2 - LEN PAJ DMF HIB */ + cof3 = vec_madd(t2r, t01, cof3); /* KEN OAJ CMF GIB + cof3 */ + t01r = vec_sld(t01, t01, 8); /* MF IB EN AJ */ + cof2 = vec_madd(t3, t01r, cof2); /* LMF PIB DEN HAJ + cof2 */ + cof3 = vec_nmsub(t2r, t01r, cof3); /* cof3 - KMF OIB CEN GAJ */ + t03 = vec_madd(t0, t3, vzero); /* AL EP ID MH */ + t03 = vec_perm(t03, t03, _VECTORMATH_PERM_YXWZ); /* EP AL MH ID */ + cof1 = vec_nmsub(t2r, t03, cof1); /* cof1 - KEP OAL CMH GID */ + cof2 = vec_madd(t1, t03, cof2); /* JEP NAL BMH FID + cof2 */ + t03r = vec_sld(t03, t03, 8); /* MH ID EP AL */ + cof1 = vec_madd(t2r, t03r, cof1); /* KMH OID CEP GAL + cof1 */ + cof2 = vec_nmsub(t1, t03r, cof2); /* cof2 - JMH NID BEP FAL */ + t02 = vec_madd(t0, t2r, vzero); /* AK EO IC MG */ + t02 = vec_perm(t02, t02, _VECTORMATH_PERM_YXWZ); /* E0 AK MG IC */ + cof1 = vec_madd(t3, t02, cof1); /* LEO PAK DMG HIC + cof1 */ + cof3 = vec_nmsub(t1, t02, cof3); /* cof3 - JEO NAK BMG FIC */ + t02r = vec_sld(t02, t02, 8); /* MG IC EO AK */ + cof1 = vec_nmsub(t3, t02r, cof1); /* cof1 - LMG PIC DEO HAK */ + cof3 = vec_madd(t1, t02r, cof3); /* JMG NIC BEO FAK + cof3 */ + /* Compute the determinant of the matrix + * + * det = sum_across(t0 * cof0); + * + * We perform a sum across the entire vector so that + * we don't have to splat the result when multiplying the + * cofactors by the inverse of the determinant. + */ + det = vec_madd(t0, cof0, vzero); + det0 = vec_splat(det, 0); + det1 = vec_splat(det, 1); + det2 = vec_splat(det, 2); + det3 = vec_splat(det, 3); + det = vec_add(det0, det1); + det2 = vec_add(det2, det3); + det = vec_add(det, det2); + /* Compute the reciprocal of the determinant. + */ + invdet = recipf4(det); + /* Multiply the cofactors by the reciprocal of the determinant. + */ + result->col0.vec128 = vec_madd(cof0, invdet, vzero); + result->col1.vec128 = vec_madd(cof1, invdet, vzero); + result->col2.vec128 = vec_madd(cof2, invdet, vzero); + result->col3.vec128 = vec_madd(cof3, invdet, vzero); +} + +static inline void vmathM4AffineInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ) +{ + VmathTransform3 affineMat, tmpT3_0; + VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2, tmpV3_3; + vmathV4GetXYZ( &tmpV3_0, &mat->col0 ); + vmathT3SetCol0( &affineMat, &tmpV3_0 ); + vmathV4GetXYZ( &tmpV3_1, &mat->col1 ); + vmathT3SetCol1( &affineMat, &tmpV3_1 ); + vmathV4GetXYZ( &tmpV3_2, &mat->col2 ); + vmathT3SetCol2( &affineMat, &tmpV3_2 ); + vmathV4GetXYZ( &tmpV3_3, &mat->col3 ); + vmathT3SetCol3( &affineMat, &tmpV3_3 ); + vmathT3Inverse( &tmpT3_0, &affineMat ); + vmathM4MakeFromT3( result, &tmpT3_0 ); +} + +static inline void vmathM4OrthoInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ) +{ + VmathTransform3 affineMat, tmpT3_0; + VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2, tmpV3_3; + vmathV4GetXYZ( &tmpV3_0, &mat->col0 ); + vmathT3SetCol0( &affineMat, &tmpV3_0 ); + vmathV4GetXYZ( &tmpV3_1, &mat->col1 ); + vmathT3SetCol1( &affineMat, &tmpV3_1 ); + vmathV4GetXYZ( &tmpV3_2, &mat->col2 ); + vmathT3SetCol2( &affineMat, &tmpV3_2 ); + vmathV4GetXYZ( &tmpV3_3, &mat->col3 ); + vmathT3SetCol3( &affineMat, &tmpV3_3 ); + vmathT3OrthoInverse( &tmpT3_0, &affineMat ); + vmathM4MakeFromT3( result, &tmpT3_0 ); +} + +static inline float vmathM4Determinant( const VmathMatrix4 *mat ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vector float in0, in1, in2, in3; + vector float tmp0, tmp1, tmp2, tmp3; + vector float cof0; + vector float t0, t1, t2, t3; + vector float t12, t23; + vector float t1r, t2r; + vector float t12r, t23r; + vector float t1r3, t1r3r; + vector float vzero = (vector float){0.0}; + union { vec_float4 v; float s[4]; } tmp; + in0 = mat->col0.vec128; + in1 = mat->col1.vec128; + in2 = mat->col2.vec128; + in3 = mat->col3.vec128; + /* Perform transform of the input matrix of the form: + * A B C D + * E F G H + * I J K L + * M N O P + * + * The pseudo transpose of the input matrix is trans: + * A E I M + * J N B F + * C G K O + * L P D H + */ + tmp0 = vec_perm(in0, in1, _VECTORMATH_PERM_XAZC); /* A E C G */ + tmp1 = vec_perm(in2, in3, _VECTORMATH_PERM_XAZC); /* I M K O */ + tmp2 = vec_perm(in0, in1, _VECTORMATH_PERM_YBWD); /* B F D H */ + tmp3 = vec_perm(in2, in3, _VECTORMATH_PERM_YBWD); /* J N L P */ + t0 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_XYAB); /* A E I M */ + t1 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_XYAB); /* J N B F */ + t2 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_ZWCD); /* C G K O */ + t3 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_ZWCD); /* L P D H */ + /* Generate a cofactor matrix. The computed cofactors reside in + * cof0, cof1, cof2, cof3. + */ + t23 = vec_madd(t2, t3, vzero); /* CL GP KD OH */ + t23 = vec_perm(t23, t23, _VECTORMATH_PERM_YXWZ); /* GP CL OH KD */ + cof0 = vec_nmsub(t1, t23, vzero); /* -(JGP NCL FOH BKD) */ + t23r = vec_sld(t23, t23, 8); /* OH KD GP CL */ + cof0 = vec_madd(t1, t23r, cof0); /* JOH NKD BGP FCL + cof0 */ + t12 = vec_madd(t1, t2, vzero); /* JC NG BK FO */ + t12 = vec_perm(t12, t12, _VECTORMATH_PERM_YXWZ); /* NG JC FO BK */ + cof0 = vec_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ + t12r = vec_sld(t12, t12, 8); /* FO BK NG JC */ + cof0 = vec_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ + t1r = vec_sld(t1, t1, 8); /* B F J N */ + t2r = vec_sld(t2, t2, 8); /* K O C G */ + t1r3 = vec_madd(t1r, t3, vzero); /* BL FP JD NH */ + t1r3 = vec_perm(t1r3, t1r3, _VECTORMATH_PERM_YXWZ); /* FP BL NH JD */ + cof0 = vec_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ + t1r3r = vec_sld(t1r3, t1r3, 8); /* NH JD FP BL */ + cof0 = vec_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ + tmp.v = _vmathVfDot4(t0,cof0); + return tmp.s[0]; +} + +static inline void vmathM4Add( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) +{ + vmathV4Add( &result->col0, &mat0->col0, &mat1->col0 ); + vmathV4Add( &result->col1, &mat0->col1, &mat1->col1 ); + vmathV4Add( &result->col2, &mat0->col2, &mat1->col2 ); + vmathV4Add( &result->col3, &mat0->col3, &mat1->col3 ); +} + +static inline void vmathM4Sub( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) +{ + vmathV4Sub( &result->col0, &mat0->col0, &mat1->col0 ); + vmathV4Sub( &result->col1, &mat0->col1, &mat1->col1 ); + vmathV4Sub( &result->col2, &mat0->col2, &mat1->col2 ); + vmathV4Sub( &result->col3, &mat0->col3, &mat1->col3 ); +} + +static inline void vmathM4Neg( VmathMatrix4 *result, const VmathMatrix4 *mat ) +{ + vmathV4Neg( &result->col0, &mat->col0 ); + vmathV4Neg( &result->col1, &mat->col1 ); + vmathV4Neg( &result->col2, &mat->col2 ); + vmathV4Neg( &result->col3, &mat->col3 ); +} + +static inline void vmathM4AbsPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat ) +{ + vmathV4AbsPerElem( &result->col0, &mat->col0 ); + vmathV4AbsPerElem( &result->col1, &mat->col1 ); + vmathV4AbsPerElem( &result->col2, &mat->col2 ); + vmathV4AbsPerElem( &result->col3, &mat->col3 ); +} + +static inline void vmathM4ScalarMul( VmathMatrix4 *result, const VmathMatrix4 *mat, float scalar ) +{ + vmathV4ScalarMul( &result->col0, &mat->col0, scalar ); + vmathV4ScalarMul( &result->col1, &mat->col1, scalar ); + vmathV4ScalarMul( &result->col2, &mat->col2, scalar ); + vmathV4ScalarMul( &result->col3, &mat->col3, scalar ); +} + +static inline void vmathM4MulV4( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector4 *vec ) +{ + vec_float4 tmp0, tmp1, res; + vec_float4 xxxx, yyyy, zzzz, wwww; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + xxxx = vec_splat( vec->vec128, 0 ); + yyyy = vec_splat( vec->vec128, 1 ); + zzzz = vec_splat( vec->vec128, 2 ); + wwww = vec_splat( vec->vec128, 3 ); + tmp0 = vec_madd( mat->col0.vec128, xxxx, zero ); + tmp1 = vec_madd( mat->col1.vec128, yyyy, zero ); + tmp0 = vec_madd( mat->col2.vec128, zzzz, tmp0 ); + tmp1 = vec_madd( mat->col3.vec128, wwww, tmp1 ); + res = vec_add( tmp0, tmp1 ); + result->vec128 = res; +} + +static inline void vmathM4MulV3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector3 *vec ) +{ + vec_float4 res; + vec_float4 xxxx, yyyy, zzzz; + xxxx = vec_splat( vec->vec128, 0 ); + yyyy = vec_splat( vec->vec128, 1 ); + zzzz = vec_splat( vec->vec128, 2 ); + res = vec_madd( mat->col0.vec128, xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + res = vec_madd( mat->col1.vec128, yyyy, res ); + res = vec_madd( mat->col2.vec128, zzzz, res ); + result->vec128 = res; +} + +static inline void vmathM4MulP3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathPoint3 *pnt ) +{ + vec_float4 tmp0, tmp1, res; + vec_float4 xxxx, yyyy, zzzz; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + xxxx = vec_splat( pnt->vec128, 0 ); + yyyy = vec_splat( pnt->vec128, 1 ); + zzzz = vec_splat( pnt->vec128, 2 ); + tmp0 = vec_madd( mat->col0.vec128, xxxx, zero ); + tmp1 = vec_madd( mat->col1.vec128, yyyy, zero ); + tmp0 = vec_madd( mat->col2.vec128, zzzz, tmp0 ); + tmp1 = vec_add( mat->col3.vec128, tmp1 ); + res = vec_add( tmp0, tmp1 ); + result->vec128 = res; +} + +static inline void vmathM4Mul( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) +{ + VmathMatrix4 tmpResult; + vmathM4MulV4( &tmpResult.col0, mat0, &mat1->col0 ); + vmathM4MulV4( &tmpResult.col1, mat0, &mat1->col1 ); + vmathM4MulV4( &tmpResult.col2, mat0, &mat1->col2 ); + vmathM4MulV4( &tmpResult.col3, mat0, &mat1->col3 ); + vmathM4Copy( result, &tmpResult ); +} + +static inline void vmathM4MulT3( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathTransform3 *tfrm1 ) +{ + VmathMatrix4 tmpResult; + VmathPoint3 tmpP3_0; + vmathM4MulV3( &tmpResult.col0, mat, &tfrm1->col0 ); + vmathM4MulV3( &tmpResult.col1, mat, &tfrm1->col1 ); + vmathM4MulV3( &tmpResult.col2, mat, &tfrm1->col2 ); + vmathP3MakeFromV3( &tmpP3_0, &tfrm1->col3 ); + vmathM4MulP3( &tmpResult.col3, mat, &tmpP3_0 ); + vmathM4Copy( result, &tmpResult ); +} + +static inline void vmathM4MulPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) +{ + vmathV4MulPerElem( &result->col0, &mat0->col0, &mat1->col0 ); + vmathV4MulPerElem( &result->col1, &mat0->col1, &mat1->col1 ); + vmathV4MulPerElem( &result->col2, &mat0->col2, &mat1->col2 ); + vmathV4MulPerElem( &result->col3, &mat0->col3, &mat1->col3 ); +} + +static inline void vmathM4MakeIdentity( VmathMatrix4 *result ) +{ + vmathV4MakeXAxis( &result->col0 ); + vmathV4MakeYAxis( &result->col1 ); + vmathV4MakeZAxis( &result->col2 ); + vmathV4MakeWAxis( &result->col3 ); +} + +static inline void vmathM4SetUpper3x3( VmathMatrix4 *result, const VmathMatrix3 *mat3 ) +{ + vmathV4SetXYZ( &result->col0, &mat3->col0 ); + vmathV4SetXYZ( &result->col1, &mat3->col1 ); + vmathV4SetXYZ( &result->col2, &mat3->col2 ); +} + +static inline void vmathM4GetUpper3x3( VmathMatrix3 *result, const VmathMatrix4 *mat ) +{ + vmathV4GetXYZ( &result->col0, &mat->col0 ); + vmathV4GetXYZ( &result->col1, &mat->col1 ); + vmathV4GetXYZ( &result->col2, &mat->col2 ); +} + +static inline void vmathM4SetTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ) +{ + vmathV4SetXYZ( &result->col3, translateVec ); +} + +static inline void vmathM4GetTranslation( VmathVector3 *result, const VmathMatrix4 *mat ) +{ + vmathV4GetXYZ( result, &mat->col3 ); +} + +static inline void vmathM4MakeRotationX( VmathMatrix4 *result, float radians ) +{ + vec_float4 s, c, res1, res2; + vec_uint4 select_y, select_z; + vec_float4 zero; + select_y = _VECTORMATH_MASK_0x0F00; + select_z = _VECTORMATH_MASK_0x00F0; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( _vmathVfSplatScalar(radians), &s, &c ); + res1 = vec_sel( zero, c, select_y ); + res1 = vec_sel( res1, s, select_z ); + res2 = vec_sel( zero, negatef4(s), select_y ); + res2 = vec_sel( res2, c, select_z ); + vmathV4MakeXAxis( &result->col0 ); + result->col1.vec128 = res1; + result->col2.vec128 = res2; + vmathV4MakeWAxis( &result->col3 ); +} + +static inline void vmathM4MakeRotationY( VmathMatrix4 *result, float radians ) +{ + vec_float4 s, c, res0, res2; + vec_uint4 select_x, select_z; + vec_float4 zero; + select_x = _VECTORMATH_MASK_0xF000; + select_z = _VECTORMATH_MASK_0x00F0; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( _vmathVfSplatScalar(radians), &s, &c ); + res0 = vec_sel( zero, c, select_x ); + res0 = vec_sel( res0, negatef4(s), select_z ); + res2 = vec_sel( zero, s, select_x ); + res2 = vec_sel( res2, c, select_z ); + result->col0.vec128 = res0; + vmathV4MakeYAxis( &result->col1 ); + result->col2.vec128 = res2; + vmathV4MakeWAxis( &result->col3 ); +} + +static inline void vmathM4MakeRotationZ( VmathMatrix4 *result, float radians ) +{ + vec_float4 s, c, res0, res1; + vec_uint4 select_x, select_y; + vec_float4 zero; + select_x = _VECTORMATH_MASK_0xF000; + select_y = _VECTORMATH_MASK_0x0F00; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( _vmathVfSplatScalar(radians), &s, &c ); + res0 = vec_sel( zero, c, select_x ); + res0 = vec_sel( res0, s, select_y ); + res1 = vec_sel( zero, negatef4(s), select_x ); + res1 = vec_sel( res1, c, select_y ); + result->col0.vec128 = res0; + result->col1.vec128 = res1; + vmathV4MakeZAxis( &result->col2 ); + vmathV4MakeWAxis( &result->col3 ); +} + +static inline void vmathM4MakeRotationZYX( VmathMatrix4 *result, const VmathVector3 *radiansXYZ ) +{ + VmathVector4 tmpV4_0; + vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + vmathV4MakeFromV3Scalar( &tmpV4_0, radiansXYZ, 0.0f ); + angles = tmpV4_0.vec128; + sincosf4( angles, &s, &c ); + negS = negatef4( s ); + Z0 = vec_mergel( c, s ); + Z1 = vec_mergel( negS, c ); + Z1 = vec_andc( Z1, (vec_float4)_VECTORMATH_MASK_0x000F ); + Y0 = vec_perm( negS, c, _VECTORMATH_PERM_BBYX ); + Y1 = vec_perm( c, s, _VECTORMATH_PERM_BBYX ); + X0 = vec_splat( s, 0 ); + X1 = vec_splat( c, 0 ); + tmp = vec_madd( Z0, Y1, zero ); + result->col0.vec128 = vec_madd( Z0, Y0, zero ); + result->col1.vec128 = vec_madd( Z1, X1, vec_madd( tmp, X0, zero ) ); + result->col2.vec128 = vec_nmsub( Z1, X0, vec_madd( tmp, X1, zero ) ); + vmathV4MakeWAxis( &result->col3 ); +} + +static inline void vmathM4MakeRotationAxis( VmathMatrix4 *result, float radians, const VmathVector3 *unitVec ) +{ + vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2, zeroW; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + axis = unitVec->vec128; + sincosf4( (vec_float4){radians,radians,radians,radians}, &s, &c ); + xxxx = vec_splat( axis, 0 ); + yyyy = vec_splat( axis, 1 ); + zzzz = vec_splat( axis, 2 ); + oneMinusC = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), c ); + axisS = vec_madd( axis, s, zero ); + negAxisS = negatef4( axisS ); + tmp0 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_XZBX ); + tmp1 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_CXXX ); + tmp2 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_YAXX ); + tmp0 = vec_sel( tmp0, c, _VECTORMATH_MASK_0xF000 ); + tmp1 = vec_sel( tmp1, c, _VECTORMATH_MASK_0x0F00 ); + tmp2 = vec_sel( tmp2, c, _VECTORMATH_MASK_0x00F0 ); + zeroW = (vec_float4)_VECTORMATH_MASK_0x000F; + axis = vec_andc( axis, zeroW ); + tmp0 = vec_andc( tmp0, zeroW ); + tmp1 = vec_andc( tmp1, zeroW ); + tmp2 = vec_andc( tmp2, zeroW ); + result->col0.vec128 = vec_madd( vec_madd( axis, xxxx, zero ), oneMinusC, tmp0 ); + result->col1.vec128 = vec_madd( vec_madd( axis, yyyy, zero ), oneMinusC, tmp1 ); + result->col2.vec128 = vec_madd( vec_madd( axis, zzzz, zero ), oneMinusC, tmp2 ); + vmathV4MakeWAxis( &result->col3 ); +} + +static inline void vmathM4MakeRotationQ( VmathMatrix4 *result, const VmathQuat *unitQuat ) +{ + VmathTransform3 tmpT3_0; + vmathT3MakeRotationQ( &tmpT3_0, unitQuat ); + vmathM4MakeFromT3( result, &tmpT3_0 ); +} + +static inline void vmathM4MakeScale( VmathMatrix4 *result, const VmathVector3 *scaleVec ) +{ + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + result->col0.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0xF000 ); + result->col1.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0x0F00 ); + result->col2.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0x00F0 ); + vmathV4MakeWAxis( &result->col3 ); +} + +static inline void vmathM4AppendScale( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathVector3 *scaleVec ) +{ + vmathV4ScalarMul( &result->col0, &mat->col0, vmathV3GetX( scaleVec ) ); + vmathV4ScalarMul( &result->col1, &mat->col1, vmathV3GetY( scaleVec ) ); + vmathV4ScalarMul( &result->col2, &mat->col2, vmathV3GetZ( scaleVec ) ); + vmathV4Copy( &result->col3, &mat->col3 ); +} + +static inline void vmathM4PrependScale( VmathMatrix4 *result, const VmathVector3 *scaleVec, const VmathMatrix4 *mat ) +{ + VmathVector4 scale4; + vmathV4MakeFromV3Scalar( &scale4, scaleVec, 1.0f ); + vmathV4MulPerElem( &result->col0, &mat->col0, &scale4 ); + vmathV4MulPerElem( &result->col1, &mat->col1, &scale4 ); + vmathV4MulPerElem( &result->col2, &mat->col2, &scale4 ); + vmathV4MulPerElem( &result->col3, &mat->col3, &scale4 ); +} + +static inline void vmathM4MakeTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ) +{ + vmathV4MakeXAxis( &result->col0 ); + vmathV4MakeYAxis( &result->col1 ); + vmathV4MakeZAxis( &result->col2 ); + vmathV4MakeFromV3Scalar( &result->col3, translateVec, 1.0f ); +} + +static inline void vmathM4MakeLookAt( VmathMatrix4 *result, const VmathPoint3 *eyePos, const VmathPoint3 *lookAtPos, const VmathVector3 *upVec ) +{ + VmathMatrix4 m4EyeFrame; + VmathVector3 v3X, v3Y, v3Z, tmpV3_0, tmpV3_1; + VmathVector4 tmpV4_0, tmpV4_1, tmpV4_2, tmpV4_3; + vmathV3Normalize( &v3Y, upVec ); + vmathP3Sub( &tmpV3_0, eyePos, lookAtPos ); + vmathV3Normalize( &v3Z, &tmpV3_0 ); + vmathV3Cross( &tmpV3_1, &v3Y, &v3Z ); + vmathV3Normalize( &v3X, &tmpV3_1 ); + vmathV3Cross( &v3Y, &v3Z, &v3X ); + vmathV4MakeFromV3( &tmpV4_0, &v3X ); + vmathV4MakeFromV3( &tmpV4_1, &v3Y ); + vmathV4MakeFromV3( &tmpV4_2, &v3Z ); + vmathV4MakeFromP3( &tmpV4_3, eyePos ); + vmathM4MakeFromCols( &m4EyeFrame, &tmpV4_0, &tmpV4_1, &tmpV4_2, &tmpV4_3 ); + vmathM4OrthoInverse( result, &m4EyeFrame ); +} + +static inline void vmathM4MakePerspective( VmathMatrix4 *result, float fovyRadians, float aspect, float zNear, float zFar ) +{ + float f, rangeInv; + vec_float4 zero, col0, col1, col2, col3; + union { vec_float4 v; float s[4]; } tmp; + f = tanf( _VECTORMATH_PI_OVER_2 - fovyRadians * 0.5f ); + rangeInv = 1.0f / ( zNear - zFar ); + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + tmp.v = zero; + tmp.s[0] = f / aspect; + col0 = tmp.v; + tmp.v = zero; + tmp.s[1] = f; + col1 = tmp.v; + tmp.v = zero; + tmp.s[2] = ( zNear + zFar ) * rangeInv; + tmp.s[3] = -1.0f; + col2 = tmp.v; + tmp.v = zero; + tmp.s[2] = zNear * zFar * rangeInv * 2.0f; + col3 = tmp.v; + result->col0.vec128 = col0; + result->col1.vec128 = col1; + result->col2.vec128 = col2; + result->col3.vec128 = col3; +} + +static inline void vmathM4MakeFrustum( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vec_float4 lbf, rtn; + vec_float4 diff, sum, inv_diff; + vec_float4 diagonal, column, near2; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + union { vec_float4 v; float s[4]; } l, f, r, n, b, t; + l.s[0] = left; + f.s[0] = zFar; + r.s[0] = right; + n.s[0] = zNear; + b.s[0] = bottom; + t.s[0] = top; + lbf = vec_mergeh( l.v, f.v ); + rtn = vec_mergeh( r.v, n.v ); + lbf = vec_mergeh( lbf, b.v ); + rtn = vec_mergeh( rtn, t.v ); + diff = vec_sub( rtn, lbf ); + sum = vec_add( rtn, lbf ); + inv_diff = recipf4( diff ); + near2 = vec_splat( n.v, 0 ); + near2 = vec_add( near2, near2 ); + diagonal = vec_madd( near2, inv_diff, zero ); + column = vec_madd( sum, inv_diff, zero ); + result->col0.vec128 = vec_sel( zero, diagonal, _VECTORMATH_MASK_0xF000 ); + result->col1.vec128 = vec_sel( zero, diagonal, _VECTORMATH_MASK_0x0F00 ); + result->col2.vec128 = vec_sel( column, ((vec_float4){-1.0f,-1.0f,-1.0f,-1.0f}), _VECTORMATH_MASK_0x000F ); + result->col3.vec128 = vec_sel( zero, vec_madd( diagonal, vec_splat( f.v, 0 ), zero ), _VECTORMATH_MASK_0x00F0 ); +} + +static inline void vmathM4MakeOrthographic( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vec_float4 lbf, rtn; + vec_float4 diff, sum, inv_diff, neg_inv_diff; + vec_float4 diagonal, column; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + union { vec_float4 v; float s[4]; } l, f, r, n, b, t; + l.s[0] = left; + f.s[0] = zFar; + r.s[0] = right; + n.s[0] = zNear; + b.s[0] = bottom; + t.s[0] = top; + lbf = vec_mergeh( l.v, f.v ); + rtn = vec_mergeh( r.v, n.v ); + lbf = vec_mergeh( lbf, b.v ); + rtn = vec_mergeh( rtn, t.v ); + diff = vec_sub( rtn, lbf ); + sum = vec_add( rtn, lbf ); + inv_diff = recipf4( diff ); + neg_inv_diff = negatef4( inv_diff ); + diagonal = vec_add( inv_diff, inv_diff ); + column = vec_madd( sum, vec_sel( neg_inv_diff, inv_diff, _VECTORMATH_MASK_0x00F0 ), zero ); + result->col0.vec128 = vec_sel( zero, diagonal, _VECTORMATH_MASK_0xF000 ); + result->col1.vec128 = vec_sel( zero, diagonal, _VECTORMATH_MASK_0x0F00 ); + result->col2.vec128 = vec_sel( zero, diagonal, _VECTORMATH_MASK_0x00F0 ); + result->col3.vec128 = vec_sel( column, ((vec_float4){1.0f,1.0f,1.0f,1.0f}), _VECTORMATH_MASK_0x000F ); +} + +static inline void vmathM4Select( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1, unsigned int select1 ) +{ + vmathV4Select( &result->col0, &mat0->col0, &mat1->col0, select1 ); + vmathV4Select( &result->col1, &mat0->col1, &mat1->col1, select1 ); + vmathV4Select( &result->col2, &mat0->col2, &mat1->col2, select1 ); + vmathV4Select( &result->col3, &mat0->col3, &mat1->col3, select1 ); +} + +#ifdef _VECTORMATH_DEBUG + +static inline void vmathM4Print( const VmathMatrix4 *mat ) +{ + VmathVector4 tmpV4_0, tmpV4_1, tmpV4_2, tmpV4_3; + vmathM4GetRow( &tmpV4_0, mat, 0 ); + vmathV4Print( &tmpV4_0 ); + vmathM4GetRow( &tmpV4_1, mat, 1 ); + vmathV4Print( &tmpV4_1 ); + vmathM4GetRow( &tmpV4_2, mat, 2 ); + vmathV4Print( &tmpV4_2 ); + vmathM4GetRow( &tmpV4_3, mat, 3 ); + vmathV4Print( &tmpV4_3 ); +} + +static inline void vmathM4Prints( const VmathMatrix4 *mat, const char *name ) +{ + printf("%s:\n", name); + vmathM4Print( mat ); +} + +#endif + +static inline void vmathT3Copy( VmathTransform3 *result, const VmathTransform3 *tfrm ) +{ + vmathV3Copy( &result->col0, &tfrm->col0 ); + vmathV3Copy( &result->col1, &tfrm->col1 ); + vmathV3Copy( &result->col2, &tfrm->col2 ); + vmathV3Copy( &result->col3, &tfrm->col3 ); +} + +static inline void vmathT3MakeFromScalar( VmathTransform3 *result, float scalar ) +{ + vmathV3MakeFromScalar( &result->col0, scalar ); + vmathV3MakeFromScalar( &result->col1, scalar ); + vmathV3MakeFromScalar( &result->col2, scalar ); + vmathV3MakeFromScalar( &result->col3, scalar ); +} + +static inline void vmathT3MakeFromCols( VmathTransform3 *result, const VmathVector3 *_col0, const VmathVector3 *_col1, const VmathVector3 *_col2, const VmathVector3 *_col3 ) +{ + vmathV3Copy( &result->col0, _col0 ); + vmathV3Copy( &result->col1, _col1 ); + vmathV3Copy( &result->col2, _col2 ); + vmathV3Copy( &result->col3, _col3 ); +} + +static inline void vmathT3MakeFromM3V3( VmathTransform3 *result, const VmathMatrix3 *tfrm, const VmathVector3 *translateVec ) +{ + vmathT3SetUpper3x3( result, tfrm ); + vmathT3SetTranslation( result, translateVec ); +} + +static inline void vmathT3MakeFromQV3( VmathTransform3 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ) +{ + VmathMatrix3 tmpM3_0; + vmathM3MakeFromQ( &tmpM3_0, unitQuat ); + vmathT3SetUpper3x3( result, &tmpM3_0 ); + vmathT3SetTranslation( result, translateVec ); +} + +static inline void vmathT3SetCol0( VmathTransform3 *result, const VmathVector3 *_col0 ) +{ + vmathV3Copy( &result->col0, _col0 ); +} + +static inline void vmathT3SetCol1( VmathTransform3 *result, const VmathVector3 *_col1 ) +{ + vmathV3Copy( &result->col1, _col1 ); +} + +static inline void vmathT3SetCol2( VmathTransform3 *result, const VmathVector3 *_col2 ) +{ + vmathV3Copy( &result->col2, _col2 ); +} + +static inline void vmathT3SetCol3( VmathTransform3 *result, const VmathVector3 *_col3 ) +{ + vmathV3Copy( &result->col3, _col3 ); +} + +static inline void vmathT3SetCol( VmathTransform3 *result, int col, const VmathVector3 *vec ) +{ + vmathV3Copy( (&result->col0 + col), vec ); +} + +static inline void vmathT3SetRow( VmathTransform3 *result, int row, const VmathVector4 *vec ) +{ + vmathV3SetElem( &result->col0, row, vmathV4GetElem( vec, 0 ) ); + vmathV3SetElem( &result->col1, row, vmathV4GetElem( vec, 1 ) ); + vmathV3SetElem( &result->col2, row, vmathV4GetElem( vec, 2 ) ); + vmathV3SetElem( &result->col3, row, vmathV4GetElem( vec, 3 ) ); +} + +static inline void vmathT3SetElem( VmathTransform3 *result, int col, int row, float val ) +{ + VmathVector3 tmpV3_0; + vmathT3GetCol( &tmpV3_0, result, col ); + vmathV3SetElem( &tmpV3_0, row, val ); + vmathT3SetCol( result, col, &tmpV3_0 ); +} + +static inline float vmathT3GetElem( const VmathTransform3 *tfrm, int col, int row ) +{ + VmathVector3 tmpV3_0; + vmathT3GetCol( &tmpV3_0, tfrm, col ); + return vmathV3GetElem( &tmpV3_0, row ); +} + +static inline void vmathT3GetCol0( VmathVector3 *result, const VmathTransform3 *tfrm ) +{ + vmathV3Copy( result, &tfrm->col0 ); +} + +static inline void vmathT3GetCol1( VmathVector3 *result, const VmathTransform3 *tfrm ) +{ + vmathV3Copy( result, &tfrm->col1 ); +} + +static inline void vmathT3GetCol2( VmathVector3 *result, const VmathTransform3 *tfrm ) +{ + vmathV3Copy( result, &tfrm->col2 ); +} + +static inline void vmathT3GetCol3( VmathVector3 *result, const VmathTransform3 *tfrm ) +{ + vmathV3Copy( result, &tfrm->col3 ); +} + +static inline void vmathT3GetCol( VmathVector3 *result, const VmathTransform3 *tfrm, int col ) +{ + vmathV3Copy( result, (&tfrm->col0 + col) ); +} + +static inline void vmathT3GetRow( VmathVector4 *result, const VmathTransform3 *tfrm, int row ) +{ + vmathV4MakeFromElems( result, vmathV3GetElem( &tfrm->col0, row ), vmathV3GetElem( &tfrm->col1, row ), vmathV3GetElem( &tfrm->col2, row ), vmathV3GetElem( &tfrm->col3, row ) ); +} + +static inline void vmathT3Inverse( VmathTransform3 *result, const VmathTransform3 *tfrm ) +{ + vec_float4 inv0, inv1, inv2, inv3; + vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet; + vec_float4 xxxx, yyyy, zzzz; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + tmp2 = _vmathVfCross( tfrm->col0.vec128, tfrm->col1.vec128 ); + tmp0 = _vmathVfCross( tfrm->col1.vec128, tfrm->col2.vec128 ); + tmp1 = _vmathVfCross( tfrm->col2.vec128, tfrm->col0.vec128 ); + inv3 = negatef4( tfrm->col3.vec128 ); + dot = _vmathVfDot3( tmp2, tfrm->col2.vec128 ); + dot = vec_splat( dot, 0 ); + invdet = recipf4( dot ); + tmp3 = vec_mergeh( tmp0, tmp2 ); + tmp4 = vec_mergel( tmp0, tmp2 ); + inv0 = vec_mergeh( tmp3, tmp1 ); + xxxx = vec_splat( inv3, 0 ); + inv1 = vec_perm( tmp3, tmp1, _VECTORMATH_PERM_ZBWX ); + inv2 = vec_perm( tmp4, tmp1, _VECTORMATH_PERM_XCYX ); + yyyy = vec_splat( inv3, 1 ); + zzzz = vec_splat( inv3, 2 ); + inv3 = vec_madd( inv0, xxxx, zero ); + inv3 = vec_madd( inv1, yyyy, inv3 ); + inv3 = vec_madd( inv2, zzzz, inv3 ); + inv0 = vec_madd( inv0, invdet, zero ); + inv1 = vec_madd( inv1, invdet, zero ); + inv2 = vec_madd( inv2, invdet, zero ); + inv3 = vec_madd( inv3, invdet, zero ); + result->col0.vec128 = inv0; + result->col1.vec128 = inv1; + result->col2.vec128 = inv2; + result->col3.vec128 = inv3; +} + +static inline void vmathT3OrthoInverse( VmathTransform3 *result, const VmathTransform3 *tfrm ) +{ + vec_float4 inv0, inv1, inv2, inv3; + vec_float4 tmp0, tmp1; + vec_float4 xxxx, yyyy, zzzz; + tmp0 = vec_mergeh( tfrm->col0.vec128, tfrm->col2.vec128 ); + tmp1 = vec_mergel( tfrm->col0.vec128, tfrm->col2.vec128 ); + inv3 = negatef4( tfrm->col3.vec128 ); + inv0 = vec_mergeh( tmp0, tfrm->col1.vec128 ); + xxxx = vec_splat( inv3, 0 ); + inv1 = vec_perm( tmp0, tfrm->col1.vec128, _VECTORMATH_PERM_ZBWX ); + inv2 = vec_perm( tmp1, tfrm->col1.vec128, _VECTORMATH_PERM_XCYX ); + yyyy = vec_splat( inv3, 1 ); + zzzz = vec_splat( inv3, 2 ); + inv3 = vec_madd( inv0, xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + inv3 = vec_madd( inv1, yyyy, inv3 ); + inv3 = vec_madd( inv2, zzzz, inv3 ); + result->col0.vec128 = inv0; + result->col1.vec128 = inv1; + result->col2.vec128 = inv2; + result->col3.vec128 = inv3; +} + +static inline void vmathT3AbsPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm ) +{ + vmathV3AbsPerElem( &result->col0, &tfrm->col0 ); + vmathV3AbsPerElem( &result->col1, &tfrm->col1 ); + vmathV3AbsPerElem( &result->col2, &tfrm->col2 ); + vmathV3AbsPerElem( &result->col3, &tfrm->col3 ); +} + +static inline void vmathT3MulV3( VmathVector3 *result, const VmathTransform3 *tfrm, const VmathVector3 *vec ) +{ + vec_float4 res; + vec_float4 xxxx, yyyy, zzzz; + xxxx = vec_splat( vec->vec128, 0 ); + yyyy = vec_splat( vec->vec128, 1 ); + zzzz = vec_splat( vec->vec128, 2 ); + res = vec_madd( tfrm->col0.vec128, xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + res = vec_madd( tfrm->col1.vec128, yyyy, res ); + res = vec_madd( tfrm->col2.vec128, zzzz, res ); + result->vec128 = res; +} + +static inline void vmathT3MulP3( VmathPoint3 *result, const VmathTransform3 *tfrm, const VmathPoint3 *pnt ) +{ + vec_float4 tmp0, tmp1, res; + vec_float4 xxxx, yyyy, zzzz; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + xxxx = vec_splat( pnt->vec128, 0 ); + yyyy = vec_splat( pnt->vec128, 1 ); + zzzz = vec_splat( pnt->vec128, 2 ); + tmp0 = vec_madd( tfrm->col0.vec128, xxxx, zero ); + tmp1 = vec_madd( tfrm->col1.vec128, yyyy, zero ); + tmp0 = vec_madd( tfrm->col2.vec128, zzzz, tmp0 ); + tmp1 = vec_add( tfrm->col3.vec128, tmp1 ); + res = vec_add( tmp0, tmp1 ); + result->vec128 = res; +} + +static inline void vmathT3Mul( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ) +{ + VmathTransform3 tmpResult; + VmathPoint3 tmpP3_0, tmpP3_1; + vmathT3MulV3( &tmpResult.col0, tfrm0, &tfrm1->col0 ); + vmathT3MulV3( &tmpResult.col1, tfrm0, &tfrm1->col1 ); + vmathT3MulV3( &tmpResult.col2, tfrm0, &tfrm1->col2 ); + vmathP3MakeFromV3( &tmpP3_0, &tfrm1->col3 ); + vmathT3MulP3( &tmpP3_1, tfrm0, &tmpP3_0 ); + vmathV3MakeFromP3( &tmpResult.col3, &tmpP3_1 ); + vmathT3Copy( result, &tmpResult ); +} + +static inline void vmathT3MulPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ) +{ + vmathV3MulPerElem( &result->col0, &tfrm0->col0, &tfrm1->col0 ); + vmathV3MulPerElem( &result->col1, &tfrm0->col1, &tfrm1->col1 ); + vmathV3MulPerElem( &result->col2, &tfrm0->col2, &tfrm1->col2 ); + vmathV3MulPerElem( &result->col3, &tfrm0->col3, &tfrm1->col3 ); +} + +static inline void vmathT3MakeIdentity( VmathTransform3 *result ) +{ + vmathV3MakeXAxis( &result->col0 ); + vmathV3MakeYAxis( &result->col1 ); + vmathV3MakeZAxis( &result->col2 ); + vmathV3MakeFromScalar( &result->col3, 0.0f ); +} + +static inline void vmathT3SetUpper3x3( VmathTransform3 *result, const VmathMatrix3 *tfrm ) +{ + vmathV3Copy( &result->col0, &tfrm->col0 ); + vmathV3Copy( &result->col1, &tfrm->col1 ); + vmathV3Copy( &result->col2, &tfrm->col2 ); +} + +static inline void vmathT3GetUpper3x3( VmathMatrix3 *result, const VmathTransform3 *tfrm ) +{ + vmathM3MakeFromCols( result, &tfrm->col0, &tfrm->col1, &tfrm->col2 ); +} + +static inline void vmathT3SetTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ) +{ + vmathV3Copy( &result->col3, translateVec ); +} + +static inline void vmathT3GetTranslation( VmathVector3 *result, const VmathTransform3 *tfrm ) +{ + vmathV3Copy( result, &tfrm->col3 ); +} + +static inline void vmathT3MakeRotationX( VmathTransform3 *result, float radians ) +{ + vec_float4 s, c, res1, res2; + vec_uint4 select_y, select_z; + vec_float4 zero; + select_y = _VECTORMATH_MASK_0x0F00; + select_z = _VECTORMATH_MASK_0x00F0; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( _vmathVfSplatScalar(radians), &s, &c ); + res1 = vec_sel( zero, c, select_y ); + res1 = vec_sel( res1, s, select_z ); + res2 = vec_sel( zero, negatef4(s), select_y ); + res2 = vec_sel( res2, c, select_z ); + vmathV3MakeXAxis( &result->col0 ); + result->col1.vec128 = res1; + result->col2.vec128 = res2; + vmathV3MakeFromScalar( &result->col3, 0.0f ); +} + +static inline void vmathT3MakeRotationY( VmathTransform3 *result, float radians ) +{ + vec_float4 s, c, res0, res2; + vec_uint4 select_x, select_z; + vec_float4 zero; + select_x = _VECTORMATH_MASK_0xF000; + select_z = _VECTORMATH_MASK_0x00F0; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( _vmathVfSplatScalar(radians), &s, &c ); + res0 = vec_sel( zero, c, select_x ); + res0 = vec_sel( res0, negatef4(s), select_z ); + res2 = vec_sel( zero, s, select_x ); + res2 = vec_sel( res2, c, select_z ); + result->col0.vec128 = res0; + vmathV3MakeYAxis( &result->col1 ); + result->col2.vec128 = res2; + vmathV3MakeFromScalar( &result->col3, 0.0f ); +} + +static inline void vmathT3MakeRotationZ( VmathTransform3 *result, float radians ) +{ + vec_float4 s, c, res0, res1; + vec_uint4 select_x, select_y; + vec_float4 zero; + select_x = _VECTORMATH_MASK_0xF000; + select_y = _VECTORMATH_MASK_0x0F00; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( _vmathVfSplatScalar(radians), &s, &c ); + res0 = vec_sel( zero, c, select_x ); + res0 = vec_sel( res0, s, select_y ); + res1 = vec_sel( zero, negatef4(s), select_x ); + res1 = vec_sel( res1, c, select_y ); + result->col0.vec128 = res0; + result->col1.vec128 = res1; + vmathV3MakeZAxis( &result->col2 ); + vmathV3MakeFromScalar( &result->col3, 0.0f ); +} + +static inline void vmathT3MakeRotationZYX( VmathTransform3 *result, const VmathVector3 *radiansXYZ ) +{ + VmathVector4 tmpV4_0; + vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + vmathV4MakeFromV3Scalar( &tmpV4_0, radiansXYZ, 0.0f ); + angles = tmpV4_0.vec128; + sincosf4( angles, &s, &c ); + negS = negatef4( s ); + Z0 = vec_mergel( c, s ); + Z1 = vec_mergel( negS, c ); + Z1 = vec_andc( Z1, (vec_float4)_VECTORMATH_MASK_0x000F ); + Y0 = vec_perm( negS, c, _VECTORMATH_PERM_BBYX ); + Y1 = vec_perm( c, s, _VECTORMATH_PERM_BBYX ); + X0 = vec_splat( s, 0 ); + X1 = vec_splat( c, 0 ); + tmp = vec_madd( Z0, Y1, zero ); + result->col0.vec128 = vec_madd( Z0, Y0, zero ); + result->col1.vec128 = vec_madd( Z1, X1, vec_madd( tmp, X0, zero ) ); + result->col2.vec128 = vec_nmsub( Z1, X0, vec_madd( tmp, X1, zero ) ); + vmathV3MakeFromScalar( &result->col3, 0.0f ); +} + +static inline void vmathT3MakeRotationAxis( VmathTransform3 *result, float radians, const VmathVector3 *unitVec ) +{ + VmathMatrix3 tmpM3_0; + VmathVector3 tmpV3_0; + vmathM3MakeRotationAxis( &tmpM3_0, radians, unitVec ); + vmathV3MakeFromScalar( &tmpV3_0, 0.0f ); + vmathT3MakeFromM3V3( result, &tmpM3_0, &tmpV3_0 ); +} + +static inline void vmathT3MakeRotationQ( VmathTransform3 *result, const VmathQuat *unitQuat ) +{ + VmathMatrix3 tmpM3_0; + VmathVector3 tmpV3_0; + vmathM3MakeFromQ( &tmpM3_0, unitQuat ); + vmathV3MakeFromScalar( &tmpV3_0, 0.0f ); + vmathT3MakeFromM3V3( result, &tmpM3_0, &tmpV3_0 ); +} + +static inline void vmathT3MakeScale( VmathTransform3 *result, const VmathVector3 *scaleVec ) +{ + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + result->col0.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0xF000 ); + result->col1.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0x0F00 ); + result->col2.vec128 = vec_sel( zero, scaleVec->vec128, _VECTORMATH_MASK_0x00F0 ); + vmathV3MakeFromScalar( &result->col3, 0.0f ); +} + +static inline void vmathT3AppendScale( VmathTransform3 *result, const VmathTransform3 *tfrm, const VmathVector3 *scaleVec ) +{ + vmathV3ScalarMul( &result->col0, &tfrm->col0, vmathV3GetX( scaleVec ) ); + vmathV3ScalarMul( &result->col1, &tfrm->col1, vmathV3GetY( scaleVec ) ); + vmathV3ScalarMul( &result->col2, &tfrm->col2, vmathV3GetZ( scaleVec ) ); + vmathV3Copy( &result->col3, &tfrm->col3 ); +} + +static inline void vmathT3PrependScale( VmathTransform3 *result, const VmathVector3 *scaleVec, const VmathTransform3 *tfrm ) +{ + vmathV3MulPerElem( &result->col0, &tfrm->col0, scaleVec ); + vmathV3MulPerElem( &result->col1, &tfrm->col1, scaleVec ); + vmathV3MulPerElem( &result->col2, &tfrm->col2, scaleVec ); + vmathV3MulPerElem( &result->col3, &tfrm->col3, scaleVec ); +} + +static inline void vmathT3MakeTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ) +{ + vmathV3MakeXAxis( &result->col0 ); + vmathV3MakeYAxis( &result->col1 ); + vmathV3MakeZAxis( &result->col2 ); + vmathV3Copy( &result->col3, translateVec ); +} + +static inline void vmathT3Select( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1, unsigned int select1 ) +{ + vmathV3Select( &result->col0, &tfrm0->col0, &tfrm1->col0, select1 ); + vmathV3Select( &result->col1, &tfrm0->col1, &tfrm1->col1, select1 ); + vmathV3Select( &result->col2, &tfrm0->col2, &tfrm1->col2, select1 ); + vmathV3Select( &result->col3, &tfrm0->col3, &tfrm1->col3, select1 ); +} + +#ifdef _VECTORMATH_DEBUG + +static inline void vmathT3Print( const VmathTransform3 *tfrm ) +{ + VmathVector4 tmpV4_0, tmpV4_1, tmpV4_2; + vmathT3GetRow( &tmpV4_0, tfrm, 0 ); + vmathV4Print( &tmpV4_0 ); + vmathT3GetRow( &tmpV4_1, tfrm, 1 ); + vmathV4Print( &tmpV4_1 ); + vmathT3GetRow( &tmpV4_2, tfrm, 2 ); + vmathV4Print( &tmpV4_2 ); +} + +static inline void vmathT3Prints( const VmathTransform3 *tfrm, const char *name ) +{ + printf("%s:\n", name); + vmathT3Print( tfrm ); +} + +#endif + +static inline void vmathQMakeFromM3( VmathQuat *result, const VmathMatrix3 *tfrm ) +{ + vec_float4 res; + vec_float4 col0, col1, col2; + vec_float4 xx_yy, xx_yy_zz_xx, yy_zz_xx_yy, zz_xx_yy_zz, diagSum, diagDiff; + vec_float4 zy_xz_yx, yz_zx_xy, sum, diff; + vec_float4 radicand, invSqrt, scale; + vec_float4 res0, res1, res2, res3; + vec_float4 xx, yy, zz; + vec_uint4 select_x = _VECTORMATH_MASK_0xF000; + vec_uint4 select_y = _VECTORMATH_MASK_0x0F00; + vec_uint4 select_z = _VECTORMATH_MASK_0x00F0; + vec_uint4 select_w = _VECTORMATH_MASK_0x000F; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + + col0 = tfrm->col0.vec128; + col1 = tfrm->col1.vec128; + col2 = tfrm->col2.vec128; + + /* four cases: */ + /* trace > 0 */ + /* else */ + /* xx largest diagonal element */ + /* yy largest diagonal element */ + /* zz largest diagonal element */ + + /* compute quaternion for each case */ + + xx_yy = vec_sel( col0, col1, select_y ); + xx_yy_zz_xx = vec_perm( xx_yy, col2, _VECTORMATH_PERM_XYCX ); + yy_zz_xx_yy = vec_perm( xx_yy, col2, _VECTORMATH_PERM_YCXY ); + zz_xx_yy_zz = vec_perm( xx_yy, col2, _VECTORMATH_PERM_CXYC ); + + diagSum = vec_add( vec_add( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); + diagDiff = vec_sub( vec_sub( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); + radicand = vec_add( vec_sel( diagDiff, diagSum, select_w ), ((vec_float4){1.0f,1.0f,1.0f,1.0f}) ); + invSqrt = rsqrtf4( radicand ); + + zy_xz_yx = vec_sel( col0, col1, select_z ); + zy_xz_yx = vec_perm( zy_xz_yx, col2, _VECTORMATH_PERM_ZAYX ); + yz_zx_xy = vec_sel( col0, col1, select_x ); + yz_zx_xy = vec_perm( yz_zx_xy, col2, _VECTORMATH_PERM_BZXX ); + + sum = vec_add( zy_xz_yx, yz_zx_xy ); + diff = vec_sub( zy_xz_yx, yz_zx_xy ); + + scale = vec_madd( invSqrt, ((vec_float4){0.5f,0.5f,0.5f,0.5f}), zero ); + res0 = vec_perm( sum, diff, _VECTORMATH_PERM_XZYA ); + res1 = vec_perm( sum, diff, _VECTORMATH_PERM_ZXXB ); + res2 = vec_perm( sum, diff, _VECTORMATH_PERM_YXXC ); + res3 = diff; + res0 = vec_sel( res0, radicand, select_x ); + res1 = vec_sel( res1, radicand, select_y ); + res2 = vec_sel( res2, radicand, select_z ); + res3 = vec_sel( res3, radicand, select_w ); + res0 = vec_madd( res0, vec_splat( scale, 0 ), zero ); + res1 = vec_madd( res1, vec_splat( scale, 1 ), zero ); + res2 = vec_madd( res2, vec_splat( scale, 2 ), zero ); + res3 = vec_madd( res3, vec_splat( scale, 3 ), zero ); + + /* determine case and select answer */ + + xx = vec_splat( col0, 0 ); + yy = vec_splat( col1, 1 ); + zz = vec_splat( col2, 2 ); + res = vec_sel( res0, res1, vec_cmpgt( yy, xx ) ); + res = vec_sel( res, res2, vec_and( vec_cmpgt( zz, xx ), vec_cmpgt( zz, yy ) ) ); + res = vec_sel( res, res3, vec_cmpgt( vec_splat( diagSum, 0 ), zero ) ); + result->vec128 = res; +} + +static inline void vmathV3Outer( VmathMatrix3 *result, const VmathVector3 *tfrm0, const VmathVector3 *tfrm1 ) +{ + vmathV3ScalarMul( &result->col0, tfrm0, vmathV3GetX( tfrm1 ) ); + vmathV3ScalarMul( &result->col1, tfrm0, vmathV3GetY( tfrm1 ) ); + vmathV3ScalarMul( &result->col2, tfrm0, vmathV3GetZ( tfrm1 ) ); +} + +static inline void vmathV4Outer( VmathMatrix4 *result, const VmathVector4 *tfrm0, const VmathVector4 *tfrm1 ) +{ + vmathV4ScalarMul( &result->col0, tfrm0, vmathV4GetX( tfrm1 ) ); + vmathV4ScalarMul( &result->col1, tfrm0, vmathV4GetY( tfrm1 ) ); + vmathV4ScalarMul( &result->col2, tfrm0, vmathV4GetZ( tfrm1 ) ); + vmathV4ScalarMul( &result->col3, tfrm0, vmathV4GetW( tfrm1 ) ); +} + +static inline void vmathV3RowMul( VmathVector3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ) +{ + vec_float4 tmp0, tmp1, mcol0, mcol1, mcol2, res; + vec_float4 xxxx, yyyy, zzzz; + tmp0 = vec_mergeh( mat->col0.vec128, mat->col2.vec128 ); + tmp1 = vec_mergel( mat->col0.vec128, mat->col2.vec128 ); + xxxx = vec_splat( vec->vec128, 0 ); + mcol0 = vec_mergeh( tmp0, mat->col1.vec128 ); + mcol1 = vec_perm( tmp0, mat->col1.vec128, _VECTORMATH_PERM_ZBWX ); + mcol2 = vec_perm( tmp1, mat->col1.vec128, _VECTORMATH_PERM_XCYX ); + yyyy = vec_splat( vec->vec128, 1 ); + res = vec_madd( mcol0, xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + zzzz = vec_splat( vec->vec128, 2 ); + res = vec_madd( mcol1, yyyy, res ); + res = vec_madd( mcol2, zzzz, res ); + result->vec128 = res; +} + +static inline void vmathV3CrossMatrix( VmathMatrix3 *result, const VmathVector3 *vec ) +{ + vec_float4 neg, res0, res1, res2; + neg = negatef4( vec->vec128 ); + res0 = vec_perm( vec->vec128, neg, _VECTORMATH_PERM_XZBX ); + res1 = vec_perm( vec->vec128, neg, _VECTORMATH_PERM_CXXX ); + res2 = vec_perm( vec->vec128, neg, _VECTORMATH_PERM_YAXX ); + res0 = vec_andc( res0, (vec_float4)_VECTORMATH_MASK_0xF000 ); + res1 = vec_andc( res1, (vec_float4)_VECTORMATH_MASK_0x0F00 ); + res2 = vec_andc( res2, (vec_float4)_VECTORMATH_MASK_0x00F0 ); + result->col0.vec128 = res0; + result->col1.vec128 = res1; + result->col2.vec128 = res2; +} + +static inline void vmathV3CrossMatrixMul( VmathMatrix3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ) +{ + VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2; + vmathV3Cross( &tmpV3_0, vec, &mat->col0 ); + vmathV3Cross( &tmpV3_1, vec, &mat->col1 ); + vmathV3Cross( &tmpV3_2, vec, &mat->col2 ); + vmathM3MakeFromCols( result, &tmpV3_0, &tmpV3_1, &tmpV3_2 ); +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/common/vectormath/ppu/c/quat_aos.h b/common/vectormath/ppu/c/quat_aos.h index 2915f4f0..e5202649 100644 --- a/common/vectormath/ppu/c/quat_aos.h +++ b/common/vectormath/ppu/c/quat_aos.h @@ -1,379 +1,379 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_QUAT_AOS_C_H -#define _VECTORMATH_QUAT_AOS_C_H -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/*----------------------------------------------------------------------------- - * Definitions - */ -#ifndef _VECTORMATH_INTERNAL_FUNCTIONS -#define _VECTORMATH_INTERNAL_FUNCTIONS - -#endif - -static inline void vmathQCopy( VmathQuat *result, const VmathQuat *quat ) -{ - result->vec128 = quat->vec128; -} - -static inline void vmathQMakeFromElems( VmathQuat *result, float _x, float _y, float _z, float _w ) -{ - if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & - __builtin_constant_p(_z) & __builtin_constant_p(_w)) { - result->vec128 = (vec_float4){_x, _y, _z, _w}; - } else { - float *pf = (float *)&result->vec128; - pf[0] = _x; - pf[1] = _y; - pf[2] = _z; - pf[3] = _w; - } -} - -static inline void vmathQMakeFromV3Scalar( VmathQuat *result, const VmathVector3 *xyz, float _w ) -{ - result->vec128 = xyz->vec128; - _vmathVfSetElement(result->vec128, _w, 3); -} - -static inline void vmathQMakeFromV4( VmathQuat *result, const VmathVector4 *vec ) -{ - result->vec128 = vec->vec128; -} - -static inline void vmathQMakeFromScalar( VmathQuat *result, float scalar ) -{ - result->vec128 = _vmathVfSplatScalar(scalar); -} - -static inline void vmathQMakeFrom128( VmathQuat *result, vec_float4 vf4 ) -{ - result->vec128 = vf4; -} - -static inline void vmathQMakeIdentity( VmathQuat *result ) -{ - result->vec128 = _VECTORMATH_UNIT_0001; -} - -static inline void vmathQLerp( VmathQuat *result, float t, const VmathQuat *quat0, const VmathQuat *quat1 ) -{ - VmathQuat tmpQ_0, tmpQ_1; - vmathQSub( &tmpQ_0, quat1, quat0 ); - vmathQScalarMul( &tmpQ_1, &tmpQ_0, t ); - vmathQAdd( result, quat0, &tmpQ_1 ); -} - -static inline void vmathQSlerp( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1 ) -{ - VmathQuat start; - vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; - vec_uint4 selectMask; - cosAngle = _vmathVfDot4( unitQuat0->vec128, unitQuat1->vec128 ); - cosAngle = vec_splat( cosAngle, 0 ); - selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), cosAngle ); - cosAngle = vec_sel( cosAngle, negatef4( cosAngle ), selectMask ); - start.vec128 = vec_sel( unitQuat0->vec128, negatef4( unitQuat0->vec128 ), selectMask ); - selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL}), cosAngle ); - angle = acosf4( cosAngle ); - tttt = _vmathVfSplatScalar(t); - oneMinusT = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); - angles = vec_mergeh( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); - angles = vec_mergeh( angles, oneMinusT ); - angles = vec_madd( angles, angle, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - sines = sinf4( angles ); - scales = divf4( sines, vec_splat( sines, 0 ) ); - scale0 = vec_sel( oneMinusT, vec_splat( scales, 1 ), selectMask ); - scale1 = vec_sel( tttt, vec_splat( scales, 2 ), selectMask ); - result->vec128 = vec_madd( start.vec128, scale0, vec_madd( unitQuat1->vec128, scale1, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); -} - -static inline void vmathQSquad( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1, const VmathQuat *unitQuat2, const VmathQuat *unitQuat3 ) -{ - VmathQuat tmp0, tmp1; - vmathQSlerp( &tmp0, t, unitQuat0, unitQuat3 ); - vmathQSlerp( &tmp1, t, unitQuat1, unitQuat2 ); - vmathQSlerp( result, ( ( 2.0f * t ) * ( 1.0f - t ) ), &tmp0, &tmp1 ); -} - -static inline vec_float4 vmathQGet128( const VmathQuat *quat ) -{ - return quat->vec128; -} - -static inline void vmathQSetXYZ( VmathQuat *result, const VmathVector3 *vec ) -{ - result->vec128 = vec_sel( vec->vec128, result->vec128, _VECTORMATH_MASK_0x000F ); -} - -static inline void vmathQGetXYZ( VmathVector3 *result, const VmathQuat *quat ) -{ - result->vec128 = quat->vec128; -} - -static inline void vmathQSetX( VmathQuat *result, float _x ) -{ - _vmathVfSetElement(result->vec128, _x, 0); -} - -static inline float vmathQGetX( const VmathQuat *quat ) -{ - return _vmathVfGetElement(quat->vec128, 0); -} - -static inline void vmathQSetY( VmathQuat *result, float _y ) -{ - _vmathVfSetElement(result->vec128, _y, 1); -} - -static inline float vmathQGetY( const VmathQuat *quat ) -{ - return _vmathVfGetElement(quat->vec128, 1); -} - -static inline void vmathQSetZ( VmathQuat *result, float _z ) -{ - _vmathVfSetElement(result->vec128, _z, 2); -} - -static inline float vmathQGetZ( const VmathQuat *quat ) -{ - return _vmathVfGetElement(quat->vec128, 2); -} - -static inline void vmathQSetW( VmathQuat *result, float _w ) -{ - _vmathVfSetElement(result->vec128, _w, 3); -} - -static inline float vmathQGetW( const VmathQuat *quat ) -{ - return _vmathVfGetElement(quat->vec128, 3); -} - -static inline void vmathQSetElem( VmathQuat *result, int idx, float value ) -{ - _vmathVfSetElement(result->vec128, value, idx); -} - -static inline float vmathQGetElem( const VmathQuat *quat, int idx ) -{ - return _vmathVfGetElement(quat->vec128, idx); -} - -static inline void vmathQAdd( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ) -{ - result->vec128 = vec_add( quat0->vec128, quat1->vec128 ); -} - -static inline void vmathQSub( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ) -{ - result->vec128 = vec_sub( quat0->vec128, quat1->vec128 ); -} - -static inline void vmathQScalarMul( VmathQuat *result, const VmathQuat *quat, float scalar ) -{ - result->vec128 = vec_madd( quat->vec128, _vmathVfSplatScalar(scalar), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); -} - -static inline void vmathQScalarDiv( VmathQuat *result, const VmathQuat *quat, float scalar ) -{ - result->vec128 = divf4( quat->vec128, _vmathVfSplatScalar(scalar) ); -} - -static inline void vmathQNeg( VmathQuat *result, const VmathQuat *quat ) -{ - result->vec128 = negatef4( quat->vec128 ); -} - -static inline float vmathQDot( const VmathQuat *quat0, const VmathQuat *quat1 ) -{ - vec_float4 result = _vmathVfDot4( quat0->vec128, quat1->vec128 ); - return _vmathVfGetElement(result, 0); -} - -static inline float vmathQNorm( const VmathQuat *quat ) -{ - vec_float4 result = _vmathVfDot4( quat->vec128, quat->vec128 ); - return _vmathVfGetElement(result, 0); -} - -static inline float vmathQLength( const VmathQuat *quat ) -{ - return sqrtf( vmathQNorm( quat ) ); -} - -static inline void vmathQNormalize( VmathQuat *result, const VmathQuat *quat ) -{ - vec_float4 dot = _vmathVfDot4( quat->vec128, quat->vec128 ); - result->vec128 = vec_madd( quat->vec128, rsqrtf4( dot ), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); -} - -static inline void vmathQMakeRotationArc( VmathQuat *result, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ) -{ - VmathVector3 crossVec, tmpV3_0; - vec_float4 cosAngle, cosAngleX2Plus2, recipCosHalfAngleX2, cosHalfAngleX2, res; - cosAngle = _vmathVfDot3( unitVec0->vec128, unitVec1->vec128 ); - cosAngle = vec_splat( cosAngle, 0 ); - cosAngleX2Plus2 = vec_madd( cosAngle, ((vec_float4){2.0f,2.0f,2.0f,2.0f}), ((vec_float4){2.0f,2.0f,2.0f,2.0f}) ); - recipCosHalfAngleX2 = rsqrtf4( cosAngleX2Plus2 ); - cosHalfAngleX2 = vec_madd( recipCosHalfAngleX2, cosAngleX2Plus2, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - vmathV3Cross( &tmpV3_0, unitVec0, unitVec1 ); - crossVec = tmpV3_0; - res = vec_madd( crossVec.vec128, recipCosHalfAngleX2, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - res = vec_sel( res, vec_madd( cosHalfAngleX2, ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ), _VECTORMATH_MASK_0x000F ); - result->vec128 = res; -} - -static inline void vmathQMakeRotationAxis( VmathQuat *result, float radians, const VmathVector3 *unitVec ) -{ - vec_float4 s, c, angle, res; - angle = vec_madd( _vmathVfSplatScalar(radians), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - sincosf4( angle, &s, &c ); - res = vec_sel( vec_madd( unitVec->vec128, s, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ), c, _VECTORMATH_MASK_0x000F ); - result->vec128 = res; -} - -static inline void vmathQMakeRotationX( VmathQuat *result, float radians ) -{ - vec_float4 s, c, angle, res; - angle = vec_madd( _vmathVfSplatScalar(radians), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - sincosf4( angle, &s, &c ); - res = vec_sel( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), s, _VECTORMATH_MASK_0xF000 ); - res = vec_sel( res, c, _VECTORMATH_MASK_0x000F ); - result->vec128 = res; -} - -static inline void vmathQMakeRotationY( VmathQuat *result, float radians ) -{ - vec_float4 s, c, angle, res; - angle = vec_madd( _vmathVfSplatScalar(radians), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - sincosf4( angle, &s, &c ); - res = vec_sel( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), s, _VECTORMATH_MASK_0x0F00 ); - res = vec_sel( res, c, _VECTORMATH_MASK_0x000F ); - result->vec128 = res; -} - -static inline void vmathQMakeRotationZ( VmathQuat *result, float radians ) -{ - vec_float4 s, c, angle, res; - angle = vec_madd( _vmathVfSplatScalar(radians), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - sincosf4( angle, &s, &c ); - res = vec_sel( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), s, _VECTORMATH_MASK_0x00F0 ); - res = vec_sel( res, c, _VECTORMATH_MASK_0x000F ); - result->vec128 = res; -} - -static inline void vmathQMul( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ) -{ - vec_float4 ldata, rdata, qv, tmp0, tmp1, tmp2, tmp3; - vec_float4 product, l_wxyz, r_wxyz, xy, qw; - ldata = quat0->vec128; - rdata = quat1->vec128; - tmp0 = vec_perm( ldata, ldata, _VECTORMATH_PERM_YZXW ); - tmp1 = vec_perm( rdata, rdata, _VECTORMATH_PERM_ZXYW ); - tmp2 = vec_perm( ldata, ldata, _VECTORMATH_PERM_ZXYW ); - tmp3 = vec_perm( rdata, rdata, _VECTORMATH_PERM_YZXW ); - qv = vec_madd( vec_splat( ldata, 3 ), rdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - qv = vec_madd( vec_splat( rdata, 3 ), ldata, qv ); - qv = vec_madd( tmp0, tmp1, qv ); - qv = vec_nmsub( tmp2, tmp3, qv ); - product = vec_madd( ldata, rdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - l_wxyz = vec_sld( ldata, ldata, 12 ); - r_wxyz = vec_sld( rdata, rdata, 12 ); - qw = vec_nmsub( l_wxyz, r_wxyz, product ); - xy = vec_madd( l_wxyz, r_wxyz, product ); - qw = vec_sub( qw, vec_sld( xy, xy, 8 ) ); - result->vec128 = vec_sel( qv, qw, _VECTORMATH_MASK_0x000F ); -} - -static inline void vmathQRotate( VmathVector3 *result, const VmathQuat *quat, const VmathVector3 *vec ) -{ - vec_float4 qdata, vdata, product, tmp0, tmp1, tmp2, tmp3, wwww, qv, qw, res; - qdata = quat->vec128; - vdata = vec->vec128; - tmp0 = vec_perm( qdata, qdata, _VECTORMATH_PERM_YZXW ); - tmp1 = vec_perm( vdata, vdata, _VECTORMATH_PERM_ZXYW ); - tmp2 = vec_perm( qdata, qdata, _VECTORMATH_PERM_ZXYW ); - tmp3 = vec_perm( vdata, vdata, _VECTORMATH_PERM_YZXW ); - wwww = vec_splat( qdata, 3 ); - qv = vec_madd( wwww, vdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - qv = vec_madd( tmp0, tmp1, qv ); - qv = vec_nmsub( tmp2, tmp3, qv ); - product = vec_madd( qdata, vdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - qw = vec_madd( vec_sld( qdata, qdata, 4 ), vec_sld( vdata, vdata, 4 ), product ); - qw = vec_add( vec_sld( product, product, 8 ), qw ); - tmp1 = vec_perm( qv, qv, _VECTORMATH_PERM_ZXYW ); - tmp3 = vec_perm( qv, qv, _VECTORMATH_PERM_YZXW ); - res = vec_madd( vec_splat( qw, 0 ), qdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - res = vec_madd( wwww, qv, res ); - res = vec_madd( tmp0, tmp1, res ); - res = vec_nmsub( tmp2, tmp3, res ); - result->vec128 = res; -} - -static inline void vmathQConj( VmathQuat *result, const VmathQuat *quat ) -{ - result->vec128 = vec_xor( quat->vec128, ((vec_float4)(vec_int4){0x80000000,0x80000000,0x80000000,0}) ); -} - -static inline void vmathQSelect( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1, unsigned int select1 ) -{ - unsigned int tmp; - tmp = (unsigned int)-(select1 > 0); - result->vec128 = vec_sel( quat0->vec128, quat1->vec128, _vmathVuiSplatScalar(tmp) ); -} - -#ifdef _VECTORMATH_DEBUG - -static inline void vmathQPrint( const VmathQuat *quat ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = quat->vec128; - printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -static inline void vmathQPrints( const VmathQuat *quat, const char *name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = quat->vec128; - printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -#endif - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_QUAT_AOS_C_H +#define _VECTORMATH_QUAT_AOS_C_H +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*----------------------------------------------------------------------------- + * Definitions + */ +#ifndef _VECTORMATH_INTERNAL_FUNCTIONS +#define _VECTORMATH_INTERNAL_FUNCTIONS + +#endif + +static inline void vmathQCopy( VmathQuat *result, const VmathQuat *quat ) +{ + result->vec128 = quat->vec128; +} + +static inline void vmathQMakeFromElems( VmathQuat *result, float _x, float _y, float _z, float _w ) +{ + if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & + __builtin_constant_p(_z) & __builtin_constant_p(_w)) { + result->vec128 = (vec_float4){_x, _y, _z, _w}; + } else { + float *pf = (float *)&result->vec128; + pf[0] = _x; + pf[1] = _y; + pf[2] = _z; + pf[3] = _w; + } +} + +static inline void vmathQMakeFromV3Scalar( VmathQuat *result, const VmathVector3 *xyz, float _w ) +{ + result->vec128 = xyz->vec128; + _vmathVfSetElement(result->vec128, _w, 3); +} + +static inline void vmathQMakeFromV4( VmathQuat *result, const VmathVector4 *vec ) +{ + result->vec128 = vec->vec128; +} + +static inline void vmathQMakeFromScalar( VmathQuat *result, float scalar ) +{ + result->vec128 = _vmathVfSplatScalar(scalar); +} + +static inline void vmathQMakeFrom128( VmathQuat *result, vec_float4 vf4 ) +{ + result->vec128 = vf4; +} + +static inline void vmathQMakeIdentity( VmathQuat *result ) +{ + result->vec128 = _VECTORMATH_UNIT_0001; +} + +static inline void vmathQLerp( VmathQuat *result, float t, const VmathQuat *quat0, const VmathQuat *quat1 ) +{ + VmathQuat tmpQ_0, tmpQ_1; + vmathQSub( &tmpQ_0, quat1, quat0 ); + vmathQScalarMul( &tmpQ_1, &tmpQ_0, t ); + vmathQAdd( result, quat0, &tmpQ_1 ); +} + +static inline void vmathQSlerp( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1 ) +{ + VmathQuat start; + vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; + vec_uint4 selectMask; + cosAngle = _vmathVfDot4( unitQuat0->vec128, unitQuat1->vec128 ); + cosAngle = vec_splat( cosAngle, 0 ); + selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), cosAngle ); + cosAngle = vec_sel( cosAngle, negatef4( cosAngle ), selectMask ); + start.vec128 = vec_sel( unitQuat0->vec128, negatef4( unitQuat0->vec128 ), selectMask ); + selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL}), cosAngle ); + angle = acosf4( cosAngle ); + tttt = _vmathVfSplatScalar(t); + oneMinusT = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); + angles = vec_mergeh( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); + angles = vec_mergeh( angles, oneMinusT ); + angles = vec_madd( angles, angle, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + sines = sinf4( angles ); + scales = divf4( sines, vec_splat( sines, 0 ) ); + scale0 = vec_sel( oneMinusT, vec_splat( scales, 1 ), selectMask ); + scale1 = vec_sel( tttt, vec_splat( scales, 2 ), selectMask ); + result->vec128 = vec_madd( start.vec128, scale0, vec_madd( unitQuat1->vec128, scale1, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); +} + +static inline void vmathQSquad( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1, const VmathQuat *unitQuat2, const VmathQuat *unitQuat3 ) +{ + VmathQuat tmp0, tmp1; + vmathQSlerp( &tmp0, t, unitQuat0, unitQuat3 ); + vmathQSlerp( &tmp1, t, unitQuat1, unitQuat2 ); + vmathQSlerp( result, ( ( 2.0f * t ) * ( 1.0f - t ) ), &tmp0, &tmp1 ); +} + +static inline vec_float4 vmathQGet128( const VmathQuat *quat ) +{ + return quat->vec128; +} + +static inline void vmathQSetXYZ( VmathQuat *result, const VmathVector3 *vec ) +{ + result->vec128 = vec_sel( vec->vec128, result->vec128, _VECTORMATH_MASK_0x000F ); +} + +static inline void vmathQGetXYZ( VmathVector3 *result, const VmathQuat *quat ) +{ + result->vec128 = quat->vec128; +} + +static inline void vmathQSetX( VmathQuat *result, float _x ) +{ + _vmathVfSetElement(result->vec128, _x, 0); +} + +static inline float vmathQGetX( const VmathQuat *quat ) +{ + return _vmathVfGetElement(quat->vec128, 0); +} + +static inline void vmathQSetY( VmathQuat *result, float _y ) +{ + _vmathVfSetElement(result->vec128, _y, 1); +} + +static inline float vmathQGetY( const VmathQuat *quat ) +{ + return _vmathVfGetElement(quat->vec128, 1); +} + +static inline void vmathQSetZ( VmathQuat *result, float _z ) +{ + _vmathVfSetElement(result->vec128, _z, 2); +} + +static inline float vmathQGetZ( const VmathQuat *quat ) +{ + return _vmathVfGetElement(quat->vec128, 2); +} + +static inline void vmathQSetW( VmathQuat *result, float _w ) +{ + _vmathVfSetElement(result->vec128, _w, 3); +} + +static inline float vmathQGetW( const VmathQuat *quat ) +{ + return _vmathVfGetElement(quat->vec128, 3); +} + +static inline void vmathQSetElem( VmathQuat *result, int idx, float value ) +{ + _vmathVfSetElement(result->vec128, value, idx); +} + +static inline float vmathQGetElem( const VmathQuat *quat, int idx ) +{ + return _vmathVfGetElement(quat->vec128, idx); +} + +static inline void vmathQAdd( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ) +{ + result->vec128 = vec_add( quat0->vec128, quat1->vec128 ); +} + +static inline void vmathQSub( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ) +{ + result->vec128 = vec_sub( quat0->vec128, quat1->vec128 ); +} + +static inline void vmathQScalarMul( VmathQuat *result, const VmathQuat *quat, float scalar ) +{ + result->vec128 = vec_madd( quat->vec128, _vmathVfSplatScalar(scalar), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); +} + +static inline void vmathQScalarDiv( VmathQuat *result, const VmathQuat *quat, float scalar ) +{ + result->vec128 = divf4( quat->vec128, _vmathVfSplatScalar(scalar) ); +} + +static inline void vmathQNeg( VmathQuat *result, const VmathQuat *quat ) +{ + result->vec128 = negatef4( quat->vec128 ); +} + +static inline float vmathQDot( const VmathQuat *quat0, const VmathQuat *quat1 ) +{ + vec_float4 result = _vmathVfDot4( quat0->vec128, quat1->vec128 ); + return _vmathVfGetElement(result, 0); +} + +static inline float vmathQNorm( const VmathQuat *quat ) +{ + vec_float4 result = _vmathVfDot4( quat->vec128, quat->vec128 ); + return _vmathVfGetElement(result, 0); +} + +static inline float vmathQLength( const VmathQuat *quat ) +{ + return sqrtf( vmathQNorm( quat ) ); +} + +static inline void vmathQNormalize( VmathQuat *result, const VmathQuat *quat ) +{ + vec_float4 dot = _vmathVfDot4( quat->vec128, quat->vec128 ); + result->vec128 = vec_madd( quat->vec128, rsqrtf4( dot ), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); +} + +static inline void vmathQMakeRotationArc( VmathQuat *result, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ) +{ + VmathVector3 crossVec, tmpV3_0; + vec_float4 cosAngle, cosAngleX2Plus2, recipCosHalfAngleX2, cosHalfAngleX2, res; + cosAngle = _vmathVfDot3( unitVec0->vec128, unitVec1->vec128 ); + cosAngle = vec_splat( cosAngle, 0 ); + cosAngleX2Plus2 = vec_madd( cosAngle, ((vec_float4){2.0f,2.0f,2.0f,2.0f}), ((vec_float4){2.0f,2.0f,2.0f,2.0f}) ); + recipCosHalfAngleX2 = rsqrtf4( cosAngleX2Plus2 ); + cosHalfAngleX2 = vec_madd( recipCosHalfAngleX2, cosAngleX2Plus2, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + vmathV3Cross( &tmpV3_0, unitVec0, unitVec1 ); + crossVec = tmpV3_0; + res = vec_madd( crossVec.vec128, recipCosHalfAngleX2, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + res = vec_sel( res, vec_madd( cosHalfAngleX2, ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ), _VECTORMATH_MASK_0x000F ); + result->vec128 = res; +} + +static inline void vmathQMakeRotationAxis( VmathQuat *result, float radians, const VmathVector3 *unitVec ) +{ + vec_float4 s, c, angle, res; + angle = vec_madd( _vmathVfSplatScalar(radians), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + sincosf4( angle, &s, &c ); + res = vec_sel( vec_madd( unitVec->vec128, s, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ), c, _VECTORMATH_MASK_0x000F ); + result->vec128 = res; +} + +static inline void vmathQMakeRotationX( VmathQuat *result, float radians ) +{ + vec_float4 s, c, angle, res; + angle = vec_madd( _vmathVfSplatScalar(radians), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + sincosf4( angle, &s, &c ); + res = vec_sel( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), s, _VECTORMATH_MASK_0xF000 ); + res = vec_sel( res, c, _VECTORMATH_MASK_0x000F ); + result->vec128 = res; +} + +static inline void vmathQMakeRotationY( VmathQuat *result, float radians ) +{ + vec_float4 s, c, angle, res; + angle = vec_madd( _vmathVfSplatScalar(radians), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + sincosf4( angle, &s, &c ); + res = vec_sel( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), s, _VECTORMATH_MASK_0x0F00 ); + res = vec_sel( res, c, _VECTORMATH_MASK_0x000F ); + result->vec128 = res; +} + +static inline void vmathQMakeRotationZ( VmathQuat *result, float radians ) +{ + vec_float4 s, c, angle, res; + angle = vec_madd( _vmathVfSplatScalar(radians), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + sincosf4( angle, &s, &c ); + res = vec_sel( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), s, _VECTORMATH_MASK_0x00F0 ); + res = vec_sel( res, c, _VECTORMATH_MASK_0x000F ); + result->vec128 = res; +} + +static inline void vmathQMul( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ) +{ + vec_float4 ldata, rdata, qv, tmp0, tmp1, tmp2, tmp3; + vec_float4 product, l_wxyz, r_wxyz, xy, qw; + ldata = quat0->vec128; + rdata = quat1->vec128; + tmp0 = vec_perm( ldata, ldata, _VECTORMATH_PERM_YZXW ); + tmp1 = vec_perm( rdata, rdata, _VECTORMATH_PERM_ZXYW ); + tmp2 = vec_perm( ldata, ldata, _VECTORMATH_PERM_ZXYW ); + tmp3 = vec_perm( rdata, rdata, _VECTORMATH_PERM_YZXW ); + qv = vec_madd( vec_splat( ldata, 3 ), rdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + qv = vec_madd( vec_splat( rdata, 3 ), ldata, qv ); + qv = vec_madd( tmp0, tmp1, qv ); + qv = vec_nmsub( tmp2, tmp3, qv ); + product = vec_madd( ldata, rdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + l_wxyz = vec_sld( ldata, ldata, 12 ); + r_wxyz = vec_sld( rdata, rdata, 12 ); + qw = vec_nmsub( l_wxyz, r_wxyz, product ); + xy = vec_madd( l_wxyz, r_wxyz, product ); + qw = vec_sub( qw, vec_sld( xy, xy, 8 ) ); + result->vec128 = vec_sel( qv, qw, _VECTORMATH_MASK_0x000F ); +} + +static inline void vmathQRotate( VmathVector3 *result, const VmathQuat *quat, const VmathVector3 *vec ) +{ + vec_float4 qdata, vdata, product, tmp0, tmp1, tmp2, tmp3, wwww, qv, qw, res; + qdata = quat->vec128; + vdata = vec->vec128; + tmp0 = vec_perm( qdata, qdata, _VECTORMATH_PERM_YZXW ); + tmp1 = vec_perm( vdata, vdata, _VECTORMATH_PERM_ZXYW ); + tmp2 = vec_perm( qdata, qdata, _VECTORMATH_PERM_ZXYW ); + tmp3 = vec_perm( vdata, vdata, _VECTORMATH_PERM_YZXW ); + wwww = vec_splat( qdata, 3 ); + qv = vec_madd( wwww, vdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + qv = vec_madd( tmp0, tmp1, qv ); + qv = vec_nmsub( tmp2, tmp3, qv ); + product = vec_madd( qdata, vdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + qw = vec_madd( vec_sld( qdata, qdata, 4 ), vec_sld( vdata, vdata, 4 ), product ); + qw = vec_add( vec_sld( product, product, 8 ), qw ); + tmp1 = vec_perm( qv, qv, _VECTORMATH_PERM_ZXYW ); + tmp3 = vec_perm( qv, qv, _VECTORMATH_PERM_YZXW ); + res = vec_madd( vec_splat( qw, 0 ), qdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + res = vec_madd( wwww, qv, res ); + res = vec_madd( tmp0, tmp1, res ); + res = vec_nmsub( tmp2, tmp3, res ); + result->vec128 = res; +} + +static inline void vmathQConj( VmathQuat *result, const VmathQuat *quat ) +{ + result->vec128 = vec_xor( quat->vec128, ((vec_float4)(vec_uint4){0x80000000,0x80000000,0x80000000,0}) ); +} + +static inline void vmathQSelect( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1, unsigned int select1 ) +{ + unsigned int tmp; + tmp = (unsigned int)-(select1 > 0); + result->vec128 = vec_sel( quat0->vec128, quat1->vec128, _vmathVuiSplatScalar(tmp) ); +} + +#ifdef _VECTORMATH_DEBUG + +static inline void vmathQPrint( const VmathQuat *quat ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = quat->vec128; + printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +static inline void vmathQPrints( const VmathQuat *quat, const char *name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = quat->vec128; + printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/common/vectormath/ppu/c/vec_aos.h b/common/vectormath/ppu/c/vec_aos.h index 58427a22..25682841 100644 --- a/common/vectormath/ppu/c/vec_aos.h +++ b/common/vectormath/ppu/c/vec_aos.h @@ -1,1125 +1,1125 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_VEC_AOS_C_H -#define _VECTORMATH_VEC_AOS_C_H -#include -#include -#include -#include "vec_types.h" -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/*----------------------------------------------------------------------------- - * Constants - * for permutes words are labeled [x,y,z,w] [a,b,c,d] - */ -#define _VECTORMATH_PERM_X 0x00010203 -#define _VECTORMATH_PERM_Y 0x04050607 -#define _VECTORMATH_PERM_Z 0x08090a0b -#define _VECTORMATH_PERM_W 0x0c0d0e0f -#define _VECTORMATH_PERM_A 0x10111213 -#define _VECTORMATH_PERM_B 0x14151617 -#define _VECTORMATH_PERM_C 0x18191a1b -#define _VECTORMATH_PERM_D 0x1c1d1e1f -#define _VECTORMATH_PERM_XYZA (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A } -#define _VECTORMATH_PERM_ZXYW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_W } -#define _VECTORMATH_PERM_YZXW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_W } -#define _VECTORMATH_PERM_YZAB (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A, _VECTORMATH_PERM_B } -#define _VECTORMATH_PERM_ZABC (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A, _VECTORMATH_PERM_B, _VECTORMATH_PERM_C } -#define _VECTORMATH_PERM_XYAW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A, _VECTORMATH_PERM_W } -#define _VECTORMATH_PERM_XAZW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_A, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_W } -#define _VECTORMATH_MASK_0xF000 (vec_uint4){ 0xffffffff, 0, 0, 0 } -#define _VECTORMATH_MASK_0x0F00 (vec_uint4){ 0, 0xffffffff, 0, 0 } -#define _VECTORMATH_MASK_0x00F0 (vec_uint4){ 0, 0, 0xffffffff, 0 } -#define _VECTORMATH_MASK_0x000F (vec_uint4){ 0, 0, 0, 0xffffffff } -#define _VECTORMATH_UNIT_1000 (vec_float4){ 1.0f, 0.0f, 0.0f, 0.0f } -#define _VECTORMATH_UNIT_0100 (vec_float4){ 0.0f, 1.0f, 0.0f, 0.0f } -#define _VECTORMATH_UNIT_0010 (vec_float4){ 0.0f, 0.0f, 1.0f, 0.0f } -#define _VECTORMATH_UNIT_0001 (vec_float4){ 0.0f, 0.0f, 0.0f, 1.0f } -#define _VECTORMATH_SLERP_TOL 0.999f - -/*----------------------------------------------------------------------------- - * Definitions - */ -#ifndef _VECTORMATH_INTERNAL_FUNCTIONS -#define _VECTORMATH_INTERNAL_FUNCTIONS - -static inline vec_float4 _vmathVfDot3( vec_float4 vec0, vec_float4 vec1 ) -{ - vec_float4 result; - result = vec_madd( vec0, vec1, (vec_float4){0.0f,0.0f,0.0f,0.0f} ); - result = vec_madd( vec_sld( vec0, vec0, 4 ), vec_sld( vec1, vec1, 4 ), result ); - return vec_madd( vec_sld( vec0, vec0, 8 ), vec_sld( vec1, vec1, 8 ), result ); -} - -static inline vec_float4 _vmathVfDot4( vec_float4 vec0, vec_float4 vec1 ) -{ - vec_float4 result; - result = vec_madd( vec0, vec1, (vec_float4){0.0f,0.0f,0.0f,0.0f} ); - result = vec_madd( vec_sld( vec0, vec0, 4 ), vec_sld( vec1, vec1, 4 ), result ); - return vec_add( vec_sld( result, result, 8 ), result ); -} - -static inline vec_float4 _vmathVfCross( vec_float4 vec0, vec_float4 vec1 ) -{ - vec_float4 tmp0, tmp1, tmp2, tmp3, result; - tmp0 = vec_perm( vec0, vec0, _VECTORMATH_PERM_YZXW ); - tmp1 = vec_perm( vec1, vec1, _VECTORMATH_PERM_ZXYW ); - tmp2 = vec_perm( vec0, vec0, _VECTORMATH_PERM_ZXYW ); - tmp3 = vec_perm( vec1, vec1, _VECTORMATH_PERM_YZXW ); - result = vec_madd( tmp0, tmp1, (vec_float4){0.0f,0.0f,0.0f,0.0f} ); - result = vec_nmsub( tmp2, tmp3, result ); - return result; -} - -static inline vec_uint4 _vmathVfToHalfFloatsUnpacked(vec_float4 v) -{ - vec_int4 bexp; - vec_uint4 mant, sign, hfloat; - vec_uint4 notZero, isInf; - const vec_uint4 hfloatInf = (vec_uint4){0x00007c00u,0x00007c00u,0x00007c00u,0x00007c00u}; - const vec_uint4 mergeMant = (vec_uint4){0x000003ffu,0x000003ffu,0x000003ffu,0x000003ffu}; - const vec_uint4 mergeSign = (vec_uint4){0x00008000u,0x00008000u,0x00008000u,0x00008000u}; - - sign = vec_sr((vec_uint4)v, (vec_uint4){16,16,16,16}); - mant = vec_sr((vec_uint4)v, (vec_uint4){13,13,13,13}); - bexp = vec_and(vec_sr((vec_int4)v, (vec_uint4){23,23,23,23}), (vec_int4){0xff,0xff,0xff,0xff}); - - notZero = (vec_uint4)vec_cmpgt(bexp, (vec_int4){112,112,112,112}); - isInf = (vec_uint4)vec_cmpgt(bexp, (vec_int4){142,142,142,142}); - - bexp = vec_add(bexp, (vec_int4){-112,-112,-112,-112}); - bexp = vec_sl(bexp, (vec_uint4){10,10,10,10}); - - hfloat = vec_sel((vec_uint4)bexp, mant, mergeMant); - hfloat = vec_sel((vec_uint4){0,0,0,0}, hfloat, notZero); - hfloat = vec_sel(hfloat, hfloatInf, isInf); - hfloat = vec_sel(hfloat, sign, mergeSign); - - return hfloat; -} - -static inline vec_ushort8 _vmath2VfToHalfFloats(vec_float4 u, vec_float4 v) -{ - vec_uint4 hfloat_u, hfloat_v; - const vec_uchar16 pack = (vec_uchar16){2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31}; - hfloat_u = _vmathVfToHalfFloatsUnpacked(u); - hfloat_v = _vmathVfToHalfFloatsUnpacked(v); - return (vec_ushort8)vec_perm(hfloat_u, hfloat_v, pack); -} - -#ifndef __GNUC__ -#define __builtin_constant_p(x) 0 -#endif - -static inline vec_float4 _vmathVfInsert(vec_float4 dst, vec_float4 src, int slot) -{ -#ifdef __GNUC__ - if (__builtin_constant_p(slot)) { - dst = vec_sld(dst, dst, slot<<2); - dst = vec_sld(dst, src, 4); - if (slot != 3) dst = vec_sld(dst, dst, (3-slot)<<2); - return dst; - } else -#endif - { - vec_uchar16 shiftpattern = vec_lvsr( 0, (float *)(size_t)(slot<<2) ); - vec_uint4 selectmask = (vec_uint4)vec_perm( (vec_uint4){0,0,0,0}, _VECTORMATH_MASK_0xF000, shiftpattern ); - return vec_sel( dst, src, selectmask ); - } -} - -#define _vmathVfGetElement(vec, slot) ((float *)&(vec))[slot] -#ifdef _VECTORMATH_SET_CONSTS_IN_MEM -#define _vmathVfSetElement(vec, scalar, slot) ((float *)&(vec))[slot] = scalar -#else -#define _vmathVfSetElement(vec, scalar, slot) \ -{ \ - if (__builtin_constant_p(scalar)) { \ - (vec) = _vmathVfInsert(vec, (vec_float4){scalar, scalar, scalar, scalar}, slot); \ - } else { \ - ((float *)&(vec))[slot] = scalar; \ - } \ -} -#endif - -static inline vec_float4 _vmathVfSplatScalar(float scalar) -{ - vec_float4 result; - if (__builtin_constant_p(scalar)) { - result = (vec_float4){scalar, scalar, scalar, scalar}; - } else { - result = vec_ld(0, &scalar); - result = vec_splat(vec_perm(result, result, vec_lvsl(0, &scalar)), 0); - } - return result; -} - -static inline vec_uint4 _vmathVuiSplatScalar(unsigned int scalar) -{ - vec_uint4 result; - if (__builtin_constant_p(scalar)) { - result = (vec_uint4){scalar, scalar, scalar, scalar}; - } else { - result = vec_ld(0, &scalar); - result = vec_splat(vec_perm(result, result, vec_lvsl(0, &scalar)), 0); - } - return result; -} - -#endif - -static inline void vmathV3Copy( VmathVector3 *result, const VmathVector3 *vec ) -{ - result->vec128 = vec->vec128; -} - -static inline void vmathV3MakeFromElems( VmathVector3 *result, float _x, float _y, float _z ) -{ - if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & __builtin_constant_p(_z)) { - result->vec128 = (vec_float4){_x, _y, _z, 0.0f}; - } else { - float *pf = (float *)&result->vec128; - pf[0] = _x; - pf[1] = _y; - pf[2] = _z; - } -} - -static inline void vmathV3MakeFromP3( VmathVector3 *result, const VmathPoint3 *pnt ) -{ - result->vec128 = pnt->vec128; -} - -static inline void vmathV3MakeFromScalar( VmathVector3 *result, float scalar ) -{ - result->vec128 = _vmathVfSplatScalar(scalar); -} - -static inline void vmathV3MakeFrom128( VmathVector3 *result, vec_float4 vf4 ) -{ - result->vec128 = vf4; -} - -static inline void vmathV3MakeXAxis( VmathVector3 *result ) -{ - result->vec128 = _VECTORMATH_UNIT_1000; -} - -static inline void vmathV3MakeYAxis( VmathVector3 *result ) -{ - result->vec128 = _VECTORMATH_UNIT_0100; -} - -static inline void vmathV3MakeZAxis( VmathVector3 *result ) -{ - result->vec128 = _VECTORMATH_UNIT_0010; -} - -static inline void vmathV3Lerp( VmathVector3 *result, float t, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - VmathVector3 tmpV3_0, tmpV3_1; - vmathV3Sub( &tmpV3_0, vec1, vec0 ); - vmathV3ScalarMul( &tmpV3_1, &tmpV3_0, t ); - vmathV3Add( result, vec0, &tmpV3_1 ); -} - -static inline void vmathV3Slerp( VmathVector3 *result, float t, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ) -{ - vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; - vec_uint4 selectMask; - cosAngle = _vmathVfDot3( unitVec0->vec128, unitVec1->vec128 ); - cosAngle = vec_splat( cosAngle, 0 ); - selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL}), cosAngle ); - angle = acosf4( cosAngle ); - tttt = _vmathVfSplatScalar(t); - oneMinusT = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); - angles = vec_mergeh( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); - angles = vec_mergeh( angles, oneMinusT ); - angles = vec_madd( angles, angle, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - sines = sinf4( angles ); - scales = divf4( sines, vec_splat( sines, 0 ) ); - scale0 = vec_sel( oneMinusT, vec_splat( scales, 1 ), selectMask ); - scale1 = vec_sel( tttt, vec_splat( scales, 2 ), selectMask ); - result->vec128 = vec_madd( unitVec0->vec128, scale0, vec_madd( unitVec1->vec128, scale1, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); -} - -static inline vec_float4 vmathV3Get128( const VmathVector3 *vec ) -{ - return vec->vec128; -} - -static inline void vmathV3StoreXYZ( const VmathVector3 *vec, vec_float4 *quad ) -{ - vec_float4 dstVec = *quad; - vec_uint4 mask = _VECTORMATH_MASK_0x000F; - dstVec = vec_sel(vec->vec128, dstVec, mask); - *quad = dstVec; -} - -static inline void vmathV3LoadXYZArray( VmathVector3 *vec0, VmathVector3 *vec1, VmathVector3 *vec2, VmathVector3 *vec3, const vec_float4 *threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; - xyzx = threeQuads[0]; - yzxy = threeQuads[1]; - zxyz = threeQuads[2]; - xyz1 = vec_sld( xyzx, yzxy, 12 ); - xyz2 = vec_sld( yzxy, zxyz, 8 ); - xyz3 = vec_sld( zxyz, zxyz, 4 ); - vec0->vec128 = xyzx; - vec1->vec128 = xyz1; - vec2->vec128 = xyz2; - vec3->vec128 = xyz3; -} - -static inline void vmathV3StoreXYZArray( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, vec_float4 *threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz; - xyzx = vec_perm( vec0->vec128, vec1->vec128, _VECTORMATH_PERM_XYZA ); - yzxy = vec_perm( vec1->vec128, vec2->vec128, _VECTORMATH_PERM_YZAB ); - zxyz = vec_perm( vec2->vec128, vec3->vec128, _VECTORMATH_PERM_ZABC ); - threeQuads[0] = xyzx; - threeQuads[1] = yzxy; - threeQuads[2] = zxyz; -} - -static inline void vmathV3StoreHalfFloats( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, const VmathVector3 *vec4, const VmathVector3 *vec5, const VmathVector3 *vec6, const VmathVector3 *vec7, vec_ushort8 *threeQuads ) -{ - vec_float4 xyz0[3]; - vec_float4 xyz1[3]; - vmathV3StoreXYZArray( vec0, vec1, vec2, vec3, xyz0 ); - vmathV3StoreXYZArray( vec4, vec5, vec6, vec7, xyz1 ); - threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); - threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); - threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); -} - -static inline void vmathV3SetX( VmathVector3 *result, float _x ) -{ - _vmathVfSetElement(result->vec128, _x, 0); -} - -static inline float vmathV3GetX( const VmathVector3 *vec ) -{ - return _vmathVfGetElement(vec->vec128, 0); -} - -static inline void vmathV3SetY( VmathVector3 *result, float _y ) -{ - _vmathVfSetElement(result->vec128, _y, 1); -} - -static inline float vmathV3GetY( const VmathVector3 *vec ) -{ - return _vmathVfGetElement(vec->vec128, 1); -} - -static inline void vmathV3SetZ( VmathVector3 *result, float _z ) -{ - _vmathVfSetElement(result->vec128, _z, 2); -} - -static inline float vmathV3GetZ( const VmathVector3 *vec ) -{ - return _vmathVfGetElement(vec->vec128, 2); -} - -static inline void vmathV3SetElem( VmathVector3 *result, int idx, float value ) -{ - _vmathVfSetElement(result->vec128, value, idx); -} - -static inline float vmathV3GetElem( const VmathVector3 *vec, int idx ) -{ - return _vmathVfGetElement(vec->vec128, idx); -} - -static inline void vmathV3Add( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = vec_add( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV3Sub( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = vec_sub( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV3AddP3( VmathPoint3 *result, const VmathVector3 *vec, const VmathPoint3 *pnt1 ) -{ - result->vec128 = vec_add( vec->vec128, pnt1->vec128 ); -} - -static inline void vmathV3ScalarMul( VmathVector3 *result, const VmathVector3 *vec, float scalar ) -{ - result->vec128 = vec_madd( vec->vec128, _vmathVfSplatScalar(scalar), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); -} - -static inline void vmathV3ScalarDiv( VmathVector3 *result, const VmathVector3 *vec, float scalar ) -{ - result->vec128 = divf4( vec->vec128, _vmathVfSplatScalar(scalar) ); -} - -static inline void vmathV3Neg( VmathVector3 *result, const VmathVector3 *vec ) -{ - result->vec128 = negatef4( vec->vec128 ); -} - -static inline void vmathV3MulPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = vec_madd( vec0->vec128, vec1->vec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); -} - -static inline void vmathV3DivPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = divf4( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV3RecipPerElem( VmathVector3 *result, const VmathVector3 *vec ) -{ - result->vec128 = recipf4( vec->vec128 ); -} - -static inline void vmathV3SqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ) -{ - result->vec128 = sqrtf4( vec->vec128 ); -} - -static inline void vmathV3RsqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ) -{ - result->vec128 = rsqrtf4( vec->vec128 ); -} - -static inline void vmathV3AbsPerElem( VmathVector3 *result, const VmathVector3 *vec ) -{ - result->vec128 = fabsf4( vec->vec128 ); -} - -static inline void vmathV3CopySignPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = copysignf4( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV3MaxPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = fmaxf4( vec0->vec128, vec1->vec128 ); -} - -static inline float vmathV3MaxElem( const VmathVector3 *vec ) -{ - vec_float4 result; - result = fmaxf4( vec_splat( vec->vec128, 1 ), vec->vec128 ); - result = fmaxf4( vec_splat( vec->vec128, 2 ), result ); - return _vmathVfGetElement(result, 0); -} - -static inline void vmathV3MinPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = fminf4( vec0->vec128, vec1->vec128 ); -} - -static inline float vmathV3MinElem( const VmathVector3 *vec ) -{ - vec_float4 result; - result = fminf4( vec_splat( vec->vec128, 1 ), vec->vec128 ); - result = fminf4( vec_splat( vec->vec128, 2 ), result ); - return _vmathVfGetElement(result, 0); -} - -static inline float vmathV3Sum( const VmathVector3 *vec ) -{ - vec_float4 result; - result = vec_add( vec_splat( vec->vec128, 1 ), vec->vec128 ); - result = vec_add( vec_splat( vec->vec128, 2 ), result ); - return _vmathVfGetElement(result, 0); -} - -static inline float vmathV3Dot( const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - vec_float4 result = _vmathVfDot3( vec0->vec128, vec1->vec128 ); - return _vmathVfGetElement(result, 0); -} - -static inline float vmathV3LengthSqr( const VmathVector3 *vec ) -{ - vec_float4 result = _vmathVfDot3( vec->vec128, vec->vec128 ); - return _vmathVfGetElement(result, 0); -} - -static inline float vmathV3Length( const VmathVector3 *vec ) -{ - return sqrtf( vmathV3LengthSqr( vec ) ); -} - -static inline void vmathV3Normalize( VmathVector3 *result, const VmathVector3 *vec ) -{ - vec_float4 dot = _vmathVfDot3( vec->vec128, vec->vec128 ); - dot = vec_splat( dot, 0 ); - result->vec128 = vec_madd( vec->vec128, rsqrtf4( dot ), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); -} - -static inline void vmathV3Cross( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = _vmathVfCross( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV3Select( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1, unsigned int select1 ) -{ - unsigned int tmp; - tmp = (unsigned int)-(select1 > 0); - result->vec128 = vec_sel( vec0->vec128, vec1->vec128, _vmathVuiSplatScalar(tmp) ); -} - -#ifdef _VECTORMATH_DEBUG - -static inline void vmathV3Print( const VmathVector3 *vec ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec->vec128; - printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -static inline void vmathV3Prints( const VmathVector3 *vec, const char *name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec->vec128; - printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -#endif - -static inline void vmathV4Copy( VmathVector4 *result, const VmathVector4 *vec ) -{ - result->vec128 = vec->vec128; -} - -static inline void vmathV4MakeFromElems( VmathVector4 *result, float _x, float _y, float _z, float _w ) -{ - if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & - __builtin_constant_p(_z) & __builtin_constant_p(_w)) { - result->vec128 = (vec_float4){_x, _y, _z, _w}; - } else { - float *pf = (float *)&result->vec128; - pf[0] = _x; - pf[1] = _y; - pf[2] = _z; - pf[3] = _w; - } -} - -static inline void vmathV4MakeFromV3Scalar( VmathVector4 *result, const VmathVector3 *xyz, float _w ) -{ - result->vec128 = xyz->vec128; - _vmathVfSetElement(result->vec128, _w, 3); -} - -static inline void vmathV4MakeFromV3( VmathVector4 *result, const VmathVector3 *vec ) -{ - result->vec128 = vec->vec128; - result->vec128 = _vmathVfInsert(result->vec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}), 3); -} - -static inline void vmathV4MakeFromP3( VmathVector4 *result, const VmathPoint3 *pnt ) -{ - result->vec128 = pnt->vec128; - result->vec128 = _vmathVfInsert(result->vec128, ((vec_float4){1.0f,1.0f,1.0f,1.0f}), 3); -} - -static inline void vmathV4MakeFromQ( VmathVector4 *result, const VmathQuat *quat ) -{ - result->vec128 = quat->vec128; -} - -static inline void vmathV4MakeFromScalar( VmathVector4 *result, float scalar ) -{ - result->vec128 = _vmathVfSplatScalar(scalar); -} - -static inline void vmathV4MakeFrom128( VmathVector4 *result, vec_float4 vf4 ) -{ - result->vec128 = vf4; -} - -static inline void vmathV4MakeXAxis( VmathVector4 *result ) -{ - result->vec128 = _VECTORMATH_UNIT_1000; -} - -static inline void vmathV4MakeYAxis( VmathVector4 *result ) -{ - result->vec128 = _VECTORMATH_UNIT_0100; -} - -static inline void vmathV4MakeZAxis( VmathVector4 *result ) -{ - result->vec128 = _VECTORMATH_UNIT_0010; -} - -static inline void vmathV4MakeWAxis( VmathVector4 *result ) -{ - result->vec128 = _VECTORMATH_UNIT_0001; -} - -static inline void vmathV4Lerp( VmathVector4 *result, float t, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - VmathVector4 tmpV4_0, tmpV4_1; - vmathV4Sub( &tmpV4_0, vec1, vec0 ); - vmathV4ScalarMul( &tmpV4_1, &tmpV4_0, t ); - vmathV4Add( result, vec0, &tmpV4_1 ); -} - -static inline void vmathV4Slerp( VmathVector4 *result, float t, const VmathVector4 *unitVec0, const VmathVector4 *unitVec1 ) -{ - vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; - vec_uint4 selectMask; - cosAngle = _vmathVfDot4( unitVec0->vec128, unitVec1->vec128 ); - cosAngle = vec_splat( cosAngle, 0 ); - selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL}), cosAngle ); - angle = acosf4( cosAngle ); - tttt = _vmathVfSplatScalar(t); - oneMinusT = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); - angles = vec_mergeh( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); - angles = vec_mergeh( angles, oneMinusT ); - angles = vec_madd( angles, angle, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - sines = sinf4( angles ); - scales = divf4( sines, vec_splat( sines, 0 ) ); - scale0 = vec_sel( oneMinusT, vec_splat( scales, 1 ), selectMask ); - scale1 = vec_sel( tttt, vec_splat( scales, 2 ), selectMask ); - result->vec128 = vec_madd( unitVec0->vec128, scale0, vec_madd( unitVec1->vec128, scale1, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); -} - -static inline vec_float4 vmathV4Get128( const VmathVector4 *vec ) -{ - return vec->vec128; -} - -static inline void vmathV4StoreHalfFloats( const VmathVector4 *vec0, const VmathVector4 *vec1, const VmathVector4 *vec2, const VmathVector4 *vec3, vec_ushort8 *twoQuads ) -{ - twoQuads[0] = _vmath2VfToHalfFloats(vec0->vec128, vec1->vec128); - twoQuads[1] = _vmath2VfToHalfFloats(vec2->vec128, vec3->vec128); -} - -static inline void vmathV4SetXYZ( VmathVector4 *result, const VmathVector3 *vec ) -{ - result->vec128 = vec_sel( vec->vec128, result->vec128, _VECTORMATH_MASK_0x000F ); -} - -static inline void vmathV4GetXYZ( VmathVector3 *result, const VmathVector4 *vec ) -{ - result->vec128 = vec->vec128; -} - -static inline void vmathV4SetX( VmathVector4 *result, float _x ) -{ - _vmathVfSetElement(result->vec128, _x, 0); -} - -static inline float vmathV4GetX( const VmathVector4 *vec ) -{ - return _vmathVfGetElement(vec->vec128, 0); -} - -static inline void vmathV4SetY( VmathVector4 *result, float _y ) -{ - _vmathVfSetElement(result->vec128, _y, 1); -} - -static inline float vmathV4GetY( const VmathVector4 *vec ) -{ - return _vmathVfGetElement(vec->vec128, 1); -} - -static inline void vmathV4SetZ( VmathVector4 *result, float _z ) -{ - _vmathVfSetElement(result->vec128, _z, 2); -} - -static inline float vmathV4GetZ( const VmathVector4 *vec ) -{ - return _vmathVfGetElement(vec->vec128, 2); -} - -static inline void vmathV4SetW( VmathVector4 *result, float _w ) -{ - _vmathVfSetElement(result->vec128, _w, 3); -} - -static inline float vmathV4GetW( const VmathVector4 *vec ) -{ - return _vmathVfGetElement(vec->vec128, 3); -} - -static inline void vmathV4SetElem( VmathVector4 *result, int idx, float value ) -{ - _vmathVfSetElement(result->vec128, value, idx); -} - -static inline float vmathV4GetElem( const VmathVector4 *vec, int idx ) -{ - return _vmathVfGetElement(vec->vec128, idx); -} - -static inline void vmathV4Add( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - result->vec128 = vec_add( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV4Sub( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - result->vec128 = vec_sub( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV4ScalarMul( VmathVector4 *result, const VmathVector4 *vec, float scalar ) -{ - result->vec128 = vec_madd( vec->vec128, _vmathVfSplatScalar(scalar), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); -} - -static inline void vmathV4ScalarDiv( VmathVector4 *result, const VmathVector4 *vec, float scalar ) -{ - result->vec128 = divf4( vec->vec128, _vmathVfSplatScalar(scalar) ); -} - -static inline void vmathV4Neg( VmathVector4 *result, const VmathVector4 *vec ) -{ - result->vec128 = negatef4( vec->vec128 ); -} - -static inline void vmathV4MulPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - result->vec128 = vec_madd( vec0->vec128, vec1->vec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); -} - -static inline void vmathV4DivPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - result->vec128 = divf4( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV4RecipPerElem( VmathVector4 *result, const VmathVector4 *vec ) -{ - result->vec128 = recipf4( vec->vec128 ); -} - -static inline void vmathV4SqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ) -{ - result->vec128 = sqrtf4( vec->vec128 ); -} - -static inline void vmathV4RsqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ) -{ - result->vec128 = rsqrtf4( vec->vec128 ); -} - -static inline void vmathV4AbsPerElem( VmathVector4 *result, const VmathVector4 *vec ) -{ - result->vec128 = fabsf4( vec->vec128 ); -} - -static inline void vmathV4CopySignPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - result->vec128 = copysignf4( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV4MaxPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - result->vec128 = fmaxf4( vec0->vec128, vec1->vec128 ); -} - -static inline float vmathV4MaxElem( const VmathVector4 *vec ) -{ - vec_float4 result; - result = fmaxf4( vec_splat( vec->vec128, 1 ), vec->vec128 ); - result = fmaxf4( vec_splat( vec->vec128, 2 ), result ); - result = fmaxf4( vec_splat( vec->vec128, 3 ), result ); - return _vmathVfGetElement(result, 0); -} - -static inline void vmathV4MinPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - result->vec128 = fminf4( vec0->vec128, vec1->vec128 ); -} - -static inline float vmathV4MinElem( const VmathVector4 *vec ) -{ - vec_float4 result; - result = fminf4( vec_splat( vec->vec128, 1 ), vec->vec128 ); - result = fminf4( vec_splat( vec->vec128, 2 ), result ); - result = fminf4( vec_splat( vec->vec128, 3 ), result ); - return _vmathVfGetElement(result, 0); -} - -static inline float vmathV4Sum( const VmathVector4 *vec ) -{ - vec_float4 result; - result = vec_add( vec_splat( vec->vec128, 1 ), vec->vec128 ); - result = vec_add( vec_splat( vec->vec128, 2 ), result ); - result = vec_add( vec_splat( vec->vec128, 3 ), result ); - return _vmathVfGetElement(result, 0); -} - -static inline float vmathV4Dot( const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - vec_float4 result = _vmathVfDot4( vec0->vec128, vec1->vec128 ); - return _vmathVfGetElement(result, 0); -} - -static inline float vmathV4LengthSqr( const VmathVector4 *vec ) -{ - vec_float4 result = _vmathVfDot4( vec->vec128, vec->vec128 ); - return _vmathVfGetElement(result, 0); -} - -static inline float vmathV4Length( const VmathVector4 *vec ) -{ - return sqrtf( vmathV4LengthSqr( vec ) ); -} - -static inline void vmathV4Normalize( VmathVector4 *result, const VmathVector4 *vec ) -{ - vec_float4 dot = _vmathVfDot4( vec->vec128, vec->vec128 ); - result->vec128 = vec_madd( vec->vec128, rsqrtf4( dot ), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); -} - -static inline void vmathV4Select( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1, unsigned int select1 ) -{ - unsigned int tmp; - tmp = (unsigned int)-(select1 > 0); - result->vec128 = vec_sel( vec0->vec128, vec1->vec128, _vmathVuiSplatScalar(tmp) ); -} - -#ifdef _VECTORMATH_DEBUG - -static inline void vmathV4Print( const VmathVector4 *vec ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec->vec128; - printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -static inline void vmathV4Prints( const VmathVector4 *vec, const char *name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec->vec128; - printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -#endif - -static inline void vmathP3Copy( VmathPoint3 *result, const VmathPoint3 *pnt ) -{ - result->vec128 = pnt->vec128; -} - -static inline void vmathP3MakeFromElems( VmathPoint3 *result, float _x, float _y, float _z ) -{ - if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & __builtin_constant_p(_z)) { - result->vec128 = (vec_float4){_x, _y, _z, 0.0f}; - } else { - float *pf = (float *)&result->vec128; - pf[0] = _x; - pf[1] = _y; - pf[2] = _z; - } -} - -static inline void vmathP3MakeFromV3( VmathPoint3 *result, const VmathVector3 *vec ) -{ - result->vec128 = vec->vec128; -} - -static inline void vmathP3MakeFromScalar( VmathPoint3 *result, float scalar ) -{ - result->vec128 = _vmathVfSplatScalar(scalar); -} - -static inline void vmathP3MakeFrom128( VmathPoint3 *result, vec_float4 vf4 ) -{ - result->vec128 = vf4; -} - -static inline void vmathP3Lerp( VmathPoint3 *result, float t, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - VmathVector3 tmpV3_0, tmpV3_1; - vmathP3Sub( &tmpV3_0, pnt1, pnt0 ); - vmathV3ScalarMul( &tmpV3_1, &tmpV3_0, t ); - vmathP3AddV3( result, pnt0, &tmpV3_1 ); -} - -static inline vec_float4 vmathP3Get128( const VmathPoint3 *pnt ) -{ - return pnt->vec128; -} - -static inline void vmathP3StoreXYZ( const VmathPoint3 *pnt, vec_float4 *quad ) -{ - vec_float4 dstVec = *quad; - vec_uint4 mask = _VECTORMATH_MASK_0x000F; - dstVec = vec_sel(pnt->vec128, dstVec, mask); - *quad = dstVec; -} - -static inline void vmathP3LoadXYZArray( VmathPoint3 *pnt0, VmathPoint3 *pnt1, VmathPoint3 *pnt2, VmathPoint3 *pnt3, const vec_float4 *threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; - xyzx = threeQuads[0]; - yzxy = threeQuads[1]; - zxyz = threeQuads[2]; - xyz1 = vec_sld( xyzx, yzxy, 12 ); - xyz2 = vec_sld( yzxy, zxyz, 8 ); - xyz3 = vec_sld( zxyz, zxyz, 4 ); - pnt0->vec128 = xyzx; - pnt1->vec128 = xyz1; - pnt2->vec128 = xyz2; - pnt3->vec128 = xyz3; -} - -static inline void vmathP3StoreXYZArray( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, vec_float4 *threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz; - xyzx = vec_perm( pnt0->vec128, pnt1->vec128, _VECTORMATH_PERM_XYZA ); - yzxy = vec_perm( pnt1->vec128, pnt2->vec128, _VECTORMATH_PERM_YZAB ); - zxyz = vec_perm( pnt2->vec128, pnt3->vec128, _VECTORMATH_PERM_ZABC ); - threeQuads[0] = xyzx; - threeQuads[1] = yzxy; - threeQuads[2] = zxyz; -} - -static inline void vmathP3StoreHalfFloats( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, const VmathPoint3 *pnt4, const VmathPoint3 *pnt5, const VmathPoint3 *pnt6, const VmathPoint3 *pnt7, vec_ushort8 *threeQuads ) -{ - vec_float4 xyz0[3]; - vec_float4 xyz1[3]; - vmathP3StoreXYZArray( pnt0, pnt1, pnt2, pnt3, xyz0 ); - vmathP3StoreXYZArray( pnt4, pnt5, pnt6, pnt7, xyz1 ); - threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); - threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); - threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); -} - -static inline void vmathP3SetX( VmathPoint3 *result, float _x ) -{ - _vmathVfSetElement(result->vec128, _x, 0); -} - -static inline float vmathP3GetX( const VmathPoint3 *pnt ) -{ - return _vmathVfGetElement(pnt->vec128, 0); -} - -static inline void vmathP3SetY( VmathPoint3 *result, float _y ) -{ - _vmathVfSetElement(result->vec128, _y, 1); -} - -static inline float vmathP3GetY( const VmathPoint3 *pnt ) -{ - return _vmathVfGetElement(pnt->vec128, 1); -} - -static inline void vmathP3SetZ( VmathPoint3 *result, float _z ) -{ - _vmathVfSetElement(result->vec128, _z, 2); -} - -static inline float vmathP3GetZ( const VmathPoint3 *pnt ) -{ - return _vmathVfGetElement(pnt->vec128, 2); -} - -static inline void vmathP3SetElem( VmathPoint3 *result, int idx, float value ) -{ - _vmathVfSetElement(result->vec128, value, idx); -} - -static inline float vmathP3GetElem( const VmathPoint3 *pnt, int idx ) -{ - return _vmathVfGetElement(pnt->vec128, idx); -} - -static inline void vmathP3Sub( VmathVector3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - result->vec128 = vec_sub( pnt0->vec128, pnt1->vec128 ); -} - -static inline void vmathP3AddV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec1 ) -{ - result->vec128 = vec_add( pnt->vec128, vec1->vec128 ); -} - -static inline void vmathP3SubV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec1 ) -{ - result->vec128 = vec_sub( pnt->vec128, vec1->vec128 ); -} - -static inline void vmathP3MulPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - result->vec128 = vec_madd( pnt0->vec128, pnt1->vec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); -} - -static inline void vmathP3DivPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - result->vec128 = divf4( pnt0->vec128, pnt1->vec128 ); -} - -static inline void vmathP3RecipPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) -{ - result->vec128 = recipf4( pnt->vec128 ); -} - -static inline void vmathP3SqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) -{ - result->vec128 = sqrtf4( pnt->vec128 ); -} - -static inline void vmathP3RsqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) -{ - result->vec128 = rsqrtf4( pnt->vec128 ); -} - -static inline void vmathP3AbsPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) -{ - result->vec128 = fabsf4( pnt->vec128 ); -} - -static inline void vmathP3CopySignPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - result->vec128 = copysignf4( pnt0->vec128, pnt1->vec128 ); -} - -static inline void vmathP3MaxPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - result->vec128 = fmaxf4( pnt0->vec128, pnt1->vec128 ); -} - -static inline float vmathP3MaxElem( const VmathPoint3 *pnt ) -{ - vec_float4 result; - result = fmaxf4( vec_splat( pnt->vec128, 1 ), pnt->vec128 ); - result = fmaxf4( vec_splat( pnt->vec128, 2 ), result ); - return _vmathVfGetElement(result, 0); -} - -static inline void vmathP3MinPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - result->vec128 = fminf4( pnt0->vec128, pnt1->vec128 ); -} - -static inline float vmathP3MinElem( const VmathPoint3 *pnt ) -{ - vec_float4 result; - result = fminf4( vec_splat( pnt->vec128, 1 ), pnt->vec128 ); - result = fminf4( vec_splat( pnt->vec128, 2 ), result ); - return _vmathVfGetElement(result, 0); -} - -static inline float vmathP3Sum( const VmathPoint3 *pnt ) -{ - vec_float4 result; - result = vec_add( vec_splat( pnt->vec128, 1 ), pnt->vec128 ); - result = vec_add( vec_splat( pnt->vec128, 2 ), result ); - return _vmathVfGetElement(result, 0); -} - -static inline void vmathP3Scale( VmathPoint3 *result, const VmathPoint3 *pnt, float scaleVal ) -{ - VmathPoint3 tmpP3_0; - vmathP3MakeFromScalar( &tmpP3_0, scaleVal ); - vmathP3MulPerElem( result, pnt, &tmpP3_0 ); -} - -static inline void vmathP3NonUniformScale( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *scaleVec ) -{ - VmathPoint3 tmpP3_0; - vmathP3MakeFromV3( &tmpP3_0, scaleVec ); - vmathP3MulPerElem( result, pnt, &tmpP3_0 ); -} - -static inline float vmathP3Projection( const VmathPoint3 *pnt, const VmathVector3 *unitVec ) -{ - vec_float4 result = _vmathVfDot3( pnt->vec128, unitVec->vec128 ); - return _vmathVfGetElement(result, 0); -} - -static inline float vmathP3DistSqrFromOrigin( const VmathPoint3 *pnt ) -{ - VmathVector3 tmpV3_0; - vmathV3MakeFromP3( &tmpV3_0, pnt ); - return vmathV3LengthSqr( &tmpV3_0 ); -} - -static inline float vmathP3DistFromOrigin( const VmathPoint3 *pnt ) -{ - VmathVector3 tmpV3_0; - vmathV3MakeFromP3( &tmpV3_0, pnt ); - return vmathV3Length( &tmpV3_0 ); -} - -static inline float vmathP3DistSqr( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - VmathVector3 tmpV3_0; - vmathP3Sub( &tmpV3_0, pnt1, pnt0 ); - return vmathV3LengthSqr( &tmpV3_0 ); -} - -static inline float vmathP3Dist( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - VmathVector3 tmpV3_0; - vmathP3Sub( &tmpV3_0, pnt1, pnt0 ); - return vmathV3Length( &tmpV3_0 ); -} - -static inline void vmathP3Select( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, unsigned int select1 ) -{ - unsigned int tmp; - tmp = (unsigned int)-(select1 > 0); - result->vec128 = vec_sel( pnt0->vec128, pnt1->vec128, _vmathVuiSplatScalar(tmp) ); -} - -#ifdef _VECTORMATH_DEBUG - -static inline void vmathP3Print( const VmathPoint3 *pnt ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = pnt->vec128; - printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -static inline void vmathP3Prints( const VmathPoint3 *pnt, const char *name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = pnt->vec128; - printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -#endif - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_VEC_AOS_C_H +#define _VECTORMATH_VEC_AOS_C_H +#include +#include +#include +#include "vec_types.h" +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*----------------------------------------------------------------------------- + * Constants + * for permutes words are labeled [x,y,z,w] [a,b,c,d] + */ +#define _VECTORMATH_PERM_X 0x00010203 +#define _VECTORMATH_PERM_Y 0x04050607 +#define _VECTORMATH_PERM_Z 0x08090a0b +#define _VECTORMATH_PERM_W 0x0c0d0e0f +#define _VECTORMATH_PERM_A 0x10111213 +#define _VECTORMATH_PERM_B 0x14151617 +#define _VECTORMATH_PERM_C 0x18191a1b +#define _VECTORMATH_PERM_D 0x1c1d1e1f +#define _VECTORMATH_PERM_XYZA (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A } +#define _VECTORMATH_PERM_ZXYW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_W } +#define _VECTORMATH_PERM_YZXW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_W } +#define _VECTORMATH_PERM_YZAB (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A, _VECTORMATH_PERM_B } +#define _VECTORMATH_PERM_ZABC (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A, _VECTORMATH_PERM_B, _VECTORMATH_PERM_C } +#define _VECTORMATH_PERM_XYAW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A, _VECTORMATH_PERM_W } +#define _VECTORMATH_PERM_XAZW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_A, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_W } +#define _VECTORMATH_MASK_0xF000 (vec_uint4){ 0xffffffff, 0, 0, 0 } +#define _VECTORMATH_MASK_0x0F00 (vec_uint4){ 0, 0xffffffff, 0, 0 } +#define _VECTORMATH_MASK_0x00F0 (vec_uint4){ 0, 0, 0xffffffff, 0 } +#define _VECTORMATH_MASK_0x000F (vec_uint4){ 0, 0, 0, 0xffffffff } +#define _VECTORMATH_UNIT_1000 (vec_float4){ 1.0f, 0.0f, 0.0f, 0.0f } +#define _VECTORMATH_UNIT_0100 (vec_float4){ 0.0f, 1.0f, 0.0f, 0.0f } +#define _VECTORMATH_UNIT_0010 (vec_float4){ 0.0f, 0.0f, 1.0f, 0.0f } +#define _VECTORMATH_UNIT_0001 (vec_float4){ 0.0f, 0.0f, 0.0f, 1.0f } +#define _VECTORMATH_SLERP_TOL 0.999f + +/*----------------------------------------------------------------------------- + * Definitions + */ +#ifndef _VECTORMATH_INTERNAL_FUNCTIONS +#define _VECTORMATH_INTERNAL_FUNCTIONS + +static inline vec_float4 _vmathVfDot3( vec_float4 vec0, vec_float4 vec1 ) +{ + vec_float4 result; + result = vec_madd( vec0, vec1, (vec_float4){0.0f,0.0f,0.0f,0.0f} ); + result = vec_madd( vec_sld( vec0, vec0, 4 ), vec_sld( vec1, vec1, 4 ), result ); + return vec_madd( vec_sld( vec0, vec0, 8 ), vec_sld( vec1, vec1, 8 ), result ); +} + +static inline vec_float4 _vmathVfDot4( vec_float4 vec0, vec_float4 vec1 ) +{ + vec_float4 result; + result = vec_madd( vec0, vec1, (vec_float4){0.0f,0.0f,0.0f,0.0f} ); + result = vec_madd( vec_sld( vec0, vec0, 4 ), vec_sld( vec1, vec1, 4 ), result ); + return vec_add( vec_sld( result, result, 8 ), result ); +} + +static inline vec_float4 _vmathVfCross( vec_float4 vec0, vec_float4 vec1 ) +{ + vec_float4 tmp0, tmp1, tmp2, tmp3, result; + tmp0 = vec_perm( vec0, vec0, _VECTORMATH_PERM_YZXW ); + tmp1 = vec_perm( vec1, vec1, _VECTORMATH_PERM_ZXYW ); + tmp2 = vec_perm( vec0, vec0, _VECTORMATH_PERM_ZXYW ); + tmp3 = vec_perm( vec1, vec1, _VECTORMATH_PERM_YZXW ); + result = vec_madd( tmp0, tmp1, (vec_float4){0.0f,0.0f,0.0f,0.0f} ); + result = vec_nmsub( tmp2, tmp3, result ); + return result; +} + +static inline vec_uint4 _vmathVfToHalfFloatsUnpacked(vec_float4 v) +{ + vec_int4 bexp; + vec_uint4 mant, sign, hfloat; + vec_uint4 notZero, isInf; + const vec_uint4 hfloatInf = (vec_uint4){0x00007c00u,0x00007c00u,0x00007c00u,0x00007c00u}; + const vec_uint4 mergeMant = (vec_uint4){0x000003ffu,0x000003ffu,0x000003ffu,0x000003ffu}; + const vec_uint4 mergeSign = (vec_uint4){0x00008000u,0x00008000u,0x00008000u,0x00008000u}; + + sign = vec_sr((vec_uint4)v, (vec_uint4){16,16,16,16}); + mant = vec_sr((vec_uint4)v, (vec_uint4){13,13,13,13}); + bexp = vec_and(vec_sr((vec_int4)v, (vec_uint4){23,23,23,23}), (vec_int4){0xff,0xff,0xff,0xff}); + + notZero = (vec_uint4)vec_cmpgt(bexp, (vec_int4){112,112,112,112}); + isInf = (vec_uint4)vec_cmpgt(bexp, (vec_int4){142,142,142,142}); + + bexp = vec_add(bexp, (vec_int4){-112,-112,-112,-112}); + bexp = vec_sl(bexp, (vec_uint4){10,10,10,10}); + + hfloat = vec_sel((vec_uint4)bexp, mant, mergeMant); + hfloat = vec_sel((vec_uint4){0,0,0,0}, hfloat, notZero); + hfloat = vec_sel(hfloat, hfloatInf, isInf); + hfloat = vec_sel(hfloat, sign, mergeSign); + + return hfloat; +} + +static inline vec_ushort8 _vmath2VfToHalfFloats(vec_float4 u, vec_float4 v) +{ + vec_uint4 hfloat_u, hfloat_v; + const vec_uchar16 pack = (vec_uchar16){2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31}; + hfloat_u = _vmathVfToHalfFloatsUnpacked(u); + hfloat_v = _vmathVfToHalfFloatsUnpacked(v); + return (vec_ushort8)vec_perm(hfloat_u, hfloat_v, pack); +} + +#ifndef __GNUC__ +#define __builtin_constant_p(x) 0 +#endif + +static inline vec_float4 _vmathVfInsert(vec_float4 dst, vec_float4 src, int slot) +{ +#ifdef __GNUC__ + if (__builtin_constant_p(slot)) { + dst = vec_sld(dst, dst, slot<<2); + dst = vec_sld(dst, src, 4); + if (slot != 3) dst = vec_sld(dst, dst, (3-slot)<<2); + return dst; + } else +#endif + { + vec_uchar16 shiftpattern = vec_lvsr( 0, (float *)(size_t)(slot<<2) ); + vec_uint4 selectmask = (vec_uint4)vec_perm( (vec_uint4){0,0,0,0}, _VECTORMATH_MASK_0xF000, shiftpattern ); + return vec_sel( dst, src, selectmask ); + } +} + +#define _vmathVfGetElement(vec, slot) ((float *)&(vec))[slot] +#ifdef _VECTORMATH_SET_CONSTS_IN_MEM +#define _vmathVfSetElement(vec, scalar, slot) ((float *)&(vec))[slot] = scalar +#else +#define _vmathVfSetElement(vec, scalar, slot) \ +{ \ + if (__builtin_constant_p(scalar)) { \ + (vec) = _vmathVfInsert(vec, (vec_float4){scalar, scalar, scalar, scalar}, slot); \ + } else { \ + ((float *)&(vec))[slot] = scalar; \ + } \ +} +#endif + +static inline vec_float4 _vmathVfSplatScalar(float scalar) +{ + vec_float4 result; + if (__builtin_constant_p(scalar)) { + result = (vec_float4){scalar, scalar, scalar, scalar}; + } else { + result = vec_ld(0, &scalar); + result = vec_splat(vec_perm(result, result, vec_lvsl(0, &scalar)), 0); + } + return result; +} + +static inline vec_uint4 _vmathVuiSplatScalar(unsigned int scalar) +{ + vec_uint4 result; + if (__builtin_constant_p(scalar)) { + result = (vec_uint4){scalar, scalar, scalar, scalar}; + } else { + result = vec_ld(0, &scalar); + result = vec_splat(vec_perm(result, result, vec_lvsl(0, &scalar)), 0); + } + return result; +} + +#endif + +static inline void vmathV3Copy( VmathVector3 *result, const VmathVector3 *vec ) +{ + result->vec128 = vec->vec128; +} + +static inline void vmathV3MakeFromElems( VmathVector3 *result, float _x, float _y, float _z ) +{ + if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & __builtin_constant_p(_z)) { + result->vec128 = (vec_float4){_x, _y, _z, 0.0f}; + } else { + float *pf = (float *)&result->vec128; + pf[0] = _x; + pf[1] = _y; + pf[2] = _z; + } +} + +static inline void vmathV3MakeFromP3( VmathVector3 *result, const VmathPoint3 *pnt ) +{ + result->vec128 = pnt->vec128; +} + +static inline void vmathV3MakeFromScalar( VmathVector3 *result, float scalar ) +{ + result->vec128 = _vmathVfSplatScalar(scalar); +} + +static inline void vmathV3MakeFrom128( VmathVector3 *result, vec_float4 vf4 ) +{ + result->vec128 = vf4; +} + +static inline void vmathV3MakeXAxis( VmathVector3 *result ) +{ + result->vec128 = _VECTORMATH_UNIT_1000; +} + +static inline void vmathV3MakeYAxis( VmathVector3 *result ) +{ + result->vec128 = _VECTORMATH_UNIT_0100; +} + +static inline void vmathV3MakeZAxis( VmathVector3 *result ) +{ + result->vec128 = _VECTORMATH_UNIT_0010; +} + +static inline void vmathV3Lerp( VmathVector3 *result, float t, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + VmathVector3 tmpV3_0, tmpV3_1; + vmathV3Sub( &tmpV3_0, vec1, vec0 ); + vmathV3ScalarMul( &tmpV3_1, &tmpV3_0, t ); + vmathV3Add( result, vec0, &tmpV3_1 ); +} + +static inline void vmathV3Slerp( VmathVector3 *result, float t, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ) +{ + vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; + vec_uint4 selectMask; + cosAngle = _vmathVfDot3( unitVec0->vec128, unitVec1->vec128 ); + cosAngle = vec_splat( cosAngle, 0 ); + selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL}), cosAngle ); + angle = acosf4( cosAngle ); + tttt = _vmathVfSplatScalar(t); + oneMinusT = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); + angles = vec_mergeh( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); + angles = vec_mergeh( angles, oneMinusT ); + angles = vec_madd( angles, angle, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + sines = sinf4( angles ); + scales = divf4( sines, vec_splat( sines, 0 ) ); + scale0 = vec_sel( oneMinusT, vec_splat( scales, 1 ), selectMask ); + scale1 = vec_sel( tttt, vec_splat( scales, 2 ), selectMask ); + result->vec128 = vec_madd( unitVec0->vec128, scale0, vec_madd( unitVec1->vec128, scale1, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); +} + +static inline vec_float4 vmathV3Get128( const VmathVector3 *vec ) +{ + return vec->vec128; +} + +static inline void vmathV3StoreXYZ( const VmathVector3 *vec, vec_float4 *quad ) +{ + vec_float4 dstVec = *quad; + vec_uint4 mask = _VECTORMATH_MASK_0x000F; + dstVec = vec_sel(vec->vec128, dstVec, mask); + *quad = dstVec; +} + +static inline void vmathV3LoadXYZArray( VmathVector3 *vec0, VmathVector3 *vec1, VmathVector3 *vec2, VmathVector3 *vec3, const vec_float4 *threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; + xyzx = threeQuads[0]; + yzxy = threeQuads[1]; + zxyz = threeQuads[2]; + xyz1 = vec_sld( xyzx, yzxy, 12 ); + xyz2 = vec_sld( yzxy, zxyz, 8 ); + xyz3 = vec_sld( zxyz, zxyz, 4 ); + vec0->vec128 = xyzx; + vec1->vec128 = xyz1; + vec2->vec128 = xyz2; + vec3->vec128 = xyz3; +} + +static inline void vmathV3StoreXYZArray( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, vec_float4 *threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz; + xyzx = vec_perm( vec0->vec128, vec1->vec128, _VECTORMATH_PERM_XYZA ); + yzxy = vec_perm( vec1->vec128, vec2->vec128, _VECTORMATH_PERM_YZAB ); + zxyz = vec_perm( vec2->vec128, vec3->vec128, _VECTORMATH_PERM_ZABC ); + threeQuads[0] = xyzx; + threeQuads[1] = yzxy; + threeQuads[2] = zxyz; +} + +static inline void vmathV3StoreHalfFloats( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, const VmathVector3 *vec4, const VmathVector3 *vec5, const VmathVector3 *vec6, const VmathVector3 *vec7, vec_ushort8 *threeQuads ) +{ + vec_float4 xyz0[3]; + vec_float4 xyz1[3]; + vmathV3StoreXYZArray( vec0, vec1, vec2, vec3, xyz0 ); + vmathV3StoreXYZArray( vec4, vec5, vec6, vec7, xyz1 ); + threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); + threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); + threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); +} + +static inline void vmathV3SetX( VmathVector3 *result, float _x ) +{ + _vmathVfSetElement(result->vec128, _x, 0); +} + +static inline float vmathV3GetX( const VmathVector3 *vec ) +{ + return _vmathVfGetElement(vec->vec128, 0); +} + +static inline void vmathV3SetY( VmathVector3 *result, float _y ) +{ + _vmathVfSetElement(result->vec128, _y, 1); +} + +static inline float vmathV3GetY( const VmathVector3 *vec ) +{ + return _vmathVfGetElement(vec->vec128, 1); +} + +static inline void vmathV3SetZ( VmathVector3 *result, float _z ) +{ + _vmathVfSetElement(result->vec128, _z, 2); +} + +static inline float vmathV3GetZ( const VmathVector3 *vec ) +{ + return _vmathVfGetElement(vec->vec128, 2); +} + +static inline void vmathV3SetElem( VmathVector3 *result, int idx, float value ) +{ + _vmathVfSetElement(result->vec128, value, idx); +} + +static inline float vmathV3GetElem( const VmathVector3 *vec, int idx ) +{ + return _vmathVfGetElement(vec->vec128, idx); +} + +static inline void vmathV3Add( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = vec_add( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV3Sub( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = vec_sub( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV3AddP3( VmathPoint3 *result, const VmathVector3 *vec, const VmathPoint3 *pnt1 ) +{ + result->vec128 = vec_add( vec->vec128, pnt1->vec128 ); +} + +static inline void vmathV3ScalarMul( VmathVector3 *result, const VmathVector3 *vec, float scalar ) +{ + result->vec128 = vec_madd( vec->vec128, _vmathVfSplatScalar(scalar), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); +} + +static inline void vmathV3ScalarDiv( VmathVector3 *result, const VmathVector3 *vec, float scalar ) +{ + result->vec128 = divf4( vec->vec128, _vmathVfSplatScalar(scalar) ); +} + +static inline void vmathV3Neg( VmathVector3 *result, const VmathVector3 *vec ) +{ + result->vec128 = negatef4( vec->vec128 ); +} + +static inline void vmathV3MulPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = vec_madd( vec0->vec128, vec1->vec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); +} + +static inline void vmathV3DivPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = divf4( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV3RecipPerElem( VmathVector3 *result, const VmathVector3 *vec ) +{ + result->vec128 = recipf4( vec->vec128 ); +} + +static inline void vmathV3SqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ) +{ + result->vec128 = sqrtf4( vec->vec128 ); +} + +static inline void vmathV3RsqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ) +{ + result->vec128 = rsqrtf4( vec->vec128 ); +} + +static inline void vmathV3AbsPerElem( VmathVector3 *result, const VmathVector3 *vec ) +{ + result->vec128 = fabsf4( vec->vec128 ); +} + +static inline void vmathV3CopySignPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = copysignf4( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV3MaxPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = fmaxf4( vec0->vec128, vec1->vec128 ); +} + +static inline float vmathV3MaxElem( const VmathVector3 *vec ) +{ + vec_float4 result; + result = fmaxf4( vec_splat( vec->vec128, 1 ), vec->vec128 ); + result = fmaxf4( vec_splat( vec->vec128, 2 ), result ); + return _vmathVfGetElement(result, 0); +} + +static inline void vmathV3MinPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = fminf4( vec0->vec128, vec1->vec128 ); +} + +static inline float vmathV3MinElem( const VmathVector3 *vec ) +{ + vec_float4 result; + result = fminf4( vec_splat( vec->vec128, 1 ), vec->vec128 ); + result = fminf4( vec_splat( vec->vec128, 2 ), result ); + return _vmathVfGetElement(result, 0); +} + +static inline float vmathV3Sum( const VmathVector3 *vec ) +{ + vec_float4 result; + result = vec_add( vec_splat( vec->vec128, 1 ), vec->vec128 ); + result = vec_add( vec_splat( vec->vec128, 2 ), result ); + return _vmathVfGetElement(result, 0); +} + +static inline float vmathV3Dot( const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + vec_float4 result = _vmathVfDot3( vec0->vec128, vec1->vec128 ); + return _vmathVfGetElement(result, 0); +} + +static inline float vmathV3LengthSqr( const VmathVector3 *vec ) +{ + vec_float4 result = _vmathVfDot3( vec->vec128, vec->vec128 ); + return _vmathVfGetElement(result, 0); +} + +static inline float vmathV3Length( const VmathVector3 *vec ) +{ + return sqrtf( vmathV3LengthSqr( vec ) ); +} + +static inline void vmathV3Normalize( VmathVector3 *result, const VmathVector3 *vec ) +{ + vec_float4 dot = _vmathVfDot3( vec->vec128, vec->vec128 ); + dot = vec_splat( dot, 0 ); + result->vec128 = vec_madd( vec->vec128, rsqrtf4( dot ), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); +} + +static inline void vmathV3Cross( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = _vmathVfCross( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV3Select( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1, unsigned int select1 ) +{ + unsigned int tmp; + tmp = (unsigned int)-(select1 > 0); + result->vec128 = vec_sel( vec0->vec128, vec1->vec128, _vmathVuiSplatScalar(tmp) ); +} + +#ifdef _VECTORMATH_DEBUG + +static inline void vmathV3Print( const VmathVector3 *vec ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec->vec128; + printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +static inline void vmathV3Prints( const VmathVector3 *vec, const char *name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec->vec128; + printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +#endif + +static inline void vmathV4Copy( VmathVector4 *result, const VmathVector4 *vec ) +{ + result->vec128 = vec->vec128; +} + +static inline void vmathV4MakeFromElems( VmathVector4 *result, float _x, float _y, float _z, float _w ) +{ + if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & + __builtin_constant_p(_z) & __builtin_constant_p(_w)) { + result->vec128 = (vec_float4){_x, _y, _z, _w}; + } else { + float *pf = (float *)&result->vec128; + pf[0] = _x; + pf[1] = _y; + pf[2] = _z; + pf[3] = _w; + } +} + +static inline void vmathV4MakeFromV3Scalar( VmathVector4 *result, const VmathVector3 *xyz, float _w ) +{ + result->vec128 = xyz->vec128; + _vmathVfSetElement(result->vec128, _w, 3); +} + +static inline void vmathV4MakeFromV3( VmathVector4 *result, const VmathVector3 *vec ) +{ + result->vec128 = vec->vec128; + result->vec128 = _vmathVfInsert(result->vec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}), 3); +} + +static inline void vmathV4MakeFromP3( VmathVector4 *result, const VmathPoint3 *pnt ) +{ + result->vec128 = pnt->vec128; + result->vec128 = _vmathVfInsert(result->vec128, ((vec_float4){1.0f,1.0f,1.0f,1.0f}), 3); +} + +static inline void vmathV4MakeFromQ( VmathVector4 *result, const VmathQuat *quat ) +{ + result->vec128 = quat->vec128; +} + +static inline void vmathV4MakeFromScalar( VmathVector4 *result, float scalar ) +{ + result->vec128 = _vmathVfSplatScalar(scalar); +} + +static inline void vmathV4MakeFrom128( VmathVector4 *result, vec_float4 vf4 ) +{ + result->vec128 = vf4; +} + +static inline void vmathV4MakeXAxis( VmathVector4 *result ) +{ + result->vec128 = _VECTORMATH_UNIT_1000; +} + +static inline void vmathV4MakeYAxis( VmathVector4 *result ) +{ + result->vec128 = _VECTORMATH_UNIT_0100; +} + +static inline void vmathV4MakeZAxis( VmathVector4 *result ) +{ + result->vec128 = _VECTORMATH_UNIT_0010; +} + +static inline void vmathV4MakeWAxis( VmathVector4 *result ) +{ + result->vec128 = _VECTORMATH_UNIT_0001; +} + +static inline void vmathV4Lerp( VmathVector4 *result, float t, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + VmathVector4 tmpV4_0, tmpV4_1; + vmathV4Sub( &tmpV4_0, vec1, vec0 ); + vmathV4ScalarMul( &tmpV4_1, &tmpV4_0, t ); + vmathV4Add( result, vec0, &tmpV4_1 ); +} + +static inline void vmathV4Slerp( VmathVector4 *result, float t, const VmathVector4 *unitVec0, const VmathVector4 *unitVec1 ) +{ + vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; + vec_uint4 selectMask; + cosAngle = _vmathVfDot4( unitVec0->vec128, unitVec1->vec128 ); + cosAngle = vec_splat( cosAngle, 0 ); + selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL}), cosAngle ); + angle = acosf4( cosAngle ); + tttt = _vmathVfSplatScalar(t); + oneMinusT = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); + angles = vec_mergeh( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); + angles = vec_mergeh( angles, oneMinusT ); + angles = vec_madd( angles, angle, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + sines = sinf4( angles ); + scales = divf4( sines, vec_splat( sines, 0 ) ); + scale0 = vec_sel( oneMinusT, vec_splat( scales, 1 ), selectMask ); + scale1 = vec_sel( tttt, vec_splat( scales, 2 ), selectMask ); + result->vec128 = vec_madd( unitVec0->vec128, scale0, vec_madd( unitVec1->vec128, scale1, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); +} + +static inline vec_float4 vmathV4Get128( const VmathVector4 *vec ) +{ + return vec->vec128; +} + +static inline void vmathV4StoreHalfFloats( const VmathVector4 *vec0, const VmathVector4 *vec1, const VmathVector4 *vec2, const VmathVector4 *vec3, vec_ushort8 *twoQuads ) +{ + twoQuads[0] = _vmath2VfToHalfFloats(vec0->vec128, vec1->vec128); + twoQuads[1] = _vmath2VfToHalfFloats(vec2->vec128, vec3->vec128); +} + +static inline void vmathV4SetXYZ( VmathVector4 *result, const VmathVector3 *vec ) +{ + result->vec128 = vec_sel( vec->vec128, result->vec128, _VECTORMATH_MASK_0x000F ); +} + +static inline void vmathV4GetXYZ( VmathVector3 *result, const VmathVector4 *vec ) +{ + result->vec128 = vec->vec128; +} + +static inline void vmathV4SetX( VmathVector4 *result, float _x ) +{ + _vmathVfSetElement(result->vec128, _x, 0); +} + +static inline float vmathV4GetX( const VmathVector4 *vec ) +{ + return _vmathVfGetElement(vec->vec128, 0); +} + +static inline void vmathV4SetY( VmathVector4 *result, float _y ) +{ + _vmathVfSetElement(result->vec128, _y, 1); +} + +static inline float vmathV4GetY( const VmathVector4 *vec ) +{ + return _vmathVfGetElement(vec->vec128, 1); +} + +static inline void vmathV4SetZ( VmathVector4 *result, float _z ) +{ + _vmathVfSetElement(result->vec128, _z, 2); +} + +static inline float vmathV4GetZ( const VmathVector4 *vec ) +{ + return _vmathVfGetElement(vec->vec128, 2); +} + +static inline void vmathV4SetW( VmathVector4 *result, float _w ) +{ + _vmathVfSetElement(result->vec128, _w, 3); +} + +static inline float vmathV4GetW( const VmathVector4 *vec ) +{ + return _vmathVfGetElement(vec->vec128, 3); +} + +static inline void vmathV4SetElem( VmathVector4 *result, int idx, float value ) +{ + _vmathVfSetElement(result->vec128, value, idx); +} + +static inline float vmathV4GetElem( const VmathVector4 *vec, int idx ) +{ + return _vmathVfGetElement(vec->vec128, idx); +} + +static inline void vmathV4Add( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + result->vec128 = vec_add( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV4Sub( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + result->vec128 = vec_sub( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV4ScalarMul( VmathVector4 *result, const VmathVector4 *vec, float scalar ) +{ + result->vec128 = vec_madd( vec->vec128, _vmathVfSplatScalar(scalar), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); +} + +static inline void vmathV4ScalarDiv( VmathVector4 *result, const VmathVector4 *vec, float scalar ) +{ + result->vec128 = divf4( vec->vec128, _vmathVfSplatScalar(scalar) ); +} + +static inline void vmathV4Neg( VmathVector4 *result, const VmathVector4 *vec ) +{ + result->vec128 = negatef4( vec->vec128 ); +} + +static inline void vmathV4MulPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + result->vec128 = vec_madd( vec0->vec128, vec1->vec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); +} + +static inline void vmathV4DivPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + result->vec128 = divf4( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV4RecipPerElem( VmathVector4 *result, const VmathVector4 *vec ) +{ + result->vec128 = recipf4( vec->vec128 ); +} + +static inline void vmathV4SqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ) +{ + result->vec128 = sqrtf4( vec->vec128 ); +} + +static inline void vmathV4RsqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ) +{ + result->vec128 = rsqrtf4( vec->vec128 ); +} + +static inline void vmathV4AbsPerElem( VmathVector4 *result, const VmathVector4 *vec ) +{ + result->vec128 = fabsf4( vec->vec128 ); +} + +static inline void vmathV4CopySignPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + result->vec128 = copysignf4( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV4MaxPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + result->vec128 = fmaxf4( vec0->vec128, vec1->vec128 ); +} + +static inline float vmathV4MaxElem( const VmathVector4 *vec ) +{ + vec_float4 result; + result = fmaxf4( vec_splat( vec->vec128, 1 ), vec->vec128 ); + result = fmaxf4( vec_splat( vec->vec128, 2 ), result ); + result = fmaxf4( vec_splat( vec->vec128, 3 ), result ); + return _vmathVfGetElement(result, 0); +} + +static inline void vmathV4MinPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + result->vec128 = fminf4( vec0->vec128, vec1->vec128 ); +} + +static inline float vmathV4MinElem( const VmathVector4 *vec ) +{ + vec_float4 result; + result = fminf4( vec_splat( vec->vec128, 1 ), vec->vec128 ); + result = fminf4( vec_splat( vec->vec128, 2 ), result ); + result = fminf4( vec_splat( vec->vec128, 3 ), result ); + return _vmathVfGetElement(result, 0); +} + +static inline float vmathV4Sum( const VmathVector4 *vec ) +{ + vec_float4 result; + result = vec_add( vec_splat( vec->vec128, 1 ), vec->vec128 ); + result = vec_add( vec_splat( vec->vec128, 2 ), result ); + result = vec_add( vec_splat( vec->vec128, 3 ), result ); + return _vmathVfGetElement(result, 0); +} + +static inline float vmathV4Dot( const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + vec_float4 result = _vmathVfDot4( vec0->vec128, vec1->vec128 ); + return _vmathVfGetElement(result, 0); +} + +static inline float vmathV4LengthSqr( const VmathVector4 *vec ) +{ + vec_float4 result = _vmathVfDot4( vec->vec128, vec->vec128 ); + return _vmathVfGetElement(result, 0); +} + +static inline float vmathV4Length( const VmathVector4 *vec ) +{ + return sqrtf( vmathV4LengthSqr( vec ) ); +} + +static inline void vmathV4Normalize( VmathVector4 *result, const VmathVector4 *vec ) +{ + vec_float4 dot = _vmathVfDot4( vec->vec128, vec->vec128 ); + result->vec128 = vec_madd( vec->vec128, rsqrtf4( dot ), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); +} + +static inline void vmathV4Select( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1, unsigned int select1 ) +{ + unsigned int tmp; + tmp = (unsigned int)-(select1 > 0); + result->vec128 = vec_sel( vec0->vec128, vec1->vec128, _vmathVuiSplatScalar(tmp) ); +} + +#ifdef _VECTORMATH_DEBUG + +static inline void vmathV4Print( const VmathVector4 *vec ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec->vec128; + printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +static inline void vmathV4Prints( const VmathVector4 *vec, const char *name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec->vec128; + printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +#endif + +static inline void vmathP3Copy( VmathPoint3 *result, const VmathPoint3 *pnt ) +{ + result->vec128 = pnt->vec128; +} + +static inline void vmathP3MakeFromElems( VmathPoint3 *result, float _x, float _y, float _z ) +{ + if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & __builtin_constant_p(_z)) { + result->vec128 = (vec_float4){_x, _y, _z, 0.0f}; + } else { + float *pf = (float *)&result->vec128; + pf[0] = _x; + pf[1] = _y; + pf[2] = _z; + } +} + +static inline void vmathP3MakeFromV3( VmathPoint3 *result, const VmathVector3 *vec ) +{ + result->vec128 = vec->vec128; +} + +static inline void vmathP3MakeFromScalar( VmathPoint3 *result, float scalar ) +{ + result->vec128 = _vmathVfSplatScalar(scalar); +} + +static inline void vmathP3MakeFrom128( VmathPoint3 *result, vec_float4 vf4 ) +{ + result->vec128 = vf4; +} + +static inline void vmathP3Lerp( VmathPoint3 *result, float t, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + VmathVector3 tmpV3_0, tmpV3_1; + vmathP3Sub( &tmpV3_0, pnt1, pnt0 ); + vmathV3ScalarMul( &tmpV3_1, &tmpV3_0, t ); + vmathP3AddV3( result, pnt0, &tmpV3_1 ); +} + +static inline vec_float4 vmathP3Get128( const VmathPoint3 *pnt ) +{ + return pnt->vec128; +} + +static inline void vmathP3StoreXYZ( const VmathPoint3 *pnt, vec_float4 *quad ) +{ + vec_float4 dstVec = *quad; + vec_uint4 mask = _VECTORMATH_MASK_0x000F; + dstVec = vec_sel(pnt->vec128, dstVec, mask); + *quad = dstVec; +} + +static inline void vmathP3LoadXYZArray( VmathPoint3 *pnt0, VmathPoint3 *pnt1, VmathPoint3 *pnt2, VmathPoint3 *pnt3, const vec_float4 *threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; + xyzx = threeQuads[0]; + yzxy = threeQuads[1]; + zxyz = threeQuads[2]; + xyz1 = vec_sld( xyzx, yzxy, 12 ); + xyz2 = vec_sld( yzxy, zxyz, 8 ); + xyz3 = vec_sld( zxyz, zxyz, 4 ); + pnt0->vec128 = xyzx; + pnt1->vec128 = xyz1; + pnt2->vec128 = xyz2; + pnt3->vec128 = xyz3; +} + +static inline void vmathP3StoreXYZArray( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, vec_float4 *threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz; + xyzx = vec_perm( pnt0->vec128, pnt1->vec128, _VECTORMATH_PERM_XYZA ); + yzxy = vec_perm( pnt1->vec128, pnt2->vec128, _VECTORMATH_PERM_YZAB ); + zxyz = vec_perm( pnt2->vec128, pnt3->vec128, _VECTORMATH_PERM_ZABC ); + threeQuads[0] = xyzx; + threeQuads[1] = yzxy; + threeQuads[2] = zxyz; +} + +static inline void vmathP3StoreHalfFloats( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, const VmathPoint3 *pnt4, const VmathPoint3 *pnt5, const VmathPoint3 *pnt6, const VmathPoint3 *pnt7, vec_ushort8 *threeQuads ) +{ + vec_float4 xyz0[3]; + vec_float4 xyz1[3]; + vmathP3StoreXYZArray( pnt0, pnt1, pnt2, pnt3, xyz0 ); + vmathP3StoreXYZArray( pnt4, pnt5, pnt6, pnt7, xyz1 ); + threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); + threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); + threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); +} + +static inline void vmathP3SetX( VmathPoint3 *result, float _x ) +{ + _vmathVfSetElement(result->vec128, _x, 0); +} + +static inline float vmathP3GetX( const VmathPoint3 *pnt ) +{ + return _vmathVfGetElement(pnt->vec128, 0); +} + +static inline void vmathP3SetY( VmathPoint3 *result, float _y ) +{ + _vmathVfSetElement(result->vec128, _y, 1); +} + +static inline float vmathP3GetY( const VmathPoint3 *pnt ) +{ + return _vmathVfGetElement(pnt->vec128, 1); +} + +static inline void vmathP3SetZ( VmathPoint3 *result, float _z ) +{ + _vmathVfSetElement(result->vec128, _z, 2); +} + +static inline float vmathP3GetZ( const VmathPoint3 *pnt ) +{ + return _vmathVfGetElement(pnt->vec128, 2); +} + +static inline void vmathP3SetElem( VmathPoint3 *result, int idx, float value ) +{ + _vmathVfSetElement(result->vec128, value, idx); +} + +static inline float vmathP3GetElem( const VmathPoint3 *pnt, int idx ) +{ + return _vmathVfGetElement(pnt->vec128, idx); +} + +static inline void vmathP3Sub( VmathVector3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + result->vec128 = vec_sub( pnt0->vec128, pnt1->vec128 ); +} + +static inline void vmathP3AddV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec1 ) +{ + result->vec128 = vec_add( pnt->vec128, vec1->vec128 ); +} + +static inline void vmathP3SubV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec1 ) +{ + result->vec128 = vec_sub( pnt->vec128, vec1->vec128 ); +} + +static inline void vmathP3MulPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + result->vec128 = vec_madd( pnt0->vec128, pnt1->vec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); +} + +static inline void vmathP3DivPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + result->vec128 = divf4( pnt0->vec128, pnt1->vec128 ); +} + +static inline void vmathP3RecipPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) +{ + result->vec128 = recipf4( pnt->vec128 ); +} + +static inline void vmathP3SqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) +{ + result->vec128 = sqrtf4( pnt->vec128 ); +} + +static inline void vmathP3RsqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) +{ + result->vec128 = rsqrtf4( pnt->vec128 ); +} + +static inline void vmathP3AbsPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) +{ + result->vec128 = fabsf4( pnt->vec128 ); +} + +static inline void vmathP3CopySignPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + result->vec128 = copysignf4( pnt0->vec128, pnt1->vec128 ); +} + +static inline void vmathP3MaxPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + result->vec128 = fmaxf4( pnt0->vec128, pnt1->vec128 ); +} + +static inline float vmathP3MaxElem( const VmathPoint3 *pnt ) +{ + vec_float4 result; + result = fmaxf4( vec_splat( pnt->vec128, 1 ), pnt->vec128 ); + result = fmaxf4( vec_splat( pnt->vec128, 2 ), result ); + return _vmathVfGetElement(result, 0); +} + +static inline void vmathP3MinPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + result->vec128 = fminf4( pnt0->vec128, pnt1->vec128 ); +} + +static inline float vmathP3MinElem( const VmathPoint3 *pnt ) +{ + vec_float4 result; + result = fminf4( vec_splat( pnt->vec128, 1 ), pnt->vec128 ); + result = fminf4( vec_splat( pnt->vec128, 2 ), result ); + return _vmathVfGetElement(result, 0); +} + +static inline float vmathP3Sum( const VmathPoint3 *pnt ) +{ + vec_float4 result; + result = vec_add( vec_splat( pnt->vec128, 1 ), pnt->vec128 ); + result = vec_add( vec_splat( pnt->vec128, 2 ), result ); + return _vmathVfGetElement(result, 0); +} + +static inline void vmathP3Scale( VmathPoint3 *result, const VmathPoint3 *pnt, float scaleVal ) +{ + VmathPoint3 tmpP3_0; + vmathP3MakeFromScalar( &tmpP3_0, scaleVal ); + vmathP3MulPerElem( result, pnt, &tmpP3_0 ); +} + +static inline void vmathP3NonUniformScale( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *scaleVec ) +{ + VmathPoint3 tmpP3_0; + vmathP3MakeFromV3( &tmpP3_0, scaleVec ); + vmathP3MulPerElem( result, pnt, &tmpP3_0 ); +} + +static inline float vmathP3Projection( const VmathPoint3 *pnt, const VmathVector3 *unitVec ) +{ + vec_float4 result = _vmathVfDot3( pnt->vec128, unitVec->vec128 ); + return _vmathVfGetElement(result, 0); +} + +static inline float vmathP3DistSqrFromOrigin( const VmathPoint3 *pnt ) +{ + VmathVector3 tmpV3_0; + vmathV3MakeFromP3( &tmpV3_0, pnt ); + return vmathV3LengthSqr( &tmpV3_0 ); +} + +static inline float vmathP3DistFromOrigin( const VmathPoint3 *pnt ) +{ + VmathVector3 tmpV3_0; + vmathV3MakeFromP3( &tmpV3_0, pnt ); + return vmathV3Length( &tmpV3_0 ); +} + +static inline float vmathP3DistSqr( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + VmathVector3 tmpV3_0; + vmathP3Sub( &tmpV3_0, pnt1, pnt0 ); + return vmathV3LengthSqr( &tmpV3_0 ); +} + +static inline float vmathP3Dist( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + VmathVector3 tmpV3_0; + vmathP3Sub( &tmpV3_0, pnt1, pnt0 ); + return vmathV3Length( &tmpV3_0 ); +} + +static inline void vmathP3Select( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, unsigned int select1 ) +{ + unsigned int tmp; + tmp = (unsigned int)-(select1 > 0); + result->vec128 = vec_sel( pnt0->vec128, pnt1->vec128, _vmathVuiSplatScalar(tmp) ); +} + +#ifdef _VECTORMATH_DEBUG + +static inline void vmathP3Print( const VmathPoint3 *pnt ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = pnt->vec128; + printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +static inline void vmathP3Prints( const VmathPoint3 *pnt, const char *name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = pnt->vec128; + printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/common/vectormath/ppu/c/vectormath_aos.h b/common/vectormath/ppu/c/vectormath_aos.h index 6a6ccd28..119e2d29 100644 --- a/common/vectormath/ppu/c/vectormath_aos.h +++ b/common/vectormath/ppu/c/vectormath_aos.h @@ -1,1960 +1,1960 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_AOS_C_PPU_H -#define _VECTORMATH_AOS_C_PPU_H - -#include -#include -#include -#include "vec_types.h" - -#ifdef _VECTORMATH_DEBUG -#include -#endif - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef _VECTORMATH_AOS_C_TYPES_H -#define _VECTORMATH_AOS_C_TYPES_H - -/* A 3-D vector in array-of-structures format - */ -typedef struct _VmathVector3 -{ - vec_float4 vec128; -} VmathVector3; - -/* A 4-D vector in array-of-structures format - */ -typedef struct _VmathVector4 -{ - vec_float4 vec128; -} VmathVector4; - -/* A 3-D point in array-of-structures format - */ -typedef struct _VmathPoint3 -{ - vec_float4 vec128; -} VmathPoint3; - -/* A quaternion in array-of-structures format - */ -typedef struct _VmathQuat -{ - vec_float4 vec128; -} VmathQuat; - -/* A 3x3 matrix in array-of-structures format - */ -typedef struct _VmathMatrix3 -{ - VmathVector3 col0; - VmathVector3 col1; - VmathVector3 col2; -} VmathMatrix3; - -/* A 4x4 matrix in array-of-structures format - */ -typedef struct _VmathMatrix4 -{ - VmathVector4 col0; - VmathVector4 col1; - VmathVector4 col2; - VmathVector4 col3; -} VmathMatrix4; - -/* A 3x4 transformation matrix in array-of-structures format - */ -typedef struct _VmathTransform3 -{ - VmathVector3 col0; - VmathVector3 col1; - VmathVector3 col2; - VmathVector3 col3; -} VmathTransform3; - -#endif - -/* - * Copy a 3-D vector - */ -static inline void vmathV3Copy( VmathVector3 *result, const VmathVector3 *vec ); - -/* - * Construct a 3-D vector from x, y, and z elements - */ -static inline void vmathV3MakeFromElems( VmathVector3 *result, float x, float y, float z ); - -/* - * Copy elements from a 3-D point into a 3-D vector - */ -static inline void vmathV3MakeFromP3( VmathVector3 *result, const VmathPoint3 *pnt ); - -/* - * Set all elements of a 3-D vector to the same scalar value - */ -static inline void vmathV3MakeFromScalar( VmathVector3 *result, float scalar ); - -/* - * Set vector float data in a 3-D vector - */ -static inline void vmathV3MakeFrom128( VmathVector3 *result, vec_float4 vf4 ); - -/* - * Get vector float data from a 3-D vector - */ -static inline vec_float4 vmathV3Get128( const VmathVector3 *vec ); - -/* - * Set the x element of a 3-D vector - */ -static inline void vmathV3SetX( VmathVector3 *result, float x ); - -/* - * Set the y element of a 3-D vector - */ -static inline void vmathV3SetY( VmathVector3 *result, float y ); - -/* - * Set the z element of a 3-D vector - */ -static inline void vmathV3SetZ( VmathVector3 *result, float z ); - -/* - * Get the x element of a 3-D vector - */ -static inline float vmathV3GetX( const VmathVector3 *vec ); - -/* - * Get the y element of a 3-D vector - */ -static inline float vmathV3GetY( const VmathVector3 *vec ); - -/* - * Get the z element of a 3-D vector - */ -static inline float vmathV3GetZ( const VmathVector3 *vec ); - -/* - * Set an x, y, or z element of a 3-D vector by index - */ -static inline void vmathV3SetElem( VmathVector3 *result, int idx, float value ); - -/* - * Get an x, y, or z element of a 3-D vector by index - */ -static inline float vmathV3GetElem( const VmathVector3 *vec, int idx ); - -/* - * Add two 3-D vectors - */ -static inline void vmathV3Add( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Subtract a 3-D vector from another 3-D vector - */ -static inline void vmathV3Sub( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Add a 3-D vector to a 3-D point - */ -static inline void vmathV3AddP3( VmathPoint3 *result, const VmathVector3 *vec, const VmathPoint3 *pnt ); - -/* - * Multiply a 3-D vector by a scalar - */ -static inline void vmathV3ScalarMul( VmathVector3 *result, const VmathVector3 *vec, float scalar ); - -/* - * Divide a 3-D vector by a scalar - */ -static inline void vmathV3ScalarDiv( VmathVector3 *result, const VmathVector3 *vec, float scalar ); - -/* - * Negate all elements of a 3-D vector - */ -static inline void vmathV3Neg( VmathVector3 *result, const VmathVector3 *vec ); - -/* - * Construct x axis - */ -static inline void vmathV3MakeXAxis( VmathVector3 *result ); - -/* - * Construct y axis - */ -static inline void vmathV3MakeYAxis( VmathVector3 *result ); - -/* - * Construct z axis - */ -static inline void vmathV3MakeZAxis( VmathVector3 *result ); - -/* - * Multiply two 3-D vectors per element - */ -static inline void vmathV3MulPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Divide two 3-D vectors per element - * NOTE: - * Floating-point behavior matches standard library function divf4. - */ -static inline void vmathV3DivPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Compute the reciprocal of a 3-D vector per element - * NOTE: - * Floating-point behavior matches standard library function recipf4. - */ -static inline void vmathV3RecipPerElem( VmathVector3 *result, const VmathVector3 *vec ); - -/* - * Compute the square root of a 3-D vector per element - * NOTE: - * Floating-point behavior matches standard library function sqrtf4. - */ -static inline void vmathV3SqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ); - -/* - * Compute the reciprocal square root of a 3-D vector per element - * NOTE: - * Floating-point behavior matches standard library function rsqrtf4. - */ -static inline void vmathV3RsqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ); - -/* - * Compute the absolute value of a 3-D vector per element - */ -static inline void vmathV3AbsPerElem( VmathVector3 *result, const VmathVector3 *vec ); - -/* - * Copy sign from one 3-D vector to another, per element - */ -static inline void vmathV3CopySignPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Maximum of two 3-D vectors per element - */ -static inline void vmathV3MaxPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Minimum of two 3-D vectors per element - */ -static inline void vmathV3MinPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Maximum element of a 3-D vector - */ -static inline float vmathV3MaxElem( const VmathVector3 *vec ); - -/* - * Minimum element of a 3-D vector - */ -static inline float vmathV3MinElem( const VmathVector3 *vec ); - -/* - * Compute the sum of all elements of a 3-D vector - */ -static inline float vmathV3Sum( const VmathVector3 *vec ); - -/* - * Compute the dot product of two 3-D vectors - */ -static inline float vmathV3Dot( const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Compute the square of the length of a 3-D vector - */ -static inline float vmathV3LengthSqr( const VmathVector3 *vec ); - -/* - * Compute the length of a 3-D vector - */ -static inline float vmathV3Length( const VmathVector3 *vec ); - -/* - * Normalize a 3-D vector - * NOTE: - * The result is unpredictable when all elements of vec are at or near zero. - */ -static inline void vmathV3Normalize( VmathVector3 *result, const VmathVector3 *vec ); - -/* - * Compute cross product of two 3-D vectors - */ -static inline void vmathV3Cross( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Outer product of two 3-D vectors - */ -static inline void vmathV3Outer( VmathMatrix3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Pre-multiply a row vector by a 3x3 matrix - * NOTE: - * Slower than column post-multiply. - */ -static inline void vmathV3RowMul( VmathVector3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ); - -/* - * Cross-product matrix of a 3-D vector - */ -static inline void vmathV3CrossMatrix( VmathMatrix3 *result, const VmathVector3 *vec ); - -/* - * Create cross-product matrix and multiply - * NOTE: - * Faster than separately creating a cross-product matrix and multiplying. - */ -static inline void vmathV3CrossMatrixMul( VmathMatrix3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ); - -/* - * Linear interpolation between two 3-D vectors - * NOTE: - * Does not clamp t between 0 and 1. - */ -static inline void vmathV3Lerp( VmathVector3 *result, float t, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Spherical linear interpolation between two 3-D vectors - * NOTE: - * The result is unpredictable if the vectors point in opposite directions. - * Does not clamp t between 0 and 1. - */ -static inline void vmathV3Slerp( VmathVector3 *result, float t, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ); - -/* - * Conditionally select between two 3-D vectors - * NOTE: - * This function uses a conditional select instruction to avoid a branch. - * However, the transfer of select1 to a VMX register may use more processing time than a branch. - */ -static inline void vmathV3Select( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1, unsigned int select1 ); - -/* - * Store x, y, and z elements of a 3-D vector in the first three words of a quadword. - * The value of the fourth word (the word with the highest address) remains unchanged - */ -static inline void vmathV3StoreXYZ( const VmathVector3 *vec, vec_float4 *quad ); - -/* - * Load four three-float 3-D vectors, stored in three quadwords - */ -static inline void vmathV3LoadXYZArray( VmathVector3 *vec0, VmathVector3 *vec1, VmathVector3 *vec2, VmathVector3 *vec3, const vec_float4 *threeQuads ); - -/* - * Store four 3-D vectors in three quadwords - */ -static inline void vmathV3StoreXYZArray( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, vec_float4 *threeQuads ); - -/* - * Store eight 3-D vectors as half-floats - */ -static inline void vmathV3StoreHalfFloats( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, const VmathVector3 *vec4, const VmathVector3 *vec5, const VmathVector3 *vec6, const VmathVector3 *vec7, vec_ushort8 *threeQuads ); - -#ifdef _VECTORMATH_DEBUG - -/* - * Print a 3-D vector - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathV3Print( const VmathVector3 *vec ); - -/* - * Print a 3-D vector and an associated string identifier - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathV3Prints( const VmathVector3 *vec, const char *name ); - -#endif - -/* - * Copy a 4-D vector - */ -static inline void vmathV4Copy( VmathVector4 *result, const VmathVector4 *vec ); - -/* - * Construct a 4-D vector from x, y, z, and w elements - */ -static inline void vmathV4MakeFromElems( VmathVector4 *result, float x, float y, float z, float w ); - -/* - * Construct a 4-D vector from a 3-D vector and a scalar - */ -static inline void vmathV4MakeFromV3Scalar( VmathVector4 *result, const VmathVector3 *xyz, float w ); - -/* - * Copy x, y, and z from a 3-D vector into a 4-D vector, and set w to 0 - */ -static inline void vmathV4MakeFromV3( VmathVector4 *result, const VmathVector3 *vec ); - -/* - * Copy x, y, and z from a 3-D point into a 4-D vector, and set w to 1 - */ -static inline void vmathV4MakeFromP3( VmathVector4 *result, const VmathPoint3 *pnt ); - -/* - * Copy elements from a quaternion into a 4-D vector - */ -static inline void vmathV4MakeFromQ( VmathVector4 *result, const VmathQuat *quat ); - -/* - * Set all elements of a 4-D vector to the same scalar value - */ -static inline void vmathV4MakeFromScalar( VmathVector4 *result, float scalar ); - -/* - * Set vector float data in a 4-D vector - */ -static inline void vmathV4MakeFrom128( VmathVector4 *result, vec_float4 vf4 ); - -/* - * Get vector float data from a 4-D vector - */ -static inline vec_float4 vmathV4Get128( const VmathVector4 *vec ); - -/* - * Set the x, y, and z elements of a 4-D vector - * NOTE: - * This function does not change the w element. - */ -static inline void vmathV4SetXYZ( VmathVector4 *result, const VmathVector3 *vec ); - -/* - * Get the x, y, and z elements of a 4-D vector - */ -static inline void vmathV4GetXYZ( VmathVector3 *result, const VmathVector4 *vec ); - -/* - * Set the x element of a 4-D vector - */ -static inline void vmathV4SetX( VmathVector4 *result, float x ); - -/* - * Set the y element of a 4-D vector - */ -static inline void vmathV4SetY( VmathVector4 *result, float y ); - -/* - * Set the z element of a 4-D vector - */ -static inline void vmathV4SetZ( VmathVector4 *result, float z ); - -/* - * Set the w element of a 4-D vector - */ -static inline void vmathV4SetW( VmathVector4 *result, float w ); - -/* - * Get the x element of a 4-D vector - */ -static inline float vmathV4GetX( const VmathVector4 *vec ); - -/* - * Get the y element of a 4-D vector - */ -static inline float vmathV4GetY( const VmathVector4 *vec ); - -/* - * Get the z element of a 4-D vector - */ -static inline float vmathV4GetZ( const VmathVector4 *vec ); - -/* - * Get the w element of a 4-D vector - */ -static inline float vmathV4GetW( const VmathVector4 *vec ); - -/* - * Set an x, y, z, or w element of a 4-D vector by index - */ -static inline void vmathV4SetElem( VmathVector4 *result, int idx, float value ); - -/* - * Get an x, y, z, or w element of a 4-D vector by index - */ -static inline float vmathV4GetElem( const VmathVector4 *vec, int idx ); - -/* - * Add two 4-D vectors - */ -static inline void vmathV4Add( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Subtract a 4-D vector from another 4-D vector - */ -static inline void vmathV4Sub( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Multiply a 4-D vector by a scalar - */ -static inline void vmathV4ScalarMul( VmathVector4 *result, const VmathVector4 *vec, float scalar ); - -/* - * Divide a 4-D vector by a scalar - */ -static inline void vmathV4ScalarDiv( VmathVector4 *result, const VmathVector4 *vec, float scalar ); - -/* - * Negate all elements of a 4-D vector - */ -static inline void vmathV4Neg( VmathVector4 *result, const VmathVector4 *vec ); - -/* - * Construct x axis - */ -static inline void vmathV4MakeXAxis( VmathVector4 *result ); - -/* - * Construct y axis - */ -static inline void vmathV4MakeYAxis( VmathVector4 *result ); - -/* - * Construct z axis - */ -static inline void vmathV4MakeZAxis( VmathVector4 *result ); - -/* - * Construct w axis - */ -static inline void vmathV4MakeWAxis( VmathVector4 *result ); - -/* - * Multiply two 4-D vectors per element - */ -static inline void vmathV4MulPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Divide two 4-D vectors per element - * NOTE: - * Floating-point behavior matches standard library function divf4. - */ -static inline void vmathV4DivPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Compute the reciprocal of a 4-D vector per element - * NOTE: - * Floating-point behavior matches standard library function recipf4. - */ -static inline void vmathV4RecipPerElem( VmathVector4 *result, const VmathVector4 *vec ); - -/* - * Compute the square root of a 4-D vector per element - * NOTE: - * Floating-point behavior matches standard library function sqrtf4. - */ -static inline void vmathV4SqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ); - -/* - * Compute the reciprocal square root of a 4-D vector per element - * NOTE: - * Floating-point behavior matches standard library function rsqrtf4. - */ -static inline void vmathV4RsqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ); - -/* - * Compute the absolute value of a 4-D vector per element - */ -static inline void vmathV4AbsPerElem( VmathVector4 *result, const VmathVector4 *vec ); - -/* - * Copy sign from one 4-D vector to another, per element - */ -static inline void vmathV4CopySignPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Maximum of two 4-D vectors per element - */ -static inline void vmathV4MaxPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Minimum of two 4-D vectors per element - */ -static inline void vmathV4MinPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Maximum element of a 4-D vector - */ -static inline float vmathV4MaxElem( const VmathVector4 *vec ); - -/* - * Minimum element of a 4-D vector - */ -static inline float vmathV4MinElem( const VmathVector4 *vec ); - -/* - * Compute the sum of all elements of a 4-D vector - */ -static inline float vmathV4Sum( const VmathVector4 *vec ); - -/* - * Compute the dot product of two 4-D vectors - */ -static inline float vmathV4Dot( const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Compute the square of the length of a 4-D vector - */ -static inline float vmathV4LengthSqr( const VmathVector4 *vec ); - -/* - * Compute the length of a 4-D vector - */ -static inline float vmathV4Length( const VmathVector4 *vec ); - -/* - * Normalize a 4-D vector - * NOTE: - * The result is unpredictable when all elements of vec are at or near zero. - */ -static inline void vmathV4Normalize( VmathVector4 *result, const VmathVector4 *vec ); - -/* - * Outer product of two 4-D vectors - */ -static inline void vmathV4Outer( VmathMatrix4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Linear interpolation between two 4-D vectors - * NOTE: - * Does not clamp t between 0 and 1. - */ -static inline void vmathV4Lerp( VmathVector4 *result, float t, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Spherical linear interpolation between two 4-D vectors - * NOTE: - * The result is unpredictable if the vectors point in opposite directions. - * Does not clamp t between 0 and 1. - */ -static inline void vmathV4Slerp( VmathVector4 *result, float t, const VmathVector4 *unitVec0, const VmathVector4 *unitVec1 ); - -/* - * Conditionally select between two 4-D vectors - * NOTE: - * This function uses a conditional select instruction to avoid a branch. - * However, the transfer of select1 to a VMX register may use more processing time than a branch. - */ -static inline void vmathV4Select( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1, unsigned int select1 ); - -/* - * Store four 4-D vectors as half-floats - */ -static inline void vmathV4StoreHalfFloats( const VmathVector4 *vec0, const VmathVector4 *vec1, const VmathVector4 *vec2, const VmathVector4 *vec3, vec_ushort8 *twoQuads ); - -#ifdef _VECTORMATH_DEBUG - -/* - * Print a 4-D vector - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathV4Print( const VmathVector4 *vec ); - -/* - * Print a 4-D vector and an associated string identifier - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathV4Prints( const VmathVector4 *vec, const char *name ); - -#endif - -/* - * Copy a 3-D point - */ -static inline void vmathP3Copy( VmathPoint3 *result, const VmathPoint3 *pnt ); - -/* - * Construct a 3-D point from x, y, and z elements - */ -static inline void vmathP3MakeFromElems( VmathPoint3 *result, float x, float y, float z ); - -/* - * Copy elements from a 3-D vector into a 3-D point - */ -static inline void vmathP3MakeFromV3( VmathPoint3 *result, const VmathVector3 *vec ); - -/* - * Set all elements of a 3-D point to the same scalar value - */ -static inline void vmathP3MakeFromScalar( VmathPoint3 *result, float scalar ); - -/* - * Set vector float data in a 3-D point - */ -static inline void vmathP3MakeFrom128( VmathPoint3 *result, vec_float4 vf4 ); - -/* - * Get vector float data from a 3-D point - */ -static inline vec_float4 vmathP3Get128( const VmathPoint3 *pnt ); - -/* - * Set the x element of a 3-D point - */ -static inline void vmathP3SetX( VmathPoint3 *result, float x ); - -/* - * Set the y element of a 3-D point - */ -static inline void vmathP3SetY( VmathPoint3 *result, float y ); - -/* - * Set the z element of a 3-D point - */ -static inline void vmathP3SetZ( VmathPoint3 *result, float z ); - -/* - * Get the x element of a 3-D point - */ -static inline float vmathP3GetX( const VmathPoint3 *pnt ); - -/* - * Get the y element of a 3-D point - */ -static inline float vmathP3GetY( const VmathPoint3 *pnt ); - -/* - * Get the z element of a 3-D point - */ -static inline float vmathP3GetZ( const VmathPoint3 *pnt ); - -/* - * Set an x, y, or z element of a 3-D point by index - */ -static inline void vmathP3SetElem( VmathPoint3 *result, int idx, float value ); - -/* - * Get an x, y, or z element of a 3-D point by index - */ -static inline float vmathP3GetElem( const VmathPoint3 *pnt, int idx ); - -/* - * Subtract a 3-D point from another 3-D point - */ -static inline void vmathP3Sub( VmathVector3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Add a 3-D point to a 3-D vector - */ -static inline void vmathP3AddV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec ); - -/* - * Subtract a 3-D vector from a 3-D point - */ -static inline void vmathP3SubV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec ); - -/* - * Multiply two 3-D points per element - */ -static inline void vmathP3MulPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Divide two 3-D points per element - * NOTE: - * Floating-point behavior matches standard library function divf4. - */ -static inline void vmathP3DivPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Compute the reciprocal of a 3-D point per element - * NOTE: - * Floating-point behavior matches standard library function recipf4. - */ -static inline void vmathP3RecipPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); - -/* - * Compute the square root of a 3-D point per element - * NOTE: - * Floating-point behavior matches standard library function sqrtf4. - */ -static inline void vmathP3SqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); - -/* - * Compute the reciprocal square root of a 3-D point per element - * NOTE: - * Floating-point behavior matches standard library function rsqrtf4. - */ -static inline void vmathP3RsqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); - -/* - * Compute the absolute value of a 3-D point per element - */ -static inline void vmathP3AbsPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); - -/* - * Copy sign from one 3-D point to another, per element - */ -static inline void vmathP3CopySignPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Maximum of two 3-D points per element - */ -static inline void vmathP3MaxPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Minimum of two 3-D points per element - */ -static inline void vmathP3MinPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Maximum element of a 3-D point - */ -static inline float vmathP3MaxElem( const VmathPoint3 *pnt ); - -/* - * Minimum element of a 3-D point - */ -static inline float vmathP3MinElem( const VmathPoint3 *pnt ); - -/* - * Compute the sum of all elements of a 3-D point - */ -static inline float vmathP3Sum( const VmathPoint3 *pnt ); - -/* - * Apply uniform scale to a 3-D point - */ -static inline void vmathP3Scale( VmathPoint3 *result, const VmathPoint3 *pnt, float scaleVal ); - -/* - * Apply non-uniform scale to a 3-D point - */ -static inline void vmathP3NonUniformScale( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *scaleVec ); - -/* - * Scalar projection of a 3-D point on a unit-length 3-D vector - */ -static inline float vmathP3Projection( const VmathPoint3 *pnt, const VmathVector3 *unitVec ); - -/* - * Compute the square of the distance of a 3-D point from the coordinate-system origin - */ -static inline float vmathP3DistSqrFromOrigin( const VmathPoint3 *pnt ); - -/* - * Compute the distance of a 3-D point from the coordinate-system origin - */ -static inline float vmathP3DistFromOrigin( const VmathPoint3 *pnt ); - -/* - * Compute the square of the distance between two 3-D points - */ -static inline float vmathP3DistSqr( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Compute the distance between two 3-D points - */ -static inline float vmathP3Dist( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Linear interpolation between two 3-D points - * NOTE: - * Does not clamp t between 0 and 1. - */ -static inline void vmathP3Lerp( VmathPoint3 *result, float t, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Conditionally select between two 3-D points - * NOTE: - * This function uses a conditional select instruction to avoid a branch. - * However, the transfer of select1 to a VMX register may use more processing time than a branch. - */ -static inline void vmathP3Select( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, unsigned int select1 ); - -/* - * Store x, y, and z elements of a 3-D point in the first three words of a quadword. - * The value of the fourth word (the word with the highest address) remains unchanged - */ -static inline void vmathP3StoreXYZ( const VmathPoint3 *pnt, vec_float4 *quad ); - -/* - * Load four three-float 3-D points, stored in three quadwords - */ -static inline void vmathP3LoadXYZArray( VmathPoint3 *pnt0, VmathPoint3 *pnt1, VmathPoint3 *pnt2, VmathPoint3 *pnt3, const vec_float4 *threeQuads ); - -/* - * Store four 3-D points in three quadwords - */ -static inline void vmathP3StoreXYZArray( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, vec_float4 *threeQuads ); - -/* - * Store eight 3-D points as half-floats - */ -static inline void vmathP3StoreHalfFloats( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, const VmathPoint3 *pnt4, const VmathPoint3 *pnt5, const VmathPoint3 *pnt6, const VmathPoint3 *pnt7, vec_ushort8 *threeQuads ); - -#ifdef _VECTORMATH_DEBUG - -/* - * Print a 3-D point - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathP3Print( const VmathPoint3 *pnt ); - -/* - * Print a 3-D point and an associated string identifier - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathP3Prints( const VmathPoint3 *pnt, const char *name ); - -#endif - -/* - * Copy a quaternion - */ -static inline void vmathQCopy( VmathQuat *result, const VmathQuat *quat ); - -/* - * Construct a quaternion from x, y, z, and w elements - */ -static inline void vmathQMakeFromElems( VmathQuat *result, float x, float y, float z, float w ); - -/* - * Construct a quaternion from a 3-D vector and a scalar - */ -static inline void vmathQMakeFromV3Scalar( VmathQuat *result, const VmathVector3 *xyz, float w ); - -/* - * Copy elements from a 4-D vector into a quaternion - */ -static inline void vmathQMakeFromV4( VmathQuat *result, const VmathVector4 *vec ); - -/* - * Convert a rotation matrix to a unit-length quaternion - */ -static inline void vmathQMakeFromM3( VmathQuat *result, const VmathMatrix3 *rotMat ); - -/* - * Set all elements of a quaternion to the same scalar value - */ -static inline void vmathQMakeFromScalar( VmathQuat *result, float scalar ); - -/* - * Set vector float data in a quaternion - */ -static inline void vmathQMakeFrom128( VmathQuat *result, vec_float4 vf4 ); - -/* - * Get vector float data from a quaternion - */ -static inline vec_float4 vmathQGet128( const VmathQuat *quat ); - -/* - * Set the x, y, and z elements of a quaternion - * NOTE: - * This function does not change the w element. - */ -static inline void vmathQSetXYZ( VmathQuat *result, const VmathVector3 *vec ); - -/* - * Get the x, y, and z elements of a quaternion - */ -static inline void vmathQGetXYZ( VmathVector3 *result, const VmathQuat *quat ); - -/* - * Set the x element of a quaternion - */ -static inline void vmathQSetX( VmathQuat *result, float x ); - -/* - * Set the y element of a quaternion - */ -static inline void vmathQSetY( VmathQuat *result, float y ); - -/* - * Set the z element of a quaternion - */ -static inline void vmathQSetZ( VmathQuat *result, float z ); - -/* - * Set the w element of a quaternion - */ -static inline void vmathQSetW( VmathQuat *result, float w ); - -/* - * Get the x element of a quaternion - */ -static inline float vmathQGetX( const VmathQuat *quat ); - -/* - * Get the y element of a quaternion - */ -static inline float vmathQGetY( const VmathQuat *quat ); - -/* - * Get the z element of a quaternion - */ -static inline float vmathQGetZ( const VmathQuat *quat ); - -/* - * Get the w element of a quaternion - */ -static inline float vmathQGetW( const VmathQuat *quat ); - -/* - * Set an x, y, z, or w element of a quaternion by index - */ -static inline void vmathQSetElem( VmathQuat *result, int idx, float value ); - -/* - * Get an x, y, z, or w element of a quaternion by index - */ -static inline float vmathQGetElem( const VmathQuat *quat, int idx ); - -/* - * Add two quaternions - */ -static inline void vmathQAdd( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ); - -/* - * Subtract a quaternion from another quaternion - */ -static inline void vmathQSub( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ); - -/* - * Multiply two quaternions - */ -static inline void vmathQMul( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ); - -/* - * Multiply a quaternion by a scalar - */ -static inline void vmathQScalarMul( VmathQuat *result, const VmathQuat *quat, float scalar ); - -/* - * Divide a quaternion by a scalar - */ -static inline void vmathQScalarDiv( VmathQuat *result, const VmathQuat *quat, float scalar ); - -/* - * Negate all elements of a quaternion - */ -static inline void vmathQNeg( VmathQuat *result, const VmathQuat *quat ); - -/* - * Construct an identity quaternion - */ -static inline void vmathQMakeIdentity( VmathQuat *result ); - -/* - * Construct a quaternion to rotate between two unit-length 3-D vectors - * NOTE: - * The result is unpredictable if unitVec0 and unitVec1 point in opposite directions. - */ -static inline void vmathQMakeRotationArc( VmathQuat *result, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ); - -/* - * Construct a quaternion to rotate around a unit-length 3-D vector - */ -static inline void vmathQMakeRotationAxis( VmathQuat *result, float radians, const VmathVector3 *unitVec ); - -/* - * Construct a quaternion to rotate around the x axis - */ -static inline void vmathQMakeRotationX( VmathQuat *result, float radians ); - -/* - * Construct a quaternion to rotate around the y axis - */ -static inline void vmathQMakeRotationY( VmathQuat *result, float radians ); - -/* - * Construct a quaternion to rotate around the z axis - */ -static inline void vmathQMakeRotationZ( VmathQuat *result, float radians ); - -/* - * Compute the conjugate of a quaternion - */ -static inline void vmathQConj( VmathQuat *result, const VmathQuat *quat ); - -/* - * Use a unit-length quaternion to rotate a 3-D vector - */ -static inline void vmathQRotate( VmathVector3 *result, const VmathQuat *unitQuat, const VmathVector3 *vec ); - -/* - * Compute the dot product of two quaternions - */ -static inline float vmathQDot( const VmathQuat *quat0, const VmathQuat *quat1 ); - -/* - * Compute the norm of a quaternion - */ -static inline float vmathQNorm( const VmathQuat *quat ); - -/* - * Compute the length of a quaternion - */ -static inline float vmathQLength( const VmathQuat *quat ); - -/* - * Normalize a quaternion - * NOTE: - * The result is unpredictable when all elements of quat are at or near zero. - */ -static inline void vmathQNormalize( VmathQuat *result, const VmathQuat *quat ); - -/* - * Linear interpolation between two quaternions - * NOTE: - * Does not clamp t between 0 and 1. - */ -static inline void vmathQLerp( VmathQuat *result, float t, const VmathQuat *quat0, const VmathQuat *quat1 ); - -/* - * Spherical linear interpolation between two quaternions - * NOTE: - * Interpolates along the shortest path between orientations. - * Does not clamp t between 0 and 1. - */ -static inline void vmathQSlerp( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1 ); - -/* - * Spherical quadrangle interpolation - */ -static inline void vmathQSquad( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1, const VmathQuat *unitQuat2, const VmathQuat *unitQuat3 ); - -/* - * Conditionally select between two quaternions - * NOTE: - * This function uses a conditional select instruction to avoid a branch. - * However, the transfer of select1 to a VMX register may use more processing time than a branch. - */ -static inline void vmathQSelect( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1, unsigned int select1 ); - -#ifdef _VECTORMATH_DEBUG - -/* - * Print a quaternion - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathQPrint( const VmathQuat *quat ); - -/* - * Print a quaternion and an associated string identifier - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathQPrints( const VmathQuat *quat, const char *name ); - -#endif - -/* - * Copy a 3x3 matrix - */ -static inline void vmathM3Copy( VmathMatrix3 *result, const VmathMatrix3 *mat ); - -/* - * Construct a 3x3 matrix containing the specified columns - */ -static inline void vmathM3MakeFromCols( VmathMatrix3 *result, const VmathVector3 *col0, const VmathVector3 *col1, const VmathVector3 *col2 ); - -/* - * Construct a 3x3 rotation matrix from a unit-length quaternion - */ -static inline void vmathM3MakeFromQ( VmathMatrix3 *result, const VmathQuat *unitQuat ); - -/* - * Set all elements of a 3x3 matrix to the same scalar value - */ -static inline void vmathM3MakeFromScalar( VmathMatrix3 *result, float scalar ); - -/* - * Set column 0 of a 3x3 matrix - */ -static inline void vmathM3SetCol0( VmathMatrix3 *result, const VmathVector3 *col0 ); - -/* - * Set column 1 of a 3x3 matrix - */ -static inline void vmathM3SetCol1( VmathMatrix3 *result, const VmathVector3 *col1 ); - -/* - * Set column 2 of a 3x3 matrix - */ -static inline void vmathM3SetCol2( VmathMatrix3 *result, const VmathVector3 *col2 ); - -/* - * Get column 0 of a 3x3 matrix - */ -static inline void vmathM3GetCol0( VmathVector3 *result, const VmathMatrix3 *mat ); - -/* - * Get column 1 of a 3x3 matrix - */ -static inline void vmathM3GetCol1( VmathVector3 *result, const VmathMatrix3 *mat ); - -/* - * Get column 2 of a 3x3 matrix - */ -static inline void vmathM3GetCol2( VmathVector3 *result, const VmathMatrix3 *mat ); - -/* - * Set the column of a 3x3 matrix referred to by the specified index - */ -static inline void vmathM3SetCol( VmathMatrix3 *result, int col, const VmathVector3 *vec ); - -/* - * Set the row of a 3x3 matrix referred to by the specified index - */ -static inline void vmathM3SetRow( VmathMatrix3 *result, int row, const VmathVector3 *vec ); - -/* - * Get the column of a 3x3 matrix referred to by the specified index - */ -static inline void vmathM3GetCol( VmathVector3 *result, const VmathMatrix3 *mat, int col ); - -/* - * Get the row of a 3x3 matrix referred to by the specified index - */ -static inline void vmathM3GetRow( VmathVector3 *result, const VmathMatrix3 *mat, int row ); - -/* - * Set the element of a 3x3 matrix referred to by column and row indices - */ -static inline void vmathM3SetElem( VmathMatrix3 *result, int col, int row, float val ); - -/* - * Get the element of a 3x3 matrix referred to by column and row indices - */ -static inline float vmathM3GetElem( const VmathMatrix3 *mat, int col, int row ); - -/* - * Add two 3x3 matrices - */ -static inline void vmathM3Add( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); - -/* - * Subtract a 3x3 matrix from another 3x3 matrix - */ -static inline void vmathM3Sub( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); - -/* - * Negate all elements of a 3x3 matrix - */ -static inline void vmathM3Neg( VmathMatrix3 *result, const VmathMatrix3 *mat ); - -/* - * Multiply a 3x3 matrix by a scalar - */ -static inline void vmathM3ScalarMul( VmathMatrix3 *result, const VmathMatrix3 *mat, float scalar ); - -/* - * Multiply a 3x3 matrix by a 3-D vector - */ -static inline void vmathM3MulV3( VmathVector3 *result, const VmathMatrix3 *mat, const VmathVector3 *vec ); - -/* - * Multiply two 3x3 matrices - */ -static inline void vmathM3Mul( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); - -/* - * Construct an identity 3x3 matrix - */ -static inline void vmathM3MakeIdentity( VmathMatrix3 *result ); - -/* - * Construct a 3x3 matrix to rotate around the x axis - */ -static inline void vmathM3MakeRotationX( VmathMatrix3 *result, float radians ); - -/* - * Construct a 3x3 matrix to rotate around the y axis - */ -static inline void vmathM3MakeRotationY( VmathMatrix3 *result, float radians ); - -/* - * Construct a 3x3 matrix to rotate around the z axis - */ -static inline void vmathM3MakeRotationZ( VmathMatrix3 *result, float radians ); - -/* - * Construct a 3x3 matrix to rotate around the x, y, and z axes - */ -static inline void vmathM3MakeRotationZYX( VmathMatrix3 *result, const VmathVector3 *radiansXYZ ); - -/* - * Construct a 3x3 matrix to rotate around a unit-length 3-D vector - */ -static inline void vmathM3MakeRotationAxis( VmathMatrix3 *result, float radians, const VmathVector3 *unitVec ); - -/* - * Construct a rotation matrix from a unit-length quaternion - */ -static inline void vmathM3MakeRotationQ( VmathMatrix3 *result, const VmathQuat *unitQuat ); - -/* - * Construct a 3x3 matrix to perform scaling - */ -static inline void vmathM3MakeScale( VmathMatrix3 *result, const VmathVector3 *scaleVec ); - -/* - * Append (post-multiply) a scale transformation to a 3x3 matrix - * NOTE: - * Faster than creating and multiplying a scale transformation matrix. - */ -static inline void vmathM3AppendScale( VmathMatrix3 *result, const VmathMatrix3 *mat, const VmathVector3 *scaleVec ); - -/* - * Prepend (pre-multiply) a scale transformation to a 3x3 matrix - * NOTE: - * Faster than creating and multiplying a scale transformation matrix. - */ -static inline void vmathM3PrependScale( VmathMatrix3 *result, const VmathVector3 *scaleVec, const VmathMatrix3 *mat ); - -/* - * Multiply two 3x3 matrices per element - */ -static inline void vmathM3MulPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); - -/* - * Compute the absolute value of a 3x3 matrix per element - */ -static inline void vmathM3AbsPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat ); - -/* - * Transpose of a 3x3 matrix - */ -static inline void vmathM3Transpose( VmathMatrix3 *result, const VmathMatrix3 *mat ); - -/* - * Compute the inverse of a 3x3 matrix - * NOTE: - * Result is unpredictable when the determinant of mat is equal to or near 0. - */ -static inline void vmathM3Inverse( VmathMatrix3 *result, const VmathMatrix3 *mat ); - -/* - * Determinant of a 3x3 matrix - */ -static inline float vmathM3Determinant( const VmathMatrix3 *mat ); - -/* - * Conditionally select between two 3x3 matrices - * NOTE: - * This function uses a conditional select instruction to avoid a branch. - * However, the transfer of select1 to a VMX register may use more processing time than a branch. - */ -static inline void vmathM3Select( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1, unsigned int select1 ); - -#ifdef _VECTORMATH_DEBUG - -/* - * Print a 3x3 matrix - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathM3Print( const VmathMatrix3 *mat ); - -/* - * Print a 3x3 matrix and an associated string identifier - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathM3Prints( const VmathMatrix3 *mat, const char *name ); - -#endif - -/* - * Copy a 4x4 matrix - */ -static inline void vmathM4Copy( VmathMatrix4 *result, const VmathMatrix4 *mat ); - -/* - * Construct a 4x4 matrix containing the specified columns - */ -static inline void vmathM4MakeFromCols( VmathMatrix4 *result, const VmathVector4 *col0, const VmathVector4 *col1, const VmathVector4 *col2, const VmathVector4 *col3 ); - -/* - * Construct a 4x4 matrix from a 3x4 transformation matrix - */ -static inline void vmathM4MakeFromT3( VmathMatrix4 *result, const VmathTransform3 *mat ); - -/* - * Construct a 4x4 matrix from a 3x3 matrix and a 3-D vector - */ -static inline void vmathM4MakeFromM3V3( VmathMatrix4 *result, const VmathMatrix3 *mat, const VmathVector3 *translateVec ); - -/* - * Construct a 4x4 matrix from a unit-length quaternion and a 3-D vector - */ -static inline void vmathM4MakeFromQV3( VmathMatrix4 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ); - -/* - * Set all elements of a 4x4 matrix to the same scalar value - */ -static inline void vmathM4MakeFromScalar( VmathMatrix4 *result, float scalar ); - -/* - * Set the upper-left 3x3 submatrix - * NOTE: - * This function does not change the bottom row elements. - */ -static inline void vmathM4SetUpper3x3( VmathMatrix4 *result, const VmathMatrix3 *mat3 ); - -/* - * Get the upper-left 3x3 submatrix of a 4x4 matrix - */ -static inline void vmathM4GetUpper3x3( VmathMatrix3 *result, const VmathMatrix4 *mat ); - -/* - * Set translation component - * NOTE: - * This function does not change the bottom row elements. - */ -static inline void vmathM4SetTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ); - -/* - * Get the translation component of a 4x4 matrix - */ -static inline void vmathM4GetTranslation( VmathVector3 *result, const VmathMatrix4 *mat ); - -/* - * Set column 0 of a 4x4 matrix - */ -static inline void vmathM4SetCol0( VmathMatrix4 *result, const VmathVector4 *col0 ); - -/* - * Set column 1 of a 4x4 matrix - */ -static inline void vmathM4SetCol1( VmathMatrix4 *result, const VmathVector4 *col1 ); - -/* - * Set column 2 of a 4x4 matrix - */ -static inline void vmathM4SetCol2( VmathMatrix4 *result, const VmathVector4 *col2 ); - -/* - * Set column 3 of a 4x4 matrix - */ -static inline void vmathM4SetCol3( VmathMatrix4 *result, const VmathVector4 *col3 ); - -/* - * Get column 0 of a 4x4 matrix - */ -static inline void vmathM4GetCol0( VmathVector4 *result, const VmathMatrix4 *mat ); - -/* - * Get column 1 of a 4x4 matrix - */ -static inline void vmathM4GetCol1( VmathVector4 *result, const VmathMatrix4 *mat ); - -/* - * Get column 2 of a 4x4 matrix - */ -static inline void vmathM4GetCol2( VmathVector4 *result, const VmathMatrix4 *mat ); - -/* - * Get column 3 of a 4x4 matrix - */ -static inline void vmathM4GetCol3( VmathVector4 *result, const VmathMatrix4 *mat ); - -/* - * Set the column of a 4x4 matrix referred to by the specified index - */ -static inline void vmathM4SetCol( VmathMatrix4 *result, int col, const VmathVector4 *vec ); - -/* - * Set the row of a 4x4 matrix referred to by the specified index - */ -static inline void vmathM4SetRow( VmathMatrix4 *result, int row, const VmathVector4 *vec ); - -/* - * Get the column of a 4x4 matrix referred to by the specified index - */ -static inline void vmathM4GetCol( VmathVector4 *result, const VmathMatrix4 *mat, int col ); - -/* - * Get the row of a 4x4 matrix referred to by the specified index - */ -static inline void vmathM4GetRow( VmathVector4 *result, const VmathMatrix4 *mat, int row ); - -/* - * Set the element of a 4x4 matrix referred to by column and row indices - */ -static inline void vmathM4SetElem( VmathMatrix4 *result, int col, int row, float val ); - -/* - * Get the element of a 4x4 matrix referred to by column and row indices - */ -static inline float vmathM4GetElem( const VmathMatrix4 *mat, int col, int row ); - -/* - * Add two 4x4 matrices - */ -static inline void vmathM4Add( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); - -/* - * Subtract a 4x4 matrix from another 4x4 matrix - */ -static inline void vmathM4Sub( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); - -/* - * Negate all elements of a 4x4 matrix - */ -static inline void vmathM4Neg( VmathMatrix4 *result, const VmathMatrix4 *mat ); - -/* - * Multiply a 4x4 matrix by a scalar - */ -static inline void vmathM4ScalarMul( VmathMatrix4 *result, const VmathMatrix4 *mat, float scalar ); - -/* - * Multiply a 4x4 matrix by a 4-D vector - */ -static inline void vmathM4MulV4( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector4 *vec ); - -/* - * Multiply a 4x4 matrix by a 3-D vector - */ -static inline void vmathM4MulV3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector3 *vec ); - -/* - * Multiply a 4x4 matrix by a 3-D point - */ -static inline void vmathM4MulP3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathPoint3 *pnt ); - -/* - * Multiply two 4x4 matrices - */ -static inline void vmathM4Mul( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); - -/* - * Multiply a 4x4 matrix by a 3x4 transformation matrix - */ -static inline void vmathM4MulT3( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathTransform3 *tfrm ); - -/* - * Construct an identity 4x4 matrix - */ -static inline void vmathM4MakeIdentity( VmathMatrix4 *result ); - -/* - * Construct a 4x4 matrix to rotate around the x axis - */ -static inline void vmathM4MakeRotationX( VmathMatrix4 *result, float radians ); - -/* - * Construct a 4x4 matrix to rotate around the y axis - */ -static inline void vmathM4MakeRotationY( VmathMatrix4 *result, float radians ); - -/* - * Construct a 4x4 matrix to rotate around the z axis - */ -static inline void vmathM4MakeRotationZ( VmathMatrix4 *result, float radians ); - -/* - * Construct a 4x4 matrix to rotate around the x, y, and z axes - */ -static inline void vmathM4MakeRotationZYX( VmathMatrix4 *result, const VmathVector3 *radiansXYZ ); - -/* - * Construct a 4x4 matrix to rotate around a unit-length 3-D vector - */ -static inline void vmathM4MakeRotationAxis( VmathMatrix4 *result, float radians, const VmathVector3 *unitVec ); - -/* - * Construct a rotation matrix from a unit-length quaternion - */ -static inline void vmathM4MakeRotationQ( VmathMatrix4 *result, const VmathQuat *unitQuat ); - -/* - * Construct a 4x4 matrix to perform scaling - */ -static inline void vmathM4MakeScale( VmathMatrix4 *result, const VmathVector3 *scaleVec ); - -/* - * Construct a 4x4 matrix to perform translation - */ -static inline void vmathM4MakeTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ); - -/* - * Construct viewing matrix based on eye position, position looked at, and up direction - */ -static inline void vmathM4MakeLookAt( VmathMatrix4 *result, const VmathPoint3 *eyePos, const VmathPoint3 *lookAtPos, const VmathVector3 *upVec ); - -/* - * Construct a perspective projection matrix - */ -static inline void vmathM4MakePerspective( VmathMatrix4 *result, float fovyRadians, float aspect, float zNear, float zFar ); - -/* - * Construct a perspective projection matrix based on frustum - */ -static inline void vmathM4MakeFrustum( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ); - -/* - * Construct an orthographic projection matrix - */ -static inline void vmathM4MakeOrthographic( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ); - -/* - * Append (post-multiply) a scale transformation to a 4x4 matrix - * NOTE: - * Faster than creating and multiplying a scale transformation matrix. - */ -static inline void vmathM4AppendScale( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathVector3 *scaleVec ); - -/* - * Prepend (pre-multiply) a scale transformation to a 4x4 matrix - * NOTE: - * Faster than creating and multiplying a scale transformation matrix. - */ -static inline void vmathM4PrependScale( VmathMatrix4 *result, const VmathVector3 *scaleVec, const VmathMatrix4 *mat ); - -/* - * Multiply two 4x4 matrices per element - */ -static inline void vmathM4MulPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); - -/* - * Compute the absolute value of a 4x4 matrix per element - */ -static inline void vmathM4AbsPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat ); - -/* - * Transpose of a 4x4 matrix - */ -static inline void vmathM4Transpose( VmathMatrix4 *result, const VmathMatrix4 *mat ); - -/* - * Compute the inverse of a 4x4 matrix - * NOTE: - * Result is unpredictable when the determinant of mat is equal to or near 0. - */ -static inline void vmathM4Inverse( VmathMatrix4 *result, const VmathMatrix4 *mat ); - -/* - * Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix - * NOTE: - * This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. The result is unpredictable when the determinant of mat is equal to or near 0. - */ -static inline void vmathM4AffineInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ); - -/* - * Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix with an orthogonal upper-left 3x3 submatrix - * NOTE: - * This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. - */ -static inline void vmathM4OrthoInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ); - -/* - * Determinant of a 4x4 matrix - */ -static inline float vmathM4Determinant( const VmathMatrix4 *mat ); - -/* - * Conditionally select between two 4x4 matrices - * NOTE: - * This function uses a conditional select instruction to avoid a branch. - * However, the transfer of select1 to a VMX register may use more processing time than a branch. - */ -static inline void vmathM4Select( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1, unsigned int select1 ); - -#ifdef _VECTORMATH_DEBUG - -/* - * Print a 4x4 matrix - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathM4Print( const VmathMatrix4 *mat ); - -/* - * Print a 4x4 matrix and an associated string identifier - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathM4Prints( const VmathMatrix4 *mat, const char *name ); - -#endif - -/* - * Copy a 3x4 transformation matrix - */ -static inline void vmathT3Copy( VmathTransform3 *result, const VmathTransform3 *tfrm ); - -/* - * Construct a 3x4 transformation matrix containing the specified columns - */ -static inline void vmathT3MakeFromCols( VmathTransform3 *result, const VmathVector3 *col0, const VmathVector3 *col1, const VmathVector3 *col2, const VmathVector3 *col3 ); - -/* - * Construct a 3x4 transformation matrix from a 3x3 matrix and a 3-D vector - */ -static inline void vmathT3MakeFromM3V3( VmathTransform3 *result, const VmathMatrix3 *tfrm, const VmathVector3 *translateVec ); - -/* - * Construct a 3x4 transformation matrix from a unit-length quaternion and a 3-D vector - */ -static inline void vmathT3MakeFromQV3( VmathTransform3 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ); - -/* - * Set all elements of a 3x4 transformation matrix to the same scalar value - */ -static inline void vmathT3MakeFromScalar( VmathTransform3 *result, float scalar ); - -/* - * Set the upper-left 3x3 submatrix - */ -static inline void vmathT3SetUpper3x3( VmathTransform3 *result, const VmathMatrix3 *mat3 ); - -/* - * Get the upper-left 3x3 submatrix of a 3x4 transformation matrix - */ -static inline void vmathT3GetUpper3x3( VmathMatrix3 *result, const VmathTransform3 *tfrm ); - -/* - * Set translation component - */ -static inline void vmathT3SetTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ); - -/* - * Get the translation component of a 3x4 transformation matrix - */ -static inline void vmathT3GetTranslation( VmathVector3 *result, const VmathTransform3 *tfrm ); - -/* - * Set column 0 of a 3x4 transformation matrix - */ -static inline void vmathT3SetCol0( VmathTransform3 *result, const VmathVector3 *col0 ); - -/* - * Set column 1 of a 3x4 transformation matrix - */ -static inline void vmathT3SetCol1( VmathTransform3 *result, const VmathVector3 *col1 ); - -/* - * Set column 2 of a 3x4 transformation matrix - */ -static inline void vmathT3SetCol2( VmathTransform3 *result, const VmathVector3 *col2 ); - -/* - * Set column 3 of a 3x4 transformation matrix - */ -static inline void vmathT3SetCol3( VmathTransform3 *result, const VmathVector3 *col3 ); - -/* - * Get column 0 of a 3x4 transformation matrix - */ -static inline void vmathT3GetCol0( VmathVector3 *result, const VmathTransform3 *tfrm ); - -/* - * Get column 1 of a 3x4 transformation matrix - */ -static inline void vmathT3GetCol1( VmathVector3 *result, const VmathTransform3 *tfrm ); - -/* - * Get column 2 of a 3x4 transformation matrix - */ -static inline void vmathT3GetCol2( VmathVector3 *result, const VmathTransform3 *tfrm ); - -/* - * Get column 3 of a 3x4 transformation matrix - */ -static inline void vmathT3GetCol3( VmathVector3 *result, const VmathTransform3 *tfrm ); - -/* - * Set the column of a 3x4 transformation matrix referred to by the specified index - */ -static inline void vmathT3SetCol( VmathTransform3 *result, int col, const VmathVector3 *vec ); - -/* - * Set the row of a 3x4 transformation matrix referred to by the specified index - */ -static inline void vmathT3SetRow( VmathTransform3 *result, int row, const VmathVector4 *vec ); - -/* - * Get the column of a 3x4 transformation matrix referred to by the specified index - */ -static inline void vmathT3GetCol( VmathVector3 *result, const VmathTransform3 *tfrm, int col ); - -/* - * Get the row of a 3x4 transformation matrix referred to by the specified index - */ -static inline void vmathT3GetRow( VmathVector4 *result, const VmathTransform3 *tfrm, int row ); - -/* - * Set the element of a 3x4 transformation matrix referred to by column and row indices - */ -static inline void vmathT3SetElem( VmathTransform3 *result, int col, int row, float val ); - -/* - * Get the element of a 3x4 transformation matrix referred to by column and row indices - */ -static inline float vmathT3GetElem( const VmathTransform3 *tfrm, int col, int row ); - -/* - * Multiply a 3x4 transformation matrix by a 3-D vector - */ -static inline void vmathT3MulV3( VmathVector3 *result, const VmathTransform3 *tfrm, const VmathVector3 *vec ); - -/* - * Multiply a 3x4 transformation matrix by a 3-D point - */ -static inline void vmathT3MulP3( VmathPoint3 *result, const VmathTransform3 *tfrm, const VmathPoint3 *pnt ); - -/* - * Multiply two 3x4 transformation matrices - */ -static inline void vmathT3Mul( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ); - -/* - * Construct an identity 3x4 transformation matrix - */ -static inline void vmathT3MakeIdentity( VmathTransform3 *result ); - -/* - * Construct a 3x4 transformation matrix to rotate around the x axis - */ -static inline void vmathT3MakeRotationX( VmathTransform3 *result, float radians ); - -/* - * Construct a 3x4 transformation matrix to rotate around the y axis - */ -static inline void vmathT3MakeRotationY( VmathTransform3 *result, float radians ); - -/* - * Construct a 3x4 transformation matrix to rotate around the z axis - */ -static inline void vmathT3MakeRotationZ( VmathTransform3 *result, float radians ); - -/* - * Construct a 3x4 transformation matrix to rotate around the x, y, and z axes - */ -static inline void vmathT3MakeRotationZYX( VmathTransform3 *result, const VmathVector3 *radiansXYZ ); - -/* - * Construct a 3x4 transformation matrix to rotate around a unit-length 3-D vector - */ -static inline void vmathT3MakeRotationAxis( VmathTransform3 *result, float radians, const VmathVector3 *unitVec ); - -/* - * Construct a rotation matrix from a unit-length quaternion - */ -static inline void vmathT3MakeRotationQ( VmathTransform3 *result, const VmathQuat *unitQuat ); - -/* - * Construct a 3x4 transformation matrix to perform scaling - */ -static inline void vmathT3MakeScale( VmathTransform3 *result, const VmathVector3 *scaleVec ); - -/* - * Construct a 3x4 transformation matrix to perform translation - */ -static inline void vmathT3MakeTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ); - -/* - * Append (post-multiply) a scale transformation to a 3x4 transformation matrix - * NOTE: - * Faster than creating and multiplying a scale transformation matrix. - */ -static inline void vmathT3AppendScale( VmathTransform3 *result, const VmathTransform3 *tfrm, const VmathVector3 *scaleVec ); - -/* - * Prepend (pre-multiply) a scale transformation to a 3x4 transformation matrix - * NOTE: - * Faster than creating and multiplying a scale transformation matrix. - */ -static inline void vmathT3PrependScale( VmathTransform3 *result, const VmathVector3 *scaleVec, const VmathTransform3 *tfrm ); - -/* - * Multiply two 3x4 transformation matrices per element - */ -static inline void vmathT3MulPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ); - -/* - * Compute the absolute value of a 3x4 transformation matrix per element - */ -static inline void vmathT3AbsPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm ); - -/* - * Inverse of a 3x4 transformation matrix - * NOTE: - * Result is unpredictable when the determinant of the left 3x3 submatrix is equal to or near 0. - */ -static inline void vmathT3Inverse( VmathTransform3 *result, const VmathTransform3 *tfrm ); - -/* - * Compute the inverse of a 3x4 transformation matrix, expected to have an orthogonal upper-left 3x3 submatrix - * NOTE: - * This can be used to achieve better performance than a general inverse when the specified 3x4 transformation matrix meets the given restrictions. - */ -static inline void vmathT3OrthoInverse( VmathTransform3 *result, const VmathTransform3 *tfrm ); - -/* - * Conditionally select between two 3x4 transformation matrices - * NOTE: - * This function uses a conditional select instruction to avoid a branch. - * However, the transfer of select1 to a VMX register may use more processing time than a branch. - */ -static inline void vmathT3Select( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1, unsigned int select1 ); - -#ifdef _VECTORMATH_DEBUG - -/* - * Print a 3x4 transformation matrix - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathT3Print( const VmathTransform3 *tfrm ); - -/* - * Print a 3x4 transformation matrix and an associated string identifier - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathT3Prints( const VmathTransform3 *tfrm, const char *name ); - -#endif - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#include "vec_aos.h" -#include "quat_aos.h" -#include "mat_aos.h" - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_AOS_C_PPU_H +#define _VECTORMATH_AOS_C_PPU_H + +#include +#include +#include +#include "vec_types.h" + +#ifdef _VECTORMATH_DEBUG +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef _VECTORMATH_AOS_C_TYPES_H +#define _VECTORMATH_AOS_C_TYPES_H + +/* A 3-D vector in array-of-structures format + */ +typedef struct _VmathVector3 +{ + vec_float4 vec128; +} VmathVector3; + +/* A 4-D vector in array-of-structures format + */ +typedef struct _VmathVector4 +{ + vec_float4 vec128; +} VmathVector4; + +/* A 3-D point in array-of-structures format + */ +typedef struct _VmathPoint3 +{ + vec_float4 vec128; +} VmathPoint3; + +/* A quaternion in array-of-structures format + */ +typedef struct _VmathQuat +{ + vec_float4 vec128; +} VmathQuat; + +/* A 3x3 matrix in array-of-structures format + */ +typedef struct _VmathMatrix3 +{ + VmathVector3 col0; + VmathVector3 col1; + VmathVector3 col2; +} VmathMatrix3; + +/* A 4x4 matrix in array-of-structures format + */ +typedef struct _VmathMatrix4 +{ + VmathVector4 col0; + VmathVector4 col1; + VmathVector4 col2; + VmathVector4 col3; +} VmathMatrix4; + +/* A 3x4 transformation matrix in array-of-structures format + */ +typedef struct _VmathTransform3 +{ + VmathVector3 col0; + VmathVector3 col1; + VmathVector3 col2; + VmathVector3 col3; +} VmathTransform3; + +#endif + +/* + * Copy a 3-D vector + */ +static inline void vmathV3Copy( VmathVector3 *result, const VmathVector3 *vec ); + +/* + * Construct a 3-D vector from x, y, and z elements + */ +static inline void vmathV3MakeFromElems( VmathVector3 *result, float x, float y, float z ); + +/* + * Copy elements from a 3-D point into a 3-D vector + */ +static inline void vmathV3MakeFromP3( VmathVector3 *result, const VmathPoint3 *pnt ); + +/* + * Set all elements of a 3-D vector to the same scalar value + */ +static inline void vmathV3MakeFromScalar( VmathVector3 *result, float scalar ); + +/* + * Set vector float data in a 3-D vector + */ +static inline void vmathV3MakeFrom128( VmathVector3 *result, vec_float4 vf4 ); + +/* + * Get vector float data from a 3-D vector + */ +static inline vec_float4 vmathV3Get128( const VmathVector3 *vec ); + +/* + * Set the x element of a 3-D vector + */ +static inline void vmathV3SetX( VmathVector3 *result, float x ); + +/* + * Set the y element of a 3-D vector + */ +static inline void vmathV3SetY( VmathVector3 *result, float y ); + +/* + * Set the z element of a 3-D vector + */ +static inline void vmathV3SetZ( VmathVector3 *result, float z ); + +/* + * Get the x element of a 3-D vector + */ +static inline float vmathV3GetX( const VmathVector3 *vec ); + +/* + * Get the y element of a 3-D vector + */ +static inline float vmathV3GetY( const VmathVector3 *vec ); + +/* + * Get the z element of a 3-D vector + */ +static inline float vmathV3GetZ( const VmathVector3 *vec ); + +/* + * Set an x, y, or z element of a 3-D vector by index + */ +static inline void vmathV3SetElem( VmathVector3 *result, int idx, float value ); + +/* + * Get an x, y, or z element of a 3-D vector by index + */ +static inline float vmathV3GetElem( const VmathVector3 *vec, int idx ); + +/* + * Add two 3-D vectors + */ +static inline void vmathV3Add( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Subtract a 3-D vector from another 3-D vector + */ +static inline void vmathV3Sub( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Add a 3-D vector to a 3-D point + */ +static inline void vmathV3AddP3( VmathPoint3 *result, const VmathVector3 *vec, const VmathPoint3 *pnt ); + +/* + * Multiply a 3-D vector by a scalar + */ +static inline void vmathV3ScalarMul( VmathVector3 *result, const VmathVector3 *vec, float scalar ); + +/* + * Divide a 3-D vector by a scalar + */ +static inline void vmathV3ScalarDiv( VmathVector3 *result, const VmathVector3 *vec, float scalar ); + +/* + * Negate all elements of a 3-D vector + */ +static inline void vmathV3Neg( VmathVector3 *result, const VmathVector3 *vec ); + +/* + * Construct x axis + */ +static inline void vmathV3MakeXAxis( VmathVector3 *result ); + +/* + * Construct y axis + */ +static inline void vmathV3MakeYAxis( VmathVector3 *result ); + +/* + * Construct z axis + */ +static inline void vmathV3MakeZAxis( VmathVector3 *result ); + +/* + * Multiply two 3-D vectors per element + */ +static inline void vmathV3MulPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Divide two 3-D vectors per element + * NOTE: + * Floating-point behavior matches standard library function divf4. + */ +static inline void vmathV3DivPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Compute the reciprocal of a 3-D vector per element + * NOTE: + * Floating-point behavior matches standard library function recipf4. + */ +static inline void vmathV3RecipPerElem( VmathVector3 *result, const VmathVector3 *vec ); + +/* + * Compute the square root of a 3-D vector per element + * NOTE: + * Floating-point behavior matches standard library function sqrtf4. + */ +static inline void vmathV3SqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ); + +/* + * Compute the reciprocal square root of a 3-D vector per element + * NOTE: + * Floating-point behavior matches standard library function rsqrtf4. + */ +static inline void vmathV3RsqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ); + +/* + * Compute the absolute value of a 3-D vector per element + */ +static inline void vmathV3AbsPerElem( VmathVector3 *result, const VmathVector3 *vec ); + +/* + * Copy sign from one 3-D vector to another, per element + */ +static inline void vmathV3CopySignPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Maximum of two 3-D vectors per element + */ +static inline void vmathV3MaxPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Minimum of two 3-D vectors per element + */ +static inline void vmathV3MinPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Maximum element of a 3-D vector + */ +static inline float vmathV3MaxElem( const VmathVector3 *vec ); + +/* + * Minimum element of a 3-D vector + */ +static inline float vmathV3MinElem( const VmathVector3 *vec ); + +/* + * Compute the sum of all elements of a 3-D vector + */ +static inline float vmathV3Sum( const VmathVector3 *vec ); + +/* + * Compute the dot product of two 3-D vectors + */ +static inline float vmathV3Dot( const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Compute the square of the length of a 3-D vector + */ +static inline float vmathV3LengthSqr( const VmathVector3 *vec ); + +/* + * Compute the length of a 3-D vector + */ +static inline float vmathV3Length( const VmathVector3 *vec ); + +/* + * Normalize a 3-D vector + * NOTE: + * The result is unpredictable when all elements of vec are at or near zero. + */ +static inline void vmathV3Normalize( VmathVector3 *result, const VmathVector3 *vec ); + +/* + * Compute cross product of two 3-D vectors + */ +static inline void vmathV3Cross( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Outer product of two 3-D vectors + */ +static inline void vmathV3Outer( VmathMatrix3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Pre-multiply a row vector by a 3x3 matrix + * NOTE: + * Slower than column post-multiply. + */ +static inline void vmathV3RowMul( VmathVector3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ); + +/* + * Cross-product matrix of a 3-D vector + */ +static inline void vmathV3CrossMatrix( VmathMatrix3 *result, const VmathVector3 *vec ); + +/* + * Create cross-product matrix and multiply + * NOTE: + * Faster than separately creating a cross-product matrix and multiplying. + */ +static inline void vmathV3CrossMatrixMul( VmathMatrix3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ); + +/* + * Linear interpolation between two 3-D vectors + * NOTE: + * Does not clamp t between 0 and 1. + */ +static inline void vmathV3Lerp( VmathVector3 *result, float t, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Spherical linear interpolation between two 3-D vectors + * NOTE: + * The result is unpredictable if the vectors point in opposite directions. + * Does not clamp t between 0 and 1. + */ +static inline void vmathV3Slerp( VmathVector3 *result, float t, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ); + +/* + * Conditionally select between two 3-D vectors + * NOTE: + * This function uses a conditional select instruction to avoid a branch. + * However, the transfer of select1 to a VMX register may use more processing time than a branch. + */ +static inline void vmathV3Select( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1, unsigned int select1 ); + +/* + * Store x, y, and z elements of a 3-D vector in the first three words of a quadword. + * The value of the fourth word (the word with the highest address) remains unchanged + */ +static inline void vmathV3StoreXYZ( const VmathVector3 *vec, vec_float4 *quad ); + +/* + * Load four three-float 3-D vectors, stored in three quadwords + */ +static inline void vmathV3LoadXYZArray( VmathVector3 *vec0, VmathVector3 *vec1, VmathVector3 *vec2, VmathVector3 *vec3, const vec_float4 *threeQuads ); + +/* + * Store four 3-D vectors in three quadwords + */ +static inline void vmathV3StoreXYZArray( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, vec_float4 *threeQuads ); + +/* + * Store eight 3-D vectors as half-floats + */ +static inline void vmathV3StoreHalfFloats( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, const VmathVector3 *vec4, const VmathVector3 *vec5, const VmathVector3 *vec6, const VmathVector3 *vec7, vec_ushort8 *threeQuads ); + +#ifdef _VECTORMATH_DEBUG + +/* + * Print a 3-D vector + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathV3Print( const VmathVector3 *vec ); + +/* + * Print a 3-D vector and an associated string identifier + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathV3Prints( const VmathVector3 *vec, const char *name ); + +#endif + +/* + * Copy a 4-D vector + */ +static inline void vmathV4Copy( VmathVector4 *result, const VmathVector4 *vec ); + +/* + * Construct a 4-D vector from x, y, z, and w elements + */ +static inline void vmathV4MakeFromElems( VmathVector4 *result, float x, float y, float z, float w ); + +/* + * Construct a 4-D vector from a 3-D vector and a scalar + */ +static inline void vmathV4MakeFromV3Scalar( VmathVector4 *result, const VmathVector3 *xyz, float w ); + +/* + * Copy x, y, and z from a 3-D vector into a 4-D vector, and set w to 0 + */ +static inline void vmathV4MakeFromV3( VmathVector4 *result, const VmathVector3 *vec ); + +/* + * Copy x, y, and z from a 3-D point into a 4-D vector, and set w to 1 + */ +static inline void vmathV4MakeFromP3( VmathVector4 *result, const VmathPoint3 *pnt ); + +/* + * Copy elements from a quaternion into a 4-D vector + */ +static inline void vmathV4MakeFromQ( VmathVector4 *result, const VmathQuat *quat ); + +/* + * Set all elements of a 4-D vector to the same scalar value + */ +static inline void vmathV4MakeFromScalar( VmathVector4 *result, float scalar ); + +/* + * Set vector float data in a 4-D vector + */ +static inline void vmathV4MakeFrom128( VmathVector4 *result, vec_float4 vf4 ); + +/* + * Get vector float data from a 4-D vector + */ +static inline vec_float4 vmathV4Get128( const VmathVector4 *vec ); + +/* + * Set the x, y, and z elements of a 4-D vector + * NOTE: + * This function does not change the w element. + */ +static inline void vmathV4SetXYZ( VmathVector4 *result, const VmathVector3 *vec ); + +/* + * Get the x, y, and z elements of a 4-D vector + */ +static inline void vmathV4GetXYZ( VmathVector3 *result, const VmathVector4 *vec ); + +/* + * Set the x element of a 4-D vector + */ +static inline void vmathV4SetX( VmathVector4 *result, float x ); + +/* + * Set the y element of a 4-D vector + */ +static inline void vmathV4SetY( VmathVector4 *result, float y ); + +/* + * Set the z element of a 4-D vector + */ +static inline void vmathV4SetZ( VmathVector4 *result, float z ); + +/* + * Set the w element of a 4-D vector + */ +static inline void vmathV4SetW( VmathVector4 *result, float w ); + +/* + * Get the x element of a 4-D vector + */ +static inline float vmathV4GetX( const VmathVector4 *vec ); + +/* + * Get the y element of a 4-D vector + */ +static inline float vmathV4GetY( const VmathVector4 *vec ); + +/* + * Get the z element of a 4-D vector + */ +static inline float vmathV4GetZ( const VmathVector4 *vec ); + +/* + * Get the w element of a 4-D vector + */ +static inline float vmathV4GetW( const VmathVector4 *vec ); + +/* + * Set an x, y, z, or w element of a 4-D vector by index + */ +static inline void vmathV4SetElem( VmathVector4 *result, int idx, float value ); + +/* + * Get an x, y, z, or w element of a 4-D vector by index + */ +static inline float vmathV4GetElem( const VmathVector4 *vec, int idx ); + +/* + * Add two 4-D vectors + */ +static inline void vmathV4Add( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Subtract a 4-D vector from another 4-D vector + */ +static inline void vmathV4Sub( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Multiply a 4-D vector by a scalar + */ +static inline void vmathV4ScalarMul( VmathVector4 *result, const VmathVector4 *vec, float scalar ); + +/* + * Divide a 4-D vector by a scalar + */ +static inline void vmathV4ScalarDiv( VmathVector4 *result, const VmathVector4 *vec, float scalar ); + +/* + * Negate all elements of a 4-D vector + */ +static inline void vmathV4Neg( VmathVector4 *result, const VmathVector4 *vec ); + +/* + * Construct x axis + */ +static inline void vmathV4MakeXAxis( VmathVector4 *result ); + +/* + * Construct y axis + */ +static inline void vmathV4MakeYAxis( VmathVector4 *result ); + +/* + * Construct z axis + */ +static inline void vmathV4MakeZAxis( VmathVector4 *result ); + +/* + * Construct w axis + */ +static inline void vmathV4MakeWAxis( VmathVector4 *result ); + +/* + * Multiply two 4-D vectors per element + */ +static inline void vmathV4MulPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Divide two 4-D vectors per element + * NOTE: + * Floating-point behavior matches standard library function divf4. + */ +static inline void vmathV4DivPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Compute the reciprocal of a 4-D vector per element + * NOTE: + * Floating-point behavior matches standard library function recipf4. + */ +static inline void vmathV4RecipPerElem( VmathVector4 *result, const VmathVector4 *vec ); + +/* + * Compute the square root of a 4-D vector per element + * NOTE: + * Floating-point behavior matches standard library function sqrtf4. + */ +static inline void vmathV4SqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ); + +/* + * Compute the reciprocal square root of a 4-D vector per element + * NOTE: + * Floating-point behavior matches standard library function rsqrtf4. + */ +static inline void vmathV4RsqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ); + +/* + * Compute the absolute value of a 4-D vector per element + */ +static inline void vmathV4AbsPerElem( VmathVector4 *result, const VmathVector4 *vec ); + +/* + * Copy sign from one 4-D vector to another, per element + */ +static inline void vmathV4CopySignPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Maximum of two 4-D vectors per element + */ +static inline void vmathV4MaxPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Minimum of two 4-D vectors per element + */ +static inline void vmathV4MinPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Maximum element of a 4-D vector + */ +static inline float vmathV4MaxElem( const VmathVector4 *vec ); + +/* + * Minimum element of a 4-D vector + */ +static inline float vmathV4MinElem( const VmathVector4 *vec ); + +/* + * Compute the sum of all elements of a 4-D vector + */ +static inline float vmathV4Sum( const VmathVector4 *vec ); + +/* + * Compute the dot product of two 4-D vectors + */ +static inline float vmathV4Dot( const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Compute the square of the length of a 4-D vector + */ +static inline float vmathV4LengthSqr( const VmathVector4 *vec ); + +/* + * Compute the length of a 4-D vector + */ +static inline float vmathV4Length( const VmathVector4 *vec ); + +/* + * Normalize a 4-D vector + * NOTE: + * The result is unpredictable when all elements of vec are at or near zero. + */ +static inline void vmathV4Normalize( VmathVector4 *result, const VmathVector4 *vec ); + +/* + * Outer product of two 4-D vectors + */ +static inline void vmathV4Outer( VmathMatrix4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Linear interpolation between two 4-D vectors + * NOTE: + * Does not clamp t between 0 and 1. + */ +static inline void vmathV4Lerp( VmathVector4 *result, float t, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Spherical linear interpolation between two 4-D vectors + * NOTE: + * The result is unpredictable if the vectors point in opposite directions. + * Does not clamp t between 0 and 1. + */ +static inline void vmathV4Slerp( VmathVector4 *result, float t, const VmathVector4 *unitVec0, const VmathVector4 *unitVec1 ); + +/* + * Conditionally select between two 4-D vectors + * NOTE: + * This function uses a conditional select instruction to avoid a branch. + * However, the transfer of select1 to a VMX register may use more processing time than a branch. + */ +static inline void vmathV4Select( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1, unsigned int select1 ); + +/* + * Store four 4-D vectors as half-floats + */ +static inline void vmathV4StoreHalfFloats( const VmathVector4 *vec0, const VmathVector4 *vec1, const VmathVector4 *vec2, const VmathVector4 *vec3, vec_ushort8 *twoQuads ); + +#ifdef _VECTORMATH_DEBUG + +/* + * Print a 4-D vector + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathV4Print( const VmathVector4 *vec ); + +/* + * Print a 4-D vector and an associated string identifier + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathV4Prints( const VmathVector4 *vec, const char *name ); + +#endif + +/* + * Copy a 3-D point + */ +static inline void vmathP3Copy( VmathPoint3 *result, const VmathPoint3 *pnt ); + +/* + * Construct a 3-D point from x, y, and z elements + */ +static inline void vmathP3MakeFromElems( VmathPoint3 *result, float x, float y, float z ); + +/* + * Copy elements from a 3-D vector into a 3-D point + */ +static inline void vmathP3MakeFromV3( VmathPoint3 *result, const VmathVector3 *vec ); + +/* + * Set all elements of a 3-D point to the same scalar value + */ +static inline void vmathP3MakeFromScalar( VmathPoint3 *result, float scalar ); + +/* + * Set vector float data in a 3-D point + */ +static inline void vmathP3MakeFrom128( VmathPoint3 *result, vec_float4 vf4 ); + +/* + * Get vector float data from a 3-D point + */ +static inline vec_float4 vmathP3Get128( const VmathPoint3 *pnt ); + +/* + * Set the x element of a 3-D point + */ +static inline void vmathP3SetX( VmathPoint3 *result, float x ); + +/* + * Set the y element of a 3-D point + */ +static inline void vmathP3SetY( VmathPoint3 *result, float y ); + +/* + * Set the z element of a 3-D point + */ +static inline void vmathP3SetZ( VmathPoint3 *result, float z ); + +/* + * Get the x element of a 3-D point + */ +static inline float vmathP3GetX( const VmathPoint3 *pnt ); + +/* + * Get the y element of a 3-D point + */ +static inline float vmathP3GetY( const VmathPoint3 *pnt ); + +/* + * Get the z element of a 3-D point + */ +static inline float vmathP3GetZ( const VmathPoint3 *pnt ); + +/* + * Set an x, y, or z element of a 3-D point by index + */ +static inline void vmathP3SetElem( VmathPoint3 *result, int idx, float value ); + +/* + * Get an x, y, or z element of a 3-D point by index + */ +static inline float vmathP3GetElem( const VmathPoint3 *pnt, int idx ); + +/* + * Subtract a 3-D point from another 3-D point + */ +static inline void vmathP3Sub( VmathVector3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Add a 3-D point to a 3-D vector + */ +static inline void vmathP3AddV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec ); + +/* + * Subtract a 3-D vector from a 3-D point + */ +static inline void vmathP3SubV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec ); + +/* + * Multiply two 3-D points per element + */ +static inline void vmathP3MulPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Divide two 3-D points per element + * NOTE: + * Floating-point behavior matches standard library function divf4. + */ +static inline void vmathP3DivPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Compute the reciprocal of a 3-D point per element + * NOTE: + * Floating-point behavior matches standard library function recipf4. + */ +static inline void vmathP3RecipPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); + +/* + * Compute the square root of a 3-D point per element + * NOTE: + * Floating-point behavior matches standard library function sqrtf4. + */ +static inline void vmathP3SqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); + +/* + * Compute the reciprocal square root of a 3-D point per element + * NOTE: + * Floating-point behavior matches standard library function rsqrtf4. + */ +static inline void vmathP3RsqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); + +/* + * Compute the absolute value of a 3-D point per element + */ +static inline void vmathP3AbsPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); + +/* + * Copy sign from one 3-D point to another, per element + */ +static inline void vmathP3CopySignPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Maximum of two 3-D points per element + */ +static inline void vmathP3MaxPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Minimum of two 3-D points per element + */ +static inline void vmathP3MinPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Maximum element of a 3-D point + */ +static inline float vmathP3MaxElem( const VmathPoint3 *pnt ); + +/* + * Minimum element of a 3-D point + */ +static inline float vmathP3MinElem( const VmathPoint3 *pnt ); + +/* + * Compute the sum of all elements of a 3-D point + */ +static inline float vmathP3Sum( const VmathPoint3 *pnt ); + +/* + * Apply uniform scale to a 3-D point + */ +static inline void vmathP3Scale( VmathPoint3 *result, const VmathPoint3 *pnt, float scaleVal ); + +/* + * Apply non-uniform scale to a 3-D point + */ +static inline void vmathP3NonUniformScale( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *scaleVec ); + +/* + * Scalar projection of a 3-D point on a unit-length 3-D vector + */ +static inline float vmathP3Projection( const VmathPoint3 *pnt, const VmathVector3 *unitVec ); + +/* + * Compute the square of the distance of a 3-D point from the coordinate-system origin + */ +static inline float vmathP3DistSqrFromOrigin( const VmathPoint3 *pnt ); + +/* + * Compute the distance of a 3-D point from the coordinate-system origin + */ +static inline float vmathP3DistFromOrigin( const VmathPoint3 *pnt ); + +/* + * Compute the square of the distance between two 3-D points + */ +static inline float vmathP3DistSqr( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Compute the distance between two 3-D points + */ +static inline float vmathP3Dist( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Linear interpolation between two 3-D points + * NOTE: + * Does not clamp t between 0 and 1. + */ +static inline void vmathP3Lerp( VmathPoint3 *result, float t, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Conditionally select between two 3-D points + * NOTE: + * This function uses a conditional select instruction to avoid a branch. + * However, the transfer of select1 to a VMX register may use more processing time than a branch. + */ +static inline void vmathP3Select( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, unsigned int select1 ); + +/* + * Store x, y, and z elements of a 3-D point in the first three words of a quadword. + * The value of the fourth word (the word with the highest address) remains unchanged + */ +static inline void vmathP3StoreXYZ( const VmathPoint3 *pnt, vec_float4 *quad ); + +/* + * Load four three-float 3-D points, stored in three quadwords + */ +static inline void vmathP3LoadXYZArray( VmathPoint3 *pnt0, VmathPoint3 *pnt1, VmathPoint3 *pnt2, VmathPoint3 *pnt3, const vec_float4 *threeQuads ); + +/* + * Store four 3-D points in three quadwords + */ +static inline void vmathP3StoreXYZArray( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, vec_float4 *threeQuads ); + +/* + * Store eight 3-D points as half-floats + */ +static inline void vmathP3StoreHalfFloats( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, const VmathPoint3 *pnt4, const VmathPoint3 *pnt5, const VmathPoint3 *pnt6, const VmathPoint3 *pnt7, vec_ushort8 *threeQuads ); + +#ifdef _VECTORMATH_DEBUG + +/* + * Print a 3-D point + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathP3Print( const VmathPoint3 *pnt ); + +/* + * Print a 3-D point and an associated string identifier + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathP3Prints( const VmathPoint3 *pnt, const char *name ); + +#endif + +/* + * Copy a quaternion + */ +static inline void vmathQCopy( VmathQuat *result, const VmathQuat *quat ); + +/* + * Construct a quaternion from x, y, z, and w elements + */ +static inline void vmathQMakeFromElems( VmathQuat *result, float x, float y, float z, float w ); + +/* + * Construct a quaternion from a 3-D vector and a scalar + */ +static inline void vmathQMakeFromV3Scalar( VmathQuat *result, const VmathVector3 *xyz, float w ); + +/* + * Copy elements from a 4-D vector into a quaternion + */ +static inline void vmathQMakeFromV4( VmathQuat *result, const VmathVector4 *vec ); + +/* + * Convert a rotation matrix to a unit-length quaternion + */ +static inline void vmathQMakeFromM3( VmathQuat *result, const VmathMatrix3 *rotMat ); + +/* + * Set all elements of a quaternion to the same scalar value + */ +static inline void vmathQMakeFromScalar( VmathQuat *result, float scalar ); + +/* + * Set vector float data in a quaternion + */ +static inline void vmathQMakeFrom128( VmathQuat *result, vec_float4 vf4 ); + +/* + * Get vector float data from a quaternion + */ +static inline vec_float4 vmathQGet128( const VmathQuat *quat ); + +/* + * Set the x, y, and z elements of a quaternion + * NOTE: + * This function does not change the w element. + */ +static inline void vmathQSetXYZ( VmathQuat *result, const VmathVector3 *vec ); + +/* + * Get the x, y, and z elements of a quaternion + */ +static inline void vmathQGetXYZ( VmathVector3 *result, const VmathQuat *quat ); + +/* + * Set the x element of a quaternion + */ +static inline void vmathQSetX( VmathQuat *result, float x ); + +/* + * Set the y element of a quaternion + */ +static inline void vmathQSetY( VmathQuat *result, float y ); + +/* + * Set the z element of a quaternion + */ +static inline void vmathQSetZ( VmathQuat *result, float z ); + +/* + * Set the w element of a quaternion + */ +static inline void vmathQSetW( VmathQuat *result, float w ); + +/* + * Get the x element of a quaternion + */ +static inline float vmathQGetX( const VmathQuat *quat ); + +/* + * Get the y element of a quaternion + */ +static inline float vmathQGetY( const VmathQuat *quat ); + +/* + * Get the z element of a quaternion + */ +static inline float vmathQGetZ( const VmathQuat *quat ); + +/* + * Get the w element of a quaternion + */ +static inline float vmathQGetW( const VmathQuat *quat ); + +/* + * Set an x, y, z, or w element of a quaternion by index + */ +static inline void vmathQSetElem( VmathQuat *result, int idx, float value ); + +/* + * Get an x, y, z, or w element of a quaternion by index + */ +static inline float vmathQGetElem( const VmathQuat *quat, int idx ); + +/* + * Add two quaternions + */ +static inline void vmathQAdd( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ); + +/* + * Subtract a quaternion from another quaternion + */ +static inline void vmathQSub( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ); + +/* + * Multiply two quaternions + */ +static inline void vmathQMul( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ); + +/* + * Multiply a quaternion by a scalar + */ +static inline void vmathQScalarMul( VmathQuat *result, const VmathQuat *quat, float scalar ); + +/* + * Divide a quaternion by a scalar + */ +static inline void vmathQScalarDiv( VmathQuat *result, const VmathQuat *quat, float scalar ); + +/* + * Negate all elements of a quaternion + */ +static inline void vmathQNeg( VmathQuat *result, const VmathQuat *quat ); + +/* + * Construct an identity quaternion + */ +static inline void vmathQMakeIdentity( VmathQuat *result ); + +/* + * Construct a quaternion to rotate between two unit-length 3-D vectors + * NOTE: + * The result is unpredictable if unitVec0 and unitVec1 point in opposite directions. + */ +static inline void vmathQMakeRotationArc( VmathQuat *result, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ); + +/* + * Construct a quaternion to rotate around a unit-length 3-D vector + */ +static inline void vmathQMakeRotationAxis( VmathQuat *result, float radians, const VmathVector3 *unitVec ); + +/* + * Construct a quaternion to rotate around the x axis + */ +static inline void vmathQMakeRotationX( VmathQuat *result, float radians ); + +/* + * Construct a quaternion to rotate around the y axis + */ +static inline void vmathQMakeRotationY( VmathQuat *result, float radians ); + +/* + * Construct a quaternion to rotate around the z axis + */ +static inline void vmathQMakeRotationZ( VmathQuat *result, float radians ); + +/* + * Compute the conjugate of a quaternion + */ +static inline void vmathQConj( VmathQuat *result, const VmathQuat *quat ); + +/* + * Use a unit-length quaternion to rotate a 3-D vector + */ +static inline void vmathQRotate( VmathVector3 *result, const VmathQuat *unitQuat, const VmathVector3 *vec ); + +/* + * Compute the dot product of two quaternions + */ +static inline float vmathQDot( const VmathQuat *quat0, const VmathQuat *quat1 ); + +/* + * Compute the norm of a quaternion + */ +static inline float vmathQNorm( const VmathQuat *quat ); + +/* + * Compute the length of a quaternion + */ +static inline float vmathQLength( const VmathQuat *quat ); + +/* + * Normalize a quaternion + * NOTE: + * The result is unpredictable when all elements of quat are at or near zero. + */ +static inline void vmathQNormalize( VmathQuat *result, const VmathQuat *quat ); + +/* + * Linear interpolation between two quaternions + * NOTE: + * Does not clamp t between 0 and 1. + */ +static inline void vmathQLerp( VmathQuat *result, float t, const VmathQuat *quat0, const VmathQuat *quat1 ); + +/* + * Spherical linear interpolation between two quaternions + * NOTE: + * Interpolates along the shortest path between orientations. + * Does not clamp t between 0 and 1. + */ +static inline void vmathQSlerp( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1 ); + +/* + * Spherical quadrangle interpolation + */ +static inline void vmathQSquad( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1, const VmathQuat *unitQuat2, const VmathQuat *unitQuat3 ); + +/* + * Conditionally select between two quaternions + * NOTE: + * This function uses a conditional select instruction to avoid a branch. + * However, the transfer of select1 to a VMX register may use more processing time than a branch. + */ +static inline void vmathQSelect( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1, unsigned int select1 ); + +#ifdef _VECTORMATH_DEBUG + +/* + * Print a quaternion + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathQPrint( const VmathQuat *quat ); + +/* + * Print a quaternion and an associated string identifier + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathQPrints( const VmathQuat *quat, const char *name ); + +#endif + +/* + * Copy a 3x3 matrix + */ +static inline void vmathM3Copy( VmathMatrix3 *result, const VmathMatrix3 *mat ); + +/* + * Construct a 3x3 matrix containing the specified columns + */ +static inline void vmathM3MakeFromCols( VmathMatrix3 *result, const VmathVector3 *col0, const VmathVector3 *col1, const VmathVector3 *col2 ); + +/* + * Construct a 3x3 rotation matrix from a unit-length quaternion + */ +static inline void vmathM3MakeFromQ( VmathMatrix3 *result, const VmathQuat *unitQuat ); + +/* + * Set all elements of a 3x3 matrix to the same scalar value + */ +static inline void vmathM3MakeFromScalar( VmathMatrix3 *result, float scalar ); + +/* + * Set column 0 of a 3x3 matrix + */ +static inline void vmathM3SetCol0( VmathMatrix3 *result, const VmathVector3 *col0 ); + +/* + * Set column 1 of a 3x3 matrix + */ +static inline void vmathM3SetCol1( VmathMatrix3 *result, const VmathVector3 *col1 ); + +/* + * Set column 2 of a 3x3 matrix + */ +static inline void vmathM3SetCol2( VmathMatrix3 *result, const VmathVector3 *col2 ); + +/* + * Get column 0 of a 3x3 matrix + */ +static inline void vmathM3GetCol0( VmathVector3 *result, const VmathMatrix3 *mat ); + +/* + * Get column 1 of a 3x3 matrix + */ +static inline void vmathM3GetCol1( VmathVector3 *result, const VmathMatrix3 *mat ); + +/* + * Get column 2 of a 3x3 matrix + */ +static inline void vmathM3GetCol2( VmathVector3 *result, const VmathMatrix3 *mat ); + +/* + * Set the column of a 3x3 matrix referred to by the specified index + */ +static inline void vmathM3SetCol( VmathMatrix3 *result, int col, const VmathVector3 *vec ); + +/* + * Set the row of a 3x3 matrix referred to by the specified index + */ +static inline void vmathM3SetRow( VmathMatrix3 *result, int row, const VmathVector3 *vec ); + +/* + * Get the column of a 3x3 matrix referred to by the specified index + */ +static inline void vmathM3GetCol( VmathVector3 *result, const VmathMatrix3 *mat, int col ); + +/* + * Get the row of a 3x3 matrix referred to by the specified index + */ +static inline void vmathM3GetRow( VmathVector3 *result, const VmathMatrix3 *mat, int row ); + +/* + * Set the element of a 3x3 matrix referred to by column and row indices + */ +static inline void vmathM3SetElem( VmathMatrix3 *result, int col, int row, float val ); + +/* + * Get the element of a 3x3 matrix referred to by column and row indices + */ +static inline float vmathM3GetElem( const VmathMatrix3 *mat, int col, int row ); + +/* + * Add two 3x3 matrices + */ +static inline void vmathM3Add( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); + +/* + * Subtract a 3x3 matrix from another 3x3 matrix + */ +static inline void vmathM3Sub( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); + +/* + * Negate all elements of a 3x3 matrix + */ +static inline void vmathM3Neg( VmathMatrix3 *result, const VmathMatrix3 *mat ); + +/* + * Multiply a 3x3 matrix by a scalar + */ +static inline void vmathM3ScalarMul( VmathMatrix3 *result, const VmathMatrix3 *mat, float scalar ); + +/* + * Multiply a 3x3 matrix by a 3-D vector + */ +static inline void vmathM3MulV3( VmathVector3 *result, const VmathMatrix3 *mat, const VmathVector3 *vec ); + +/* + * Multiply two 3x3 matrices + */ +static inline void vmathM3Mul( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); + +/* + * Construct an identity 3x3 matrix + */ +static inline void vmathM3MakeIdentity( VmathMatrix3 *result ); + +/* + * Construct a 3x3 matrix to rotate around the x axis + */ +static inline void vmathM3MakeRotationX( VmathMatrix3 *result, float radians ); + +/* + * Construct a 3x3 matrix to rotate around the y axis + */ +static inline void vmathM3MakeRotationY( VmathMatrix3 *result, float radians ); + +/* + * Construct a 3x3 matrix to rotate around the z axis + */ +static inline void vmathM3MakeRotationZ( VmathMatrix3 *result, float radians ); + +/* + * Construct a 3x3 matrix to rotate around the x, y, and z axes + */ +static inline void vmathM3MakeRotationZYX( VmathMatrix3 *result, const VmathVector3 *radiansXYZ ); + +/* + * Construct a 3x3 matrix to rotate around a unit-length 3-D vector + */ +static inline void vmathM3MakeRotationAxis( VmathMatrix3 *result, float radians, const VmathVector3 *unitVec ); + +/* + * Construct a rotation matrix from a unit-length quaternion + */ +static inline void vmathM3MakeRotationQ( VmathMatrix3 *result, const VmathQuat *unitQuat ); + +/* + * Construct a 3x3 matrix to perform scaling + */ +static inline void vmathM3MakeScale( VmathMatrix3 *result, const VmathVector3 *scaleVec ); + +/* + * Append (post-multiply) a scale transformation to a 3x3 matrix + * NOTE: + * Faster than creating and multiplying a scale transformation matrix. + */ +static inline void vmathM3AppendScale( VmathMatrix3 *result, const VmathMatrix3 *mat, const VmathVector3 *scaleVec ); + +/* + * Prepend (pre-multiply) a scale transformation to a 3x3 matrix + * NOTE: + * Faster than creating and multiplying a scale transformation matrix. + */ +static inline void vmathM3PrependScale( VmathMatrix3 *result, const VmathVector3 *scaleVec, const VmathMatrix3 *mat ); + +/* + * Multiply two 3x3 matrices per element + */ +static inline void vmathM3MulPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); + +/* + * Compute the absolute value of a 3x3 matrix per element + */ +static inline void vmathM3AbsPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat ); + +/* + * Transpose of a 3x3 matrix + */ +static inline void vmathM3Transpose( VmathMatrix3 *result, const VmathMatrix3 *mat ); + +/* + * Compute the inverse of a 3x3 matrix + * NOTE: + * Result is unpredictable when the determinant of mat is equal to or near 0. + */ +static inline void vmathM3Inverse( VmathMatrix3 *result, const VmathMatrix3 *mat ); + +/* + * Determinant of a 3x3 matrix + */ +static inline float vmathM3Determinant( const VmathMatrix3 *mat ); + +/* + * Conditionally select between two 3x3 matrices + * NOTE: + * This function uses a conditional select instruction to avoid a branch. + * However, the transfer of select1 to a VMX register may use more processing time than a branch. + */ +static inline void vmathM3Select( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1, unsigned int select1 ); + +#ifdef _VECTORMATH_DEBUG + +/* + * Print a 3x3 matrix + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathM3Print( const VmathMatrix3 *mat ); + +/* + * Print a 3x3 matrix and an associated string identifier + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathM3Prints( const VmathMatrix3 *mat, const char *name ); + +#endif + +/* + * Copy a 4x4 matrix + */ +static inline void vmathM4Copy( VmathMatrix4 *result, const VmathMatrix4 *mat ); + +/* + * Construct a 4x4 matrix containing the specified columns + */ +static inline void vmathM4MakeFromCols( VmathMatrix4 *result, const VmathVector4 *col0, const VmathVector4 *col1, const VmathVector4 *col2, const VmathVector4 *col3 ); + +/* + * Construct a 4x4 matrix from a 3x4 transformation matrix + */ +static inline void vmathM4MakeFromT3( VmathMatrix4 *result, const VmathTransform3 *mat ); + +/* + * Construct a 4x4 matrix from a 3x3 matrix and a 3-D vector + */ +static inline void vmathM4MakeFromM3V3( VmathMatrix4 *result, const VmathMatrix3 *mat, const VmathVector3 *translateVec ); + +/* + * Construct a 4x4 matrix from a unit-length quaternion and a 3-D vector + */ +static inline void vmathM4MakeFromQV3( VmathMatrix4 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ); + +/* + * Set all elements of a 4x4 matrix to the same scalar value + */ +static inline void vmathM4MakeFromScalar( VmathMatrix4 *result, float scalar ); + +/* + * Set the upper-left 3x3 submatrix + * NOTE: + * This function does not change the bottom row elements. + */ +static inline void vmathM4SetUpper3x3( VmathMatrix4 *result, const VmathMatrix3 *mat3 ); + +/* + * Get the upper-left 3x3 submatrix of a 4x4 matrix + */ +static inline void vmathM4GetUpper3x3( VmathMatrix3 *result, const VmathMatrix4 *mat ); + +/* + * Set translation component + * NOTE: + * This function does not change the bottom row elements. + */ +static inline void vmathM4SetTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ); + +/* + * Get the translation component of a 4x4 matrix + */ +static inline void vmathM4GetTranslation( VmathVector3 *result, const VmathMatrix4 *mat ); + +/* + * Set column 0 of a 4x4 matrix + */ +static inline void vmathM4SetCol0( VmathMatrix4 *result, const VmathVector4 *col0 ); + +/* + * Set column 1 of a 4x4 matrix + */ +static inline void vmathM4SetCol1( VmathMatrix4 *result, const VmathVector4 *col1 ); + +/* + * Set column 2 of a 4x4 matrix + */ +static inline void vmathM4SetCol2( VmathMatrix4 *result, const VmathVector4 *col2 ); + +/* + * Set column 3 of a 4x4 matrix + */ +static inline void vmathM4SetCol3( VmathMatrix4 *result, const VmathVector4 *col3 ); + +/* + * Get column 0 of a 4x4 matrix + */ +static inline void vmathM4GetCol0( VmathVector4 *result, const VmathMatrix4 *mat ); + +/* + * Get column 1 of a 4x4 matrix + */ +static inline void vmathM4GetCol1( VmathVector4 *result, const VmathMatrix4 *mat ); + +/* + * Get column 2 of a 4x4 matrix + */ +static inline void vmathM4GetCol2( VmathVector4 *result, const VmathMatrix4 *mat ); + +/* + * Get column 3 of a 4x4 matrix + */ +static inline void vmathM4GetCol3( VmathVector4 *result, const VmathMatrix4 *mat ); + +/* + * Set the column of a 4x4 matrix referred to by the specified index + */ +static inline void vmathM4SetCol( VmathMatrix4 *result, int col, const VmathVector4 *vec ); + +/* + * Set the row of a 4x4 matrix referred to by the specified index + */ +static inline void vmathM4SetRow( VmathMatrix4 *result, int row, const VmathVector4 *vec ); + +/* + * Get the column of a 4x4 matrix referred to by the specified index + */ +static inline void vmathM4GetCol( VmathVector4 *result, const VmathMatrix4 *mat, int col ); + +/* + * Get the row of a 4x4 matrix referred to by the specified index + */ +static inline void vmathM4GetRow( VmathVector4 *result, const VmathMatrix4 *mat, int row ); + +/* + * Set the element of a 4x4 matrix referred to by column and row indices + */ +static inline void vmathM4SetElem( VmathMatrix4 *result, int col, int row, float val ); + +/* + * Get the element of a 4x4 matrix referred to by column and row indices + */ +static inline float vmathM4GetElem( const VmathMatrix4 *mat, int col, int row ); + +/* + * Add two 4x4 matrices + */ +static inline void vmathM4Add( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); + +/* + * Subtract a 4x4 matrix from another 4x4 matrix + */ +static inline void vmathM4Sub( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); + +/* + * Negate all elements of a 4x4 matrix + */ +static inline void vmathM4Neg( VmathMatrix4 *result, const VmathMatrix4 *mat ); + +/* + * Multiply a 4x4 matrix by a scalar + */ +static inline void vmathM4ScalarMul( VmathMatrix4 *result, const VmathMatrix4 *mat, float scalar ); + +/* + * Multiply a 4x4 matrix by a 4-D vector + */ +static inline void vmathM4MulV4( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector4 *vec ); + +/* + * Multiply a 4x4 matrix by a 3-D vector + */ +static inline void vmathM4MulV3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector3 *vec ); + +/* + * Multiply a 4x4 matrix by a 3-D point + */ +static inline void vmathM4MulP3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathPoint3 *pnt ); + +/* + * Multiply two 4x4 matrices + */ +static inline void vmathM4Mul( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); + +/* + * Multiply a 4x4 matrix by a 3x4 transformation matrix + */ +static inline void vmathM4MulT3( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathTransform3 *tfrm ); + +/* + * Construct an identity 4x4 matrix + */ +static inline void vmathM4MakeIdentity( VmathMatrix4 *result ); + +/* + * Construct a 4x4 matrix to rotate around the x axis + */ +static inline void vmathM4MakeRotationX( VmathMatrix4 *result, float radians ); + +/* + * Construct a 4x4 matrix to rotate around the y axis + */ +static inline void vmathM4MakeRotationY( VmathMatrix4 *result, float radians ); + +/* + * Construct a 4x4 matrix to rotate around the z axis + */ +static inline void vmathM4MakeRotationZ( VmathMatrix4 *result, float radians ); + +/* + * Construct a 4x4 matrix to rotate around the x, y, and z axes + */ +static inline void vmathM4MakeRotationZYX( VmathMatrix4 *result, const VmathVector3 *radiansXYZ ); + +/* + * Construct a 4x4 matrix to rotate around a unit-length 3-D vector + */ +static inline void vmathM4MakeRotationAxis( VmathMatrix4 *result, float radians, const VmathVector3 *unitVec ); + +/* + * Construct a rotation matrix from a unit-length quaternion + */ +static inline void vmathM4MakeRotationQ( VmathMatrix4 *result, const VmathQuat *unitQuat ); + +/* + * Construct a 4x4 matrix to perform scaling + */ +static inline void vmathM4MakeScale( VmathMatrix4 *result, const VmathVector3 *scaleVec ); + +/* + * Construct a 4x4 matrix to perform translation + */ +static inline void vmathM4MakeTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ); + +/* + * Construct viewing matrix based on eye position, position looked at, and up direction + */ +static inline void vmathM4MakeLookAt( VmathMatrix4 *result, const VmathPoint3 *eyePos, const VmathPoint3 *lookAtPos, const VmathVector3 *upVec ); + +/* + * Construct a perspective projection matrix + */ +static inline void vmathM4MakePerspective( VmathMatrix4 *result, float fovyRadians, float aspect, float zNear, float zFar ); + +/* + * Construct a perspective projection matrix based on frustum + */ +static inline void vmathM4MakeFrustum( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ); + +/* + * Construct an orthographic projection matrix + */ +static inline void vmathM4MakeOrthographic( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ); + +/* + * Append (post-multiply) a scale transformation to a 4x4 matrix + * NOTE: + * Faster than creating and multiplying a scale transformation matrix. + */ +static inline void vmathM4AppendScale( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathVector3 *scaleVec ); + +/* + * Prepend (pre-multiply) a scale transformation to a 4x4 matrix + * NOTE: + * Faster than creating and multiplying a scale transformation matrix. + */ +static inline void vmathM4PrependScale( VmathMatrix4 *result, const VmathVector3 *scaleVec, const VmathMatrix4 *mat ); + +/* + * Multiply two 4x4 matrices per element + */ +static inline void vmathM4MulPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); + +/* + * Compute the absolute value of a 4x4 matrix per element + */ +static inline void vmathM4AbsPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat ); + +/* + * Transpose of a 4x4 matrix + */ +static inline void vmathM4Transpose( VmathMatrix4 *result, const VmathMatrix4 *mat ); + +/* + * Compute the inverse of a 4x4 matrix + * NOTE: + * Result is unpredictable when the determinant of mat is equal to or near 0. + */ +static inline void vmathM4Inverse( VmathMatrix4 *result, const VmathMatrix4 *mat ); + +/* + * Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix + * NOTE: + * This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. The result is unpredictable when the determinant of mat is equal to or near 0. + */ +static inline void vmathM4AffineInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ); + +/* + * Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix with an orthogonal upper-left 3x3 submatrix + * NOTE: + * This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. + */ +static inline void vmathM4OrthoInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ); + +/* + * Determinant of a 4x4 matrix + */ +static inline float vmathM4Determinant( const VmathMatrix4 *mat ); + +/* + * Conditionally select between two 4x4 matrices + * NOTE: + * This function uses a conditional select instruction to avoid a branch. + * However, the transfer of select1 to a VMX register may use more processing time than a branch. + */ +static inline void vmathM4Select( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1, unsigned int select1 ); + +#ifdef _VECTORMATH_DEBUG + +/* + * Print a 4x4 matrix + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathM4Print( const VmathMatrix4 *mat ); + +/* + * Print a 4x4 matrix and an associated string identifier + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathM4Prints( const VmathMatrix4 *mat, const char *name ); + +#endif + +/* + * Copy a 3x4 transformation matrix + */ +static inline void vmathT3Copy( VmathTransform3 *result, const VmathTransform3 *tfrm ); + +/* + * Construct a 3x4 transformation matrix containing the specified columns + */ +static inline void vmathT3MakeFromCols( VmathTransform3 *result, const VmathVector3 *col0, const VmathVector3 *col1, const VmathVector3 *col2, const VmathVector3 *col3 ); + +/* + * Construct a 3x4 transformation matrix from a 3x3 matrix and a 3-D vector + */ +static inline void vmathT3MakeFromM3V3( VmathTransform3 *result, const VmathMatrix3 *tfrm, const VmathVector3 *translateVec ); + +/* + * Construct a 3x4 transformation matrix from a unit-length quaternion and a 3-D vector + */ +static inline void vmathT3MakeFromQV3( VmathTransform3 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ); + +/* + * Set all elements of a 3x4 transformation matrix to the same scalar value + */ +static inline void vmathT3MakeFromScalar( VmathTransform3 *result, float scalar ); + +/* + * Set the upper-left 3x3 submatrix + */ +static inline void vmathT3SetUpper3x3( VmathTransform3 *result, const VmathMatrix3 *mat3 ); + +/* + * Get the upper-left 3x3 submatrix of a 3x4 transformation matrix + */ +static inline void vmathT3GetUpper3x3( VmathMatrix3 *result, const VmathTransform3 *tfrm ); + +/* + * Set translation component + */ +static inline void vmathT3SetTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ); + +/* + * Get the translation component of a 3x4 transformation matrix + */ +static inline void vmathT3GetTranslation( VmathVector3 *result, const VmathTransform3 *tfrm ); + +/* + * Set column 0 of a 3x4 transformation matrix + */ +static inline void vmathT3SetCol0( VmathTransform3 *result, const VmathVector3 *col0 ); + +/* + * Set column 1 of a 3x4 transformation matrix + */ +static inline void vmathT3SetCol1( VmathTransform3 *result, const VmathVector3 *col1 ); + +/* + * Set column 2 of a 3x4 transformation matrix + */ +static inline void vmathT3SetCol2( VmathTransform3 *result, const VmathVector3 *col2 ); + +/* + * Set column 3 of a 3x4 transformation matrix + */ +static inline void vmathT3SetCol3( VmathTransform3 *result, const VmathVector3 *col3 ); + +/* + * Get column 0 of a 3x4 transformation matrix + */ +static inline void vmathT3GetCol0( VmathVector3 *result, const VmathTransform3 *tfrm ); + +/* + * Get column 1 of a 3x4 transformation matrix + */ +static inline void vmathT3GetCol1( VmathVector3 *result, const VmathTransform3 *tfrm ); + +/* + * Get column 2 of a 3x4 transformation matrix + */ +static inline void vmathT3GetCol2( VmathVector3 *result, const VmathTransform3 *tfrm ); + +/* + * Get column 3 of a 3x4 transformation matrix + */ +static inline void vmathT3GetCol3( VmathVector3 *result, const VmathTransform3 *tfrm ); + +/* + * Set the column of a 3x4 transformation matrix referred to by the specified index + */ +static inline void vmathT3SetCol( VmathTransform3 *result, int col, const VmathVector3 *vec ); + +/* + * Set the row of a 3x4 transformation matrix referred to by the specified index + */ +static inline void vmathT3SetRow( VmathTransform3 *result, int row, const VmathVector4 *vec ); + +/* + * Get the column of a 3x4 transformation matrix referred to by the specified index + */ +static inline void vmathT3GetCol( VmathVector3 *result, const VmathTransform3 *tfrm, int col ); + +/* + * Get the row of a 3x4 transformation matrix referred to by the specified index + */ +static inline void vmathT3GetRow( VmathVector4 *result, const VmathTransform3 *tfrm, int row ); + +/* + * Set the element of a 3x4 transformation matrix referred to by column and row indices + */ +static inline void vmathT3SetElem( VmathTransform3 *result, int col, int row, float val ); + +/* + * Get the element of a 3x4 transformation matrix referred to by column and row indices + */ +static inline float vmathT3GetElem( const VmathTransform3 *tfrm, int col, int row ); + +/* + * Multiply a 3x4 transformation matrix by a 3-D vector + */ +static inline void vmathT3MulV3( VmathVector3 *result, const VmathTransform3 *tfrm, const VmathVector3 *vec ); + +/* + * Multiply a 3x4 transformation matrix by a 3-D point + */ +static inline void vmathT3MulP3( VmathPoint3 *result, const VmathTransform3 *tfrm, const VmathPoint3 *pnt ); + +/* + * Multiply two 3x4 transformation matrices + */ +static inline void vmathT3Mul( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ); + +/* + * Construct an identity 3x4 transformation matrix + */ +static inline void vmathT3MakeIdentity( VmathTransform3 *result ); + +/* + * Construct a 3x4 transformation matrix to rotate around the x axis + */ +static inline void vmathT3MakeRotationX( VmathTransform3 *result, float radians ); + +/* + * Construct a 3x4 transformation matrix to rotate around the y axis + */ +static inline void vmathT3MakeRotationY( VmathTransform3 *result, float radians ); + +/* + * Construct a 3x4 transformation matrix to rotate around the z axis + */ +static inline void vmathT3MakeRotationZ( VmathTransform3 *result, float radians ); + +/* + * Construct a 3x4 transformation matrix to rotate around the x, y, and z axes + */ +static inline void vmathT3MakeRotationZYX( VmathTransform3 *result, const VmathVector3 *radiansXYZ ); + +/* + * Construct a 3x4 transformation matrix to rotate around a unit-length 3-D vector + */ +static inline void vmathT3MakeRotationAxis( VmathTransform3 *result, float radians, const VmathVector3 *unitVec ); + +/* + * Construct a rotation matrix from a unit-length quaternion + */ +static inline void vmathT3MakeRotationQ( VmathTransform3 *result, const VmathQuat *unitQuat ); + +/* + * Construct a 3x4 transformation matrix to perform scaling + */ +static inline void vmathT3MakeScale( VmathTransform3 *result, const VmathVector3 *scaleVec ); + +/* + * Construct a 3x4 transformation matrix to perform translation + */ +static inline void vmathT3MakeTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ); + +/* + * Append (post-multiply) a scale transformation to a 3x4 transformation matrix + * NOTE: + * Faster than creating and multiplying a scale transformation matrix. + */ +static inline void vmathT3AppendScale( VmathTransform3 *result, const VmathTransform3 *tfrm, const VmathVector3 *scaleVec ); + +/* + * Prepend (pre-multiply) a scale transformation to a 3x4 transformation matrix + * NOTE: + * Faster than creating and multiplying a scale transformation matrix. + */ +static inline void vmathT3PrependScale( VmathTransform3 *result, const VmathVector3 *scaleVec, const VmathTransform3 *tfrm ); + +/* + * Multiply two 3x4 transformation matrices per element + */ +static inline void vmathT3MulPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ); + +/* + * Compute the absolute value of a 3x4 transformation matrix per element + */ +static inline void vmathT3AbsPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm ); + +/* + * Inverse of a 3x4 transformation matrix + * NOTE: + * Result is unpredictable when the determinant of the left 3x3 submatrix is equal to or near 0. + */ +static inline void vmathT3Inverse( VmathTransform3 *result, const VmathTransform3 *tfrm ); + +/* + * Compute the inverse of a 3x4 transformation matrix, expected to have an orthogonal upper-left 3x3 submatrix + * NOTE: + * This can be used to achieve better performance than a general inverse when the specified 3x4 transformation matrix meets the given restrictions. + */ +static inline void vmathT3OrthoInverse( VmathTransform3 *result, const VmathTransform3 *tfrm ); + +/* + * Conditionally select between two 3x4 transformation matrices + * NOTE: + * This function uses a conditional select instruction to avoid a branch. + * However, the transfer of select1 to a VMX register may use more processing time than a branch. + */ +static inline void vmathT3Select( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1, unsigned int select1 ); + +#ifdef _VECTORMATH_DEBUG + +/* + * Print a 3x4 transformation matrix + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathT3Print( const VmathTransform3 *tfrm ); + +/* + * Print a 3x4 transformation matrix and an associated string identifier + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathT3Prints( const VmathTransform3 *tfrm, const char *name ); + +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#include "vec_aos.h" +#include "quat_aos.h" +#include "mat_aos.h" + +#endif diff --git a/common/vectormath/ppu/cpp/boolInVec.h b/common/vectormath/ppu/cpp/boolInVec.h index 8d116af2..d5882ec5 100644 --- a/common/vectormath/ppu/cpp/boolInVec.h +++ b/common/vectormath/ppu/cpp/boolInVec.h @@ -1,261 +1,261 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _BOOLINVEC_H -#define _BOOLINVEC_H - -#include -#include -#include -#undef bool - -namespace Vectormath { - -class floatInVec; - -//-------------------------------------------------------------------------------------------------- -// boolInVec class -// - -class boolInVec -{ - private: - vec_uint4 mData; - - inline boolInVec(vec_uint4 vec); - public: - inline boolInVec() {} - - // matches standard type conversions - // - inline boolInVec(floatInVec vec); - - // explicit cast from bool - // - explicit inline boolInVec(bool scalar); - -#ifdef _VECTORMATH_NO_SCALAR_CAST - // explicit cast to bool - // - inline bool getAsBool() const; -#else - // implicit cast to bool - // - inline operator bool() const; -#endif - - // get vector data - // bool value is splatted across all word slots of vector as 0 (false) or -1 (true) - // - inline vec_uint4 get128() const; - - // operators - // - inline const boolInVec operator ! () const; - inline boolInVec& operator = (boolInVec vec); - inline boolInVec& operator &= (boolInVec vec); - inline boolInVec& operator ^= (boolInVec vec); - inline boolInVec& operator |= (boolInVec vec); - - // friend functions - // - friend inline const boolInVec operator == (boolInVec vec0, boolInVec vec1); - friend inline const boolInVec operator != (boolInVec vec0, boolInVec vec1); - friend inline const boolInVec operator < (floatInVec vec0, floatInVec vec1); - friend inline const boolInVec operator <= (floatInVec vec0, floatInVec vec1); - friend inline const boolInVec operator > (floatInVec vec0, floatInVec vec1); - friend inline const boolInVec operator >= (floatInVec vec0, floatInVec vec1); - friend inline const boolInVec operator == (floatInVec vec0, floatInVec vec1); - friend inline const boolInVec operator != (floatInVec vec0, floatInVec vec1); - friend inline const boolInVec operator & (boolInVec vec0, boolInVec vec1); - friend inline const boolInVec operator ^ (boolInVec vec0, boolInVec vec1); - friend inline const boolInVec operator | (boolInVec vec0, boolInVec vec1); - friend inline const boolInVec select(boolInVec vec0, boolInVec vec1, boolInVec select_vec1); -}; - -//-------------------------------------------------------------------------------------------------- -// boolInVec functions -// - -// operators -// -inline const boolInVec operator == (boolInVec vec0, boolInVec vec1); -inline const boolInVec operator != (boolInVec vec0, boolInVec vec1); -inline const boolInVec operator & (boolInVec vec0, boolInVec vec1); -inline const boolInVec operator ^ (boolInVec vec0, boolInVec vec1); -inline const boolInVec operator | (boolInVec vec0, boolInVec vec1); - -// select between vec0 and vec1 using boolInVec. -// false selects vec0, true selects vec1 -// -inline const boolInVec select(boolInVec vec0, boolInVec vec1, boolInVec select_vec1); - -} // namespace Vectormath - -//-------------------------------------------------------------------------------------------------- -// boolInVec implementation -// - -#include "floatInVec.h" - -namespace Vectormath { - -inline -boolInVec::boolInVec(vec_uint4 vec) -{ - mData = vec; -} - -inline -boolInVec::boolInVec(floatInVec vec) -{ - *this = (vec != floatInVec(0.0f)); -} - -inline -boolInVec::boolInVec(bool scalar) -{ -#ifdef __GNUC__ - if (__builtin_constant_p(scalar)) - { - const unsigned int mask = -(int)scalar; - mData = (vec_uint4){mask, mask, mask, mask}; - } - else -#endif - { - unsigned int mask = -(int)scalar; - vec_uint4 vec = vec_ld(0, &mask); - mData = vec_splat(vec_perm(vec, vec, vec_lvsl(0, &mask)), 0); - } -} - -#ifdef _VECTORMATH_NO_SCALAR_CAST -inline -bool -boolInVec::getAsBool() const -#else -inline -boolInVec::operator bool() const -#endif -{ - return vec_all_gt(mData, ((vec_uint4){0,0,0,0})); -} - -inline -vec_uint4 -boolInVec::get128() const -{ - return mData; -} - -inline -const boolInVec -boolInVec::operator ! () const -{ - return boolInVec(vec_nor(mData, mData)); -} - -inline -boolInVec& -boolInVec::operator = (boolInVec vec) -{ - mData = vec.mData; - return *this; -} - -inline -boolInVec& -boolInVec::operator &= (boolInVec vec) -{ - *this = *this & vec; - return *this; -} - -inline -boolInVec& -boolInVec::operator ^= (boolInVec vec) -{ - *this = *this ^ vec; - return *this; -} - -inline -boolInVec& -boolInVec::operator |= (boolInVec vec) -{ - *this = *this | vec; - return *this; -} - -inline -const boolInVec -operator == (boolInVec vec0, boolInVec vec1) -{ - return boolInVec((vec_uint4)vec_cmpeq(vec0.get128(), vec1.get128())); -} - -inline -const boolInVec -operator != (boolInVec vec0, boolInVec vec1) -{ - return !(vec0 == vec1); -} - -inline -const boolInVec -operator & (boolInVec vec0, boolInVec vec1) -{ - return boolInVec(vec_and(vec0.get128(), vec1.get128())); -} - -inline -const boolInVec -operator | (boolInVec vec0, boolInVec vec1) -{ - return boolInVec(vec_or(vec0.get128(), vec1.get128())); -} - -inline -const boolInVec -operator ^ (boolInVec vec0, boolInVec vec1) -{ - return boolInVec(vec_xor(vec0.get128(), vec1.get128())); -} - -inline -const boolInVec -select(boolInVec vec0, boolInVec vec1, boolInVec select_vec1) -{ - return boolInVec(vec_sel(vec0.get128(), vec1.get128(), select_vec1.get128())); -} - -} // namespace Vectormath - -#endif // boolInVec_h +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _BOOLINVEC_H +#define _BOOLINVEC_H + +#include +#include +#include +#undef bool + +namespace Vectormath { + +class floatInVec; + +//-------------------------------------------------------------------------------------------------- +// boolInVec class +// + +class boolInVec +{ + private: + vec_uint4 mData; + + inline boolInVec(vec_uint4 vec); + public: + inline boolInVec() {} + + // matches standard type conversions + // + inline boolInVec(floatInVec vec); + + // explicit cast from bool + // + explicit inline boolInVec(bool scalar); + +#ifdef _VECTORMATH_NO_SCALAR_CAST + // explicit cast to bool + // + inline bool getAsBool() const; +#else + // implicit cast to bool + // + inline operator bool() const; +#endif + + // get vector data + // bool value is splatted across all word slots of vector as 0 (false) or -1 (true) + // + inline vec_uint4 get128() const; + + // operators + // + inline const boolInVec operator ! () const; + inline boolInVec& operator = (boolInVec vec); + inline boolInVec& operator &= (boolInVec vec); + inline boolInVec& operator ^= (boolInVec vec); + inline boolInVec& operator |= (boolInVec vec); + + // friend functions + // + friend inline const boolInVec operator == (boolInVec vec0, boolInVec vec1); + friend inline const boolInVec operator != (boolInVec vec0, boolInVec vec1); + friend inline const boolInVec operator < (floatInVec vec0, floatInVec vec1); + friend inline const boolInVec operator <= (floatInVec vec0, floatInVec vec1); + friend inline const boolInVec operator > (floatInVec vec0, floatInVec vec1); + friend inline const boolInVec operator >= (floatInVec vec0, floatInVec vec1); + friend inline const boolInVec operator == (floatInVec vec0, floatInVec vec1); + friend inline const boolInVec operator != (floatInVec vec0, floatInVec vec1); + friend inline const boolInVec operator & (boolInVec vec0, boolInVec vec1); + friend inline const boolInVec operator ^ (boolInVec vec0, boolInVec vec1); + friend inline const boolInVec operator | (boolInVec vec0, boolInVec vec1); + friend inline const boolInVec select(boolInVec vec0, boolInVec vec1, boolInVec select_vec1); +}; + +//-------------------------------------------------------------------------------------------------- +// boolInVec functions +// + +// operators +// +inline const boolInVec operator == (boolInVec vec0, boolInVec vec1); +inline const boolInVec operator != (boolInVec vec0, boolInVec vec1); +inline const boolInVec operator & (boolInVec vec0, boolInVec vec1); +inline const boolInVec operator ^ (boolInVec vec0, boolInVec vec1); +inline const boolInVec operator | (boolInVec vec0, boolInVec vec1); + +// select between vec0 and vec1 using boolInVec. +// false selects vec0, true selects vec1 +// +inline const boolInVec select(boolInVec vec0, boolInVec vec1, boolInVec select_vec1); + +} // namespace Vectormath + +//-------------------------------------------------------------------------------------------------- +// boolInVec implementation +// + +#include "floatInVec.h" + +namespace Vectormath { + +inline +boolInVec::boolInVec(vec_uint4 vec) +{ + mData = vec; +} + +inline +boolInVec::boolInVec(floatInVec vec) +{ + *this = (vec != floatInVec(0.0f)); +} + +inline +boolInVec::boolInVec(bool scalar) +{ +#ifdef __GNUC__ + if (__builtin_constant_p(scalar)) + { + const unsigned int mask = -(int)scalar; + mData = (vec_uint4){mask, mask, mask, mask}; + } + else +#endif + { + unsigned int mask = -(int)scalar; + vec_uint4 vec = vec_ld(0, &mask); + mData = vec_splat(vec_perm(vec, vec, vec_lvsl(0, &mask)), 0); + } +} + +#ifdef _VECTORMATH_NO_SCALAR_CAST +inline +bool +boolInVec::getAsBool() const +#else +inline +boolInVec::operator bool() const +#endif +{ + return vec_all_gt(mData, ((vec_uint4){0,0,0,0})); +} + +inline +vec_uint4 +boolInVec::get128() const +{ + return mData; +} + +inline +const boolInVec +boolInVec::operator ! () const +{ + return boolInVec(vec_nor(mData, mData)); +} + +inline +boolInVec& +boolInVec::operator = (boolInVec vec) +{ + mData = vec.mData; + return *this; +} + +inline +boolInVec& +boolInVec::operator &= (boolInVec vec) +{ + *this = *this & vec; + return *this; +} + +inline +boolInVec& +boolInVec::operator ^= (boolInVec vec) +{ + *this = *this ^ vec; + return *this; +} + +inline +boolInVec& +boolInVec::operator |= (boolInVec vec) +{ + *this = *this | vec; + return *this; +} + +inline +const boolInVec +operator == (boolInVec vec0, boolInVec vec1) +{ + return boolInVec((vec_uint4)vec_cmpeq(vec0.get128(), vec1.get128())); +} + +inline +const boolInVec +operator != (boolInVec vec0, boolInVec vec1) +{ + return !(vec0 == vec1); +} + +inline +const boolInVec +operator & (boolInVec vec0, boolInVec vec1) +{ + return boolInVec(vec_and(vec0.get128(), vec1.get128())); +} + +inline +const boolInVec +operator | (boolInVec vec0, boolInVec vec1) +{ + return boolInVec(vec_or(vec0.get128(), vec1.get128())); +} + +inline +const boolInVec +operator ^ (boolInVec vec0, boolInVec vec1) +{ + return boolInVec(vec_xor(vec0.get128(), vec1.get128())); +} + +inline +const boolInVec +select(boolInVec vec0, boolInVec vec1, boolInVec select_vec1) +{ + return boolInVec(vec_sel(vec0.get128(), vec1.get128(), select_vec1.get128())); +} + +} // namespace Vectormath + +#endif // boolInVec_h diff --git a/common/vectormath/ppu/cpp/floatInVec.h b/common/vectormath/ppu/cpp/floatInVec.h index 369ec2ec..31415b21 100644 --- a/common/vectormath/ppu/cpp/floatInVec.h +++ b/common/vectormath/ppu/cpp/floatInVec.h @@ -1,361 +1,361 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _FLOATINVEC_H -#define _FLOATINVEC_H - -#include -#include -#include -#include -#include -#undef bool - -namespace Vectormath { - -class boolInVec; - -//-------------------------------------------------------------------------------------------------- -// floatInVec class -// - -class floatInVec -{ - private: - vec_float4 mData; - - inline floatInVec(vec_float4 vec); - public: - inline floatInVec() {} - - // matches standard type conversions - // - inline floatInVec(boolInVec vec); - - // construct from a slot of vec_float4 - // - inline floatInVec(vec_float4 vec, int slot); - - // explicit cast from float - // - explicit inline floatInVec(float scalar); - -#ifdef _VECTORMATH_NO_SCALAR_CAST - // explicit cast to float - // - inline float getAsFloat() const; -#else - // implicit cast to float - // - inline operator float() const; -#endif - - // get vector data - // float value is splatted across all word slots of vector - // - inline vec_float4 get128() const; - - // operators - // - inline const floatInVec operator ++ (int); - inline const floatInVec operator -- (int); - inline floatInVec& operator ++ (); - inline floatInVec& operator -- (); - inline const floatInVec operator - () const; - inline floatInVec& operator = (floatInVec vec); - inline floatInVec& operator *= (floatInVec vec); - inline floatInVec& operator /= (floatInVec vec); - inline floatInVec& operator += (floatInVec vec); - inline floatInVec& operator -= (floatInVec vec); - - // friend functions - // - friend inline const floatInVec operator * (floatInVec vec0, floatInVec vec1); - friend inline const floatInVec operator / (floatInVec vec0, floatInVec vec1); - friend inline const floatInVec operator + (floatInVec vec0, floatInVec vec1); - friend inline const floatInVec operator - (floatInVec vec0, floatInVec vec1); - friend inline const floatInVec select(floatInVec vec0, floatInVec vec1, boolInVec select_vec1); -}; - -//-------------------------------------------------------------------------------------------------- -// floatInVec functions -// - -// operators -// -inline const floatInVec operator * (floatInVec vec0, floatInVec vec1); -inline const floatInVec operator / (floatInVec vec0, floatInVec vec1); -inline const floatInVec operator + (floatInVec vec0, floatInVec vec1); -inline const floatInVec operator - (floatInVec vec0, floatInVec vec1); -inline const boolInVec operator < (floatInVec vec0, floatInVec vec1); -inline const boolInVec operator <= (floatInVec vec0, floatInVec vec1); -inline const boolInVec operator > (floatInVec vec0, floatInVec vec1); -inline const boolInVec operator >= (floatInVec vec0, floatInVec vec1); -inline const boolInVec operator == (floatInVec vec0, floatInVec vec1); -inline const boolInVec operator != (floatInVec vec0, floatInVec vec1); - -// select between vec0 and vec1 using boolInVec. -// false selects vec0, true selects vec1 -// -inline const floatInVec select(floatInVec vec0, floatInVec vec1, boolInVec select_vec1); - -} // namespace Vectormath - -//-------------------------------------------------------------------------------------------------- -// floatInVec implementation -// - -#include "boolInVec.h" - -namespace Vectormath { - -inline -floatInVec::floatInVec(vec_float4 vec) -{ - mData = vec; -} - -inline -floatInVec::floatInVec(boolInVec vec) -{ - mData = vec_ctf(vec_sub((vec_uint4){0,0,0,0}, vec.get128()), 0); -} - -inline -floatInVec::floatInVec(vec_float4 vec, int slot) -{ -#ifdef __GNUC__ - if (__builtin_constant_p(slot)) - { - mData = vec_splat(vec, slot); - } - else -#endif - { - const vec_uchar16 shiftpattern = vec_lvsl(0, (float *)(size_t)(slot << 2)); - mData = vec_splat(vec_perm(vec, vec, shiftpattern), 0); - } -} - -inline -floatInVec::floatInVec(float scalar) -{ -#ifdef __GNUC__ - if (__builtin_constant_p(scalar)) - { - mData = (vec_float4){scalar, scalar, scalar, scalar}; - } - else -#endif - { - vec_float4 vec = vec_ld(0, &scalar); - mData = vec_splat(vec_perm(vec, vec, vec_lvsl(0, &scalar)), 0); - } -} - -#ifdef _VECTORMATH_NO_SCALAR_CAST -inline -float -floatInVec::getAsFloat() const -#else -inline -floatInVec::operator float() const -#endif -{ - return *((float *)&mData); -} - -inline -vec_float4 -floatInVec::get128() const -{ - return mData; -} - -inline -const floatInVec -floatInVec::operator ++ (int) -{ - vec_float4 olddata = mData; - operator ++(); - return floatInVec(olddata); -} - -inline -const floatInVec -floatInVec::operator -- (int) -{ - vec_float4 olddata = mData; - operator --(); - return floatInVec(olddata); -} - -inline -floatInVec& -floatInVec::operator ++ () -{ - *this += floatInVec((vec_float4){1.0f,1.0f,1.0f,1.0f}); - return *this; -} - -inline -floatInVec& -floatInVec::operator -- () -{ - *this -= floatInVec((vec_float4){1.0f,1.0f,1.0f,1.0f}); - return *this; -} - -inline -const floatInVec -floatInVec::operator - () const -{ - return floatInVec((vec_float4)vec_xor((vec_uint4)mData, (vec_uint4){0x80000000,0x80000000,0x80000000,0x80000000})); -} - -inline -floatInVec& -floatInVec::operator = (floatInVec vec) -{ - mData = vec.mData; - return *this; -} - -inline -floatInVec& -floatInVec::operator *= (floatInVec vec) -{ - *this = *this * vec; - return *this; -} - -inline -floatInVec& -floatInVec::operator /= (floatInVec vec) -{ - *this = *this / vec; - return *this; -} - -inline -floatInVec& -floatInVec::operator += (floatInVec vec) -{ - *this = *this + vec; - return *this; -} - -inline -floatInVec& -floatInVec::operator -= (floatInVec vec) -{ - *this = *this - vec; - return *this; -} - -inline -const floatInVec -operator * (floatInVec vec0, floatInVec vec1) -{ - return floatInVec(vec_madd(vec0.get128(), vec1.get128(), (vec_float4){0,0,0,0})); -} - -inline -const floatInVec -operator / (floatInVec num, floatInVec den) -{ - return floatInVec(divf4(num.get128(), den.get128())); -} - -inline -const floatInVec -operator + (floatInVec vec0, floatInVec vec1) -{ - return floatInVec(vec_add(vec0.get128(), vec1.get128())); -} - -inline -const floatInVec -operator - (floatInVec vec0, floatInVec vec1) -{ - return floatInVec(vec_sub(vec0.get128(), vec1.get128())); -} - -inline -const boolInVec -operator < (floatInVec vec0, floatInVec vec1) -{ - return boolInVec((vec_uint4)vec_cmpgt(vec1.get128(), vec0.get128())); -} - -inline -const boolInVec -operator <= (floatInVec vec0, floatInVec vec1) -{ - return !(vec0 > vec1); -} - -inline -const boolInVec -operator > (floatInVec vec0, floatInVec vec1) -{ - return boolInVec((vec_uint4)vec_cmpgt(vec0.get128(), vec1.get128())); -} - -inline -const boolInVec -operator >= (floatInVec vec0, floatInVec vec1) -{ - return !(vec0 < vec1); -} - -inline -const boolInVec -operator == (floatInVec vec0, floatInVec vec1) -{ - return boolInVec((vec_uint4)vec_cmpeq(vec0.get128(), vec1.get128())); -} - -inline -const boolInVec -operator != (floatInVec vec0, floatInVec vec1) -{ - return !(vec0 == vec1); -} - -inline -const floatInVec -select(floatInVec vec0, floatInVec vec1, boolInVec select_vec1) -{ - return floatInVec(vec_sel(vec0.get128(), vec1.get128(), select_vec1.get128())); -} - -} // namespace Vectormath - -#endif // floatInVec_h +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _FLOATINVEC_H +#define _FLOATINVEC_H + +#include +#include +#include +#include +#include +#undef bool + +namespace Vectormath { + +class boolInVec; + +//-------------------------------------------------------------------------------------------------- +// floatInVec class +// + +class floatInVec +{ + private: + vec_float4 mData; + + inline floatInVec(vec_float4 vec); + public: + inline floatInVec() {} + + // matches standard type conversions + // + inline floatInVec(boolInVec vec); + + // construct from a slot of vec_float4 + // + inline floatInVec(vec_float4 vec, int slot); + + // explicit cast from float + // + explicit inline floatInVec(float scalar); + +#ifdef _VECTORMATH_NO_SCALAR_CAST + // explicit cast to float + // + inline float getAsFloat() const; +#else + // implicit cast to float + // + inline operator float() const; +#endif + + // get vector data + // float value is splatted across all word slots of vector + // + inline vec_float4 get128() const; + + // operators + // + inline const floatInVec operator ++ (int); + inline const floatInVec operator -- (int); + inline floatInVec& operator ++ (); + inline floatInVec& operator -- (); + inline const floatInVec operator - () const; + inline floatInVec& operator = (floatInVec vec); + inline floatInVec& operator *= (floatInVec vec); + inline floatInVec& operator /= (floatInVec vec); + inline floatInVec& operator += (floatInVec vec); + inline floatInVec& operator -= (floatInVec vec); + + // friend functions + // + friend inline const floatInVec operator * (floatInVec vec0, floatInVec vec1); + friend inline const floatInVec operator / (floatInVec vec0, floatInVec vec1); + friend inline const floatInVec operator + (floatInVec vec0, floatInVec vec1); + friend inline const floatInVec operator - (floatInVec vec0, floatInVec vec1); + friend inline const floatInVec select(floatInVec vec0, floatInVec vec1, boolInVec select_vec1); +}; + +//-------------------------------------------------------------------------------------------------- +// floatInVec functions +// + +// operators +// +inline const floatInVec operator * (floatInVec vec0, floatInVec vec1); +inline const floatInVec operator / (floatInVec vec0, floatInVec vec1); +inline const floatInVec operator + (floatInVec vec0, floatInVec vec1); +inline const floatInVec operator - (floatInVec vec0, floatInVec vec1); +inline const boolInVec operator < (floatInVec vec0, floatInVec vec1); +inline const boolInVec operator <= (floatInVec vec0, floatInVec vec1); +inline const boolInVec operator > (floatInVec vec0, floatInVec vec1); +inline const boolInVec operator >= (floatInVec vec0, floatInVec vec1); +inline const boolInVec operator == (floatInVec vec0, floatInVec vec1); +inline const boolInVec operator != (floatInVec vec0, floatInVec vec1); + +// select between vec0 and vec1 using boolInVec. +// false selects vec0, true selects vec1 +// +inline const floatInVec select(floatInVec vec0, floatInVec vec1, boolInVec select_vec1); + +} // namespace Vectormath + +//-------------------------------------------------------------------------------------------------- +// floatInVec implementation +// + +#include "boolInVec.h" + +namespace Vectormath { + +inline +floatInVec::floatInVec(vec_float4 vec) +{ + mData = vec; +} + +inline +floatInVec::floatInVec(boolInVec vec) +{ + mData = vec_ctf(vec_sub((vec_uint4){0,0,0,0}, vec.get128()), 0); +} + +inline +floatInVec::floatInVec(vec_float4 vec, int slot) +{ +#ifdef __GNUC__ + if (__builtin_constant_p(slot)) + { + mData = vec_splat(vec, slot); + } + else +#endif + { + const vec_uchar16 shiftpattern = vec_lvsl(0, (float *)(size_t)(slot << 2)); + mData = vec_splat(vec_perm(vec, vec, shiftpattern), 0); + } +} + +inline +floatInVec::floatInVec(float scalar) +{ +#ifdef __GNUC__ + if (__builtin_constant_p(scalar)) + { + mData = (vec_float4){scalar, scalar, scalar, scalar}; + } + else +#endif + { + vec_float4 vec = vec_ld(0, &scalar); + mData = vec_splat(vec_perm(vec, vec, vec_lvsl(0, &scalar)), 0); + } +} + +#ifdef _VECTORMATH_NO_SCALAR_CAST +inline +float +floatInVec::getAsFloat() const +#else +inline +floatInVec::operator float() const +#endif +{ + return *((float *)&mData); +} + +inline +vec_float4 +floatInVec::get128() const +{ + return mData; +} + +inline +const floatInVec +floatInVec::operator ++ (int) +{ + vec_float4 olddata = mData; + operator ++(); + return floatInVec(olddata); +} + +inline +const floatInVec +floatInVec::operator -- (int) +{ + vec_float4 olddata = mData; + operator --(); + return floatInVec(olddata); +} + +inline +floatInVec& +floatInVec::operator ++ () +{ + *this += floatInVec((vec_float4){1.0f,1.0f,1.0f,1.0f}); + return *this; +} + +inline +floatInVec& +floatInVec::operator -- () +{ + *this -= floatInVec((vec_float4){1.0f,1.0f,1.0f,1.0f}); + return *this; +} + +inline +const floatInVec +floatInVec::operator - () const +{ + return floatInVec((vec_float4)vec_xor((vec_uint4)mData, (vec_uint4){0x80000000,0x80000000,0x80000000,0x80000000})); +} + +inline +floatInVec& +floatInVec::operator = (floatInVec vec) +{ + mData = vec.mData; + return *this; +} + +inline +floatInVec& +floatInVec::operator *= (floatInVec vec) +{ + *this = *this * vec; + return *this; +} + +inline +floatInVec& +floatInVec::operator /= (floatInVec vec) +{ + *this = *this / vec; + return *this; +} + +inline +floatInVec& +floatInVec::operator += (floatInVec vec) +{ + *this = *this + vec; + return *this; +} + +inline +floatInVec& +floatInVec::operator -= (floatInVec vec) +{ + *this = *this - vec; + return *this; +} + +inline +const floatInVec +operator * (floatInVec vec0, floatInVec vec1) +{ + return floatInVec(vec_madd(vec0.get128(), vec1.get128(), (vec_float4){0,0,0,0})); +} + +inline +const floatInVec +operator / (floatInVec num, floatInVec den) +{ + return floatInVec(divf4(num.get128(), den.get128())); +} + +inline +const floatInVec +operator + (floatInVec vec0, floatInVec vec1) +{ + return floatInVec(vec_add(vec0.get128(), vec1.get128())); +} + +inline +const floatInVec +operator - (floatInVec vec0, floatInVec vec1) +{ + return floatInVec(vec_sub(vec0.get128(), vec1.get128())); +} + +inline +const boolInVec +operator < (floatInVec vec0, floatInVec vec1) +{ + return boolInVec((vec_uint4)vec_cmpgt(vec1.get128(), vec0.get128())); +} + +inline +const boolInVec +operator <= (floatInVec vec0, floatInVec vec1) +{ + return !(vec0 > vec1); +} + +inline +const boolInVec +operator > (floatInVec vec0, floatInVec vec1) +{ + return boolInVec((vec_uint4)vec_cmpgt(vec0.get128(), vec1.get128())); +} + +inline +const boolInVec +operator >= (floatInVec vec0, floatInVec vec1) +{ + return !(vec0 < vec1); +} + +inline +const boolInVec +operator == (floatInVec vec0, floatInVec vec1) +{ + return boolInVec((vec_uint4)vec_cmpeq(vec0.get128(), vec1.get128())); +} + +inline +const boolInVec +operator != (floatInVec vec0, floatInVec vec1) +{ + return !(vec0 == vec1); +} + +inline +const floatInVec +select(floatInVec vec0, floatInVec vec1, boolInVec select_vec1) +{ + return floatInVec(vec_sel(vec0.get128(), vec1.get128(), select_vec1.get128())); +} + +} // namespace Vectormath + +#endif // floatInVec_h diff --git a/common/vectormath/ppu/cpp/mat_aos.h b/common/vectormath/ppu/cpp/mat_aos.h index 783e9c58..8607985e 100644 --- a/common/vectormath/ppu/cpp/mat_aos.h +++ b/common/vectormath/ppu/cpp/mat_aos.h @@ -1,2188 +1,2201 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_MAT_AOS_CPP_H -#define _VECTORMATH_MAT_AOS_CPP_H - -namespace Vectormath { -namespace Aos { - -//----------------------------------------------------------------------------- -// Constants -// for shuffles, words are labeled [x,y,z,w] [a,b,c,d] - -#define _VECTORMATH_PERM_ZBWX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_B, _VECTORMATH_PERM_W, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_XCYX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_C, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_XYAB ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A, _VECTORMATH_PERM_B }) -#define _VECTORMATH_PERM_ZWCD ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_W, _VECTORMATH_PERM_C, _VECTORMATH_PERM_D }) -#define _VECTORMATH_PERM_XZBX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_B, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_CXXX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_C, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_YAXX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_XAZC ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_A, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_C }) -#define _VECTORMATH_PERM_YXWZ ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X, _VECTORMATH_PERM_W, _VECTORMATH_PERM_Z }) -#define _VECTORMATH_PERM_YBWD ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_B, _VECTORMATH_PERM_W, _VECTORMATH_PERM_D }) -#define _VECTORMATH_PERM_XYCX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_C, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_YCXY ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_C, _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y }) -#define _VECTORMATH_PERM_CXYC ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_C, _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_C }) -#define _VECTORMATH_PERM_ZAYX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_BZXX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_B, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PERM_XZYA ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A }) -#define _VECTORMATH_PERM_ZXXB ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X, _VECTORMATH_PERM_B }) -#define _VECTORMATH_PERM_YXXC ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X, _VECTORMATH_PERM_C }) -#define _VECTORMATH_PERM_BBYX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_B, _VECTORMATH_PERM_B, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X }) -#define _VECTORMATH_PI_OVER_2 1.570796327f - -//----------------------------------------------------------------------------- -// Definitions - -inline Matrix3::Matrix3( const Matrix3 & mat ) -{ - mCol0 = mat.mCol0; - mCol1 = mat.mCol1; - mCol2 = mat.mCol2; -} - -inline Matrix3::Matrix3( float scalar ) -{ - mCol0 = Vector3( scalar ); - mCol1 = Vector3( scalar ); - mCol2 = Vector3( scalar ); -} - -inline Matrix3::Matrix3( floatInVec scalar ) -{ - mCol0 = Vector3( scalar ); - mCol1 = Vector3( scalar ); - mCol2 = Vector3( scalar ); -} - -inline Matrix3::Matrix3( Quat unitQuat ) -{ - vec_float4 xyzw_2, wwww, yzxw, zxyw, yzxw_2, zxyw_2; - vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; - vec_uint4 select_x = _VECTORMATH_MASK_0xF000; - vec_uint4 select_z = _VECTORMATH_MASK_0x00F0; - xyzw_2 = vec_add( unitQuat.get128(), unitQuat.get128() ); - wwww = vec_splat( unitQuat.get128(), 3 ); - yzxw = vec_perm( unitQuat.get128(), unitQuat.get128(), _VECTORMATH_PERM_YZXW ); - zxyw = vec_perm( unitQuat.get128(), unitQuat.get128(), _VECTORMATH_PERM_ZXYW ); - yzxw_2 = vec_perm( xyzw_2, xyzw_2, _VECTORMATH_PERM_YZXW ); - zxyw_2 = vec_perm( xyzw_2, xyzw_2, _VECTORMATH_PERM_ZXYW ); - tmp0 = vec_madd( yzxw_2, wwww, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - tmp1 = vec_nmsub( yzxw, yzxw_2, ((vec_float4){1.0f,1.0f,1.0f,1.0f}) ); - tmp2 = vec_madd( yzxw, xyzw_2, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - tmp0 = vec_madd( zxyw, xyzw_2, tmp0 ); - tmp1 = vec_nmsub( zxyw, zxyw_2, tmp1 ); - tmp2 = vec_nmsub( zxyw_2, wwww, tmp2 ); - tmp3 = vec_sel( tmp0, tmp1, select_x ); - tmp4 = vec_sel( tmp1, tmp2, select_x ); - tmp5 = vec_sel( tmp2, tmp0, select_x ); - mCol0 = Vector3( vec_sel( tmp3, tmp2, select_z ) ); - mCol1 = Vector3( vec_sel( tmp4, tmp0, select_z ) ); - mCol2 = Vector3( vec_sel( tmp5, tmp1, select_z ) ); -} - -inline Matrix3::Matrix3( Vector3 _col0, Vector3 _col1, Vector3 _col2 ) -{ - mCol0 = _col0; - mCol1 = _col1; - mCol2 = _col2; -} - -inline Matrix3 & Matrix3::setCol0( Vector3 _col0 ) -{ - mCol0 = _col0; - return *this; -} - -inline Matrix3 & Matrix3::setCol1( Vector3 _col1 ) -{ - mCol1 = _col1; - return *this; -} - -inline Matrix3 & Matrix3::setCol2( Vector3 _col2 ) -{ - mCol2 = _col2; - return *this; -} - -inline Matrix3 & Matrix3::setCol( int col, Vector3 vec ) -{ - *(&mCol0 + col) = vec; - return *this; -} - -inline Matrix3 & Matrix3::setRow( int row, Vector3 vec ) -{ - mCol0.setElem( row, vec.getElem( 0 ) ); - mCol1.setElem( row, vec.getElem( 1 ) ); - mCol2.setElem( row, vec.getElem( 2 ) ); - return *this; -} - -inline Matrix3 & Matrix3::setElem( int col, int row, float val ) -{ - (*this)[col].setElem(row, val); - return *this; -} - -inline Matrix3 & Matrix3::setElem( int col, int row, floatInVec val ) -{ - Vector3 tmpV3_0; - tmpV3_0 = this->getCol( col ); - tmpV3_0.setElem( row, val ); - this->setCol( col, tmpV3_0 ); - return *this; -} - -inline const floatInVec Matrix3::getElem( int col, int row ) const -{ - return this->getCol( col ).getElem( row ); -} - -inline const Vector3 Matrix3::getCol0( ) const -{ - return mCol0; -} - -inline const Vector3 Matrix3::getCol1( ) const -{ - return mCol1; -} - -inline const Vector3 Matrix3::getCol2( ) const -{ - return mCol2; -} - -inline const Vector3 Matrix3::getCol( int col ) const -{ - return *(&mCol0 + col); -} - -inline const Vector3 Matrix3::getRow( int row ) const -{ - return Vector3( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ) ); -} - -inline Vector3 & Matrix3::operator []( int col ) -{ - return *(&mCol0 + col); -} - -inline const Vector3 Matrix3::operator []( int col ) const -{ - return *(&mCol0 + col); -} - -inline Matrix3 & Matrix3::operator =( const Matrix3 & mat ) -{ - mCol0 = mat.mCol0; - mCol1 = mat.mCol1; - mCol2 = mat.mCol2; - return *this; -} - -inline const Matrix3 transpose( const Matrix3 & mat ) -{ - vec_float4 tmp0, tmp1, res0, res1, res2; - tmp0 = vec_mergeh( mat.getCol0().get128(), mat.getCol2().get128() ); - tmp1 = vec_mergel( mat.getCol0().get128(), mat.getCol2().get128() ); - res0 = vec_mergeh( tmp0, mat.getCol1().get128() ); - res1 = vec_perm( tmp0, mat.getCol1().get128(), _VECTORMATH_PERM_ZBWX ); - res2 = vec_perm( tmp1, mat.getCol1().get128(), _VECTORMATH_PERM_XCYX ); - return Matrix3( - Vector3( res0 ), - Vector3( res1 ), - Vector3( res2 ) - ); -} - -inline const Matrix3 inverse( const Matrix3 & mat ) -{ - vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet, inv0, inv1, inv2; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - tmp2 = _vmathVfCross( mat.getCol0().get128(), mat.getCol1().get128() ); - tmp0 = _vmathVfCross( mat.getCol1().get128(), mat.getCol2().get128() ); - tmp1 = _vmathVfCross( mat.getCol2().get128(), mat.getCol0().get128() ); - dot = _vmathVfDot3( tmp2, mat.getCol2().get128() ); - dot = vec_splat( dot, 0 ); - invdet = recipf4( dot ); - tmp3 = vec_mergeh( tmp0, tmp2 ); - tmp4 = vec_mergel( tmp0, tmp2 ); - inv0 = vec_mergeh( tmp3, tmp1 ); - inv1 = vec_perm( tmp3, tmp1, _VECTORMATH_PERM_ZBWX ); - inv2 = vec_perm( tmp4, tmp1, _VECTORMATH_PERM_XCYX ); - inv0 = vec_madd( inv0, invdet, zero ); - inv1 = vec_madd( inv1, invdet, zero ); - inv2 = vec_madd( inv2, invdet, zero ); - return Matrix3( - Vector3( inv0 ), - Vector3( inv1 ), - Vector3( inv2 ) - ); -} - -inline const floatInVec determinant( const Matrix3 & mat ) -{ - return dot( mat.getCol2(), cross( mat.getCol0(), mat.getCol1() ) ); -} - -inline const Matrix3 Matrix3::operator +( const Matrix3 & mat ) const -{ - return Matrix3( - ( mCol0 + mat.mCol0 ), - ( mCol1 + mat.mCol1 ), - ( mCol2 + mat.mCol2 ) - ); -} - -inline const Matrix3 Matrix3::operator -( const Matrix3 & mat ) const -{ - return Matrix3( - ( mCol0 - mat.mCol0 ), - ( mCol1 - mat.mCol1 ), - ( mCol2 - mat.mCol2 ) - ); -} - -inline Matrix3 & Matrix3::operator +=( const Matrix3 & mat ) -{ - *this = *this + mat; - return *this; -} - -inline Matrix3 & Matrix3::operator -=( const Matrix3 & mat ) -{ - *this = *this - mat; - return *this; -} - -inline const Matrix3 Matrix3::operator -( ) const -{ - return Matrix3( - ( -mCol0 ), - ( -mCol1 ), - ( -mCol2 ) - ); -} - -inline const Matrix3 absPerElem( const Matrix3 & mat ) -{ - return Matrix3( - absPerElem( mat.getCol0() ), - absPerElem( mat.getCol1() ), - absPerElem( mat.getCol2() ) - ); -} - -inline const Matrix3 Matrix3::operator *( float scalar ) const -{ - return *this * floatInVec(scalar); -} - -inline const Matrix3 Matrix3::operator *( floatInVec scalar ) const -{ - return Matrix3( - ( mCol0 * scalar ), - ( mCol1 * scalar ), - ( mCol2 * scalar ) - ); -} - -inline Matrix3 & Matrix3::operator *=( float scalar ) -{ - return *this *= floatInVec(scalar); -} - -inline Matrix3 & Matrix3::operator *=( floatInVec scalar ) -{ - *this = *this * scalar; - return *this; -} - -inline const Matrix3 operator *( float scalar, const Matrix3 & mat ) -{ - return floatInVec(scalar) * mat; -} - -inline const Matrix3 operator *( floatInVec scalar, const Matrix3 & mat ) -{ - return mat * scalar; -} - -inline const Vector3 Matrix3::operator *( Vector3 vec ) const -{ - vec_float4 res; - vec_float4 xxxx, yyyy, zzzz; - xxxx = vec_splat( vec.get128(), 0 ); - yyyy = vec_splat( vec.get128(), 1 ); - zzzz = vec_splat( vec.get128(), 2 ); - res = vec_madd( mCol0.get128(), xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - res = vec_madd( mCol1.get128(), yyyy, res ); - res = vec_madd( mCol2.get128(), zzzz, res ); - return Vector3( res ); -} - -inline const Matrix3 Matrix3::operator *( const Matrix3 & mat ) const -{ - return Matrix3( - ( *this * mat.mCol0 ), - ( *this * mat.mCol1 ), - ( *this * mat.mCol2 ) - ); -} - -inline Matrix3 & Matrix3::operator *=( const Matrix3 & mat ) -{ - *this = *this * mat; - return *this; -} - -inline const Matrix3 mulPerElem( const Matrix3 & mat0, const Matrix3 & mat1 ) -{ - return Matrix3( - mulPerElem( mat0.getCol0(), mat1.getCol0() ), - mulPerElem( mat0.getCol1(), mat1.getCol1() ), - mulPerElem( mat0.getCol2(), mat1.getCol2() ) - ); -} - -inline const Matrix3 Matrix3::identity( ) -{ - return Matrix3( - Vector3::xAxis( ), - Vector3::yAxis( ), - Vector3::zAxis( ) - ); -} - -inline const Matrix3 Matrix3::rotationX( float radians ) -{ - return rotationX( floatInVec(radians) ); -} - -inline const Matrix3 Matrix3::rotationX( floatInVec radians ) -{ - vec_float4 s, c, res1, res2; - vec_uint4 select_y, select_z; - vec_float4 zero; - select_y = _VECTORMATH_MASK_0x0F00; - select_z = _VECTORMATH_MASK_0x00F0; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( radians.get128(), &s, &c ); - res1 = vec_sel( zero, c, select_y ); - res1 = vec_sel( res1, s, select_z ); - res2 = vec_sel( zero, negatef4(s), select_y ); - res2 = vec_sel( res2, c, select_z ); - return Matrix3( - Vector3::xAxis( ), - Vector3( res1 ), - Vector3( res2 ) - ); -} - -inline const Matrix3 Matrix3::rotationY( float radians ) -{ - return rotationY( floatInVec(radians) ); -} - -inline const Matrix3 Matrix3::rotationY( floatInVec radians ) -{ - vec_float4 s, c, res0, res2; - vec_uint4 select_x, select_z; - vec_float4 zero; - select_x = _VECTORMATH_MASK_0xF000; - select_z = _VECTORMATH_MASK_0x00F0; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( radians.get128(), &s, &c ); - res0 = vec_sel( zero, c, select_x ); - res0 = vec_sel( res0, negatef4(s), select_z ); - res2 = vec_sel( zero, s, select_x ); - res2 = vec_sel( res2, c, select_z ); - return Matrix3( - Vector3( res0 ), - Vector3::yAxis( ), - Vector3( res2 ) - ); -} - -inline const Matrix3 Matrix3::rotationZ( float radians ) -{ - return rotationZ( floatInVec(radians) ); -} - -inline const Matrix3 Matrix3::rotationZ( floatInVec radians ) -{ - vec_float4 s, c, res0, res1; - vec_uint4 select_x, select_y; - vec_float4 zero; - select_x = _VECTORMATH_MASK_0xF000; - select_y = _VECTORMATH_MASK_0x0F00; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( radians.get128(), &s, &c ); - res0 = vec_sel( zero, c, select_x ); - res0 = vec_sel( res0, s, select_y ); - res1 = vec_sel( zero, negatef4(s), select_x ); - res1 = vec_sel( res1, c, select_y ); - return Matrix3( - Vector3( res0 ), - Vector3( res1 ), - Vector3::zAxis( ) - ); -} - -inline const Matrix3 Matrix3::rotationZYX( Vector3 radiansXYZ ) -{ - vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - angles = Vector4( radiansXYZ, 0.0f ).get128(); - sincosf4( angles, &s, &c ); - negS = negatef4( s ); - Z0 = vec_mergel( c, s ); - Z1 = vec_mergel( negS, c ); - Z1 = vec_andc( Z1, (vec_float4)_VECTORMATH_MASK_0x000F ); - Y0 = vec_perm( negS, c, _VECTORMATH_PERM_BBYX ); - Y1 = vec_perm( c, s, _VECTORMATH_PERM_BBYX ); - X0 = vec_splat( s, 0 ); - X1 = vec_splat( c, 0 ); - tmp = vec_madd( Z0, Y1, zero ); - return Matrix3( - Vector3( vec_madd( Z0, Y0, zero ) ), - Vector3( vec_madd( Z1, X1, vec_madd( tmp, X0, zero ) ) ), - Vector3( vec_nmsub( Z1, X0, vec_madd( tmp, X1, zero ) ) ) - ); -} - -inline const Matrix3 Matrix3::rotation( float radians, Vector3 unitVec ) -{ - return rotation( floatInVec(radians), unitVec ); -} - -inline const Matrix3 Matrix3::rotation( floatInVec radians, Vector3 unitVec ) -{ - vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - axis = unitVec.get128(); - sincosf4( radians.get128(), &s, &c ); - xxxx = vec_splat( axis, 0 ); - yyyy = vec_splat( axis, 1 ); - zzzz = vec_splat( axis, 2 ); - oneMinusC = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), c ); - axisS = vec_madd( axis, s, zero ); - negAxisS = negatef4( axisS ); - tmp0 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_XZBX ); - tmp1 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_CXXX ); - tmp2 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_YAXX ); - tmp0 = vec_sel( tmp0, c, _VECTORMATH_MASK_0xF000 ); - tmp1 = vec_sel( tmp1, c, _VECTORMATH_MASK_0x0F00 ); - tmp2 = vec_sel( tmp2, c, _VECTORMATH_MASK_0x00F0 ); - return Matrix3( - Vector3( vec_madd( vec_madd( axis, xxxx, zero ), oneMinusC, tmp0 ) ), - Vector3( vec_madd( vec_madd( axis, yyyy, zero ), oneMinusC, tmp1 ) ), - Vector3( vec_madd( vec_madd( axis, zzzz, zero ), oneMinusC, tmp2 ) ) - ); -} - -inline const Matrix3 Matrix3::rotation( Quat unitQuat ) -{ - return Matrix3( unitQuat ); -} - -inline const Matrix3 Matrix3::scale( Vector3 scaleVec ) -{ - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - return Matrix3( - Vector3( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0xF000 ) ), - Vector3( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0x0F00 ) ), - Vector3( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0x00F0 ) ) - ); -} - -inline const Matrix3 appendScale( const Matrix3 & mat, Vector3 scaleVec ) -{ - return Matrix3( - ( mat.getCol0() * scaleVec.getX( ) ), - ( mat.getCol1() * scaleVec.getY( ) ), - ( mat.getCol2() * scaleVec.getZ( ) ) - ); -} - -inline const Matrix3 prependScale( Vector3 scaleVec, const Matrix3 & mat ) -{ - return Matrix3( - mulPerElem( mat.getCol0(), scaleVec ), - mulPerElem( mat.getCol1(), scaleVec ), - mulPerElem( mat.getCol2(), scaleVec ) - ); -} - -inline const Matrix3 select( const Matrix3 & mat0, const Matrix3 & mat1, bool select1 ) -{ - return Matrix3( - select( mat0.getCol0(), mat1.getCol0(), select1 ), - select( mat0.getCol1(), mat1.getCol1(), select1 ), - select( mat0.getCol2(), mat1.getCol2(), select1 ) - ); -} - -inline const Matrix3 select( const Matrix3 & mat0, const Matrix3 & mat1, boolInVec select1 ) -{ - return Matrix3( - select( mat0.getCol0(), mat1.getCol0(), select1 ), - select( mat0.getCol1(), mat1.getCol1(), select1 ), - select( mat0.getCol2(), mat1.getCol2(), select1 ) - ); -} - -#ifdef _VECTORMATH_DEBUG - -inline void print( const Matrix3 & mat ) -{ - print( mat.getRow( 0 ) ); - print( mat.getRow( 1 ) ); - print( mat.getRow( 2 ) ); -} - -inline void print( const Matrix3 & mat, const char * name ) -{ - printf("%s:\n", name); - print( mat ); -} - -#endif - -inline Matrix4::Matrix4( const Matrix4 & mat ) -{ - mCol0 = mat.mCol0; - mCol1 = mat.mCol1; - mCol2 = mat.mCol2; - mCol3 = mat.mCol3; -} - -inline Matrix4::Matrix4( float scalar ) -{ - mCol0 = Vector4( scalar ); - mCol1 = Vector4( scalar ); - mCol2 = Vector4( scalar ); - mCol3 = Vector4( scalar ); -} - -inline Matrix4::Matrix4( floatInVec scalar ) -{ - mCol0 = Vector4( scalar ); - mCol1 = Vector4( scalar ); - mCol2 = Vector4( scalar ); - mCol3 = Vector4( scalar ); -} - -inline Matrix4::Matrix4( const Transform3 & mat ) -{ - mCol0 = Vector4( mat.getCol0(), 0.0f ); - mCol1 = Vector4( mat.getCol1(), 0.0f ); - mCol2 = Vector4( mat.getCol2(), 0.0f ); - mCol3 = Vector4( mat.getCol3(), 1.0f ); -} - -inline Matrix4::Matrix4( Vector4 _col0, Vector4 _col1, Vector4 _col2, Vector4 _col3 ) -{ - mCol0 = _col0; - mCol1 = _col1; - mCol2 = _col2; - mCol3 = _col3; -} - -inline Matrix4::Matrix4( const Matrix3 & mat, Vector3 translateVec ) -{ - mCol0 = Vector4( mat.getCol0(), 0.0f ); - mCol1 = Vector4( mat.getCol1(), 0.0f ); - mCol2 = Vector4( mat.getCol2(), 0.0f ); - mCol3 = Vector4( translateVec, 1.0f ); -} - -inline Matrix4::Matrix4( Quat unitQuat, Vector3 translateVec ) -{ - Matrix3 mat; - mat = Matrix3( unitQuat ); - mCol0 = Vector4( mat.getCol0(), 0.0f ); - mCol1 = Vector4( mat.getCol1(), 0.0f ); - mCol2 = Vector4( mat.getCol2(), 0.0f ); - mCol3 = Vector4( translateVec, 1.0f ); -} - -inline Matrix4 & Matrix4::setCol0( Vector4 _col0 ) -{ - mCol0 = _col0; - return *this; -} - -inline Matrix4 & Matrix4::setCol1( Vector4 _col1 ) -{ - mCol1 = _col1; - return *this; -} - -inline Matrix4 & Matrix4::setCol2( Vector4 _col2 ) -{ - mCol2 = _col2; - return *this; -} - -inline Matrix4 & Matrix4::setCol3( Vector4 _col3 ) -{ - mCol3 = _col3; - return *this; -} - -inline Matrix4 & Matrix4::setCol( int col, Vector4 vec ) -{ - *(&mCol0 + col) = vec; - return *this; -} - -inline Matrix4 & Matrix4::setRow( int row, Vector4 vec ) -{ - mCol0.setElem( row, vec.getElem( 0 ) ); - mCol1.setElem( row, vec.getElem( 1 ) ); - mCol2.setElem( row, vec.getElem( 2 ) ); - mCol3.setElem( row, vec.getElem( 3 ) ); - return *this; -} - -inline Matrix4 & Matrix4::setElem( int col, int row, float val ) -{ - (*this)[col].setElem(row, val); - return *this; -} - -inline Matrix4 & Matrix4::setElem( int col, int row, floatInVec val ) -{ - Vector4 tmpV3_0; - tmpV3_0 = this->getCol( col ); - tmpV3_0.setElem( row, val ); - this->setCol( col, tmpV3_0 ); - return *this; -} - -inline const floatInVec Matrix4::getElem( int col, int row ) const -{ - return this->getCol( col ).getElem( row ); -} - -inline const Vector4 Matrix4::getCol0( ) const -{ - return mCol0; -} - -inline const Vector4 Matrix4::getCol1( ) const -{ - return mCol1; -} - -inline const Vector4 Matrix4::getCol2( ) const -{ - return mCol2; -} - -inline const Vector4 Matrix4::getCol3( ) const -{ - return mCol3; -} - -inline const Vector4 Matrix4::getCol( int col ) const -{ - return *(&mCol0 + col); -} - -inline const Vector4 Matrix4::getRow( int row ) const -{ - return Vector4( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ), mCol3.getElem( row ) ); -} - -inline Vector4 & Matrix4::operator []( int col ) -{ - return *(&mCol0 + col); -} - -inline const Vector4 Matrix4::operator []( int col ) const -{ - return *(&mCol0 + col); -} - -inline Matrix4 & Matrix4::operator =( const Matrix4 & mat ) -{ - mCol0 = mat.mCol0; - mCol1 = mat.mCol1; - mCol2 = mat.mCol2; - mCol3 = mat.mCol3; - return *this; -} - -inline const Matrix4 transpose( const Matrix4 & mat ) -{ - vec_float4 tmp0, tmp1, tmp2, tmp3, res0, res1, res2, res3; - tmp0 = vec_mergeh( mat.getCol0().get128(), mat.getCol2().get128() ); - tmp1 = vec_mergeh( mat.getCol1().get128(), mat.getCol3().get128() ); - tmp2 = vec_mergel( mat.getCol0().get128(), mat.getCol2().get128() ); - tmp3 = vec_mergel( mat.getCol1().get128(), mat.getCol3().get128() ); - res0 = vec_mergeh( tmp0, tmp1 ); - res1 = vec_mergel( tmp0, tmp1 ); - res2 = vec_mergeh( tmp2, tmp3 ); - res3 = vec_mergel( tmp2, tmp3 ); - return Matrix4( - Vector4( res0 ), - Vector4( res1 ), - Vector4( res2 ), - Vector4( res3 ) - ); -} - -inline const Matrix4 inverse( const Matrix4 & mat ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vector float in0, in1, in2, in3; - vector float tmp0, tmp1, tmp2, tmp3; - vector float cof0, cof1, cof2, cof3; - vector float t0, t1, t2, t3; - vector float t01, t02, t03, t12, t23; - vector float t1r, t2r; - vector float t01r, t02r, t03r, t12r, t23r; - vector float t1r3, t1r3r; - vector float det, det0, det1, det2, det3, invdet; - vector float vzero = (vector float){0.0}; - in0 = mat.getCol0().get128(); - in1 = mat.getCol1().get128(); - in2 = mat.getCol2().get128(); - in3 = mat.getCol3().get128(); - /* Perform transform of the input matrix of the form: - * A B C D - * E F G H - * I J K L - * M N O P - * - * The pseudo transpose of the input matrix is trans: - * A E I M - * J N B F - * C G K O - * L P D H - */ - tmp0 = vec_perm(in0, in1, _VECTORMATH_PERM_XAZC); /* A E C G */ - tmp1 = vec_perm(in2, in3, _VECTORMATH_PERM_XAZC); /* I M K O */ - tmp2 = vec_perm(in0, in1, _VECTORMATH_PERM_YBWD); /* B F D H */ - tmp3 = vec_perm(in2, in3, _VECTORMATH_PERM_YBWD); /* J N L P */ - t0 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_XYAB); /* A E I M */ - t1 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_XYAB); /* J N B F */ - t2 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_ZWCD); /* C G K O */ - t3 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_ZWCD); /* L P D H */ - /* Generate a cofactor matrix. The computed cofactors reside in - * cof0, cof1, cof2, cof3. - */ - t23 = vec_madd(t2, t3, vzero); /* CL GP KD OH */ - t23 = vec_perm(t23, t23, _VECTORMATH_PERM_YXWZ); /* GP CL OH KD */ - cof0 = vec_nmsub(t1, t23, vzero); /* -(JGP NCL FOH BKD) */ - cof1 = vec_nmsub(t0, t23, vzero); /* -(AGP ECL IOH MKD) */ - t23r = vec_sld(t23, t23, 8); /* OH KD GP CL */ - cof0 = vec_madd(t1, t23r, cof0); /* JOH NKD BGP FCL + cof0 */ - cof1 = vec_madd(t0, t23r, cof1); /* AOH EKD IGP MCL + cof1 */ - cof1 = vec_sld(cof1, cof1, 8); /* IGP MCL AOH EKD - IOH MKD AGP ECL */ - t12 = vec_madd(t1, t2, vzero); /* JC NG BK FO */ - t12 = vec_perm(t12, t12, _VECTORMATH_PERM_YXWZ); /* NG JC FO BK */ - cof0 = vec_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ - cof3 = vec_madd(t0, t12, vzero); /* ANG EJC IFO MBK */ - t12r = vec_sld(t12, t12, 8); /* FO BK NG JC */ - cof0 = vec_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ - cof3 = vec_nmsub(t0, t12r, cof3); /* cof3 - AFO EBK ING MJC */ - cof3 = vec_sld(cof3, cof3, 8); /* ING MJC AFO EBK - IFO MBK ANG EJC */ - t1r = vec_sld(t1, t1, 8); /* B F J N */ - t2r = vec_sld(t2, t2, 8); /* K O C G */ - t1r3 = vec_madd(t1r, t3, vzero); /* BL FP JD NH */ - t1r3 = vec_perm(t1r3, t1r3, _VECTORMATH_PERM_YXWZ); /* FP BL NH JD */ - cof0 = vec_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ - cof2 = vec_madd(t0, t1r3, vzero); /* AFP EBL INH MJD */ - t1r3r = vec_sld(t1r3, t1r3, 8); /* NH JD FP BL */ - cof0 = vec_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ - cof2 = vec_nmsub(t0, t1r3r, cof2); /* cof2 - ANH EJD IFP MBL */ - cof2 = vec_sld(cof2, cof2, 8); /* IFP MBL ANH EJD - INH MJD AFP EBL */ - t01 = vec_madd(t0, t1, vzero); /* AJ EN IB MF */ - t01 = vec_perm(t01, t01, _VECTORMATH_PERM_YXWZ); /* EN AJ MF IB */ - cof2 = vec_nmsub(t3, t01, cof2); /* cof2 - LEN PAJ DMF HIB */ - cof3 = vec_madd(t2r, t01, cof3); /* KEN OAJ CMF GIB + cof3 */ - t01r = vec_sld(t01, t01, 8); /* MF IB EN AJ */ - cof2 = vec_madd(t3, t01r, cof2); /* LMF PIB DEN HAJ + cof2 */ - cof3 = vec_nmsub(t2r, t01r, cof3); /* cof3 - KMF OIB CEN GAJ */ - t03 = vec_madd(t0, t3, vzero); /* AL EP ID MH */ - t03 = vec_perm(t03, t03, _VECTORMATH_PERM_YXWZ); /* EP AL MH ID */ - cof1 = vec_nmsub(t2r, t03, cof1); /* cof1 - KEP OAL CMH GID */ - cof2 = vec_madd(t1, t03, cof2); /* JEP NAL BMH FID + cof2 */ - t03r = vec_sld(t03, t03, 8); /* MH ID EP AL */ - cof1 = vec_madd(t2r, t03r, cof1); /* KMH OID CEP GAL + cof1 */ - cof2 = vec_nmsub(t1, t03r, cof2); /* cof2 - JMH NID BEP FAL */ - t02 = vec_madd(t0, t2r, vzero); /* AK EO IC MG */ - t02 = vec_perm(t02, t02, _VECTORMATH_PERM_YXWZ); /* E0 AK MG IC */ - cof1 = vec_madd(t3, t02, cof1); /* LEO PAK DMG HIC + cof1 */ - cof3 = vec_nmsub(t1, t02, cof3); /* cof3 - JEO NAK BMG FIC */ - t02r = vec_sld(t02, t02, 8); /* MG IC EO AK */ - cof1 = vec_nmsub(t3, t02r, cof1); /* cof1 - LMG PIC DEO HAK */ - cof3 = vec_madd(t1, t02r, cof3); /* JMG NIC BEO FAK + cof3 */ - /* Compute the determinant of the matrix - * - * det = sum_across(t0 * cof0); - * - * We perform a sum across the entire vector so that - * we don't have to splat the result when multiplying the - * cofactors by the inverse of the determinant. - */ - det = vec_madd(t0, cof0, vzero); - det0 = vec_splat(det, 0); - det1 = vec_splat(det, 1); - det2 = vec_splat(det, 2); - det3 = vec_splat(det, 3); - det = vec_add(det0, det1); - det2 = vec_add(det2, det3); - det = vec_add(det, det2); - /* Compute the reciprocal of the determinant. - */ - invdet = recipf4(det); - /* Multiply the cofactors by the reciprocal of the determinant. - */ - return Matrix4( - Vector4( vec_madd(cof0, invdet, vzero) ), - Vector4( vec_madd(cof1, invdet, vzero) ), - Vector4( vec_madd(cof2, invdet, vzero) ), - Vector4( vec_madd(cof3, invdet, vzero) ) - ); -} - -inline const Matrix4 affineInverse( const Matrix4 & mat ) -{ - Transform3 affineMat; - affineMat.setCol0( mat.getCol0().getXYZ( ) ); - affineMat.setCol1( mat.getCol1().getXYZ( ) ); - affineMat.setCol2( mat.getCol2().getXYZ( ) ); - affineMat.setCol3( mat.getCol3().getXYZ( ) ); - return Matrix4( inverse( affineMat ) ); -} - -inline const Matrix4 orthoInverse( const Matrix4 & mat ) -{ - Transform3 affineMat; - affineMat.setCol0( mat.getCol0().getXYZ( ) ); - affineMat.setCol1( mat.getCol1().getXYZ( ) ); - affineMat.setCol2( mat.getCol2().getXYZ( ) ); - affineMat.setCol3( mat.getCol3().getXYZ( ) ); - return Matrix4( orthoInverse( affineMat ) ); -} - -inline const floatInVec determinant( const Matrix4 & mat ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vector float in0, in1, in2, in3; - vector float tmp0, tmp1, tmp2, tmp3; - vector float cof0; - vector float t0, t1, t2, t3; - vector float t12, t23; - vector float t1r, t2r; - vector float t12r, t23r; - vector float t1r3, t1r3r; - vector float vzero = (vector float){0.0}; - in0 = mat.getCol0().get128(); - in1 = mat.getCol1().get128(); - in2 = mat.getCol2().get128(); - in3 = mat.getCol3().get128(); - /* Perform transform of the input matrix of the form: - * A B C D - * E F G H - * I J K L - * M N O P - * - * The pseudo transpose of the input matrix is trans: - * A E I M - * J N B F - * C G K O - * L P D H - */ - tmp0 = vec_perm(in0, in1, _VECTORMATH_PERM_XAZC); /* A E C G */ - tmp1 = vec_perm(in2, in3, _VECTORMATH_PERM_XAZC); /* I M K O */ - tmp2 = vec_perm(in0, in1, _VECTORMATH_PERM_YBWD); /* B F D H */ - tmp3 = vec_perm(in2, in3, _VECTORMATH_PERM_YBWD); /* J N L P */ - t0 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_XYAB); /* A E I M */ - t1 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_XYAB); /* J N B F */ - t2 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_ZWCD); /* C G K O */ - t3 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_ZWCD); /* L P D H */ - /* Generate a cofactor matrix. The computed cofactors reside in - * cof0, cof1, cof2, cof3. - */ - t23 = vec_madd(t2, t3, vzero); /* CL GP KD OH */ - t23 = vec_perm(t23, t23, _VECTORMATH_PERM_YXWZ); /* GP CL OH KD */ - cof0 = vec_nmsub(t1, t23, vzero); /* -(JGP NCL FOH BKD) */ - t23r = vec_sld(t23, t23, 8); /* OH KD GP CL */ - cof0 = vec_madd(t1, t23r, cof0); /* JOH NKD BGP FCL + cof0 */ - t12 = vec_madd(t1, t2, vzero); /* JC NG BK FO */ - t12 = vec_perm(t12, t12, _VECTORMATH_PERM_YXWZ); /* NG JC FO BK */ - cof0 = vec_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ - t12r = vec_sld(t12, t12, 8); /* FO BK NG JC */ - cof0 = vec_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ - t1r = vec_sld(t1, t1, 8); /* B F J N */ - t2r = vec_sld(t2, t2, 8); /* K O C G */ - t1r3 = vec_madd(t1r, t3, vzero); /* BL FP JD NH */ - t1r3 = vec_perm(t1r3, t1r3, _VECTORMATH_PERM_YXWZ); /* FP BL NH JD */ - cof0 = vec_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ - t1r3r = vec_sld(t1r3, t1r3, 8); /* NH JD FP BL */ - cof0 = vec_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ - return floatInVec( _vmathVfDot4(t0,cof0), 0 ); -} - -inline const Matrix4 Matrix4::operator +( const Matrix4 & mat ) const -{ - return Matrix4( - ( mCol0 + mat.mCol0 ), - ( mCol1 + mat.mCol1 ), - ( mCol2 + mat.mCol2 ), - ( mCol3 + mat.mCol3 ) - ); -} - -inline const Matrix4 Matrix4::operator -( const Matrix4 & mat ) const -{ - return Matrix4( - ( mCol0 - mat.mCol0 ), - ( mCol1 - mat.mCol1 ), - ( mCol2 - mat.mCol2 ), - ( mCol3 - mat.mCol3 ) - ); -} - -inline Matrix4 & Matrix4::operator +=( const Matrix4 & mat ) -{ - *this = *this + mat; - return *this; -} - -inline Matrix4 & Matrix4::operator -=( const Matrix4 & mat ) -{ - *this = *this - mat; - return *this; -} - -inline const Matrix4 Matrix4::operator -( ) const -{ - return Matrix4( - ( -mCol0 ), - ( -mCol1 ), - ( -mCol2 ), - ( -mCol3 ) - ); -} - -inline const Matrix4 absPerElem( const Matrix4 & mat ) -{ - return Matrix4( - absPerElem( mat.getCol0() ), - absPerElem( mat.getCol1() ), - absPerElem( mat.getCol2() ), - absPerElem( mat.getCol3() ) - ); -} - -inline const Matrix4 Matrix4::operator *( float scalar ) const -{ - return *this * floatInVec(scalar); -} - -inline const Matrix4 Matrix4::operator *( floatInVec scalar ) const -{ - return Matrix4( - ( mCol0 * scalar ), - ( mCol1 * scalar ), - ( mCol2 * scalar ), - ( mCol3 * scalar ) - ); -} - -inline Matrix4 & Matrix4::operator *=( float scalar ) -{ - return *this *= floatInVec(scalar); -} - -inline Matrix4 & Matrix4::operator *=( floatInVec scalar ) -{ - *this = *this * scalar; - return *this; -} - -inline const Matrix4 operator *( float scalar, const Matrix4 & mat ) -{ - return floatInVec(scalar) * mat; -} - -inline const Matrix4 operator *( floatInVec scalar, const Matrix4 & mat ) -{ - return mat * scalar; -} - -inline const Vector4 Matrix4::operator *( Vector4 vec ) const -{ - vec_float4 tmp0, tmp1, res; - vec_float4 xxxx, yyyy, zzzz, wwww; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - xxxx = vec_splat( vec.get128(), 0 ); - yyyy = vec_splat( vec.get128(), 1 ); - zzzz = vec_splat( vec.get128(), 2 ); - wwww = vec_splat( vec.get128(), 3 ); - tmp0 = vec_madd( mCol0.get128(), xxxx, zero ); - tmp1 = vec_madd( mCol1.get128(), yyyy, zero ); - tmp0 = vec_madd( mCol2.get128(), zzzz, tmp0 ); - tmp1 = vec_madd( mCol3.get128(), wwww, tmp1 ); - res = vec_add( tmp0, tmp1 ); - return Vector4( res ); -} - -inline const Vector4 Matrix4::operator *( Vector3 vec ) const -{ - vec_float4 res; - vec_float4 xxxx, yyyy, zzzz; - xxxx = vec_splat( vec.get128(), 0 ); - yyyy = vec_splat( vec.get128(), 1 ); - zzzz = vec_splat( vec.get128(), 2 ); - res = vec_madd( mCol0.get128(), xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - res = vec_madd( mCol1.get128(), yyyy, res ); - res = vec_madd( mCol2.get128(), zzzz, res ); - return Vector4( res ); -} - -inline const Vector4 Matrix4::operator *( Point3 pnt ) const -{ - vec_float4 tmp0, tmp1, res; - vec_float4 xxxx, yyyy, zzzz; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - xxxx = vec_splat( pnt.get128(), 0 ); - yyyy = vec_splat( pnt.get128(), 1 ); - zzzz = vec_splat( pnt.get128(), 2 ); - tmp0 = vec_madd( mCol0.get128(), xxxx, zero ); - tmp1 = vec_madd( mCol1.get128(), yyyy, zero ); - tmp0 = vec_madd( mCol2.get128(), zzzz, tmp0 ); - tmp1 = vec_add( mCol3.get128(), tmp1 ); - res = vec_add( tmp0, tmp1 ); - return Vector4( res ); -} - -inline const Matrix4 Matrix4::operator *( const Matrix4 & mat ) const -{ - return Matrix4( - ( *this * mat.mCol0 ), - ( *this * mat.mCol1 ), - ( *this * mat.mCol2 ), - ( *this * mat.mCol3 ) - ); -} - -inline Matrix4 & Matrix4::operator *=( const Matrix4 & mat ) -{ - *this = *this * mat; - return *this; -} - -inline const Matrix4 Matrix4::operator *( const Transform3 & tfrm ) const -{ - return Matrix4( - ( *this * tfrm.getCol0() ), - ( *this * tfrm.getCol1() ), - ( *this * tfrm.getCol2() ), - ( *this * Point3( tfrm.getCol3() ) ) - ); -} - -inline Matrix4 & Matrix4::operator *=( const Transform3 & tfrm ) -{ - *this = *this * tfrm; - return *this; -} - -inline const Matrix4 mulPerElem( const Matrix4 & mat0, const Matrix4 & mat1 ) -{ - return Matrix4( - mulPerElem( mat0.getCol0(), mat1.getCol0() ), - mulPerElem( mat0.getCol1(), mat1.getCol1() ), - mulPerElem( mat0.getCol2(), mat1.getCol2() ), - mulPerElem( mat0.getCol3(), mat1.getCol3() ) - ); -} - -inline const Matrix4 Matrix4::identity( ) -{ - return Matrix4( - Vector4::xAxis( ), - Vector4::yAxis( ), - Vector4::zAxis( ), - Vector4::wAxis( ) - ); -} - -inline Matrix4 & Matrix4::setUpper3x3( const Matrix3 & mat3 ) -{ - mCol0.setXYZ( mat3.getCol0() ); - mCol1.setXYZ( mat3.getCol1() ); - mCol2.setXYZ( mat3.getCol2() ); - return *this; -} - -inline const Matrix3 Matrix4::getUpper3x3( ) const -{ - return Matrix3( - mCol0.getXYZ( ), - mCol1.getXYZ( ), - mCol2.getXYZ( ) - ); -} - -inline Matrix4 & Matrix4::setTranslation( Vector3 translateVec ) -{ - mCol3.setXYZ( translateVec ); - return *this; -} - -inline const Vector3 Matrix4::getTranslation( ) const -{ - return mCol3.getXYZ( ); -} - -inline const Matrix4 Matrix4::rotationX( float radians ) -{ - return rotationX( floatInVec(radians) ); -} - -inline const Matrix4 Matrix4::rotationX( floatInVec radians ) -{ - vec_float4 s, c, res1, res2; - vec_uint4 select_y, select_z; - vec_float4 zero; - select_y = _VECTORMATH_MASK_0x0F00; - select_z = _VECTORMATH_MASK_0x00F0; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( radians.get128(), &s, &c ); - res1 = vec_sel( zero, c, select_y ); - res1 = vec_sel( res1, s, select_z ); - res2 = vec_sel( zero, negatef4(s), select_y ); - res2 = vec_sel( res2, c, select_z ); - return Matrix4( - Vector4::xAxis( ), - Vector4( res1 ), - Vector4( res2 ), - Vector4::wAxis( ) - ); -} - -inline const Matrix4 Matrix4::rotationY( float radians ) -{ - return rotationY( floatInVec(radians) ); -} - -inline const Matrix4 Matrix4::rotationY( floatInVec radians ) -{ - vec_float4 s, c, res0, res2; - vec_uint4 select_x, select_z; - vec_float4 zero; - select_x = _VECTORMATH_MASK_0xF000; - select_z = _VECTORMATH_MASK_0x00F0; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( radians.get128(), &s, &c ); - res0 = vec_sel( zero, c, select_x ); - res0 = vec_sel( res0, negatef4(s), select_z ); - res2 = vec_sel( zero, s, select_x ); - res2 = vec_sel( res2, c, select_z ); - return Matrix4( - Vector4( res0 ), - Vector4::yAxis( ), - Vector4( res2 ), - Vector4::wAxis( ) - ); -} - -inline const Matrix4 Matrix4::rotationZ( float radians ) -{ - return rotationZ( floatInVec(radians) ); -} - -inline const Matrix4 Matrix4::rotationZ( floatInVec radians ) -{ - vec_float4 s, c, res0, res1; - vec_uint4 select_x, select_y; - vec_float4 zero; - select_x = _VECTORMATH_MASK_0xF000; - select_y = _VECTORMATH_MASK_0x0F00; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( radians.get128(), &s, &c ); - res0 = vec_sel( zero, c, select_x ); - res0 = vec_sel( res0, s, select_y ); - res1 = vec_sel( zero, negatef4(s), select_x ); - res1 = vec_sel( res1, c, select_y ); - return Matrix4( - Vector4( res0 ), - Vector4( res1 ), - Vector4::zAxis( ), - Vector4::wAxis( ) - ); -} - -inline const Matrix4 Matrix4::rotationZYX( Vector3 radiansXYZ ) -{ - vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - angles = Vector4( radiansXYZ, 0.0f ).get128(); - sincosf4( angles, &s, &c ); - negS = negatef4( s ); - Z0 = vec_mergel( c, s ); - Z1 = vec_mergel( negS, c ); - Z1 = vec_andc( Z1, (vec_float4)_VECTORMATH_MASK_0x000F ); - Y0 = vec_perm( negS, c, _VECTORMATH_PERM_BBYX ); - Y1 = vec_perm( c, s, _VECTORMATH_PERM_BBYX ); - X0 = vec_splat( s, 0 ); - X1 = vec_splat( c, 0 ); - tmp = vec_madd( Z0, Y1, zero ); - return Matrix4( - Vector4( vec_madd( Z0, Y0, zero ) ), - Vector4( vec_madd( Z1, X1, vec_madd( tmp, X0, zero ) ) ), - Vector4( vec_nmsub( Z1, X0, vec_madd( tmp, X1, zero ) ) ), - Vector4::wAxis( ) - ); -} - -inline const Matrix4 Matrix4::rotation( float radians, Vector3 unitVec ) -{ - return rotation( floatInVec(radians), unitVec ); -} - -inline const Matrix4 Matrix4::rotation( floatInVec radians, Vector3 unitVec ) -{ - vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2, zeroW; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - axis = unitVec.get128(); - sincosf4( radians.get128(), &s, &c ); - xxxx = vec_splat( axis, 0 ); - yyyy = vec_splat( axis, 1 ); - zzzz = vec_splat( axis, 2 ); - oneMinusC = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), c ); - axisS = vec_madd( axis, s, zero ); - negAxisS = negatef4( axisS ); - tmp0 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_XZBX ); - tmp1 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_CXXX ); - tmp2 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_YAXX ); - tmp0 = vec_sel( tmp0, c, _VECTORMATH_MASK_0xF000 ); - tmp1 = vec_sel( tmp1, c, _VECTORMATH_MASK_0x0F00 ); - tmp2 = vec_sel( tmp2, c, _VECTORMATH_MASK_0x00F0 ); - zeroW = (vec_float4)_VECTORMATH_MASK_0x000F; - axis = vec_andc( axis, zeroW ); - tmp0 = vec_andc( tmp0, zeroW ); - tmp1 = vec_andc( tmp1, zeroW ); - tmp2 = vec_andc( tmp2, zeroW ); - return Matrix4( - Vector4( vec_madd( vec_madd( axis, xxxx, zero ), oneMinusC, tmp0 ) ), - Vector4( vec_madd( vec_madd( axis, yyyy, zero ), oneMinusC, tmp1 ) ), - Vector4( vec_madd( vec_madd( axis, zzzz, zero ), oneMinusC, tmp2 ) ), - Vector4::wAxis( ) - ); -} - -inline const Matrix4 Matrix4::rotation( Quat unitQuat ) -{ - return Matrix4( Transform3::rotation( unitQuat ) ); -} - -inline const Matrix4 Matrix4::scale( Vector3 scaleVec ) -{ - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - return Matrix4( - Vector4( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0xF000 ) ), - Vector4( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0x0F00 ) ), - Vector4( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0x00F0 ) ), - Vector4::wAxis( ) - ); -} - -inline const Matrix4 appendScale( const Matrix4 & mat, Vector3 scaleVec ) -{ - return Matrix4( - ( mat.getCol0() * scaleVec.getX( ) ), - ( mat.getCol1() * scaleVec.getY( ) ), - ( mat.getCol2() * scaleVec.getZ( ) ), - mat.getCol3() - ); -} - -inline const Matrix4 prependScale( Vector3 scaleVec, const Matrix4 & mat ) -{ - Vector4 scale4; - scale4 = Vector4( scaleVec, 1.0f ); - return Matrix4( - mulPerElem( mat.getCol0(), scale4 ), - mulPerElem( mat.getCol1(), scale4 ), - mulPerElem( mat.getCol2(), scale4 ), - mulPerElem( mat.getCol3(), scale4 ) - ); -} - -inline const Matrix4 Matrix4::translation( Vector3 translateVec ) -{ - return Matrix4( - Vector4::xAxis( ), - Vector4::yAxis( ), - Vector4::zAxis( ), - Vector4( translateVec, 1.0f ) - ); -} - -inline const Matrix4 Matrix4::lookAt( Point3 eyePos, Point3 lookAtPos, Vector3 upVec ) -{ - Matrix4 m4EyeFrame; - Vector3 v3X, v3Y, v3Z; - v3Y = normalize( upVec ); - v3Z = normalize( ( eyePos - lookAtPos ) ); - v3X = normalize( cross( v3Y, v3Z ) ); - v3Y = cross( v3Z, v3X ); - m4EyeFrame = Matrix4( Vector4( v3X ), Vector4( v3Y ), Vector4( v3Z ), Vector4( eyePos ) ); - return orthoInverse( m4EyeFrame ); -} - -inline const Matrix4 Matrix4::perspective( float fovyRadians, float aspect, float zNear, float zFar ) -{ - float f, rangeInv; - vec_float4 zero, col0, col1, col2, col3; - union { vec_float4 v; float s[4]; } tmp; - f = tanf( _VECTORMATH_PI_OVER_2 - fovyRadians * 0.5f ); - rangeInv = 1.0f / ( zNear - zFar ); - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - tmp.v = zero; - tmp.s[0] = f / aspect; - col0 = tmp.v; - tmp.v = zero; - tmp.s[1] = f; - col1 = tmp.v; - tmp.v = zero; - tmp.s[2] = ( zNear + zFar ) * rangeInv; - tmp.s[3] = -1.0f; - col2 = tmp.v; - tmp.v = zero; - tmp.s[2] = zNear * zFar * rangeInv * 2.0f; - col3 = tmp.v; - return Matrix4( - Vector4( col0 ), - Vector4( col1 ), - Vector4( col2 ), - Vector4( col3 ) - ); -} - -inline const Matrix4 Matrix4::frustum( float left, float right, float bottom, float top, float zNear, float zFar ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vec_float4 lbf, rtn; - vec_float4 diff, sum, inv_diff; - vec_float4 diagonal, column, near2; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - union { vec_float4 v; float s[4]; } l, f, r, n, b, t; - l.s[0] = left; - f.s[0] = zFar; - r.s[0] = right; - n.s[0] = zNear; - b.s[0] = bottom; - t.s[0] = top; - lbf = vec_mergeh( l.v, f.v ); - rtn = vec_mergeh( r.v, n.v ); - lbf = vec_mergeh( lbf, b.v ); - rtn = vec_mergeh( rtn, t.v ); - diff = vec_sub( rtn, lbf ); - sum = vec_add( rtn, lbf ); - inv_diff = recipf4( diff ); - near2 = vec_splat( n.v, 0 ); - near2 = vec_add( near2, near2 ); - diagonal = vec_madd( near2, inv_diff, zero ); - column = vec_madd( sum, inv_diff, zero ); - return Matrix4( - Vector4( vec_sel( zero, diagonal, _VECTORMATH_MASK_0xF000 ) ), - Vector4( vec_sel( zero, diagonal, _VECTORMATH_MASK_0x0F00 ) ), - Vector4( vec_sel( column, ((vec_float4){-1.0f,-1.0f,-1.0f,-1.0f}), _VECTORMATH_MASK_0x000F ) ), - Vector4( vec_sel( zero, vec_madd( diagonal, vec_splat( f.v, 0 ), zero ), _VECTORMATH_MASK_0x00F0 ) ) - ); -} - -inline const Matrix4 Matrix4::orthographic( float left, float right, float bottom, float top, float zNear, float zFar ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vec_float4 lbf, rtn; - vec_float4 diff, sum, inv_diff, neg_inv_diff; - vec_float4 diagonal, column; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - union { vec_float4 v; float s[4]; } l, f, r, n, b, t; - l.s[0] = left; - f.s[0] = zFar; - r.s[0] = right; - n.s[0] = zNear; - b.s[0] = bottom; - t.s[0] = top; - lbf = vec_mergeh( l.v, f.v ); - rtn = vec_mergeh( r.v, n.v ); - lbf = vec_mergeh( lbf, b.v ); - rtn = vec_mergeh( rtn, t.v ); - diff = vec_sub( rtn, lbf ); - sum = vec_add( rtn, lbf ); - inv_diff = recipf4( diff ); - neg_inv_diff = negatef4( inv_diff ); - diagonal = vec_add( inv_diff, inv_diff ); - column = vec_madd( sum, vec_sel( neg_inv_diff, inv_diff, _VECTORMATH_MASK_0x00F0 ), zero ); - return Matrix4( - Vector4( vec_sel( zero, diagonal, _VECTORMATH_MASK_0xF000 ) ), - Vector4( vec_sel( zero, diagonal, _VECTORMATH_MASK_0x0F00 ) ), - Vector4( vec_sel( zero, diagonal, _VECTORMATH_MASK_0x00F0 ) ), - Vector4( vec_sel( column, ((vec_float4){1.0f,1.0f,1.0f,1.0f}), _VECTORMATH_MASK_0x000F ) ) - ); -} - -inline const Matrix4 select( const Matrix4 & mat0, const Matrix4 & mat1, bool select1 ) -{ - return Matrix4( - select( mat0.getCol0(), mat1.getCol0(), select1 ), - select( mat0.getCol1(), mat1.getCol1(), select1 ), - select( mat0.getCol2(), mat1.getCol2(), select1 ), - select( mat0.getCol3(), mat1.getCol3(), select1 ) - ); -} - -inline const Matrix4 select( const Matrix4 & mat0, const Matrix4 & mat1, boolInVec select1 ) -{ - return Matrix4( - select( mat0.getCol0(), mat1.getCol0(), select1 ), - select( mat0.getCol1(), mat1.getCol1(), select1 ), - select( mat0.getCol2(), mat1.getCol2(), select1 ), - select( mat0.getCol3(), mat1.getCol3(), select1 ) - ); -} - -#ifdef _VECTORMATH_DEBUG - -inline void print( const Matrix4 & mat ) -{ - print( mat.getRow( 0 ) ); - print( mat.getRow( 1 ) ); - print( mat.getRow( 2 ) ); - print( mat.getRow( 3 ) ); -} - -inline void print( const Matrix4 & mat, const char * name ) -{ - printf("%s:\n", name); - print( mat ); -} - -#endif - -inline Transform3::Transform3( const Transform3 & tfrm ) -{ - mCol0 = tfrm.mCol0; - mCol1 = tfrm.mCol1; - mCol2 = tfrm.mCol2; - mCol3 = tfrm.mCol3; -} - -inline Transform3::Transform3( float scalar ) -{ - mCol0 = Vector3( scalar ); - mCol1 = Vector3( scalar ); - mCol2 = Vector3( scalar ); - mCol3 = Vector3( scalar ); -} - -inline Transform3::Transform3( floatInVec scalar ) -{ - mCol0 = Vector3( scalar ); - mCol1 = Vector3( scalar ); - mCol2 = Vector3( scalar ); - mCol3 = Vector3( scalar ); -} - -inline Transform3::Transform3( Vector3 _col0, Vector3 _col1, Vector3 _col2, Vector3 _col3 ) -{ - mCol0 = _col0; - mCol1 = _col1; - mCol2 = _col2; - mCol3 = _col3; -} - -inline Transform3::Transform3( const Matrix3 & tfrm, Vector3 translateVec ) -{ - this->setUpper3x3( tfrm ); - this->setTranslation( translateVec ); -} - -inline Transform3::Transform3( Quat unitQuat, Vector3 translateVec ) -{ - this->setUpper3x3( Matrix3( unitQuat ) ); - this->setTranslation( translateVec ); -} - -inline Transform3 & Transform3::setCol0( Vector3 _col0 ) -{ - mCol0 = _col0; - return *this; -} - -inline Transform3 & Transform3::setCol1( Vector3 _col1 ) -{ - mCol1 = _col1; - return *this; -} - -inline Transform3 & Transform3::setCol2( Vector3 _col2 ) -{ - mCol2 = _col2; - return *this; -} - -inline Transform3 & Transform3::setCol3( Vector3 _col3 ) -{ - mCol3 = _col3; - return *this; -} - -inline Transform3 & Transform3::setCol( int col, Vector3 vec ) -{ - *(&mCol0 + col) = vec; - return *this; -} - -inline Transform3 & Transform3::setRow( int row, Vector4 vec ) -{ - mCol0.setElem( row, vec.getElem( 0 ) ); - mCol1.setElem( row, vec.getElem( 1 ) ); - mCol2.setElem( row, vec.getElem( 2 ) ); - mCol3.setElem( row, vec.getElem( 3 ) ); - return *this; -} - -inline Transform3 & Transform3::setElem( int col, int row, float val ) -{ - (*this)[col].setElem(row, val); - return *this; -} - -inline Transform3 & Transform3::setElem( int col, int row, floatInVec val ) -{ - Vector3 tmpV3_0; - tmpV3_0 = this->getCol( col ); - tmpV3_0.setElem( row, val ); - this->setCol( col, tmpV3_0 ); - return *this; -} - -inline const floatInVec Transform3::getElem( int col, int row ) const -{ - return this->getCol( col ).getElem( row ); -} - -inline const Vector3 Transform3::getCol0( ) const -{ - return mCol0; -} - -inline const Vector3 Transform3::getCol1( ) const -{ - return mCol1; -} - -inline const Vector3 Transform3::getCol2( ) const -{ - return mCol2; -} - -inline const Vector3 Transform3::getCol3( ) const -{ - return mCol3; -} - -inline const Vector3 Transform3::getCol( int col ) const -{ - return *(&mCol0 + col); -} - -inline const Vector4 Transform3::getRow( int row ) const -{ - return Vector4( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ), mCol3.getElem( row ) ); -} - -inline Vector3 & Transform3::operator []( int col ) -{ - return *(&mCol0 + col); -} - -inline const Vector3 Transform3::operator []( int col ) const -{ - return *(&mCol0 + col); -} - -inline Transform3 & Transform3::operator =( const Transform3 & tfrm ) -{ - mCol0 = tfrm.mCol0; - mCol1 = tfrm.mCol1; - mCol2 = tfrm.mCol2; - mCol3 = tfrm.mCol3; - return *this; -} - -inline const Transform3 inverse( const Transform3 & tfrm ) -{ - vec_float4 inv0, inv1, inv2, inv3; - vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet; - vec_float4 xxxx, yyyy, zzzz; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - tmp2 = _vmathVfCross( tfrm.getCol0().get128(), tfrm.getCol1().get128() ); - tmp0 = _vmathVfCross( tfrm.getCol1().get128(), tfrm.getCol2().get128() ); - tmp1 = _vmathVfCross( tfrm.getCol2().get128(), tfrm.getCol0().get128() ); - inv3 = negatef4( tfrm.getCol3().get128() ); - dot = _vmathVfDot3( tmp2, tfrm.getCol2().get128() ); - dot = vec_splat( dot, 0 ); - invdet = recipf4( dot ); - tmp3 = vec_mergeh( tmp0, tmp2 ); - tmp4 = vec_mergel( tmp0, tmp2 ); - inv0 = vec_mergeh( tmp3, tmp1 ); - xxxx = vec_splat( inv3, 0 ); - inv1 = vec_perm( tmp3, tmp1, _VECTORMATH_PERM_ZBWX ); - inv2 = vec_perm( tmp4, tmp1, _VECTORMATH_PERM_XCYX ); - yyyy = vec_splat( inv3, 1 ); - zzzz = vec_splat( inv3, 2 ); - inv3 = vec_madd( inv0, xxxx, zero ); - inv3 = vec_madd( inv1, yyyy, inv3 ); - inv3 = vec_madd( inv2, zzzz, inv3 ); - inv0 = vec_madd( inv0, invdet, zero ); - inv1 = vec_madd( inv1, invdet, zero ); - inv2 = vec_madd( inv2, invdet, zero ); - inv3 = vec_madd( inv3, invdet, zero ); - return Transform3( - Vector3( inv0 ), - Vector3( inv1 ), - Vector3( inv2 ), - Vector3( inv3 ) - ); -} - -inline const Transform3 orthoInverse( const Transform3 & tfrm ) -{ - vec_float4 inv0, inv1, inv2, inv3; - vec_float4 tmp0, tmp1; - vec_float4 xxxx, yyyy, zzzz; - tmp0 = vec_mergeh( tfrm.getCol0().get128(), tfrm.getCol2().get128() ); - tmp1 = vec_mergel( tfrm.getCol0().get128(), tfrm.getCol2().get128() ); - inv3 = negatef4( tfrm.getCol3().get128() ); - inv0 = vec_mergeh( tmp0, tfrm.getCol1().get128() ); - xxxx = vec_splat( inv3, 0 ); - inv1 = vec_perm( tmp0, tfrm.getCol1().get128(), _VECTORMATH_PERM_ZBWX ); - inv2 = vec_perm( tmp1, tfrm.getCol1().get128(), _VECTORMATH_PERM_XCYX ); - yyyy = vec_splat( inv3, 1 ); - zzzz = vec_splat( inv3, 2 ); - inv3 = vec_madd( inv0, xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - inv3 = vec_madd( inv1, yyyy, inv3 ); - inv3 = vec_madd( inv2, zzzz, inv3 ); - return Transform3( - Vector3( inv0 ), - Vector3( inv1 ), - Vector3( inv2 ), - Vector3( inv3 ) - ); -} - -inline const Transform3 absPerElem( const Transform3 & tfrm ) -{ - return Transform3( - absPerElem( tfrm.getCol0() ), - absPerElem( tfrm.getCol1() ), - absPerElem( tfrm.getCol2() ), - absPerElem( tfrm.getCol3() ) - ); -} - -inline const Vector3 Transform3::operator *( Vector3 vec ) const -{ - vec_float4 res; - vec_float4 xxxx, yyyy, zzzz; - xxxx = vec_splat( vec.get128(), 0 ); - yyyy = vec_splat( vec.get128(), 1 ); - zzzz = vec_splat( vec.get128(), 2 ); - res = vec_madd( mCol0.get128(), xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - res = vec_madd( mCol1.get128(), yyyy, res ); - res = vec_madd( mCol2.get128(), zzzz, res ); - return Vector3( res ); -} - -inline const Point3 Transform3::operator *( Point3 pnt ) const -{ - vec_float4 tmp0, tmp1, res; - vec_float4 xxxx, yyyy, zzzz; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - xxxx = vec_splat( pnt.get128(), 0 ); - yyyy = vec_splat( pnt.get128(), 1 ); - zzzz = vec_splat( pnt.get128(), 2 ); - tmp0 = vec_madd( mCol0.get128(), xxxx, zero ); - tmp1 = vec_madd( mCol1.get128(), yyyy, zero ); - tmp0 = vec_madd( mCol2.get128(), zzzz, tmp0 ); - tmp1 = vec_add( mCol3.get128(), tmp1 ); - res = vec_add( tmp0, tmp1 ); - return Point3( res ); -} - -inline const Transform3 Transform3::operator *( const Transform3 & tfrm ) const -{ - return Transform3( - ( *this * tfrm.mCol0 ), - ( *this * tfrm.mCol1 ), - ( *this * tfrm.mCol2 ), - Vector3( ( *this * Point3( tfrm.mCol3 ) ) ) - ); -} - -inline Transform3 & Transform3::operator *=( const Transform3 & tfrm ) -{ - *this = *this * tfrm; - return *this; -} - -inline const Transform3 mulPerElem( const Transform3 & tfrm0, const Transform3 & tfrm1 ) -{ - return Transform3( - mulPerElem( tfrm0.getCol0(), tfrm1.getCol0() ), - mulPerElem( tfrm0.getCol1(), tfrm1.getCol1() ), - mulPerElem( tfrm0.getCol2(), tfrm1.getCol2() ), - mulPerElem( tfrm0.getCol3(), tfrm1.getCol3() ) - ); -} - -inline const Transform3 Transform3::identity( ) -{ - return Transform3( - Vector3::xAxis( ), - Vector3::yAxis( ), - Vector3::zAxis( ), - Vector3( 0.0f ) - ); -} - -inline Transform3 & Transform3::setUpper3x3( const Matrix3 & tfrm ) -{ - mCol0 = tfrm.getCol0(); - mCol1 = tfrm.getCol1(); - mCol2 = tfrm.getCol2(); - return *this; -} - -inline const Matrix3 Transform3::getUpper3x3( ) const -{ - return Matrix3( mCol0, mCol1, mCol2 ); -} - -inline Transform3 & Transform3::setTranslation( Vector3 translateVec ) -{ - mCol3 = translateVec; - return *this; -} - -inline const Vector3 Transform3::getTranslation( ) const -{ - return mCol3; -} - -inline const Transform3 Transform3::rotationX( float radians ) -{ - return rotationX( floatInVec(radians) ); -} - -inline const Transform3 Transform3::rotationX( floatInVec radians ) -{ - vec_float4 s, c, res1, res2; - vec_uint4 select_y, select_z; - vec_float4 zero; - select_y = _VECTORMATH_MASK_0x0F00; - select_z = _VECTORMATH_MASK_0x00F0; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( radians.get128(), &s, &c ); - res1 = vec_sel( zero, c, select_y ); - res1 = vec_sel( res1, s, select_z ); - res2 = vec_sel( zero, negatef4(s), select_y ); - res2 = vec_sel( res2, c, select_z ); - return Transform3( - Vector3::xAxis( ), - Vector3( res1 ), - Vector3( res2 ), - Vector3( 0.0f ) - ); -} - -inline const Transform3 Transform3::rotationY( float radians ) -{ - return rotationY( floatInVec(radians) ); -} - -inline const Transform3 Transform3::rotationY( floatInVec radians ) -{ - vec_float4 s, c, res0, res2; - vec_uint4 select_x, select_z; - vec_float4 zero; - select_x = _VECTORMATH_MASK_0xF000; - select_z = _VECTORMATH_MASK_0x00F0; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( radians.get128(), &s, &c ); - res0 = vec_sel( zero, c, select_x ); - res0 = vec_sel( res0, negatef4(s), select_z ); - res2 = vec_sel( zero, s, select_x ); - res2 = vec_sel( res2, c, select_z ); - return Transform3( - Vector3( res0 ), - Vector3::yAxis( ), - Vector3( res2 ), - Vector3( 0.0f ) - ); -} - -inline const Transform3 Transform3::rotationZ( float radians ) -{ - return rotationZ( floatInVec(radians) ); -} - -inline const Transform3 Transform3::rotationZ( floatInVec radians ) -{ - vec_float4 s, c, res0, res1; - vec_uint4 select_x, select_y; - vec_float4 zero; - select_x = _VECTORMATH_MASK_0xF000; - select_y = _VECTORMATH_MASK_0x0F00; - zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - sincosf4( radians.get128(), &s, &c ); - res0 = vec_sel( zero, c, select_x ); - res0 = vec_sel( res0, s, select_y ); - res1 = vec_sel( zero, negatef4(s), select_x ); - res1 = vec_sel( res1, c, select_y ); - return Transform3( - Vector3( res0 ), - Vector3( res1 ), - Vector3::zAxis( ), - Vector3( 0.0f ) - ); -} - -inline const Transform3 Transform3::rotationZYX( Vector3 radiansXYZ ) -{ - vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - angles = Vector4( radiansXYZ, 0.0f ).get128(); - sincosf4( angles, &s, &c ); - negS = negatef4( s ); - Z0 = vec_mergel( c, s ); - Z1 = vec_mergel( negS, c ); - Z1 = vec_andc( Z1, (vec_float4)_VECTORMATH_MASK_0x000F ); - Y0 = vec_perm( negS, c, _VECTORMATH_PERM_BBYX ); - Y1 = vec_perm( c, s, _VECTORMATH_PERM_BBYX ); - X0 = vec_splat( s, 0 ); - X1 = vec_splat( c, 0 ); - tmp = vec_madd( Z0, Y1, zero ); - return Transform3( - Vector3( vec_madd( Z0, Y0, zero ) ), - Vector3( vec_madd( Z1, X1, vec_madd( tmp, X0, zero ) ) ), - Vector3( vec_nmsub( Z1, X0, vec_madd( tmp, X1, zero ) ) ), - Vector3( 0.0f ) - ); -} - -inline const Transform3 Transform3::rotation( float radians, Vector3 unitVec ) -{ - return rotation( floatInVec(radians), unitVec ); -} - -inline const Transform3 Transform3::rotation( floatInVec radians, Vector3 unitVec ) -{ - return Transform3( Matrix3::rotation( radians, unitVec ), Vector3( 0.0f ) ); -} - -inline const Transform3 Transform3::rotation( Quat unitQuat ) -{ - return Transform3( Matrix3( unitQuat ), Vector3( 0.0f ) ); -} - -inline const Transform3 Transform3::scale( Vector3 scaleVec ) -{ - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - return Transform3( - Vector3( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0xF000 ) ), - Vector3( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0x0F00 ) ), - Vector3( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0x00F0 ) ), - Vector3( 0.0f ) - ); -} - -inline const Transform3 appendScale( const Transform3 & tfrm, Vector3 scaleVec ) -{ - return Transform3( - ( tfrm.getCol0() * scaleVec.getX( ) ), - ( tfrm.getCol1() * scaleVec.getY( ) ), - ( tfrm.getCol2() * scaleVec.getZ( ) ), - tfrm.getCol3() - ); -} - -inline const Transform3 prependScale( Vector3 scaleVec, const Transform3 & tfrm ) -{ - return Transform3( - mulPerElem( tfrm.getCol0(), scaleVec ), - mulPerElem( tfrm.getCol1(), scaleVec ), - mulPerElem( tfrm.getCol2(), scaleVec ), - mulPerElem( tfrm.getCol3(), scaleVec ) - ); -} - -inline const Transform3 Transform3::translation( Vector3 translateVec ) -{ - return Transform3( - Vector3::xAxis( ), - Vector3::yAxis( ), - Vector3::zAxis( ), - translateVec - ); -} - -inline const Transform3 select( const Transform3 & tfrm0, const Transform3 & tfrm1, bool select1 ) -{ - return Transform3( - select( tfrm0.getCol0(), tfrm1.getCol0(), select1 ), - select( tfrm0.getCol1(), tfrm1.getCol1(), select1 ), - select( tfrm0.getCol2(), tfrm1.getCol2(), select1 ), - select( tfrm0.getCol3(), tfrm1.getCol3(), select1 ) - ); -} - -inline const Transform3 select( const Transform3 & tfrm0, const Transform3 & tfrm1, boolInVec select1 ) -{ - return Transform3( - select( tfrm0.getCol0(), tfrm1.getCol0(), select1 ), - select( tfrm0.getCol1(), tfrm1.getCol1(), select1 ), - select( tfrm0.getCol2(), tfrm1.getCol2(), select1 ), - select( tfrm0.getCol3(), tfrm1.getCol3(), select1 ) - ); -} - -#ifdef _VECTORMATH_DEBUG - -inline void print( const Transform3 & tfrm ) -{ - print( tfrm.getRow( 0 ) ); - print( tfrm.getRow( 1 ) ); - print( tfrm.getRow( 2 ) ); -} - -inline void print( const Transform3 & tfrm, const char * name ) -{ - printf("%s:\n", name); - print( tfrm ); -} - -#endif - -inline Quat::Quat( const Matrix3 & tfrm ) -{ - vec_float4 res; - vec_float4 col0, col1, col2; - vec_float4 xx_yy, xx_yy_zz_xx, yy_zz_xx_yy, zz_xx_yy_zz, diagSum, diagDiff; - vec_float4 zy_xz_yx, yz_zx_xy, sum, diff; - vec_float4 radicand, invSqrt, scale; - vec_float4 res0, res1, res2, res3; - vec_float4 xx, yy, zz; - vec_uint4 select_x = _VECTORMATH_MASK_0xF000; - vec_uint4 select_y = _VECTORMATH_MASK_0x0F00; - vec_uint4 select_z = _VECTORMATH_MASK_0x00F0; - vec_uint4 select_w = _VECTORMATH_MASK_0x000F; - vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); - - col0 = tfrm.getCol0().get128(); - col1 = tfrm.getCol1().get128(); - col2 = tfrm.getCol2().get128(); - - /* four cases: */ - /* trace > 0 */ - /* else */ - /* xx largest diagonal element */ - /* yy largest diagonal element */ - /* zz largest diagonal element */ - - /* compute quaternion for each case */ - - xx_yy = vec_sel( col0, col1, select_y ); - xx_yy_zz_xx = vec_perm( xx_yy, col2, _VECTORMATH_PERM_XYCX ); - yy_zz_xx_yy = vec_perm( xx_yy, col2, _VECTORMATH_PERM_YCXY ); - zz_xx_yy_zz = vec_perm( xx_yy, col2, _VECTORMATH_PERM_CXYC ); - - diagSum = vec_add( vec_add( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); - diagDiff = vec_sub( vec_sub( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); - radicand = vec_add( vec_sel( diagDiff, diagSum, select_w ), ((vec_float4){1.0f,1.0f,1.0f,1.0f}) ); - invSqrt = rsqrtf4( radicand ); - - zy_xz_yx = vec_sel( col0, col1, select_z ); - zy_xz_yx = vec_perm( zy_xz_yx, col2, _VECTORMATH_PERM_ZAYX ); - yz_zx_xy = vec_sel( col0, col1, select_x ); - yz_zx_xy = vec_perm( yz_zx_xy, col2, _VECTORMATH_PERM_BZXX ); - - sum = vec_add( zy_xz_yx, yz_zx_xy ); - diff = vec_sub( zy_xz_yx, yz_zx_xy ); - - scale = vec_madd( invSqrt, ((vec_float4){0.5f,0.5f,0.5f,0.5f}), zero ); - res0 = vec_perm( sum, diff, _VECTORMATH_PERM_XZYA ); - res1 = vec_perm( sum, diff, _VECTORMATH_PERM_ZXXB ); - res2 = vec_perm( sum, diff, _VECTORMATH_PERM_YXXC ); - res3 = diff; - res0 = vec_sel( res0, radicand, select_x ); - res1 = vec_sel( res1, radicand, select_y ); - res2 = vec_sel( res2, radicand, select_z ); - res3 = vec_sel( res3, radicand, select_w ); - res0 = vec_madd( res0, vec_splat( scale, 0 ), zero ); - res1 = vec_madd( res1, vec_splat( scale, 1 ), zero ); - res2 = vec_madd( res2, vec_splat( scale, 2 ), zero ); - res3 = vec_madd( res3, vec_splat( scale, 3 ), zero ); - - /* determine case and select answer */ - - xx = vec_splat( col0, 0 ); - yy = vec_splat( col1, 1 ); - zz = vec_splat( col2, 2 ); - res = vec_sel( res0, res1, vec_cmpgt( yy, xx ) ); - res = vec_sel( res, res2, vec_and( vec_cmpgt( zz, xx ), vec_cmpgt( zz, yy ) ) ); - res = vec_sel( res, res3, vec_cmpgt( vec_splat( diagSum, 0 ), zero ) ); - mVec128 = res; -} - -inline const Matrix3 outer( Vector3 tfrm0, Vector3 tfrm1 ) -{ - return Matrix3( - ( tfrm0 * tfrm1.getX( ) ), - ( tfrm0 * tfrm1.getY( ) ), - ( tfrm0 * tfrm1.getZ( ) ) - ); -} - -inline const Matrix4 outer( Vector4 tfrm0, Vector4 tfrm1 ) -{ - return Matrix4( - ( tfrm0 * tfrm1.getX( ) ), - ( tfrm0 * tfrm1.getY( ) ), - ( tfrm0 * tfrm1.getZ( ) ), - ( tfrm0 * tfrm1.getW( ) ) - ); -} - -inline const Vector3 rowMul( Vector3 vec, const Matrix3 & mat ) -{ - vec_float4 tmp0, tmp1, mcol0, mcol1, mcol2, res; - vec_float4 xxxx, yyyy, zzzz; - tmp0 = vec_mergeh( mat.getCol0().get128(), mat.getCol2().get128() ); - tmp1 = vec_mergel( mat.getCol0().get128(), mat.getCol2().get128() ); - xxxx = vec_splat( vec.get128(), 0 ); - mcol0 = vec_mergeh( tmp0, mat.getCol1().get128() ); - mcol1 = vec_perm( tmp0, mat.getCol1().get128(), _VECTORMATH_PERM_ZBWX ); - mcol2 = vec_perm( tmp1, mat.getCol1().get128(), _VECTORMATH_PERM_XCYX ); - yyyy = vec_splat( vec.get128(), 1 ); - res = vec_madd( mcol0, xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - zzzz = vec_splat( vec.get128(), 2 ); - res = vec_madd( mcol1, yyyy, res ); - res = vec_madd( mcol2, zzzz, res ); - return Vector3( res ); -} - -inline const Matrix3 crossMatrix( Vector3 vec ) -{ - vec_float4 neg, res0, res1, res2; - neg = negatef4( vec.get128() ); - res0 = vec_perm( vec.get128(), neg, _VECTORMATH_PERM_XZBX ); - res1 = vec_perm( vec.get128(), neg, _VECTORMATH_PERM_CXXX ); - res2 = vec_perm( vec.get128(), neg, _VECTORMATH_PERM_YAXX ); - res0 = vec_andc( res0, (vec_float4)_VECTORMATH_MASK_0xF000 ); - res1 = vec_andc( res1, (vec_float4)_VECTORMATH_MASK_0x0F00 ); - res2 = vec_andc( res2, (vec_float4)_VECTORMATH_MASK_0x00F0 ); - return Matrix3( - Vector3( res0 ), - Vector3( res1 ), - Vector3( res2 ) - ); -} - -inline const Matrix3 crossMatrixMul( Vector3 vec, const Matrix3 & mat ) -{ - return Matrix3( cross( vec, mat.getCol0() ), cross( vec, mat.getCol1() ), cross( vec, mat.getCol2() ) ); -} - -} // namespace Aos -} // namespace Vectormath - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_MAT_AOS_CPP_H +#define _VECTORMATH_MAT_AOS_CPP_H + +namespace Vectormath { +namespace Aos { + +//----------------------------------------------------------------------------- +// Constants +// for shuffles, words are labeled [x,y,z,w] [a,b,c,d] + +#define _VECTORMATH_PERM_ZBWX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_B, _VECTORMATH_PERM_W, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_XCYX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_C, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_XYAB ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A, _VECTORMATH_PERM_B }) +#define _VECTORMATH_PERM_ZWCD ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_W, _VECTORMATH_PERM_C, _VECTORMATH_PERM_D }) +#define _VECTORMATH_PERM_XZBX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_B, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_CXXX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_C, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_YAXX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_XAZC ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_A, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_C }) +#define _VECTORMATH_PERM_YXWZ ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X, _VECTORMATH_PERM_W, _VECTORMATH_PERM_Z }) +#define _VECTORMATH_PERM_YBWD ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_B, _VECTORMATH_PERM_W, _VECTORMATH_PERM_D }) +#define _VECTORMATH_PERM_XYCX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_C, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_YCXY ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_C, _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y }) +#define _VECTORMATH_PERM_CXYC ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_C, _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_C }) +#define _VECTORMATH_PERM_ZAYX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_BZXX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_B, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PERM_XZYA ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A }) +#define _VECTORMATH_PERM_ZXXB ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X, _VECTORMATH_PERM_B }) +#define _VECTORMATH_PERM_YXXC ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X, _VECTORMATH_PERM_X, _VECTORMATH_PERM_C }) +#define _VECTORMATH_PERM_BBYX ((vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_B, _VECTORMATH_PERM_B, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_X }) +#define _VECTORMATH_PI_OVER_2 1.570796327f + +//----------------------------------------------------------------------------- +// Definitions + +inline Matrix3::Matrix3( const Matrix3 & mat ) +{ + mCol0 = mat.mCol0; + mCol1 = mat.mCol1; + mCol2 = mat.mCol2; +} + +inline Matrix3::Matrix3( float scalar ) +{ + mCol0 = Vector3( scalar ); + mCol1 = Vector3( scalar ); + mCol2 = Vector3( scalar ); +} + +inline Matrix3::Matrix3( floatInVec scalar ) +{ + mCol0 = Vector3( scalar ); + mCol1 = Vector3( scalar ); + mCol2 = Vector3( scalar ); +} + +inline Matrix3::Matrix3( Quat unitQuat ) +{ + vec_float4 xyzw_2, wwww, yzxw, zxyw, yzxw_2, zxyw_2; + vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + vec_uint4 select_x = _VECTORMATH_MASK_0xF000; + vec_uint4 select_z = _VECTORMATH_MASK_0x00F0; + xyzw_2 = vec_add( unitQuat.get128(), unitQuat.get128() ); + wwww = vec_splat( unitQuat.get128(), 3 ); + yzxw = vec_perm( unitQuat.get128(), unitQuat.get128(), _VECTORMATH_PERM_YZXW ); + zxyw = vec_perm( unitQuat.get128(), unitQuat.get128(), _VECTORMATH_PERM_ZXYW ); + yzxw_2 = vec_perm( xyzw_2, xyzw_2, _VECTORMATH_PERM_YZXW ); + zxyw_2 = vec_perm( xyzw_2, xyzw_2, _VECTORMATH_PERM_ZXYW ); + tmp0 = vec_madd( yzxw_2, wwww, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + tmp1 = vec_nmsub( yzxw, yzxw_2, ((vec_float4){1.0f,1.0f,1.0f,1.0f}) ); + tmp2 = vec_madd( yzxw, xyzw_2, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + tmp0 = vec_madd( zxyw, xyzw_2, tmp0 ); + tmp1 = vec_nmsub( zxyw, zxyw_2, tmp1 ); + tmp2 = vec_nmsub( zxyw_2, wwww, tmp2 ); + tmp3 = vec_sel( tmp0, tmp1, select_x ); + tmp4 = vec_sel( tmp1, tmp2, select_x ); + tmp5 = vec_sel( tmp2, tmp0, select_x ); + mCol0 = Vector3( vec_sel( tmp3, tmp2, select_z ) ); + mCol1 = Vector3( vec_sel( tmp4, tmp0, select_z ) ); + mCol2 = Vector3( vec_sel( tmp5, tmp1, select_z ) ); +} + +inline Matrix3::Matrix3( Vector3 _col0, Vector3 _col1, Vector3 _col2 ) +{ + mCol0 = _col0; + mCol1 = _col1; + mCol2 = _col2; +} + +inline Matrix3 & Matrix3::setCol0( Vector3 _col0 ) +{ + mCol0 = _col0; + return *this; +} + +inline Matrix3 & Matrix3::setCol1( Vector3 _col1 ) +{ + mCol1 = _col1; + return *this; +} + +inline Matrix3 & Matrix3::setCol2( Vector3 _col2 ) +{ + mCol2 = _col2; + return *this; +} + +inline Matrix3 & Matrix3::setCol( int col, Vector3 vec ) +{ + *(&mCol0 + col) = vec; + return *this; +} + +inline Matrix3 & Matrix3::setRow( int row, Vector3 vec ) +{ + mCol0.setElem( row, vec.getElem( 0 ) ); + mCol1.setElem( row, vec.getElem( 1 ) ); + mCol2.setElem( row, vec.getElem( 2 ) ); + return *this; +} + +inline Matrix3 & Matrix3::setElem( int col, int row, float val ) +{ + (*this)[col].setElem(row, val); + return *this; +} + +inline Matrix3 & Matrix3::setElem( int col, int row, floatInVec val ) +{ + Vector3 tmpV3_0; + tmpV3_0 = this->getCol( col ); + tmpV3_0.setElem( row, val ); + this->setCol( col, tmpV3_0 ); + return *this; +} + +inline const floatInVec Matrix3::getElem( int col, int row ) const +{ + return this->getCol( col ).getElem( row ); +} + +inline const Vector3 Matrix3::getCol0( ) const +{ + return mCol0; +} + +inline const Vector3 Matrix3::getCol1( ) const +{ + return mCol1; +} + +inline const Vector3 Matrix3::getCol2( ) const +{ + return mCol2; +} + +inline const Vector3 Matrix3::getCol( int col ) const +{ + return *(&mCol0 + col); +} + +inline const Vector3 Matrix3::getRow( int row ) const +{ + return Vector3( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ) ); +} + +inline Vector3 & Matrix3::operator []( int col ) +{ + return *(&mCol0 + col); +} + +inline const Vector3 Matrix3::operator []( int col ) const +{ + return *(&mCol0 + col); +} + +inline Matrix3 & Matrix3::operator =( const Matrix3 & mat ) +{ + mCol0 = mat.mCol0; + mCol1 = mat.mCol1; + mCol2 = mat.mCol2; + return *this; +} + +inline const Matrix3 transpose( const Matrix3 & mat ) +{ + vec_float4 tmp0, tmp1, res0, res1, res2; + tmp0 = vec_mergeh( mat.getCol0().get128(), mat.getCol2().get128() ); + tmp1 = vec_mergel( mat.getCol0().get128(), mat.getCol2().get128() ); + res0 = vec_mergeh( tmp0, mat.getCol1().get128() ); + res1 = vec_perm( tmp0, mat.getCol1().get128(), _VECTORMATH_PERM_ZBWX ); + res2 = vec_perm( tmp1, mat.getCol1().get128(), _VECTORMATH_PERM_XCYX ); + return Matrix3( + Vector3( res0 ), + Vector3( res1 ), + Vector3( res2 ) + ); +} + +inline const Matrix3 inverse( const Matrix3 & mat ) +{ + vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet, inv0, inv1, inv2; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + tmp2 = _vmathVfCross( mat.getCol0().get128(), mat.getCol1().get128() ); + tmp0 = _vmathVfCross( mat.getCol1().get128(), mat.getCol2().get128() ); + tmp1 = _vmathVfCross( mat.getCol2().get128(), mat.getCol0().get128() ); + dot = _vmathVfDot3( tmp2, mat.getCol2().get128() ); + dot = vec_splat( dot, 0 ); + invdet = recipf4( dot ); + tmp3 = vec_mergeh( tmp0, tmp2 ); + tmp4 = vec_mergel( tmp0, tmp2 ); + inv0 = vec_mergeh( tmp3, tmp1 ); + inv1 = vec_perm( tmp3, tmp1, _VECTORMATH_PERM_ZBWX ); + inv2 = vec_perm( tmp4, tmp1, _VECTORMATH_PERM_XCYX ); + inv0 = vec_madd( inv0, invdet, zero ); + inv1 = vec_madd( inv1, invdet, zero ); + inv2 = vec_madd( inv2, invdet, zero ); + return Matrix3( + Vector3( inv0 ), + Vector3( inv1 ), + Vector3( inv2 ) + ); +} + +inline const floatInVec determinant( const Matrix3 & mat ) +{ + return dot( mat.getCol2(), cross( mat.getCol0(), mat.getCol1() ) ); +} + +inline const Matrix3 Matrix3::operator +( const Matrix3 & mat ) const +{ + return Matrix3( + ( mCol0 + mat.mCol0 ), + ( mCol1 + mat.mCol1 ), + ( mCol2 + mat.mCol2 ) + ); +} + +inline const Matrix3 Matrix3::operator -( const Matrix3 & mat ) const +{ + return Matrix3( + ( mCol0 - mat.mCol0 ), + ( mCol1 - mat.mCol1 ), + ( mCol2 - mat.mCol2 ) + ); +} + +inline Matrix3 & Matrix3::operator +=( const Matrix3 & mat ) +{ + *this = *this + mat; + return *this; +} + +inline Matrix3 & Matrix3::operator -=( const Matrix3 & mat ) +{ + *this = *this - mat; + return *this; +} + +inline const Matrix3 Matrix3::operator -( ) const +{ + return Matrix3( + ( -mCol0 ), + ( -mCol1 ), + ( -mCol2 ) + ); +} + +inline const Matrix3 absPerElem( const Matrix3 & mat ) +{ + return Matrix3( + absPerElem( mat.getCol0() ), + absPerElem( mat.getCol1() ), + absPerElem( mat.getCol2() ) + ); +} + +inline const Matrix3 Matrix3::operator *( float scalar ) const +{ + return *this * floatInVec(scalar); +} + +inline const Matrix3 Matrix3::operator *( floatInVec scalar ) const +{ + return Matrix3( + ( mCol0 * scalar ), + ( mCol1 * scalar ), + ( mCol2 * scalar ) + ); +} + +inline Matrix3 & Matrix3::operator *=( float scalar ) +{ + return *this *= floatInVec(scalar); +} + +inline Matrix3 & Matrix3::operator *=( floatInVec scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Matrix3 operator *( float scalar, const Matrix3 & mat ) +{ + return floatInVec(scalar) * mat; +} + +inline const Matrix3 operator *( floatInVec scalar, const Matrix3 & mat ) +{ + return mat * scalar; +} + +inline const Vector3 Matrix3::operator *( Vector3 vec ) const +{ + vec_float4 res; + vec_float4 xxxx, yyyy, zzzz; + xxxx = vec_splat( vec.get128(), 0 ); + yyyy = vec_splat( vec.get128(), 1 ); + zzzz = vec_splat( vec.get128(), 2 ); + res = vec_madd( mCol0.get128(), xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + res = vec_madd( mCol1.get128(), yyyy, res ); + res = vec_madd( mCol2.get128(), zzzz, res ); + return Vector3( res ); +} + +inline const Matrix3 Matrix3::operator *( const Matrix3 & mat ) const +{ + return Matrix3( + ( *this * mat.mCol0 ), + ( *this * mat.mCol1 ), + ( *this * mat.mCol2 ) + ); +} + +inline Matrix3 & Matrix3::operator *=( const Matrix3 & mat ) +{ + *this = *this * mat; + return *this; +} + +inline const Matrix3 mulPerElem( const Matrix3 & mat0, const Matrix3 & mat1 ) +{ + return Matrix3( + mulPerElem( mat0.getCol0(), mat1.getCol0() ), + mulPerElem( mat0.getCol1(), mat1.getCol1() ), + mulPerElem( mat0.getCol2(), mat1.getCol2() ) + ); +} + +inline const Matrix3 Matrix3::identity( ) +{ + return Matrix3( + Vector3::xAxis( ), + Vector3::yAxis( ), + Vector3::zAxis( ) + ); +} + +inline const Matrix3 Matrix3::rotationX( float radians ) +{ + return rotationX( floatInVec(radians) ); +} + +inline const Matrix3 Matrix3::rotationX( floatInVec radians ) +{ + vec_float4 s, c, res1, res2; + vec_uint4 select_y, select_z; + vec_float4 zero; + select_y = _VECTORMATH_MASK_0x0F00; + select_z = _VECTORMATH_MASK_0x00F0; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( radians.get128(), &s, &c ); + res1 = vec_sel( zero, c, select_y ); + res1 = vec_sel( res1, s, select_z ); + res2 = vec_sel( zero, negatef4(s), select_y ); + res2 = vec_sel( res2, c, select_z ); + return Matrix3( + Vector3::xAxis( ), + Vector3( res1 ), + Vector3( res2 ) + ); +} + +inline const Matrix3 Matrix3::rotationY( float radians ) +{ + return rotationY( floatInVec(radians) ); +} + +inline const Matrix3 Matrix3::rotationY( floatInVec radians ) +{ + vec_float4 s, c, res0, res2; + vec_uint4 select_x, select_z; + vec_float4 zero; + select_x = _VECTORMATH_MASK_0xF000; + select_z = _VECTORMATH_MASK_0x00F0; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( radians.get128(), &s, &c ); + res0 = vec_sel( zero, c, select_x ); + res0 = vec_sel( res0, negatef4(s), select_z ); + res2 = vec_sel( zero, s, select_x ); + res2 = vec_sel( res2, c, select_z ); + return Matrix3( + Vector3( res0 ), + Vector3::yAxis( ), + Vector3( res2 ) + ); +} + +inline const Matrix3 Matrix3::rotationZ( float radians ) +{ + return rotationZ( floatInVec(radians) ); +} + +inline const Matrix3 Matrix3::rotationZ( floatInVec radians ) +{ + vec_float4 s, c, res0, res1; + vec_uint4 select_x, select_y; + vec_float4 zero; + select_x = _VECTORMATH_MASK_0xF000; + select_y = _VECTORMATH_MASK_0x0F00; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( radians.get128(), &s, &c ); + res0 = vec_sel( zero, c, select_x ); + res0 = vec_sel( res0, s, select_y ); + res1 = vec_sel( zero, negatef4(s), select_x ); + res1 = vec_sel( res1, c, select_y ); + return Matrix3( + Vector3( res0 ), + Vector3( res1 ), + Vector3::zAxis( ) + ); +} + +inline const Matrix3 Matrix3::rotationZYX( Vector3 radiansXYZ ) +{ + vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + angles = Vector4( radiansXYZ, 0.0f ).get128(); + sincosf4( angles, &s, &c ); + negS = negatef4( s ); + Z0 = vec_mergel( c, s ); + Z1 = vec_mergel( negS, c ); + Z1 = vec_andc( Z1, (vec_float4)_VECTORMATH_MASK_0x000F ); + Y0 = vec_perm( negS, c, _VECTORMATH_PERM_BBYX ); + Y1 = vec_perm( c, s, _VECTORMATH_PERM_BBYX ); + X0 = vec_splat( s, 0 ); + X1 = vec_splat( c, 0 ); + tmp = vec_madd( Z0, Y1, zero ); + return Matrix3( + Vector3( vec_madd( Z0, Y0, zero ) ), + Vector3( vec_madd( Z1, X1, vec_madd( tmp, X0, zero ) ) ), + Vector3( vec_nmsub( Z1, X0, vec_madd( tmp, X1, zero ) ) ) + ); +} + +inline const Matrix3 Matrix3::rotation( float radians, Vector3 unitVec ) +{ + return rotation( floatInVec(radians), unitVec ); +} + +inline const Matrix3 Matrix3::rotation( floatInVec radians, Vector3 unitVec ) +{ + vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + axis = unitVec.get128(); + sincosf4( radians.get128(), &s, &c ); + xxxx = vec_splat( axis, 0 ); + yyyy = vec_splat( axis, 1 ); + zzzz = vec_splat( axis, 2 ); + oneMinusC = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), c ); + axisS = vec_madd( axis, s, zero ); + negAxisS = negatef4( axisS ); + tmp0 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_XZBX ); + tmp1 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_CXXX ); + tmp2 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_YAXX ); + tmp0 = vec_sel( tmp0, c, _VECTORMATH_MASK_0xF000 ); + tmp1 = vec_sel( tmp1, c, _VECTORMATH_MASK_0x0F00 ); + tmp2 = vec_sel( tmp2, c, _VECTORMATH_MASK_0x00F0 ); + return Matrix3( + Vector3( vec_madd( vec_madd( axis, xxxx, zero ), oneMinusC, tmp0 ) ), + Vector3( vec_madd( vec_madd( axis, yyyy, zero ), oneMinusC, tmp1 ) ), + Vector3( vec_madd( vec_madd( axis, zzzz, zero ), oneMinusC, tmp2 ) ) + ); +} + +inline const Matrix3 Matrix3::rotation( Quat unitQuat ) +{ + return Matrix3( unitQuat ); +} + +inline const Matrix3 Matrix3::scale( Vector3 scaleVec ) +{ + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + return Matrix3( + Vector3( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0xF000 ) ), + Vector3( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0x0F00 ) ), + Vector3( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0x00F0 ) ) + ); +} + +inline const Matrix3 appendScale( const Matrix3 & mat, Vector3 scaleVec ) +{ + return Matrix3( + ( mat.getCol0() * scaleVec.getX( ) ), + ( mat.getCol1() * scaleVec.getY( ) ), + ( mat.getCol2() * scaleVec.getZ( ) ) + ); +} + +inline const Matrix3 prependScale( Vector3 scaleVec, const Matrix3 & mat ) +{ + return Matrix3( + mulPerElem( mat.getCol0(), scaleVec ), + mulPerElem( mat.getCol1(), scaleVec ), + mulPerElem( mat.getCol2(), scaleVec ) + ); +} + +inline const Matrix3 select( const Matrix3 & mat0, const Matrix3 & mat1, bool select1 ) +{ + return Matrix3( + select( mat0.getCol0(), mat1.getCol0(), select1 ), + select( mat0.getCol1(), mat1.getCol1(), select1 ), + select( mat0.getCol2(), mat1.getCol2(), select1 ) + ); +} + +inline const Matrix3 select( const Matrix3 & mat0, const Matrix3 & mat1, boolInVec select1 ) +{ + return Matrix3( + select( mat0.getCol0(), mat1.getCol0(), select1 ), + select( mat0.getCol1(), mat1.getCol1(), select1 ), + select( mat0.getCol2(), mat1.getCol2(), select1 ) + ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( const Matrix3 & mat ) +{ + print( mat.getRow( 0 ) ); + print( mat.getRow( 1 ) ); + print( mat.getRow( 2 ) ); +} + +inline void print( const Matrix3 & mat, const char * name ) +{ + printf("%s:\n", name); + print( mat ); +} + +#endif + +inline Matrix4::Matrix4( const Matrix4 & mat ) +{ + mCol0 = mat.mCol0; + mCol1 = mat.mCol1; + mCol2 = mat.mCol2; + mCol3 = mat.mCol3; +} + +inline Matrix4::Matrix4( float scalar ) +{ + mCol0 = Vector4( scalar ); + mCol1 = Vector4( scalar ); + mCol2 = Vector4( scalar ); + mCol3 = Vector4( scalar ); +} + +inline Matrix4::Matrix4( floatInVec scalar ) +{ + mCol0 = Vector4( scalar ); + mCol1 = Vector4( scalar ); + mCol2 = Vector4( scalar ); + mCol3 = Vector4( scalar ); +} + +inline Matrix4::Matrix4( const Transform3 & mat ) +{ + mCol0 = Vector4( mat.getCol0(), 0.0f ); + mCol1 = Vector4( mat.getCol1(), 0.0f ); + mCol2 = Vector4( mat.getCol2(), 0.0f ); + mCol3 = Vector4( mat.getCol3(), 1.0f ); +} + +inline Matrix4::Matrix4( Vector4 _col0, Vector4 _col1, Vector4 _col2, Vector4 _col3 ) +{ + mCol0 = _col0; + mCol1 = _col1; + mCol2 = _col2; + mCol3 = _col3; +} + +inline Matrix4::Matrix4( const Matrix3 & mat, Vector3 translateVec ) +{ + mCol0 = Vector4( mat.getCol0(), 0.0f ); + mCol1 = Vector4( mat.getCol1(), 0.0f ); + mCol2 = Vector4( mat.getCol2(), 0.0f ); + mCol3 = Vector4( translateVec, 1.0f ); +} + +inline Matrix4::Matrix4( Quat unitQuat, Vector3 translateVec ) +{ + Matrix3 mat; + mat = Matrix3( unitQuat ); + mCol0 = Vector4( mat.getCol0(), 0.0f ); + mCol1 = Vector4( mat.getCol1(), 0.0f ); + mCol2 = Vector4( mat.getCol2(), 0.0f ); + mCol3 = Vector4( translateVec, 1.0f ); +} + +inline Matrix4 & Matrix4::setCol0( Vector4 _col0 ) +{ + mCol0 = _col0; + return *this; +} + +inline Matrix4 & Matrix4::setCol1( Vector4 _col1 ) +{ + mCol1 = _col1; + return *this; +} + +inline Matrix4 & Matrix4::setCol2( Vector4 _col2 ) +{ + mCol2 = _col2; + return *this; +} + +inline Matrix4 & Matrix4::setCol3( Vector4 _col3 ) +{ + mCol3 = _col3; + return *this; +} + +inline Matrix4 & Matrix4::setCol( int col, Vector4 vec ) +{ + *(&mCol0 + col) = vec; + return *this; +} + +inline Matrix4 & Matrix4::setRow( int row, Vector4 vec ) +{ + mCol0.setElem( row, vec.getElem( 0 ) ); + mCol1.setElem( row, vec.getElem( 1 ) ); + mCol2.setElem( row, vec.getElem( 2 ) ); + mCol3.setElem( row, vec.getElem( 3 ) ); + return *this; +} + +inline Matrix4 & Matrix4::setElem( int col, int row, float val ) +{ + (*this)[col].setElem(row, val); + return *this; +} + +inline Matrix4 & Matrix4::setElem( int col, int row, floatInVec val ) +{ + Vector4 tmpV3_0; + tmpV3_0 = this->getCol( col ); + tmpV3_0.setElem( row, val ); + this->setCol( col, tmpV3_0 ); + return *this; +} + +inline const floatInVec Matrix4::getElem( int col, int row ) const +{ + return this->getCol( col ).getElem( row ); +} + +inline const Vector4 Matrix4::getCol0( ) const +{ + return mCol0; +} + +inline const Vector4 Matrix4::getCol1( ) const +{ + return mCol1; +} + +inline const Vector4 Matrix4::getCol2( ) const +{ + return mCol2; +} + +inline const Vector4 Matrix4::getCol3( ) const +{ + return mCol3; +} + +inline const Vector4 Matrix4::getCol( int col ) const +{ + return *(&mCol0 + col); +} + +inline const Vector4 Matrix4::getRow( int row ) const +{ + return Vector4( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ), mCol3.getElem( row ) ); +} + +inline Vector4 & Matrix4::operator []( int col ) +{ + return *(&mCol0 + col); +} + +inline const Vector4 Matrix4::operator []( int col ) const +{ + return *(&mCol0 + col); +} + +inline Matrix4 & Matrix4::operator =( const Matrix4 & mat ) +{ + mCol0 = mat.mCol0; + mCol1 = mat.mCol1; + mCol2 = mat.mCol2; + mCol3 = mat.mCol3; + return *this; +} + +inline const Matrix4 transpose( const Matrix4 & mat ) +{ + vec_float4 tmp0, tmp1, tmp2, tmp3, res0, res1, res2, res3; + tmp0 = vec_mergeh( mat.getCol0().get128(), mat.getCol2().get128() ); + tmp1 = vec_mergeh( mat.getCol1().get128(), mat.getCol3().get128() ); + tmp2 = vec_mergel( mat.getCol0().get128(), mat.getCol2().get128() ); + tmp3 = vec_mergel( mat.getCol1().get128(), mat.getCol3().get128() ); + res0 = vec_mergeh( tmp0, tmp1 ); + res1 = vec_mergel( tmp0, tmp1 ); + res2 = vec_mergeh( tmp2, tmp3 ); + res3 = vec_mergel( tmp2, tmp3 ); + return Matrix4( + Vector4( res0 ), + Vector4( res1 ), + Vector4( res2 ), + Vector4( res3 ) + ); +} + +inline const Matrix4 inverse( const Matrix4 & mat ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vector float in0, in1, in2, in3; + vector float tmp0, tmp1, tmp2, tmp3; + vector float cof0, cof1, cof2, cof3; + vector float t0, t1, t2, t3; + vector float t01, t02, t03, t12, t23; + vector float t1r, t2r; + vector float t01r, t02r, t03r, t12r, t23r; + vector float t1r3, t1r3r; + vector float det, det0, det1, det2, det3, invdet; + vector float vzero = (vector float){0.0}; + in0 = mat.getCol0().get128(); + in1 = mat.getCol1().get128(); + in2 = mat.getCol2().get128(); + in3 = mat.getCol3().get128(); + /* Perform transform of the input matrix of the form: + * A B C D + * E F G H + * I J K L + * M N O P + * + * The pseudo transpose of the input matrix is trans: + * A E I M + * J N B F + * C G K O + * L P D H + */ + tmp0 = vec_perm(in0, in1, _VECTORMATH_PERM_XAZC); /* A E C G */ + tmp1 = vec_perm(in2, in3, _VECTORMATH_PERM_XAZC); /* I M K O */ + tmp2 = vec_perm(in0, in1, _VECTORMATH_PERM_YBWD); /* B F D H */ + tmp3 = vec_perm(in2, in3, _VECTORMATH_PERM_YBWD); /* J N L P */ + t0 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_XYAB); /* A E I M */ + t1 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_XYAB); /* J N B F */ + t2 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_ZWCD); /* C G K O */ + t3 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_ZWCD); /* L P D H */ + /* Generate a cofactor matrix. The computed cofactors reside in + * cof0, cof1, cof2, cof3. + */ + t23 = vec_madd(t2, t3, vzero); /* CL GP KD OH */ + t23 = vec_perm(t23, t23, _VECTORMATH_PERM_YXWZ); /* GP CL OH KD */ + cof0 = vec_nmsub(t1, t23, vzero); /* -(JGP NCL FOH BKD) */ + cof1 = vec_nmsub(t0, t23, vzero); /* -(AGP ECL IOH MKD) */ + t23r = vec_sld(t23, t23, 8); /* OH KD GP CL */ + cof0 = vec_madd(t1, t23r, cof0); /* JOH NKD BGP FCL + cof0 */ + cof1 = vec_madd(t0, t23r, cof1); /* AOH EKD IGP MCL + cof1 */ + cof1 = vec_sld(cof1, cof1, 8); /* IGP MCL AOH EKD - IOH MKD AGP ECL */ + t12 = vec_madd(t1, t2, vzero); /* JC NG BK FO */ + t12 = vec_perm(t12, t12, _VECTORMATH_PERM_YXWZ); /* NG JC FO BK */ + cof0 = vec_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ + cof3 = vec_madd(t0, t12, vzero); /* ANG EJC IFO MBK */ + t12r = vec_sld(t12, t12, 8); /* FO BK NG JC */ + cof0 = vec_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ + cof3 = vec_nmsub(t0, t12r, cof3); /* cof3 - AFO EBK ING MJC */ + cof3 = vec_sld(cof3, cof3, 8); /* ING MJC AFO EBK - IFO MBK ANG EJC */ + t1r = vec_sld(t1, t1, 8); /* B F J N */ + t2r = vec_sld(t2, t2, 8); /* K O C G */ + t1r3 = vec_madd(t1r, t3, vzero); /* BL FP JD NH */ + t1r3 = vec_perm(t1r3, t1r3, _VECTORMATH_PERM_YXWZ); /* FP BL NH JD */ + cof0 = vec_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ + cof2 = vec_madd(t0, t1r3, vzero); /* AFP EBL INH MJD */ + t1r3r = vec_sld(t1r3, t1r3, 8); /* NH JD FP BL */ + cof0 = vec_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ + cof2 = vec_nmsub(t0, t1r3r, cof2); /* cof2 - ANH EJD IFP MBL */ + cof2 = vec_sld(cof2, cof2, 8); /* IFP MBL ANH EJD - INH MJD AFP EBL */ + t01 = vec_madd(t0, t1, vzero); /* AJ EN IB MF */ + t01 = vec_perm(t01, t01, _VECTORMATH_PERM_YXWZ); /* EN AJ MF IB */ + cof2 = vec_nmsub(t3, t01, cof2); /* cof2 - LEN PAJ DMF HIB */ + cof3 = vec_madd(t2r, t01, cof3); /* KEN OAJ CMF GIB + cof3 */ + t01r = vec_sld(t01, t01, 8); /* MF IB EN AJ */ + cof2 = vec_madd(t3, t01r, cof2); /* LMF PIB DEN HAJ + cof2 */ + cof3 = vec_nmsub(t2r, t01r, cof3); /* cof3 - KMF OIB CEN GAJ */ + t03 = vec_madd(t0, t3, vzero); /* AL EP ID MH */ + t03 = vec_perm(t03, t03, _VECTORMATH_PERM_YXWZ); /* EP AL MH ID */ + cof1 = vec_nmsub(t2r, t03, cof1); /* cof1 - KEP OAL CMH GID */ + cof2 = vec_madd(t1, t03, cof2); /* JEP NAL BMH FID + cof2 */ + t03r = vec_sld(t03, t03, 8); /* MH ID EP AL */ + cof1 = vec_madd(t2r, t03r, cof1); /* KMH OID CEP GAL + cof1 */ + cof2 = vec_nmsub(t1, t03r, cof2); /* cof2 - JMH NID BEP FAL */ + t02 = vec_madd(t0, t2r, vzero); /* AK EO IC MG */ + t02 = vec_perm(t02, t02, _VECTORMATH_PERM_YXWZ); /* E0 AK MG IC */ + cof1 = vec_madd(t3, t02, cof1); /* LEO PAK DMG HIC + cof1 */ + cof3 = vec_nmsub(t1, t02, cof3); /* cof3 - JEO NAK BMG FIC */ + t02r = vec_sld(t02, t02, 8); /* MG IC EO AK */ + cof1 = vec_nmsub(t3, t02r, cof1); /* cof1 - LMG PIC DEO HAK */ + cof3 = vec_madd(t1, t02r, cof3); /* JMG NIC BEO FAK + cof3 */ + /* Compute the determinant of the matrix + * + * det = sum_across(t0 * cof0); + * + * We perform a sum across the entire vector so that + * we don't have to splat the result when multiplying the + * cofactors by the inverse of the determinant. + */ + det = vec_madd(t0, cof0, vzero); + det0 = vec_splat(det, 0); + det1 = vec_splat(det, 1); + det2 = vec_splat(det, 2); + det3 = vec_splat(det, 3); + det = vec_add(det0, det1); + det2 = vec_add(det2, det3); + det = vec_add(det, det2); + /* Compute the reciprocal of the determinant. + */ + invdet = recipf4(det); + /* Multiply the cofactors by the reciprocal of the determinant. + */ + return Matrix4( + Vector4( vec_madd(cof0, invdet, vzero) ), + Vector4( vec_madd(cof1, invdet, vzero) ), + Vector4( vec_madd(cof2, invdet, vzero) ), + Vector4( vec_madd(cof3, invdet, vzero) ) + ); +} + +inline const Matrix4 affineInverse( const Matrix4 & mat ) +{ + Transform3 affineMat; + affineMat.setCol0( mat.getCol0().getXYZ( ) ); + affineMat.setCol1( mat.getCol1().getXYZ( ) ); + affineMat.setCol2( mat.getCol2().getXYZ( ) ); + affineMat.setCol3( mat.getCol3().getXYZ( ) ); + return Matrix4( inverse( affineMat ) ); +} + +inline const Matrix4 orthoInverse( const Matrix4 & mat ) +{ + Transform3 affineMat; + affineMat.setCol0( mat.getCol0().getXYZ( ) ); + affineMat.setCol1( mat.getCol1().getXYZ( ) ); + affineMat.setCol2( mat.getCol2().getXYZ( ) ); + affineMat.setCol3( mat.getCol3().getXYZ( ) ); + return Matrix4( orthoInverse( affineMat ) ); +} + +inline const floatInVec determinant( const Matrix4 & mat ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vector float in0, in1, in2, in3; + vector float tmp0, tmp1, tmp2, tmp3; + vector float cof0; + vector float t0, t1, t2, t3; + vector float t12, t23; + vector float t1r, t2r; + vector float t12r, t23r; + vector float t1r3, t1r3r; + vector float vzero = (vector float){0.0}; + in0 = mat.getCol0().get128(); + in1 = mat.getCol1().get128(); + in2 = mat.getCol2().get128(); + in3 = mat.getCol3().get128(); + /* Perform transform of the input matrix of the form: + * A B C D + * E F G H + * I J K L + * M N O P + * + * The pseudo transpose of the input matrix is trans: + * A E I M + * J N B F + * C G K O + * L P D H + */ + tmp0 = vec_perm(in0, in1, _VECTORMATH_PERM_XAZC); /* A E C G */ + tmp1 = vec_perm(in2, in3, _VECTORMATH_PERM_XAZC); /* I M K O */ + tmp2 = vec_perm(in0, in1, _VECTORMATH_PERM_YBWD); /* B F D H */ + tmp3 = vec_perm(in2, in3, _VECTORMATH_PERM_YBWD); /* J N L P */ + t0 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_XYAB); /* A E I M */ + t1 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_XYAB); /* J N B F */ + t2 = vec_perm(tmp0, tmp1, _VECTORMATH_PERM_ZWCD); /* C G K O */ + t3 = vec_perm(tmp3, tmp2, _VECTORMATH_PERM_ZWCD); /* L P D H */ + /* Generate a cofactor matrix. The computed cofactors reside in + * cof0, cof1, cof2, cof3. + */ + t23 = vec_madd(t2, t3, vzero); /* CL GP KD OH */ + t23 = vec_perm(t23, t23, _VECTORMATH_PERM_YXWZ); /* GP CL OH KD */ + cof0 = vec_nmsub(t1, t23, vzero); /* -(JGP NCL FOH BKD) */ + t23r = vec_sld(t23, t23, 8); /* OH KD GP CL */ + cof0 = vec_madd(t1, t23r, cof0); /* JOH NKD BGP FCL + cof0 */ + t12 = vec_madd(t1, t2, vzero); /* JC NG BK FO */ + t12 = vec_perm(t12, t12, _VECTORMATH_PERM_YXWZ); /* NG JC FO BK */ + cof0 = vec_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ + t12r = vec_sld(t12, t12, 8); /* FO BK NG JC */ + cof0 = vec_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ + t1r = vec_sld(t1, t1, 8); /* B F J N */ + t2r = vec_sld(t2, t2, 8); /* K O C G */ + t1r3 = vec_madd(t1r, t3, vzero); /* BL FP JD NH */ + t1r3 = vec_perm(t1r3, t1r3, _VECTORMATH_PERM_YXWZ); /* FP BL NH JD */ + cof0 = vec_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ + t1r3r = vec_sld(t1r3, t1r3, 8); /* NH JD FP BL */ + cof0 = vec_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ + return floatInVec( _vmathVfDot4(t0,cof0), 0 ); +} + +inline const Matrix4 Matrix4::operator +( const Matrix4 & mat ) const +{ + return Matrix4( + ( mCol0 + mat.mCol0 ), + ( mCol1 + mat.mCol1 ), + ( mCol2 + mat.mCol2 ), + ( mCol3 + mat.mCol3 ) + ); +} + +inline const Matrix4 Matrix4::operator -( const Matrix4 & mat ) const +{ + return Matrix4( + ( mCol0 - mat.mCol0 ), + ( mCol1 - mat.mCol1 ), + ( mCol2 - mat.mCol2 ), + ( mCol3 - mat.mCol3 ) + ); +} + +inline Matrix4 & Matrix4::operator +=( const Matrix4 & mat ) +{ + *this = *this + mat; + return *this; +} + +inline Matrix4 & Matrix4::operator -=( const Matrix4 & mat ) +{ + *this = *this - mat; + return *this; +} + +inline const Matrix4 Matrix4::operator -( ) const +{ + return Matrix4( + ( -mCol0 ), + ( -mCol1 ), + ( -mCol2 ), + ( -mCol3 ) + ); +} + +inline const Matrix4 absPerElem( const Matrix4 & mat ) +{ + return Matrix4( + absPerElem( mat.getCol0() ), + absPerElem( mat.getCol1() ), + absPerElem( mat.getCol2() ), + absPerElem( mat.getCol3() ) + ); +} + +inline const Matrix4 Matrix4::operator *( float scalar ) const +{ + return *this * floatInVec(scalar); +} + +inline const Matrix4 Matrix4::operator *( floatInVec scalar ) const +{ + return Matrix4( + ( mCol0 * scalar ), + ( mCol1 * scalar ), + ( mCol2 * scalar ), + ( mCol3 * scalar ) + ); +} + +inline Matrix4 & Matrix4::operator *=( float scalar ) +{ + return *this *= floatInVec(scalar); +} + +inline Matrix4 & Matrix4::operator *=( floatInVec scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Matrix4 operator *( float scalar, const Matrix4 & mat ) +{ + return floatInVec(scalar) * mat; +} + +inline const Matrix4 operator *( floatInVec scalar, const Matrix4 & mat ) +{ + return mat * scalar; +} + +inline const Vector4 Matrix4::operator *( Vector4 vec ) const +{ + vec_float4 tmp0, tmp1, res; + vec_float4 xxxx, yyyy, zzzz, wwww; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + xxxx = vec_splat( vec.get128(), 0 ); + yyyy = vec_splat( vec.get128(), 1 ); + zzzz = vec_splat( vec.get128(), 2 ); + wwww = vec_splat( vec.get128(), 3 ); + tmp0 = vec_madd( mCol0.get128(), xxxx, zero ); + tmp1 = vec_madd( mCol1.get128(), yyyy, zero ); + tmp0 = vec_madd( mCol2.get128(), zzzz, tmp0 ); + tmp1 = vec_madd( mCol3.get128(), wwww, tmp1 ); + res = vec_add( tmp0, tmp1 ); + return Vector4( res ); +} + +inline const Vector4 Matrix4::operator *( Vector3 vec ) const +{ + vec_float4 res; + vec_float4 xxxx, yyyy, zzzz; + xxxx = vec_splat( vec.get128(), 0 ); + yyyy = vec_splat( vec.get128(), 1 ); + zzzz = vec_splat( vec.get128(), 2 ); + res = vec_madd( mCol0.get128(), xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + res = vec_madd( mCol1.get128(), yyyy, res ); + res = vec_madd( mCol2.get128(), zzzz, res ); + return Vector4( res ); +} + +inline const Vector4 Matrix4::operator *( Point3 pnt ) const +{ + vec_float4 tmp0, tmp1, res; + vec_float4 xxxx, yyyy, zzzz; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + xxxx = vec_splat( pnt.get128(), 0 ); + yyyy = vec_splat( pnt.get128(), 1 ); + zzzz = vec_splat( pnt.get128(), 2 ); + tmp0 = vec_madd( mCol0.get128(), xxxx, zero ); + tmp1 = vec_madd( mCol1.get128(), yyyy, zero ); + tmp0 = vec_madd( mCol2.get128(), zzzz, tmp0 ); + tmp1 = vec_add( mCol3.get128(), tmp1 ); + res = vec_add( tmp0, tmp1 ); + return Vector4( res ); +} + +inline const Matrix4 Matrix4::operator *( const Matrix4 & mat ) const +{ + return Matrix4( + ( *this * mat.mCol0 ), + ( *this * mat.mCol1 ), + ( *this * mat.mCol2 ), + ( *this * mat.mCol3 ) + ); +} + +inline Matrix4 & Matrix4::operator *=( const Matrix4 & mat ) +{ + *this = *this * mat; + return *this; +} + +inline const Matrix4 Matrix4::operator *( const Transform3 & tfrm ) const +{ + return Matrix4( + ( *this * tfrm.getCol0() ), + ( *this * tfrm.getCol1() ), + ( *this * tfrm.getCol2() ), + ( *this * Point3( tfrm.getCol3() ) ) + ); +} + +inline Matrix4 & Matrix4::operator *=( const Transform3 & tfrm ) +{ + *this = *this * tfrm; + return *this; +} + +inline bool Matrix4::operator == (const Matrix4& mat) const +{ + return (vec_all_gt(vec_cmpeq(mCol0.get128(), mat.mCol0.get128()),((vec_uint4){0,0,0,0})) && + vec_all_gt(vec_cmpeq(mCol1.get128(), mat.mCol1.get128()),((vec_uint4){0,0,0,0})) && + vec_all_gt(vec_cmpeq(mCol2.get128(), mat.mCol2.get128()),((vec_uint4){0,0,0,0})) && + vec_all_gt(vec_cmpeq(mCol3.get128(), mat.mCol3.get128()),((vec_uint4){0,0,0,0}))); +} + +inline bool Matrix4::operator != (const Matrix4& mat) const +{ + return !(*this == mat); +} + +inline const Matrix4 mulPerElem( const Matrix4 & mat0, const Matrix4 & mat1 ) +{ + return Matrix4( + mulPerElem( mat0.getCol0(), mat1.getCol0() ), + mulPerElem( mat0.getCol1(), mat1.getCol1() ), + mulPerElem( mat0.getCol2(), mat1.getCol2() ), + mulPerElem( mat0.getCol3(), mat1.getCol3() ) + ); +} + +inline const Matrix4 Matrix4::identity( ) +{ + return Matrix4( + Vector4::xAxis( ), + Vector4::yAxis( ), + Vector4::zAxis( ), + Vector4::wAxis( ) + ); +} + +inline Matrix4 & Matrix4::setUpper3x3( const Matrix3 & mat3 ) +{ + mCol0.setXYZ( mat3.getCol0() ); + mCol1.setXYZ( mat3.getCol1() ); + mCol2.setXYZ( mat3.getCol2() ); + return *this; +} + +inline const Matrix3 Matrix4::getUpper3x3( ) const +{ + return Matrix3( + mCol0.getXYZ( ), + mCol1.getXYZ( ), + mCol2.getXYZ( ) + ); +} + +inline Matrix4 & Matrix4::setTranslation( Vector3 translateVec ) +{ + mCol3.setXYZ( translateVec ); + return *this; +} + +inline const Vector3 Matrix4::getTranslation( ) const +{ + return mCol3.getXYZ( ); +} + +inline const Matrix4 Matrix4::rotationX( float radians ) +{ + return rotationX( floatInVec(radians) ); +} + +inline const Matrix4 Matrix4::rotationX( floatInVec radians ) +{ + vec_float4 s, c, res1, res2; + vec_uint4 select_y, select_z; + vec_float4 zero; + select_y = _VECTORMATH_MASK_0x0F00; + select_z = _VECTORMATH_MASK_0x00F0; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( radians.get128(), &s, &c ); + res1 = vec_sel( zero, c, select_y ); + res1 = vec_sel( res1, s, select_z ); + res2 = vec_sel( zero, negatef4(s), select_y ); + res2 = vec_sel( res2, c, select_z ); + return Matrix4( + Vector4::xAxis( ), + Vector4( res1 ), + Vector4( res2 ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotationY( float radians ) +{ + return rotationY( floatInVec(radians) ); +} + +inline const Matrix4 Matrix4::rotationY( floatInVec radians ) +{ + vec_float4 s, c, res0, res2; + vec_uint4 select_x, select_z; + vec_float4 zero; + select_x = _VECTORMATH_MASK_0xF000; + select_z = _VECTORMATH_MASK_0x00F0; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( radians.get128(), &s, &c ); + res0 = vec_sel( zero, c, select_x ); + res0 = vec_sel( res0, negatef4(s), select_z ); + res2 = vec_sel( zero, s, select_x ); + res2 = vec_sel( res2, c, select_z ); + return Matrix4( + Vector4( res0 ), + Vector4::yAxis( ), + Vector4( res2 ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotationZ( float radians ) +{ + return rotationZ( floatInVec(radians) ); +} + +inline const Matrix4 Matrix4::rotationZ( floatInVec radians ) +{ + vec_float4 s, c, res0, res1; + vec_uint4 select_x, select_y; + vec_float4 zero; + select_x = _VECTORMATH_MASK_0xF000; + select_y = _VECTORMATH_MASK_0x0F00; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( radians.get128(), &s, &c ); + res0 = vec_sel( zero, c, select_x ); + res0 = vec_sel( res0, s, select_y ); + res1 = vec_sel( zero, negatef4(s), select_x ); + res1 = vec_sel( res1, c, select_y ); + return Matrix4( + Vector4( res0 ), + Vector4( res1 ), + Vector4::zAxis( ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotationZYX( Vector3 radiansXYZ ) +{ + vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + angles = Vector4( radiansXYZ, 0.0f ).get128(); + sincosf4( angles, &s, &c ); + negS = negatef4( s ); + Z0 = vec_mergel( c, s ); + Z1 = vec_mergel( negS, c ); + Z1 = vec_andc( Z1, (vec_float4)_VECTORMATH_MASK_0x000F ); + Y0 = vec_perm( negS, c, _VECTORMATH_PERM_BBYX ); + Y1 = vec_perm( c, s, _VECTORMATH_PERM_BBYX ); + X0 = vec_splat( s, 0 ); + X1 = vec_splat( c, 0 ); + tmp = vec_madd( Z0, Y1, zero ); + return Matrix4( + Vector4( vec_madd( Z0, Y0, zero ) ), + Vector4( vec_madd( Z1, X1, vec_madd( tmp, X0, zero ) ) ), + Vector4( vec_nmsub( Z1, X0, vec_madd( tmp, X1, zero ) ) ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotation( float radians, Vector3 unitVec ) +{ + return rotation( floatInVec(radians), unitVec ); +} + +inline const Matrix4 Matrix4::rotation( floatInVec radians, Vector3 unitVec ) +{ + vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2, zeroW; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + axis = unitVec.get128(); + sincosf4( radians.get128(), &s, &c ); + xxxx = vec_splat( axis, 0 ); + yyyy = vec_splat( axis, 1 ); + zzzz = vec_splat( axis, 2 ); + oneMinusC = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), c ); + axisS = vec_madd( axis, s, zero ); + negAxisS = negatef4( axisS ); + tmp0 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_XZBX ); + tmp1 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_CXXX ); + tmp2 = vec_perm( axisS, negAxisS, _VECTORMATH_PERM_YAXX ); + tmp0 = vec_sel( tmp0, c, _VECTORMATH_MASK_0xF000 ); + tmp1 = vec_sel( tmp1, c, _VECTORMATH_MASK_0x0F00 ); + tmp2 = vec_sel( tmp2, c, _VECTORMATH_MASK_0x00F0 ); + zeroW = (vec_float4)_VECTORMATH_MASK_0x000F; + axis = vec_andc( axis, zeroW ); + tmp0 = vec_andc( tmp0, zeroW ); + tmp1 = vec_andc( tmp1, zeroW ); + tmp2 = vec_andc( tmp2, zeroW ); + return Matrix4( + Vector4( vec_madd( vec_madd( axis, xxxx, zero ), oneMinusC, tmp0 ) ), + Vector4( vec_madd( vec_madd( axis, yyyy, zero ), oneMinusC, tmp1 ) ), + Vector4( vec_madd( vec_madd( axis, zzzz, zero ), oneMinusC, tmp2 ) ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotation( Quat unitQuat ) +{ + return Matrix4( Transform3::rotation( unitQuat ) ); +} + +inline const Matrix4 Matrix4::scale( Vector3 scaleVec ) +{ + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + return Matrix4( + Vector4( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0xF000 ) ), + Vector4( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0x0F00 ) ), + Vector4( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0x00F0 ) ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 appendScale( const Matrix4 & mat, Vector3 scaleVec ) +{ + return Matrix4( + ( mat.getCol0() * scaleVec.getX( ) ), + ( mat.getCol1() * scaleVec.getY( ) ), + ( mat.getCol2() * scaleVec.getZ( ) ), + mat.getCol3() + ); +} + +inline const Matrix4 prependScale( Vector3 scaleVec, const Matrix4 & mat ) +{ + Vector4 scale4; + scale4 = Vector4( scaleVec, 1.0f ); + return Matrix4( + mulPerElem( mat.getCol0(), scale4 ), + mulPerElem( mat.getCol1(), scale4 ), + mulPerElem( mat.getCol2(), scale4 ), + mulPerElem( mat.getCol3(), scale4 ) + ); +} + +inline const Matrix4 Matrix4::translation( Vector3 translateVec ) +{ + return Matrix4( + Vector4::xAxis( ), + Vector4::yAxis( ), + Vector4::zAxis( ), + Vector4( translateVec, 1.0f ) + ); +} + +inline const Matrix4 Matrix4::lookAt( Point3 eyePos, Point3 lookAtPos, Vector3 upVec ) +{ + Matrix4 m4EyeFrame; + Vector3 v3X, v3Y, v3Z; + v3Y = normalize( upVec ); + v3Z = normalize( ( eyePos - lookAtPos ) ); + v3X = normalize( cross( v3Y, v3Z ) ); + v3Y = cross( v3Z, v3X ); + m4EyeFrame = Matrix4( Vector4( v3X ), Vector4( v3Y ), Vector4( v3Z ), Vector4( eyePos ) ); + return orthoInverse( m4EyeFrame ); +} + +inline const Matrix4 Matrix4::perspective( float fovyRadians, float aspect, float zNear, float zFar ) +{ + float f, rangeInv; + vec_float4 zero, col0, col1, col2, col3; + union { vec_float4 v; float s[4]; } tmp; + f = tanf( _VECTORMATH_PI_OVER_2 - fovyRadians * 0.5f ); + rangeInv = 1.0f / ( zNear - zFar ); + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + tmp.v = zero; + tmp.s[0] = f / aspect; + col0 = tmp.v; + tmp.v = zero; + tmp.s[1] = f; + col1 = tmp.v; + tmp.v = zero; + tmp.s[2] = ( zNear + zFar ) * rangeInv; + tmp.s[3] = -1.0f; + col2 = tmp.v; + tmp.v = zero; + tmp.s[2] = zNear * zFar * rangeInv * 2.0f; + col3 = tmp.v; + return Matrix4( + Vector4( col0 ), + Vector4( col1 ), + Vector4( col2 ), + Vector4( col3 ) + ); +} + +inline const Matrix4 Matrix4::frustum( float left, float right, float bottom, float top, float zNear, float zFar ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vec_float4 lbf, rtn; + vec_float4 diff, sum, inv_diff; + vec_float4 diagonal, column, near2; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + union { vec_float4 v; float s[4]; } l, f, r, n, b, t; + l.s[0] = left; + f.s[0] = zFar; + r.s[0] = right; + n.s[0] = zNear; + b.s[0] = bottom; + t.s[0] = top; + lbf = vec_mergeh( l.v, f.v ); + rtn = vec_mergeh( r.v, n.v ); + lbf = vec_mergeh( lbf, b.v ); + rtn = vec_mergeh( rtn, t.v ); + diff = vec_sub( rtn, lbf ); + sum = vec_add( rtn, lbf ); + inv_diff = recipf4( diff ); + near2 = vec_splat( n.v, 0 ); + near2 = vec_add( near2, near2 ); + diagonal = vec_madd( near2, inv_diff, zero ); + column = vec_madd( sum, inv_diff, zero ); + return Matrix4( + Vector4( vec_sel( zero, diagonal, _VECTORMATH_MASK_0xF000 ) ), + Vector4( vec_sel( zero, diagonal, _VECTORMATH_MASK_0x0F00 ) ), + Vector4( vec_sel( column, ((vec_float4){-1.0f,-1.0f,-1.0f,-1.0f}), _VECTORMATH_MASK_0x000F ) ), + Vector4( vec_sel( zero, vec_madd( diagonal, vec_splat( f.v, 0 ), zero ), _VECTORMATH_MASK_0x00F0 ) ) + ); +} + +inline const Matrix4 Matrix4::orthographic( float left, float right, float bottom, float top, float zNear, float zFar ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vec_float4 lbf, rtn; + vec_float4 diff, sum, inv_diff, neg_inv_diff; + vec_float4 diagonal, column; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + union { vec_float4 v; float s[4]; } l, f, r, n, b, t; + l.s[0] = left; + f.s[0] = zFar; + r.s[0] = right; + n.s[0] = zNear; + b.s[0] = bottom; + t.s[0] = top; + lbf = vec_mergeh( l.v, f.v ); + rtn = vec_mergeh( r.v, n.v ); + lbf = vec_mergeh( lbf, b.v ); + rtn = vec_mergeh( rtn, t.v ); + diff = vec_sub( rtn, lbf ); + sum = vec_add( rtn, lbf ); + inv_diff = recipf4( diff ); + neg_inv_diff = negatef4( inv_diff ); + diagonal = vec_add( inv_diff, inv_diff ); + column = vec_madd( sum, vec_sel( neg_inv_diff, inv_diff, _VECTORMATH_MASK_0x00F0 ), zero ); + return Matrix4( + Vector4( vec_sel( zero, diagonal, _VECTORMATH_MASK_0xF000 ) ), + Vector4( vec_sel( zero, diagonal, _VECTORMATH_MASK_0x0F00 ) ), + Vector4( vec_sel( zero, diagonal, _VECTORMATH_MASK_0x00F0 ) ), + Vector4( vec_sel( column, ((vec_float4){1.0f,1.0f,1.0f,1.0f}), _VECTORMATH_MASK_0x000F ) ) + ); +} + +inline const Matrix4 select( const Matrix4 & mat0, const Matrix4 & mat1, bool select1 ) +{ + return Matrix4( + select( mat0.getCol0(), mat1.getCol0(), select1 ), + select( mat0.getCol1(), mat1.getCol1(), select1 ), + select( mat0.getCol2(), mat1.getCol2(), select1 ), + select( mat0.getCol3(), mat1.getCol3(), select1 ) + ); +} + +inline const Matrix4 select( const Matrix4 & mat0, const Matrix4 & mat1, boolInVec select1 ) +{ + return Matrix4( + select( mat0.getCol0(), mat1.getCol0(), select1 ), + select( mat0.getCol1(), mat1.getCol1(), select1 ), + select( mat0.getCol2(), mat1.getCol2(), select1 ), + select( mat0.getCol3(), mat1.getCol3(), select1 ) + ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( const Matrix4 & mat ) +{ + print( mat.getRow( 0 ) ); + print( mat.getRow( 1 ) ); + print( mat.getRow( 2 ) ); + print( mat.getRow( 3 ) ); +} + +inline void print( const Matrix4 & mat, const char * name ) +{ + printf("%s:\n", name); + print( mat ); +} + +#endif + +inline Transform3::Transform3( const Transform3 & tfrm ) +{ + mCol0 = tfrm.mCol0; + mCol1 = tfrm.mCol1; + mCol2 = tfrm.mCol2; + mCol3 = tfrm.mCol3; +} + +inline Transform3::Transform3( float scalar ) +{ + mCol0 = Vector3( scalar ); + mCol1 = Vector3( scalar ); + mCol2 = Vector3( scalar ); + mCol3 = Vector3( scalar ); +} + +inline Transform3::Transform3( floatInVec scalar ) +{ + mCol0 = Vector3( scalar ); + mCol1 = Vector3( scalar ); + mCol2 = Vector3( scalar ); + mCol3 = Vector3( scalar ); +} + +inline Transform3::Transform3( Vector3 _col0, Vector3 _col1, Vector3 _col2, Vector3 _col3 ) +{ + mCol0 = _col0; + mCol1 = _col1; + mCol2 = _col2; + mCol3 = _col3; +} + +inline Transform3::Transform3( const Matrix3 & tfrm, Vector3 translateVec ) +{ + this->setUpper3x3( tfrm ); + this->setTranslation( translateVec ); +} + +inline Transform3::Transform3( Quat unitQuat, Vector3 translateVec ) +{ + this->setUpper3x3( Matrix3( unitQuat ) ); + this->setTranslation( translateVec ); +} + +inline Transform3 & Transform3::setCol0( Vector3 _col0 ) +{ + mCol0 = _col0; + return *this; +} + +inline Transform3 & Transform3::setCol1( Vector3 _col1 ) +{ + mCol1 = _col1; + return *this; +} + +inline Transform3 & Transform3::setCol2( Vector3 _col2 ) +{ + mCol2 = _col2; + return *this; +} + +inline Transform3 & Transform3::setCol3( Vector3 _col3 ) +{ + mCol3 = _col3; + return *this; +} + +inline Transform3 & Transform3::setCol( int col, Vector3 vec ) +{ + *(&mCol0 + col) = vec; + return *this; +} + +inline Transform3 & Transform3::setRow( int row, Vector4 vec ) +{ + mCol0.setElem( row, vec.getElem( 0 ) ); + mCol1.setElem( row, vec.getElem( 1 ) ); + mCol2.setElem( row, vec.getElem( 2 ) ); + mCol3.setElem( row, vec.getElem( 3 ) ); + return *this; +} + +inline Transform3 & Transform3::setElem( int col, int row, float val ) +{ + (*this)[col].setElem(row, val); + return *this; +} + +inline Transform3 & Transform3::setElem( int col, int row, floatInVec val ) +{ + Vector3 tmpV3_0; + tmpV3_0 = this->getCol( col ); + tmpV3_0.setElem( row, val ); + this->setCol( col, tmpV3_0 ); + return *this; +} + +inline const floatInVec Transform3::getElem( int col, int row ) const +{ + return this->getCol( col ).getElem( row ); +} + +inline const Vector3 Transform3::getCol0( ) const +{ + return mCol0; +} + +inline const Vector3 Transform3::getCol1( ) const +{ + return mCol1; +} + +inline const Vector3 Transform3::getCol2( ) const +{ + return mCol2; +} + +inline const Vector3 Transform3::getCol3( ) const +{ + return mCol3; +} + +inline const Vector3 Transform3::getCol( int col ) const +{ + return *(&mCol0 + col); +} + +inline const Vector4 Transform3::getRow( int row ) const +{ + return Vector4( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ), mCol3.getElem( row ) ); +} + +inline Vector3 & Transform3::operator []( int col ) +{ + return *(&mCol0 + col); +} + +inline const Vector3 Transform3::operator []( int col ) const +{ + return *(&mCol0 + col); +} + +inline Transform3 & Transform3::operator =( const Transform3 & tfrm ) +{ + mCol0 = tfrm.mCol0; + mCol1 = tfrm.mCol1; + mCol2 = tfrm.mCol2; + mCol3 = tfrm.mCol3; + return *this; +} + +inline const Transform3 inverse( const Transform3 & tfrm ) +{ + vec_float4 inv0, inv1, inv2, inv3; + vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet; + vec_float4 xxxx, yyyy, zzzz; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + tmp2 = _vmathVfCross( tfrm.getCol0().get128(), tfrm.getCol1().get128() ); + tmp0 = _vmathVfCross( tfrm.getCol1().get128(), tfrm.getCol2().get128() ); + tmp1 = _vmathVfCross( tfrm.getCol2().get128(), tfrm.getCol0().get128() ); + inv3 = negatef4( tfrm.getCol3().get128() ); + dot = _vmathVfDot3( tmp2, tfrm.getCol2().get128() ); + dot = vec_splat( dot, 0 ); + invdet = recipf4( dot ); + tmp3 = vec_mergeh( tmp0, tmp2 ); + tmp4 = vec_mergel( tmp0, tmp2 ); + inv0 = vec_mergeh( tmp3, tmp1 ); + xxxx = vec_splat( inv3, 0 ); + inv1 = vec_perm( tmp3, tmp1, _VECTORMATH_PERM_ZBWX ); + inv2 = vec_perm( tmp4, tmp1, _VECTORMATH_PERM_XCYX ); + yyyy = vec_splat( inv3, 1 ); + zzzz = vec_splat( inv3, 2 ); + inv3 = vec_madd( inv0, xxxx, zero ); + inv3 = vec_madd( inv1, yyyy, inv3 ); + inv3 = vec_madd( inv2, zzzz, inv3 ); + inv0 = vec_madd( inv0, invdet, zero ); + inv1 = vec_madd( inv1, invdet, zero ); + inv2 = vec_madd( inv2, invdet, zero ); + inv3 = vec_madd( inv3, invdet, zero ); + return Transform3( + Vector3( inv0 ), + Vector3( inv1 ), + Vector3( inv2 ), + Vector3( inv3 ) + ); +} + +inline const Transform3 orthoInverse( const Transform3 & tfrm ) +{ + vec_float4 inv0, inv1, inv2, inv3; + vec_float4 tmp0, tmp1; + vec_float4 xxxx, yyyy, zzzz; + tmp0 = vec_mergeh( tfrm.getCol0().get128(), tfrm.getCol2().get128() ); + tmp1 = vec_mergel( tfrm.getCol0().get128(), tfrm.getCol2().get128() ); + inv3 = negatef4( tfrm.getCol3().get128() ); + inv0 = vec_mergeh( tmp0, tfrm.getCol1().get128() ); + xxxx = vec_splat( inv3, 0 ); + inv1 = vec_perm( tmp0, tfrm.getCol1().get128(), _VECTORMATH_PERM_ZBWX ); + inv2 = vec_perm( tmp1, tfrm.getCol1().get128(), _VECTORMATH_PERM_XCYX ); + yyyy = vec_splat( inv3, 1 ); + zzzz = vec_splat( inv3, 2 ); + inv3 = vec_madd( inv0, xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + inv3 = vec_madd( inv1, yyyy, inv3 ); + inv3 = vec_madd( inv2, zzzz, inv3 ); + return Transform3( + Vector3( inv0 ), + Vector3( inv1 ), + Vector3( inv2 ), + Vector3( inv3 ) + ); +} + +inline const Transform3 absPerElem( const Transform3 & tfrm ) +{ + return Transform3( + absPerElem( tfrm.getCol0() ), + absPerElem( tfrm.getCol1() ), + absPerElem( tfrm.getCol2() ), + absPerElem( tfrm.getCol3() ) + ); +} + +inline const Vector3 Transform3::operator *( Vector3 vec ) const +{ + vec_float4 res; + vec_float4 xxxx, yyyy, zzzz; + xxxx = vec_splat( vec.get128(), 0 ); + yyyy = vec_splat( vec.get128(), 1 ); + zzzz = vec_splat( vec.get128(), 2 ); + res = vec_madd( mCol0.get128(), xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + res = vec_madd( mCol1.get128(), yyyy, res ); + res = vec_madd( mCol2.get128(), zzzz, res ); + return Vector3( res ); +} + +inline const Point3 Transform3::operator *( Point3 pnt ) const +{ + vec_float4 tmp0, tmp1, res; + vec_float4 xxxx, yyyy, zzzz; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + xxxx = vec_splat( pnt.get128(), 0 ); + yyyy = vec_splat( pnt.get128(), 1 ); + zzzz = vec_splat( pnt.get128(), 2 ); + tmp0 = vec_madd( mCol0.get128(), xxxx, zero ); + tmp1 = vec_madd( mCol1.get128(), yyyy, zero ); + tmp0 = vec_madd( mCol2.get128(), zzzz, tmp0 ); + tmp1 = vec_add( mCol3.get128(), tmp1 ); + res = vec_add( tmp0, tmp1 ); + return Point3( res ); +} + +inline const Transform3 Transform3::operator *( const Transform3 & tfrm ) const +{ + return Transform3( + ( *this * tfrm.mCol0 ), + ( *this * tfrm.mCol1 ), + ( *this * tfrm.mCol2 ), + Vector3( ( *this * Point3( tfrm.mCol3 ) ) ) + ); +} + +inline Transform3 & Transform3::operator *=( const Transform3 & tfrm ) +{ + *this = *this * tfrm; + return *this; +} + +inline const Transform3 mulPerElem( const Transform3 & tfrm0, const Transform3 & tfrm1 ) +{ + return Transform3( + mulPerElem( tfrm0.getCol0(), tfrm1.getCol0() ), + mulPerElem( tfrm0.getCol1(), tfrm1.getCol1() ), + mulPerElem( tfrm0.getCol2(), tfrm1.getCol2() ), + mulPerElem( tfrm0.getCol3(), tfrm1.getCol3() ) + ); +} + +inline const Transform3 Transform3::identity( ) +{ + return Transform3( + Vector3::xAxis( ), + Vector3::yAxis( ), + Vector3::zAxis( ), + Vector3( 0.0f ) + ); +} + +inline Transform3 & Transform3::setUpper3x3( const Matrix3 & tfrm ) +{ + mCol0 = tfrm.getCol0(); + mCol1 = tfrm.getCol1(); + mCol2 = tfrm.getCol2(); + return *this; +} + +inline const Matrix3 Transform3::getUpper3x3( ) const +{ + return Matrix3( mCol0, mCol1, mCol2 ); +} + +inline Transform3 & Transform3::setTranslation( Vector3 translateVec ) +{ + mCol3 = translateVec; + return *this; +} + +inline const Vector3 Transform3::getTranslation( ) const +{ + return mCol3; +} + +inline const Transform3 Transform3::rotationX( float radians ) +{ + return rotationX( floatInVec(radians) ); +} + +inline const Transform3 Transform3::rotationX( floatInVec radians ) +{ + vec_float4 s, c, res1, res2; + vec_uint4 select_y, select_z; + vec_float4 zero; + select_y = _VECTORMATH_MASK_0x0F00; + select_z = _VECTORMATH_MASK_0x00F0; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( radians.get128(), &s, &c ); + res1 = vec_sel( zero, c, select_y ); + res1 = vec_sel( res1, s, select_z ); + res2 = vec_sel( zero, negatef4(s), select_y ); + res2 = vec_sel( res2, c, select_z ); + return Transform3( + Vector3::xAxis( ), + Vector3( res1 ), + Vector3( res2 ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 Transform3::rotationY( float radians ) +{ + return rotationY( floatInVec(radians) ); +} + +inline const Transform3 Transform3::rotationY( floatInVec radians ) +{ + vec_float4 s, c, res0, res2; + vec_uint4 select_x, select_z; + vec_float4 zero; + select_x = _VECTORMATH_MASK_0xF000; + select_z = _VECTORMATH_MASK_0x00F0; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( radians.get128(), &s, &c ); + res0 = vec_sel( zero, c, select_x ); + res0 = vec_sel( res0, negatef4(s), select_z ); + res2 = vec_sel( zero, s, select_x ); + res2 = vec_sel( res2, c, select_z ); + return Transform3( + Vector3( res0 ), + Vector3::yAxis( ), + Vector3( res2 ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 Transform3::rotationZ( float radians ) +{ + return rotationZ( floatInVec(radians) ); +} + +inline const Transform3 Transform3::rotationZ( floatInVec radians ) +{ + vec_float4 s, c, res0, res1; + vec_uint4 select_x, select_y; + vec_float4 zero; + select_x = _VECTORMATH_MASK_0xF000; + select_y = _VECTORMATH_MASK_0x0F00; + zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + sincosf4( radians.get128(), &s, &c ); + res0 = vec_sel( zero, c, select_x ); + res0 = vec_sel( res0, s, select_y ); + res1 = vec_sel( zero, negatef4(s), select_x ); + res1 = vec_sel( res1, c, select_y ); + return Transform3( + Vector3( res0 ), + Vector3( res1 ), + Vector3::zAxis( ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 Transform3::rotationZYX( Vector3 radiansXYZ ) +{ + vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + angles = Vector4( radiansXYZ, 0.0f ).get128(); + sincosf4( angles, &s, &c ); + negS = negatef4( s ); + Z0 = vec_mergel( c, s ); + Z1 = vec_mergel( negS, c ); + Z1 = vec_andc( Z1, (vec_float4)_VECTORMATH_MASK_0x000F ); + Y0 = vec_perm( negS, c, _VECTORMATH_PERM_BBYX ); + Y1 = vec_perm( c, s, _VECTORMATH_PERM_BBYX ); + X0 = vec_splat( s, 0 ); + X1 = vec_splat( c, 0 ); + tmp = vec_madd( Z0, Y1, zero ); + return Transform3( + Vector3( vec_madd( Z0, Y0, zero ) ), + Vector3( vec_madd( Z1, X1, vec_madd( tmp, X0, zero ) ) ), + Vector3( vec_nmsub( Z1, X0, vec_madd( tmp, X1, zero ) ) ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 Transform3::rotation( float radians, Vector3 unitVec ) +{ + return rotation( floatInVec(radians), unitVec ); +} + +inline const Transform3 Transform3::rotation( floatInVec radians, Vector3 unitVec ) +{ + return Transform3( Matrix3::rotation( radians, unitVec ), Vector3( 0.0f ) ); +} + +inline const Transform3 Transform3::rotation( Quat unitQuat ) +{ + return Transform3( Matrix3( unitQuat ), Vector3( 0.0f ) ); +} + +inline const Transform3 Transform3::scale( Vector3 scaleVec ) +{ + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + return Transform3( + Vector3( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0xF000 ) ), + Vector3( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0x0F00 ) ), + Vector3( vec_sel( zero, scaleVec.get128(), _VECTORMATH_MASK_0x00F0 ) ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 appendScale( const Transform3 & tfrm, Vector3 scaleVec ) +{ + return Transform3( + ( tfrm.getCol0() * scaleVec.getX( ) ), + ( tfrm.getCol1() * scaleVec.getY( ) ), + ( tfrm.getCol2() * scaleVec.getZ( ) ), + tfrm.getCol3() + ); +} + +inline const Transform3 prependScale( Vector3 scaleVec, const Transform3 & tfrm ) +{ + return Transform3( + mulPerElem( tfrm.getCol0(), scaleVec ), + mulPerElem( tfrm.getCol1(), scaleVec ), + mulPerElem( tfrm.getCol2(), scaleVec ), + mulPerElem( tfrm.getCol3(), scaleVec ) + ); +} + +inline const Transform3 Transform3::translation( Vector3 translateVec ) +{ + return Transform3( + Vector3::xAxis( ), + Vector3::yAxis( ), + Vector3::zAxis( ), + translateVec + ); +} + +inline const Transform3 select( const Transform3 & tfrm0, const Transform3 & tfrm1, bool select1 ) +{ + return Transform3( + select( tfrm0.getCol0(), tfrm1.getCol0(), select1 ), + select( tfrm0.getCol1(), tfrm1.getCol1(), select1 ), + select( tfrm0.getCol2(), tfrm1.getCol2(), select1 ), + select( tfrm0.getCol3(), tfrm1.getCol3(), select1 ) + ); +} + +inline const Transform3 select( const Transform3 & tfrm0, const Transform3 & tfrm1, boolInVec select1 ) +{ + return Transform3( + select( tfrm0.getCol0(), tfrm1.getCol0(), select1 ), + select( tfrm0.getCol1(), tfrm1.getCol1(), select1 ), + select( tfrm0.getCol2(), tfrm1.getCol2(), select1 ), + select( tfrm0.getCol3(), tfrm1.getCol3(), select1 ) + ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( const Transform3 & tfrm ) +{ + print( tfrm.getRow( 0 ) ); + print( tfrm.getRow( 1 ) ); + print( tfrm.getRow( 2 ) ); +} + +inline void print( const Transform3 & tfrm, const char * name ) +{ + printf("%s:\n", name); + print( tfrm ); +} + +#endif + +inline Quat::Quat( const Matrix3 & tfrm ) +{ + vec_float4 res; + vec_float4 col0, col1, col2; + vec_float4 xx_yy, xx_yy_zz_xx, yy_zz_xx_yy, zz_xx_yy_zz, diagSum, diagDiff; + vec_float4 zy_xz_yx, yz_zx_xy, sum, diff; + vec_float4 radicand, invSqrt, scale; + vec_float4 res0, res1, res2, res3; + vec_float4 xx, yy, zz; + vec_uint4 select_x = _VECTORMATH_MASK_0xF000; + vec_uint4 select_y = _VECTORMATH_MASK_0x0F00; + vec_uint4 select_z = _VECTORMATH_MASK_0x00F0; + vec_uint4 select_w = _VECTORMATH_MASK_0x000F; + vec_float4 zero = ((vec_float4){0.0f,0.0f,0.0f,0.0f}); + + col0 = tfrm.getCol0().get128(); + col1 = tfrm.getCol1().get128(); + col2 = tfrm.getCol2().get128(); + + /* four cases: */ + /* trace > 0 */ + /* else */ + /* xx largest diagonal element */ + /* yy largest diagonal element */ + /* zz largest diagonal element */ + + /* compute quaternion for each case */ + + xx_yy = vec_sel( col0, col1, select_y ); + xx_yy_zz_xx = vec_perm( xx_yy, col2, _VECTORMATH_PERM_XYCX ); + yy_zz_xx_yy = vec_perm( xx_yy, col2, _VECTORMATH_PERM_YCXY ); + zz_xx_yy_zz = vec_perm( xx_yy, col2, _VECTORMATH_PERM_CXYC ); + + diagSum = vec_add( vec_add( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); + diagDiff = vec_sub( vec_sub( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); + radicand = vec_add( vec_sel( diagDiff, diagSum, select_w ), ((vec_float4){1.0f,1.0f,1.0f,1.0f}) ); + invSqrt = rsqrtf4( radicand ); + + zy_xz_yx = vec_sel( col0, col1, select_z ); + zy_xz_yx = vec_perm( zy_xz_yx, col2, _VECTORMATH_PERM_ZAYX ); + yz_zx_xy = vec_sel( col0, col1, select_x ); + yz_zx_xy = vec_perm( yz_zx_xy, col2, _VECTORMATH_PERM_BZXX ); + + sum = vec_add( zy_xz_yx, yz_zx_xy ); + diff = vec_sub( zy_xz_yx, yz_zx_xy ); + + scale = vec_madd( invSqrt, ((vec_float4){0.5f,0.5f,0.5f,0.5f}), zero ); + res0 = vec_perm( sum, diff, _VECTORMATH_PERM_XZYA ); + res1 = vec_perm( sum, diff, _VECTORMATH_PERM_ZXXB ); + res2 = vec_perm( sum, diff, _VECTORMATH_PERM_YXXC ); + res3 = diff; + res0 = vec_sel( res0, radicand, select_x ); + res1 = vec_sel( res1, radicand, select_y ); + res2 = vec_sel( res2, radicand, select_z ); + res3 = vec_sel( res3, radicand, select_w ); + res0 = vec_madd( res0, vec_splat( scale, 0 ), zero ); + res1 = vec_madd( res1, vec_splat( scale, 1 ), zero ); + res2 = vec_madd( res2, vec_splat( scale, 2 ), zero ); + res3 = vec_madd( res3, vec_splat( scale, 3 ), zero ); + + /* determine case and select answer */ + + xx = vec_splat( col0, 0 ); + yy = vec_splat( col1, 1 ); + zz = vec_splat( col2, 2 ); + res = vec_sel( res0, res1, vec_cmpgt( yy, xx ) ); + res = vec_sel( res, res2, vec_and( vec_cmpgt( zz, xx ), vec_cmpgt( zz, yy ) ) ); + res = vec_sel( res, res3, vec_cmpgt( vec_splat( diagSum, 0 ), zero ) ); + mVec128 = res; +} + +inline const Matrix3 outer( Vector3 tfrm0, Vector3 tfrm1 ) +{ + return Matrix3( + ( tfrm0 * tfrm1.getX( ) ), + ( tfrm0 * tfrm1.getY( ) ), + ( tfrm0 * tfrm1.getZ( ) ) + ); +} + +inline const Matrix4 outer( Vector4 tfrm0, Vector4 tfrm1 ) +{ + return Matrix4( + ( tfrm0 * tfrm1.getX( ) ), + ( tfrm0 * tfrm1.getY( ) ), + ( tfrm0 * tfrm1.getZ( ) ), + ( tfrm0 * tfrm1.getW( ) ) + ); +} + +inline const Vector3 rowMul( Vector3 vec, const Matrix3 & mat ) +{ + vec_float4 tmp0, tmp1, mcol0, mcol1, mcol2, res; + vec_float4 xxxx, yyyy, zzzz; + tmp0 = vec_mergeh( mat.getCol0().get128(), mat.getCol2().get128() ); + tmp1 = vec_mergel( mat.getCol0().get128(), mat.getCol2().get128() ); + xxxx = vec_splat( vec.get128(), 0 ); + mcol0 = vec_mergeh( tmp0, mat.getCol1().get128() ); + mcol1 = vec_perm( tmp0, mat.getCol1().get128(), _VECTORMATH_PERM_ZBWX ); + mcol2 = vec_perm( tmp1, mat.getCol1().get128(), _VECTORMATH_PERM_XCYX ); + yyyy = vec_splat( vec.get128(), 1 ); + res = vec_madd( mcol0, xxxx, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + zzzz = vec_splat( vec.get128(), 2 ); + res = vec_madd( mcol1, yyyy, res ); + res = vec_madd( mcol2, zzzz, res ); + return Vector3( res ); +} + +inline const Matrix3 crossMatrix( Vector3 vec ) +{ + vec_float4 neg, res0, res1, res2; + neg = negatef4( vec.get128() ); + res0 = vec_perm( vec.get128(), neg, _VECTORMATH_PERM_XZBX ); + res1 = vec_perm( vec.get128(), neg, _VECTORMATH_PERM_CXXX ); + res2 = vec_perm( vec.get128(), neg, _VECTORMATH_PERM_YAXX ); + res0 = vec_andc( res0, (vec_float4)_VECTORMATH_MASK_0xF000 ); + res1 = vec_andc( res1, (vec_float4)_VECTORMATH_MASK_0x0F00 ); + res2 = vec_andc( res2, (vec_float4)_VECTORMATH_MASK_0x00F0 ); + return Matrix3( + Vector3( res0 ), + Vector3( res1 ), + Vector3( res2 ) + ); +} + +inline const Matrix3 crossMatrixMul( Vector3 vec, const Matrix3 & mat ) +{ + return Matrix3( cross( vec, mat.getCol0() ), cross( vec, mat.getCol1() ), cross( vec, mat.getCol2() ) ); +} + +} // namespace Aos +} // namespace Vectormath + +#endif diff --git a/common/vectormath/ppu/cpp/quat_aos.h b/common/vectormath/ppu/cpp/quat_aos.h index a99b68bf..2f2f1e2b 100644 --- a/common/vectormath/ppu/cpp/quat_aos.h +++ b/common/vectormath/ppu/cpp/quat_aos.h @@ -1,536 +1,556 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_QUAT_AOS_CPP_H -#define _VECTORMATH_QUAT_AOS_CPP_H -//----------------------------------------------------------------------------- -// Definitions - -#ifndef _VECTORMATH_INTERNAL_FUNCTIONS -#define _VECTORMATH_INTERNAL_FUNCTIONS - -#endif - -namespace Vectormath { -namespace Aos { - -inline Quat::Quat( float _x, float _y, float _z, float _w ) -{ - if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & - __builtin_constant_p(_z) & __builtin_constant_p(_w)) { - mVec128 = (vec_float4){_x, _y, _z, _w}; - } else { - float *pf = (float *)&mVec128; - pf[0] = _x; - pf[1] = _y; - pf[2] = _z; - pf[3] = _w; - } -} - -inline Quat::Quat( floatInVec _x, floatInVec _y, floatInVec _z, floatInVec _w ) -{ - vec_float4 xz = vec_mergeh( _x.get128(), _z.get128() ); - vec_float4 yw = vec_mergeh( _y.get128(), _w.get128() ); - mVec128 = vec_mergeh( xz, yw ); -} - -inline Quat::Quat( Vector3 xyz, float _w ) -{ - mVec128 = xyz.get128(); - _vmathVfSetElement(mVec128, _w, 3); -} - -inline Quat::Quat( Vector3 xyz, floatInVec _w ) -{ - mVec128 = xyz.get128(); - mVec128 = _vmathVfInsert(mVec128, _w.get128(), 3); -} - -inline Quat::Quat( Vector4 vec ) -{ - mVec128 = vec.get128(); -} - -inline Quat::Quat( float scalar ) -{ - mVec128 = floatInVec(scalar).get128(); -} - -inline Quat::Quat( floatInVec scalar ) -{ - mVec128 = scalar.get128(); -} - -inline Quat::Quat( vec_float4 vf4 ) -{ - mVec128 = vf4; -} - -inline const Quat Quat::identity( ) -{ - return Quat( _VECTORMATH_UNIT_0001 ); -} - -inline const Quat lerp( float t, Quat quat0, Quat quat1 ) -{ - return lerp( floatInVec(t), quat0, quat1 ); -} - -inline const Quat lerp( floatInVec t, Quat quat0, Quat quat1 ) -{ - return ( quat0 + ( ( quat1 - quat0 ) * t ) ); -} - -inline const Quat slerp( float t, Quat unitQuat0, Quat unitQuat1 ) -{ - return slerp( floatInVec(t), unitQuat0, unitQuat1 ); -} - -inline const Quat slerp( floatInVec t, Quat unitQuat0, Quat unitQuat1 ) -{ - Quat start; - vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; - vec_uint4 selectMask; - cosAngle = _vmathVfDot4( unitQuat0.get128(), unitQuat1.get128() ); - cosAngle = vec_splat( cosAngle, 0 ); - selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), cosAngle ); - cosAngle = vec_sel( cosAngle, negatef4( cosAngle ), selectMask ); - start = Quat( vec_sel( unitQuat0.get128(), negatef4( unitQuat0.get128() ), selectMask ) ); - selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL}), cosAngle ); - angle = acosf4( cosAngle ); - tttt = t.get128(); - oneMinusT = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); - angles = vec_mergeh( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); - angles = vec_mergeh( angles, oneMinusT ); - angles = vec_madd( angles, angle, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - sines = sinf4( angles ); - scales = divf4( sines, vec_splat( sines, 0 ) ); - scale0 = vec_sel( oneMinusT, vec_splat( scales, 1 ), selectMask ); - scale1 = vec_sel( tttt, vec_splat( scales, 2 ), selectMask ); - return Quat( vec_madd( start.get128(), scale0, vec_madd( unitQuat1.get128(), scale1, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ) ); -} - -inline const Quat squad( float t, Quat unitQuat0, Quat unitQuat1, Quat unitQuat2, Quat unitQuat3 ) -{ - return squad( floatInVec(t), unitQuat0, unitQuat1, unitQuat2, unitQuat3 ); -} - -inline const Quat squad( floatInVec t, Quat unitQuat0, Quat unitQuat1, Quat unitQuat2, Quat unitQuat3 ) -{ - Quat tmp0, tmp1; - tmp0 = slerp( t, unitQuat0, unitQuat3 ); - tmp1 = slerp( t, unitQuat1, unitQuat2 ); - return slerp( ( ( floatInVec(2.0f) * t ) * ( floatInVec(1.0f) - t ) ), tmp0, tmp1 ); -} - -inline vec_float4 Quat::get128( ) const -{ - return mVec128; -} - -inline Quat & Quat::operator =( Quat quat ) -{ - mVec128 = quat.mVec128; - return *this; -} - -inline Quat & Quat::setXYZ( Vector3 vec ) -{ - mVec128 = vec_sel( vec.get128(), mVec128, _VECTORMATH_MASK_0x000F ); - return *this; -} - -inline const Vector3 Quat::getXYZ( ) const -{ - return Vector3( mVec128 ); -} - -inline Quat & Quat::setX( float _x ) -{ - _vmathVfSetElement(mVec128, _x, 0); - return *this; -} - -inline Quat & Quat::setX( floatInVec _x ) -{ - mVec128 = _vmathVfInsert(mVec128, _x.get128(), 0); - return *this; -} - -inline const floatInVec Quat::getX( ) const -{ - return floatInVec( mVec128, 0 ); -} - -inline Quat & Quat::setY( float _y ) -{ - _vmathVfSetElement(mVec128, _y, 1); - return *this; -} - -inline Quat & Quat::setY( floatInVec _y ) -{ - mVec128 = _vmathVfInsert(mVec128, _y.get128(), 1); - return *this; -} - -inline const floatInVec Quat::getY( ) const -{ - return floatInVec( mVec128, 1 ); -} - -inline Quat & Quat::setZ( float _z ) -{ - _vmathVfSetElement(mVec128, _z, 2); - return *this; -} - -inline Quat & Quat::setZ( floatInVec _z ) -{ - mVec128 = _vmathVfInsert(mVec128, _z.get128(), 2); - return *this; -} - -inline const floatInVec Quat::getZ( ) const -{ - return floatInVec( mVec128, 2 ); -} - -inline Quat & Quat::setW( float _w ) -{ - _vmathVfSetElement(mVec128, _w, 3); - return *this; -} - -inline Quat & Quat::setW( floatInVec _w ) -{ - mVec128 = _vmathVfInsert(mVec128, _w.get128(), 3); - return *this; -} - -inline const floatInVec Quat::getW( ) const -{ - return floatInVec( mVec128, 3 ); -} - -inline Quat & Quat::setElem( int idx, float value ) -{ - _vmathVfSetElement(mVec128, value, idx); - return *this; -} - -inline Quat & Quat::setElem( int idx, floatInVec value ) -{ - mVec128 = _vmathVfInsert(mVec128, value.get128(), idx); - return *this; -} - -inline const floatInVec Quat::getElem( int idx ) const -{ - return floatInVec( mVec128, idx ); -} - -inline VecIdx Quat::operator []( int idx ) -{ - return VecIdx( mVec128, idx ); -} - -inline const floatInVec Quat::operator []( int idx ) const -{ - return floatInVec( mVec128, idx ); -} - -inline const Quat Quat::operator +( Quat quat ) const -{ - return Quat( vec_add( mVec128, quat.mVec128 ) ); -} - -inline const Quat Quat::operator -( Quat quat ) const -{ - return Quat( vec_sub( mVec128, quat.mVec128 ) ); -} - -inline const Quat Quat::operator *( float scalar ) const -{ - return *this * floatInVec(scalar); -} - -inline const Quat Quat::operator *( floatInVec scalar ) const -{ - return Quat( vec_madd( mVec128, scalar.get128(), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); -} - -inline Quat & Quat::operator +=( Quat quat ) -{ - *this = *this + quat; - return *this; -} - -inline Quat & Quat::operator -=( Quat quat ) -{ - *this = *this - quat; - return *this; -} - -inline Quat & Quat::operator *=( float scalar ) -{ - *this = *this * scalar; - return *this; -} - -inline Quat & Quat::operator *=( floatInVec scalar ) -{ - *this = *this * scalar; - return *this; -} - -inline const Quat Quat::operator /( float scalar ) const -{ - return *this / floatInVec(scalar); -} - -inline const Quat Quat::operator /( floatInVec scalar ) const -{ - return Quat( divf4( mVec128, scalar.get128() ) ); -} - -inline Quat & Quat::operator /=( float scalar ) -{ - *this = *this / scalar; - return *this; -} - -inline Quat & Quat::operator /=( floatInVec scalar ) -{ - *this = *this / scalar; - return *this; -} - -inline const Quat Quat::operator -( ) const -{ - return Quat( negatef4( mVec128 ) ); -} - -inline const Quat operator *( float scalar, Quat quat ) -{ - return floatInVec(scalar) * quat; -} - -inline const Quat operator *( floatInVec scalar, Quat quat ) -{ - return quat * scalar; -} - -inline const floatInVec dot( Quat quat0, Quat quat1 ) -{ - return floatInVec( _vmathVfDot4( quat0.get128(), quat1.get128() ), 0 ); -} - -inline const floatInVec norm( Quat quat ) -{ - return floatInVec( _vmathVfDot4( quat.get128(), quat.get128() ), 0 ); -} - -inline const floatInVec length( Quat quat ) -{ - return floatInVec( sqrtf4(_vmathVfDot4( quat.get128(), quat.get128() )), 0 ); -} - -inline const Quat normalize( Quat quat ) -{ - vec_float4 dot = _vmathVfDot4( quat.get128(), quat.get128() ); - return Quat( vec_madd( quat.get128(), rsqrtf4( dot ), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); -} - -inline const Quat Quat::rotation( Vector3 unitVec0, Vector3 unitVec1 ) -{ - Vector3 crossVec; - vec_float4 cosAngle, cosAngleX2Plus2, recipCosHalfAngleX2, cosHalfAngleX2, res; - cosAngle = _vmathVfDot3( unitVec0.get128(), unitVec1.get128() ); - cosAngle = vec_splat( cosAngle, 0 ); - cosAngleX2Plus2 = vec_madd( cosAngle, ((vec_float4){2.0f,2.0f,2.0f,2.0f}), ((vec_float4){2.0f,2.0f,2.0f,2.0f}) ); - recipCosHalfAngleX2 = rsqrtf4( cosAngleX2Plus2 ); - cosHalfAngleX2 = vec_madd( recipCosHalfAngleX2, cosAngleX2Plus2, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - crossVec = cross( unitVec0, unitVec1 ); - res = vec_madd( crossVec.get128(), recipCosHalfAngleX2, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - res = vec_sel( res, vec_madd( cosHalfAngleX2, ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ), _VECTORMATH_MASK_0x000F ); - return Quat( res ); -} - -inline const Quat Quat::rotation( float radians, Vector3 unitVec ) -{ - return rotation( floatInVec(radians), unitVec ); -} - -inline const Quat Quat::rotation( floatInVec radians, Vector3 unitVec ) -{ - vec_float4 s, c, angle, res; - angle = vec_madd( radians.get128(), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - sincosf4( angle, &s, &c ); - res = vec_sel( vec_madd( unitVec.get128(), s, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ), c, _VECTORMATH_MASK_0x000F ); - return Quat( res ); -} - -inline const Quat Quat::rotationX( float radians ) -{ - return rotationX( floatInVec(radians) ); -} - -inline const Quat Quat::rotationX( floatInVec radians ) -{ - vec_float4 s, c, angle, res; - angle = vec_madd( radians.get128(), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - sincosf4( angle, &s, &c ); - res = vec_sel( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), s, _VECTORMATH_MASK_0xF000 ); - res = vec_sel( res, c, _VECTORMATH_MASK_0x000F ); - return Quat( res ); -} - -inline const Quat Quat::rotationY( float radians ) -{ - return rotationY( floatInVec(radians) ); -} - -inline const Quat Quat::rotationY( floatInVec radians ) -{ - vec_float4 s, c, angle, res; - angle = vec_madd( radians.get128(), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - sincosf4( angle, &s, &c ); - res = vec_sel( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), s, _VECTORMATH_MASK_0x0F00 ); - res = vec_sel( res, c, _VECTORMATH_MASK_0x000F ); - return Quat( res ); -} - -inline const Quat Quat::rotationZ( float radians ) -{ - return rotationZ( floatInVec(radians) ); -} - -inline const Quat Quat::rotationZ( floatInVec radians ) -{ - vec_float4 s, c, angle, res; - angle = vec_madd( radians.get128(), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - sincosf4( angle, &s, &c ); - res = vec_sel( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), s, _VECTORMATH_MASK_0x00F0 ); - res = vec_sel( res, c, _VECTORMATH_MASK_0x000F ); - return Quat( res ); -} - -inline const Quat Quat::operator *( Quat quat ) const -{ - vec_float4 ldata, rdata, qv, tmp0, tmp1, tmp2, tmp3; - vec_float4 product, l_wxyz, r_wxyz, xy, qw; - ldata = mVec128; - rdata = quat.mVec128; - tmp0 = vec_perm( ldata, ldata, _VECTORMATH_PERM_YZXW ); - tmp1 = vec_perm( rdata, rdata, _VECTORMATH_PERM_ZXYW ); - tmp2 = vec_perm( ldata, ldata, _VECTORMATH_PERM_ZXYW ); - tmp3 = vec_perm( rdata, rdata, _VECTORMATH_PERM_YZXW ); - qv = vec_madd( vec_splat( ldata, 3 ), rdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - qv = vec_madd( vec_splat( rdata, 3 ), ldata, qv ); - qv = vec_madd( tmp0, tmp1, qv ); - qv = vec_nmsub( tmp2, tmp3, qv ); - product = vec_madd( ldata, rdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - l_wxyz = vec_sld( ldata, ldata, 12 ); - r_wxyz = vec_sld( rdata, rdata, 12 ); - qw = vec_nmsub( l_wxyz, r_wxyz, product ); - xy = vec_madd( l_wxyz, r_wxyz, product ); - qw = vec_sub( qw, vec_sld( xy, xy, 8 ) ); - return Quat( vec_sel( qv, qw, _VECTORMATH_MASK_0x000F ) ); -} - -inline Quat & Quat::operator *=( Quat quat ) -{ - *this = *this * quat; - return *this; -} - -inline const Vector3 rotate( Quat quat, Vector3 vec ) -{ - vec_float4 qdata, vdata, product, tmp0, tmp1, tmp2, tmp3, wwww, qv, qw, res; - qdata = quat.get128(); - vdata = vec.get128(); - tmp0 = vec_perm( qdata, qdata, _VECTORMATH_PERM_YZXW ); - tmp1 = vec_perm( vdata, vdata, _VECTORMATH_PERM_ZXYW ); - tmp2 = vec_perm( qdata, qdata, _VECTORMATH_PERM_ZXYW ); - tmp3 = vec_perm( vdata, vdata, _VECTORMATH_PERM_YZXW ); - wwww = vec_splat( qdata, 3 ); - qv = vec_madd( wwww, vdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - qv = vec_madd( tmp0, tmp1, qv ); - qv = vec_nmsub( tmp2, tmp3, qv ); - product = vec_madd( qdata, vdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - qw = vec_madd( vec_sld( qdata, qdata, 4 ), vec_sld( vdata, vdata, 4 ), product ); - qw = vec_add( vec_sld( product, product, 8 ), qw ); - tmp1 = vec_perm( qv, qv, _VECTORMATH_PERM_ZXYW ); - tmp3 = vec_perm( qv, qv, _VECTORMATH_PERM_YZXW ); - res = vec_madd( vec_splat( qw, 0 ), qdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - res = vec_madd( wwww, qv, res ); - res = vec_madd( tmp0, tmp1, res ); - res = vec_nmsub( tmp2, tmp3, res ); - return Vector3( res ); -} - -inline const Quat conj( Quat quat ) -{ - return Quat( vec_xor( quat.get128(), ((vec_float4)(vec_uint4){0x80000000,0x80000000,0x80000000,0}) ) ); -} - -inline const Quat select( Quat quat0, Quat quat1, bool select1 ) -{ - return select( quat0, quat1, boolInVec(select1) ); -} - -inline const Quat select( Quat quat0, Quat quat1, boolInVec select1 ) -{ - return Quat( vec_sel( quat0.get128(), quat1.get128(), select1.get128() ) ); -} - -#ifdef _VECTORMATH_DEBUG - -inline void print( Quat quat ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = quat.get128(); - printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -inline void print( Quat quat, const char * name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = quat.get128(); - printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -#endif - -} // namespace Aos -} // namespace Vectormath - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_QUAT_AOS_CPP_H +#define _VECTORMATH_QUAT_AOS_CPP_H +//----------------------------------------------------------------------------- +// Definitions + +#ifndef _VECTORMATH_INTERNAL_FUNCTIONS +#define _VECTORMATH_INTERNAL_FUNCTIONS + +#endif + +namespace Vectormath { +namespace Aos { + +inline Quat::Quat( float _x, float _y, float _z, float _w ) +{ + if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & + __builtin_constant_p(_z) & __builtin_constant_p(_w)) { + mVec128 = (vec_float4){_x, _y, _z, _w}; + } else { + float *pf = (float *)&mVec128; + pf[0] = _x; + pf[1] = _y; + pf[2] = _z; + pf[3] = _w; + } +} + +inline Quat::Quat( floatInVec _x, floatInVec _y, floatInVec _z, floatInVec _w ) +{ + vec_float4 xz = vec_mergeh( _x.get128(), _z.get128() ); + vec_float4 yw = vec_mergeh( _y.get128(), _w.get128() ); + mVec128 = vec_mergeh( xz, yw ); +} + +inline Quat::Quat( Vector3 xyz, float _w ) +{ + mVec128 = xyz.get128(); + _vmathVfSetElement(mVec128, _w, 3); +} + +inline Quat::Quat( Vector3 xyz, floatInVec _w ) +{ + mVec128 = xyz.get128(); + mVec128 = _vmathVfInsert(mVec128, _w.get128(), 3); +} + +inline Quat::Quat( Vector4 vec ) +{ + mVec128 = vec.get128(); +} + +inline Quat::Quat( float scalar ) +{ + mVec128 = floatInVec(scalar).get128(); +} + +inline Quat::Quat( floatInVec scalar ) +{ + mVec128 = scalar.get128(); +} + +inline Quat::Quat( vec_float4 vf4 ) +{ + mVec128 = vf4; +} + +inline const Quat Quat::identity( ) +{ + return Quat( _VECTORMATH_UNIT_0001 ); +} + +inline const Quat lerp( float t, Quat quat0, Quat quat1 ) +{ + return lerp( floatInVec(t), quat0, quat1 ); +} + +inline const Quat lerp( floatInVec t, Quat quat0, Quat quat1 ) +{ + return ( quat0 + ( ( quat1 - quat0 ) * t ) ); +} + +inline const Quat slerp( float t, Quat unitQuat0, Quat unitQuat1 ) +{ + return slerp( floatInVec(t), unitQuat0, unitQuat1 ); +} + +inline const Quat slerp( floatInVec t, Quat unitQuat0, Quat unitQuat1 ) +{ + Quat start; + vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; + vec_uint4 selectMask; + cosAngle = _vmathVfDot4( unitQuat0.get128(), unitQuat1.get128() ); + cosAngle = vec_splat( cosAngle, 0 ); + selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), cosAngle ); + cosAngle = vec_sel( cosAngle, negatef4( cosAngle ), selectMask ); + start = Quat( vec_sel( unitQuat0.get128(), negatef4( unitQuat0.get128() ), selectMask ) ); + selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL}), cosAngle ); + angle = acosf4( cosAngle ); + tttt = t.get128(); + oneMinusT = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); + angles = vec_mergeh( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); + angles = vec_mergeh( angles, oneMinusT ); + angles = vec_madd( angles, angle, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + sines = sinf4( angles ); + scales = divf4( sines, vec_splat( sines, 0 ) ); + scale0 = vec_sel( oneMinusT, vec_splat( scales, 1 ), selectMask ); + scale1 = vec_sel( tttt, vec_splat( scales, 2 ), selectMask ); + return Quat( vec_madd( start.get128(), scale0, vec_madd( unitQuat1.get128(), scale1, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ) ); +} + +inline const Quat squad( float t, Quat unitQuat0, Quat unitQuat1, Quat unitQuat2, Quat unitQuat3 ) +{ + return squad( floatInVec(t), unitQuat0, unitQuat1, unitQuat2, unitQuat3 ); +} + +inline const Quat squad( floatInVec t, Quat unitQuat0, Quat unitQuat1, Quat unitQuat2, Quat unitQuat3 ) +{ + Quat tmp0, tmp1; + tmp0 = slerp( t, unitQuat0, unitQuat3 ); + tmp1 = slerp( t, unitQuat1, unitQuat2 ); + return slerp( ( ( floatInVec(2.0f) * t ) * ( floatInVec(1.0f) - t ) ), tmp0, tmp1 ); +} + +inline vec_float4 Quat::get128( ) const +{ + return mVec128; +} + +inline Quat & Quat::operator =( Quat quat ) +{ + mVec128 = quat.mVec128; + return *this; +} + +inline Quat & Quat::setXYZ( Vector3 vec ) +{ + mVec128 = vec_sel( vec.get128(), mVec128, _VECTORMATH_MASK_0x000F ); + return *this; +} + +inline const Vector3 Quat::getXYZ( ) const +{ + return Vector3( mVec128 ); +} + +inline Quat & Quat::setX( float _x ) +{ + _vmathVfSetElement(mVec128, _x, 0); + return *this; +} + +inline Quat & Quat::setX( floatInVec _x ) +{ + mVec128 = _vmathVfInsert(mVec128, _x.get128(), 0); + return *this; +} + +inline const floatInVec Quat::getX( ) const +{ + return floatInVec( mVec128, 0 ); +} + +inline Quat & Quat::setY( float _y ) +{ + _vmathVfSetElement(mVec128, _y, 1); + return *this; +} + +inline Quat & Quat::setY( floatInVec _y ) +{ + mVec128 = _vmathVfInsert(mVec128, _y.get128(), 1); + return *this; +} + +inline const floatInVec Quat::getY( ) const +{ + return floatInVec( mVec128, 1 ); +} + +inline Quat & Quat::setZ( float _z ) +{ + _vmathVfSetElement(mVec128, _z, 2); + return *this; +} + +inline Quat & Quat::setZ( floatInVec _z ) +{ + mVec128 = _vmathVfInsert(mVec128, _z.get128(), 2); + return *this; +} + +inline const floatInVec Quat::getZ( ) const +{ + return floatInVec( mVec128, 2 ); +} + +inline Quat & Quat::setW( float _w ) +{ + _vmathVfSetElement(mVec128, _w, 3); + return *this; +} + +inline Quat & Quat::setW( floatInVec _w ) +{ + mVec128 = _vmathVfInsert(mVec128, _w.get128(), 3); + return *this; +} + +inline const floatInVec Quat::getW( ) const +{ + return floatInVec( mVec128, 3 ); +} + +inline Quat & Quat::setElem( int idx, float value ) +{ + _vmathVfSetElement(mVec128, value, idx); + return *this; +} + +inline Quat & Quat::setElem( int idx, floatInVec value ) +{ + mVec128 = _vmathVfInsert(mVec128, value.get128(), idx); + return *this; +} + +inline const floatInVec Quat::getElem( int idx ) const +{ + return floatInVec( mVec128, idx ); +} + +inline VecIdx Quat::operator []( int idx ) +{ + return VecIdx( mVec128, idx ); +} + +inline const floatInVec Quat::operator []( int idx ) const +{ + return floatInVec( mVec128, idx ); +} + +inline const Quat Quat::operator +( Quat quat ) const +{ + return Quat( vec_add( mVec128, quat.mVec128 ) ); +} + +inline const Quat Quat::operator -( Quat quat ) const +{ + return Quat( vec_sub( mVec128, quat.mVec128 ) ); +} + +inline const Quat Quat::operator *( float scalar ) const +{ + return *this * floatInVec(scalar); +} + +inline const Quat Quat::operator *( floatInVec scalar ) const +{ + return Quat( vec_madd( mVec128, scalar.get128(), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); +} + +inline Quat & Quat::operator +=( Quat quat ) +{ + *this = *this + quat; + return *this; +} + +inline Quat & Quat::operator -=( Quat quat ) +{ + *this = *this - quat; + return *this; +} + +inline Quat & Quat::operator *=( float scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline Quat & Quat::operator *=( floatInVec scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Quat Quat::operator /( float scalar ) const +{ + return *this / floatInVec(scalar); +} + +inline const Quat Quat::operator /( floatInVec scalar ) const +{ + return Quat( divf4( mVec128, scalar.get128() ) ); +} + +inline Quat & Quat::operator /=( float scalar ) +{ + *this = *this / scalar; + return *this; +} + +inline Quat & Quat::operator /=( floatInVec scalar ) +{ + *this = *this / scalar; + return *this; +} + +inline const Quat Quat::operator -( ) const +{ + return Quat( negatef4( mVec128 ) ); +} + +inline const Quat operator *( float scalar, Quat quat ) +{ + return floatInVec(scalar) * quat; +} + +inline const Quat operator *( floatInVec scalar, Quat quat ) +{ + return quat * scalar; +} + +inline const floatInVec dot( Quat quat0, Quat quat1 ) +{ + return floatInVec( _vmathVfDot4( quat0.get128(), quat1.get128() ), 0 ); +} + +inline const floatInVec norm( Quat quat ) +{ + return floatInVec( _vmathVfDot4( quat.get128(), quat.get128() ), 0 ); +} + +inline const floatInVec length( Quat quat ) +{ + return floatInVec( sqrtf4(_vmathVfDot4( quat.get128(), quat.get128() )), 0 ); +} + +inline const Quat normalize( Quat quat ) +{ + vec_float4 dot = _vmathVfDot4( quat.get128(), quat.get128() ); + return Quat( vec_madd( quat.get128(), rsqrtf4( dot ), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); +} + +inline const Quat Quat::rotation( Vector3 unitVec0, Vector3 unitVec1 ) +{ + Vector3 crossVec; + vec_float4 cosAngle, cosAngleX2Plus2, recipCosHalfAngleX2, cosHalfAngleX2, res; + cosAngle = _vmathVfDot3( unitVec0.get128(), unitVec1.get128() ); + cosAngle = vec_splat( cosAngle, 0 ); + cosAngleX2Plus2 = vec_madd( cosAngle, ((vec_float4){2.0f,2.0f,2.0f,2.0f}), ((vec_float4){2.0f,2.0f,2.0f,2.0f}) ); + recipCosHalfAngleX2 = rsqrtf4( cosAngleX2Plus2 ); + cosHalfAngleX2 = vec_madd( recipCosHalfAngleX2, cosAngleX2Plus2, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + crossVec = cross( unitVec0, unitVec1 ); + res = vec_madd( crossVec.get128(), recipCosHalfAngleX2, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + res = vec_sel( res, vec_madd( cosHalfAngleX2, ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ), _VECTORMATH_MASK_0x000F ); + return Quat( res ); +} + +inline const Quat Quat::rotation( float radians, Vector3 unitVec ) +{ + return rotation( floatInVec(radians), unitVec ); +} + +inline const Quat Quat::rotation( floatInVec radians, Vector3 unitVec ) +{ + vec_float4 s, c, angle, res; + angle = vec_madd( radians.get128(), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + sincosf4( angle, &s, &c ); + res = vec_sel( vec_madd( unitVec.get128(), s, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ), c, _VECTORMATH_MASK_0x000F ); + return Quat( res ); +} + +inline const Quat Quat::rotationX( float radians ) +{ + return rotationX( floatInVec(radians) ); +} + +inline const Quat Quat::rotationX( floatInVec radians ) +{ + vec_float4 s, c, angle, res; + angle = vec_madd( radians.get128(), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + sincosf4( angle, &s, &c ); + res = vec_sel( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), s, _VECTORMATH_MASK_0xF000 ); + res = vec_sel( res, c, _VECTORMATH_MASK_0x000F ); + return Quat( res ); +} + +inline const Quat Quat::rotationY( float radians ) +{ + return rotationY( floatInVec(radians) ); +} + +inline const Quat Quat::rotationY( floatInVec radians ) +{ + vec_float4 s, c, angle, res; + angle = vec_madd( radians.get128(), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + sincosf4( angle, &s, &c ); + res = vec_sel( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), s, _VECTORMATH_MASK_0x0F00 ); + res = vec_sel( res, c, _VECTORMATH_MASK_0x000F ); + return Quat( res ); +} + +inline const Quat Quat::rotationZ( float radians ) +{ + return rotationZ( floatInVec(radians) ); +} + +inline const Quat Quat::rotationZ( floatInVec radians ) +{ + vec_float4 s, c, angle, res; + angle = vec_madd( radians.get128(), ((vec_float4){0.5f,0.5f,0.5f,0.5f}), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + sincosf4( angle, &s, &c ); + res = vec_sel( ((vec_float4){0.0f,0.0f,0.0f,0.0f}), s, _VECTORMATH_MASK_0x00F0 ); + res = vec_sel( res, c, _VECTORMATH_MASK_0x000F ); + return Quat( res ); +} + +inline const Quat Quat::operator *( Quat quat ) const +{ + vec_float4 ldata, rdata, qv, tmp0, tmp1, tmp2, tmp3; + vec_float4 product, l_wxyz, r_wxyz, xy, qw; + ldata = mVec128; + rdata = quat.mVec128; + tmp0 = vec_perm( ldata, ldata, _VECTORMATH_PERM_YZXW ); + tmp1 = vec_perm( rdata, rdata, _VECTORMATH_PERM_ZXYW ); + tmp2 = vec_perm( ldata, ldata, _VECTORMATH_PERM_ZXYW ); + tmp3 = vec_perm( rdata, rdata, _VECTORMATH_PERM_YZXW ); + qv = vec_madd( vec_splat( ldata, 3 ), rdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + qv = vec_madd( vec_splat( rdata, 3 ), ldata, qv ); + qv = vec_madd( tmp0, tmp1, qv ); + qv = vec_nmsub( tmp2, tmp3, qv ); + product = vec_madd( ldata, rdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + l_wxyz = vec_sld( ldata, ldata, 12 ); + r_wxyz = vec_sld( rdata, rdata, 12 ); + qw = vec_nmsub( l_wxyz, r_wxyz, product ); + xy = vec_madd( l_wxyz, r_wxyz, product ); + qw = vec_sub( qw, vec_sld( xy, xy, 8 ) ); + return Quat( vec_sel( qv, qw, _VECTORMATH_MASK_0x000F ) ); +} + +inline Quat & Quat::operator *=( Quat quat ) +{ + *this = *this * quat; + return *this; +} + +inline const Vector3 rotate( Quat quat, Vector3 vec ) +{ + vec_float4 qdata, vdata, product, tmp0, tmp1, tmp2, tmp3, wwww, qv, qw, res; + qdata = quat.get128(); + vdata = vec.get128(); + tmp0 = vec_perm( qdata, qdata, _VECTORMATH_PERM_YZXW ); + tmp1 = vec_perm( vdata, vdata, _VECTORMATH_PERM_ZXYW ); + tmp2 = vec_perm( qdata, qdata, _VECTORMATH_PERM_ZXYW ); + tmp3 = vec_perm( vdata, vdata, _VECTORMATH_PERM_YZXW ); + wwww = vec_splat( qdata, 3 ); + qv = vec_madd( wwww, vdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + qv = vec_madd( tmp0, tmp1, qv ); + qv = vec_nmsub( tmp2, tmp3, qv ); + product = vec_madd( qdata, vdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + qw = vec_madd( vec_sld( qdata, qdata, 4 ), vec_sld( vdata, vdata, 4 ), product ); + qw = vec_add( vec_sld( product, product, 8 ), qw ); + tmp1 = vec_perm( qv, qv, _VECTORMATH_PERM_ZXYW ); + tmp3 = vec_perm( qv, qv, _VECTORMATH_PERM_YZXW ); + res = vec_madd( vec_splat( qw, 0 ), qdata, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + res = vec_madd( wwww, qv, res ); + res = vec_madd( tmp0, tmp1, res ); + res = vec_nmsub( tmp2, tmp3, res ); + return Vector3( res ); +} + +inline const Quat conj( Quat quat ) +{ + return Quat( vec_xor( quat.get128(), ((vec_float4)(vec_uint4){0x80000000,0x80000000,0x80000000,0}) ) ); +} + +inline const Quat select( Quat quat0, Quat quat1, bool select1 ) +{ + return select( quat0, quat1, boolInVec(select1) ); +} + +inline const Quat select( Quat quat0, Quat quat1, boolInVec select1 ) +{ + return Quat( vec_sel( quat0.get128(), quat1.get128(), select1.get128() ) ); +} + +inline void loadXYZW( Quat & quat, const float * fptr ) +{ + vec_float4 vec0 = vec_ld(0, fptr); + vec_float4 vec1 = vec_ld(16, fptr); + quat = Quat( vec_perm(vec0, vec1, vec_lvsl(0, fptr)) ); +} + +inline void storeXYZW( Quat quat, float * fptr ) +{ + vec_float4 vsrc = quat.get128(); + vec_float4 x = vec_splat(vsrc, 0); + vec_float4 y = vec_splat(vsrc, 1); + vec_float4 z = vec_splat(vsrc, 2); + vec_float4 w = vec_splat(vsrc, 3); + vec_ste(x, 0, fptr); + vec_ste(y, 4, fptr); + vec_ste(z, 8, fptr); + vec_ste(w, 12, fptr); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( Quat quat ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = quat.get128(); + printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +inline void print( Quat quat, const char * name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = quat.get128(); + printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +#endif + +} // namespace Aos +} // namespace Vectormath + +#endif diff --git a/common/vectormath/ppu/cpp/vec_aos.h b/common/vectormath/ppu/cpp/vec_aos.h index dc1f9849..921a156f 100644 --- a/common/vectormath/ppu/cpp/vec_aos.h +++ b/common/vectormath/ppu/cpp/vec_aos.h @@ -1,1492 +1,1652 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_VEC_AOS_CPP_H -#define _VECTORMATH_VEC_AOS_CPP_H -//----------------------------------------------------------------------------- -// Constants -// for permutes words are labeled [x,y,z,w] [a,b,c,d] - -#define _VECTORMATH_PERM_X 0x00010203 -#define _VECTORMATH_PERM_Y 0x04050607 -#define _VECTORMATH_PERM_Z 0x08090a0b -#define _VECTORMATH_PERM_W 0x0c0d0e0f -#define _VECTORMATH_PERM_A 0x10111213 -#define _VECTORMATH_PERM_B 0x14151617 -#define _VECTORMATH_PERM_C 0x18191a1b -#define _VECTORMATH_PERM_D 0x1c1d1e1f -#define _VECTORMATH_PERM_XYZA (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A } -#define _VECTORMATH_PERM_ZXYW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_W } -#define _VECTORMATH_PERM_YZXW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_W } -#define _VECTORMATH_PERM_YZAB (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A, _VECTORMATH_PERM_B } -#define _VECTORMATH_PERM_ZABC (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A, _VECTORMATH_PERM_B, _VECTORMATH_PERM_C } -#define _VECTORMATH_PERM_XYAW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A, _VECTORMATH_PERM_W } -#define _VECTORMATH_PERM_XAZW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_A, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_W } -#define _VECTORMATH_MASK_0xF000 (vec_uint4){ 0xffffffff, 0, 0, 0 } -#define _VECTORMATH_MASK_0x0F00 (vec_uint4){ 0, 0xffffffff, 0, 0 } -#define _VECTORMATH_MASK_0x00F0 (vec_uint4){ 0, 0, 0xffffffff, 0 } -#define _VECTORMATH_MASK_0x000F (vec_uint4){ 0, 0, 0, 0xffffffff } -#define _VECTORMATH_UNIT_1000 (vec_float4){ 1.0f, 0.0f, 0.0f, 0.0f } -#define _VECTORMATH_UNIT_0100 (vec_float4){ 0.0f, 1.0f, 0.0f, 0.0f } -#define _VECTORMATH_UNIT_0010 (vec_float4){ 0.0f, 0.0f, 1.0f, 0.0f } -#define _VECTORMATH_UNIT_0001 (vec_float4){ 0.0f, 0.0f, 0.0f, 1.0f } -#define _VECTORMATH_SLERP_TOL 0.999f - -//----------------------------------------------------------------------------- -// Definitions - -#ifndef _VECTORMATH_INTERNAL_FUNCTIONS -#define _VECTORMATH_INTERNAL_FUNCTIONS - -static inline vec_float4 _vmathVfDot3( vec_float4 vec0, vec_float4 vec1 ) -{ - vec_float4 result; - result = vec_madd( vec0, vec1, (vec_float4){0.0f,0.0f,0.0f,0.0f} ); - result = vec_madd( vec_sld( vec0, vec0, 4 ), vec_sld( vec1, vec1, 4 ), result ); - return vec_madd( vec_sld( vec0, vec0, 8 ), vec_sld( vec1, vec1, 8 ), result ); -} - -static inline vec_float4 _vmathVfDot4( vec_float4 vec0, vec_float4 vec1 ) -{ - vec_float4 result; - result = vec_madd( vec0, vec1, (vec_float4){0.0f,0.0f,0.0f,0.0f} ); - result = vec_madd( vec_sld( vec0, vec0, 4 ), vec_sld( vec1, vec1, 4 ), result ); - return vec_add( vec_sld( result, result, 8 ), result ); -} - -static inline vec_float4 _vmathVfCross( vec_float4 vec0, vec_float4 vec1 ) -{ - vec_float4 tmp0, tmp1, tmp2, tmp3, result; - tmp0 = vec_perm( vec0, vec0, _VECTORMATH_PERM_YZXW ); - tmp1 = vec_perm( vec1, vec1, _VECTORMATH_PERM_ZXYW ); - tmp2 = vec_perm( vec0, vec0, _VECTORMATH_PERM_ZXYW ); - tmp3 = vec_perm( vec1, vec1, _VECTORMATH_PERM_YZXW ); - result = vec_madd( tmp0, tmp1, (vec_float4){0.0f,0.0f,0.0f,0.0f} ); - result = vec_nmsub( tmp2, tmp3, result ); - return result; -} - -static inline vec_uint4 _vmathVfToHalfFloatsUnpacked(vec_float4 v) -{ - vec_int4 bexp; - vec_uint4 mant, sign, hfloat; - vec_uint4 notZero, isInf; - const vec_uint4 hfloatInf = (vec_uint4){0x00007c00u,0x00007c00u,0x00007c00u,0x00007c00u}; - const vec_uint4 mergeMant = (vec_uint4){0x000003ffu,0x000003ffu,0x000003ffu,0x000003ffu}; - const vec_uint4 mergeSign = (vec_uint4){0x00008000u,0x00008000u,0x00008000u,0x00008000u}; - - sign = vec_sr((vec_uint4)v, (vec_uint4){16,16,16,16}); - mant = vec_sr((vec_uint4)v, (vec_uint4){13,13,13,13}); - bexp = vec_and(vec_sr((vec_int4)v, (vec_uint4){23,23,23,23}), (vec_int4){0xff,0xff,0xff,0xff}); - - notZero = (vec_uint4)vec_cmpgt(bexp, (vec_int4){112,112,112,112}); - isInf = (vec_uint4)vec_cmpgt(bexp, (vec_int4){142,142,142,142}); - - bexp = vec_add(bexp, (vec_int4){-112,-112,-112,-112}); - bexp = vec_sl(bexp, (vec_uint4){10,10,10,10}); - - hfloat = vec_sel((vec_uint4)bexp, mant, mergeMant); - hfloat = vec_sel((vec_uint4){0,0,0,0}, hfloat, notZero); - hfloat = vec_sel(hfloat, hfloatInf, isInf); - hfloat = vec_sel(hfloat, sign, mergeSign); - - return hfloat; -} - -static inline vec_ushort8 _vmath2VfToHalfFloats(vec_float4 u, vec_float4 v) -{ - vec_uint4 hfloat_u, hfloat_v; - const vec_uchar16 pack = (vec_uchar16){2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31}; - hfloat_u = _vmathVfToHalfFloatsUnpacked(u); - hfloat_v = _vmathVfToHalfFloatsUnpacked(v); - return (vec_ushort8)vec_perm(hfloat_u, hfloat_v, pack); -} - -#ifndef __GNUC__ -#define __builtin_constant_p(x) 0 -#endif - -static inline vec_float4 _vmathVfInsert(vec_float4 dst, vec_float4 src, int slot) -{ -#ifdef __GNUC__ - if (__builtin_constant_p(slot)) { - dst = vec_sld(dst, dst, slot<<2); - dst = vec_sld(dst, src, 4); - if (slot != 3) dst = vec_sld(dst, dst, (3-slot)<<2); - return dst; - } else -#endif - { - vec_uchar16 shiftpattern = vec_lvsr( 0, (float *)(size_t)(slot<<2) ); - vec_uint4 selectmask = (vec_uint4)vec_perm( (vec_uint4){0,0,0,0}, _VECTORMATH_MASK_0xF000, shiftpattern ); - return vec_sel( dst, src, selectmask ); - } -} - -#define _vmathVfGetElement(vec, slot) ((float *)&(vec))[slot] -#ifdef _VECTORMATH_SET_CONSTS_IN_MEM -#define _vmathVfSetElement(vec, scalar, slot) ((float *)&(vec))[slot] = scalar -#else -#define _vmathVfSetElement(vec, scalar, slot) \ -{ \ - if (__builtin_constant_p(scalar)) { \ - (vec) = _vmathVfInsert(vec, (vec_float4){scalar, scalar, scalar, scalar}, slot); \ - } else { \ - ((float *)&(vec))[slot] = scalar; \ - } \ -} -#endif - -static inline vec_float4 _vmathVfSplatScalar(float scalar) -{ - vec_float4 result; - if (__builtin_constant_p(scalar)) { - result = (vec_float4){scalar, scalar, scalar, scalar}; - } else { - result = vec_ld(0, &scalar); - result = vec_splat(vec_perm(result, result, vec_lvsl(0, &scalar)), 0); - } - return result; -} - -static inline vec_uint4 _vmathVuiSplatScalar(unsigned int scalar) -{ - vec_uint4 result; - if (__builtin_constant_p(scalar)) { - result = (vec_uint4){scalar, scalar, scalar, scalar}; - } else { - result = vec_ld(0, &scalar); - result = vec_splat(vec_perm(result, result, vec_lvsl(0, &scalar)), 0); - } - return result; -} - -#endif - -namespace Vectormath { -namespace Aos { - -#ifdef _VECTORMATH_NO_SCALAR_CAST -inline VecIdx::operator floatInVec() const -{ - return floatInVec(ref, i); -} - -inline float VecIdx::getAsFloat() const -#else -inline VecIdx::operator float() const -#endif -{ - return _vmathVfGetElement(ref, i); -} - -inline float VecIdx::operator =( float scalar ) -{ - _vmathVfSetElement(ref, scalar, i); - return scalar; -} - -inline floatInVec VecIdx::operator =( floatInVec scalar ) -{ - ref = _vmathVfInsert(ref, scalar.get128(), i); - return scalar; -} - -inline floatInVec VecIdx::operator =( const VecIdx& scalar ) -{ - return *this = floatInVec(scalar.ref, scalar.i); -} - -inline floatInVec VecIdx::operator *=( float scalar ) -{ - return *this *= floatInVec(scalar); -} - -inline floatInVec VecIdx::operator *=( floatInVec scalar ) -{ - return *this = floatInVec(ref, i) * scalar; -} - -inline floatInVec VecIdx::operator /=( float scalar ) -{ - return *this /= floatInVec(scalar); -} - -inline floatInVec VecIdx::operator /=( floatInVec scalar ) -{ - return *this = floatInVec(ref, i) / scalar; -} - -inline floatInVec VecIdx::operator +=( float scalar ) -{ - return *this += floatInVec(scalar); -} - -inline floatInVec VecIdx::operator +=( floatInVec scalar ) -{ - return *this = floatInVec(ref, i) + scalar; -} - -inline floatInVec VecIdx::operator -=( float scalar ) -{ - return *this -= floatInVec(scalar); -} - -inline floatInVec VecIdx::operator -=( floatInVec scalar ) -{ - return *this = floatInVec(ref, i) - scalar; -} - -inline Vector3::Vector3( float _x, float _y, float _z ) -{ - if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & __builtin_constant_p(_z)) { - mVec128 = (vec_float4){_x, _y, _z, 0.0f}; - } else { - float *pf = (float *)&mVec128; - pf[0] = _x; - pf[1] = _y; - pf[2] = _z; - } -} - -inline Vector3::Vector3( floatInVec _x, floatInVec _y, floatInVec _z ) -{ - vec_float4 xz = vec_mergeh( _x.get128(), _z.get128() ); - mVec128 = vec_mergeh( xz, _y.get128() ); -} - -inline Vector3::Vector3( Point3 pnt ) -{ - mVec128 = pnt.get128(); -} - -inline Vector3::Vector3( float scalar ) -{ - mVec128 = floatInVec(scalar).get128(); -} - -inline Vector3::Vector3( floatInVec scalar ) -{ - mVec128 = scalar.get128(); -} - -inline Vector3::Vector3( vec_float4 vf4 ) -{ - mVec128 = vf4; -} - -inline const Vector3 Vector3::xAxis( ) -{ - return Vector3( _VECTORMATH_UNIT_1000 ); -} - -inline const Vector3 Vector3::yAxis( ) -{ - return Vector3( _VECTORMATH_UNIT_0100 ); -} - -inline const Vector3 Vector3::zAxis( ) -{ - return Vector3( _VECTORMATH_UNIT_0010 ); -} - -inline const Vector3 lerp( float t, Vector3 vec0, Vector3 vec1 ) -{ - return lerp( floatInVec(t), vec0, vec1 ); -} - -inline const Vector3 lerp( floatInVec t, Vector3 vec0, Vector3 vec1 ) -{ - return ( vec0 + ( ( vec1 - vec0 ) * t ) ); -} - -inline const Vector3 slerp( float t, Vector3 unitVec0, Vector3 unitVec1 ) -{ - return slerp( floatInVec(t), unitVec0, unitVec1 ); -} - -inline const Vector3 slerp( floatInVec t, Vector3 unitVec0, Vector3 unitVec1 ) -{ - vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; - vec_uint4 selectMask; - cosAngle = _vmathVfDot3( unitVec0.get128(), unitVec1.get128() ); - cosAngle = vec_splat( cosAngle, 0 ); - selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL}), cosAngle ); - angle = acosf4( cosAngle ); - tttt = t.get128(); - oneMinusT = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); - angles = vec_mergeh( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); - angles = vec_mergeh( angles, oneMinusT ); - angles = vec_madd( angles, angle, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - sines = sinf4( angles ); - scales = divf4( sines, vec_splat( sines, 0 ) ); - scale0 = vec_sel( oneMinusT, vec_splat( scales, 1 ), selectMask ); - scale1 = vec_sel( tttt, vec_splat( scales, 2 ), selectMask ); - return Vector3( vec_madd( unitVec0.get128(), scale0, vec_madd( unitVec1.get128(), scale1, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ) ); -} - -inline vec_float4 Vector3::get128( ) const -{ - return mVec128; -} - -inline void storeXYZ( Vector3 vec, vec_float4 * quad ) -{ - vec_float4 dstVec = *quad; - vec_uint4 mask = _VECTORMATH_MASK_0x000F; - dstVec = vec_sel(vec.get128(), dstVec, mask); - *quad = dstVec; -} - -inline void loadXYZArray( Vector3 & vec0, Vector3 & vec1, Vector3 & vec2, Vector3 & vec3, const vec_float4 * threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; - xyzx = threeQuads[0]; - yzxy = threeQuads[1]; - zxyz = threeQuads[2]; - xyz1 = vec_sld( xyzx, yzxy, 12 ); - xyz2 = vec_sld( yzxy, zxyz, 8 ); - xyz3 = vec_sld( zxyz, zxyz, 4 ); - vec0 = Vector3( xyzx ); - vec1 = Vector3( xyz1 ); - vec2 = Vector3( xyz2 ); - vec3 = Vector3( xyz3 ); -} - -inline void storeXYZArray( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, vec_float4 * threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz; - xyzx = vec_perm( vec0.get128(), vec1.get128(), _VECTORMATH_PERM_XYZA ); - yzxy = vec_perm( vec1.get128(), vec2.get128(), _VECTORMATH_PERM_YZAB ); - zxyz = vec_perm( vec2.get128(), vec3.get128(), _VECTORMATH_PERM_ZABC ); - threeQuads[0] = xyzx; - threeQuads[1] = yzxy; - threeQuads[2] = zxyz; -} - -inline void storeHalfFloats( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, Vector3 vec4, Vector3 vec5, Vector3 vec6, Vector3 vec7, vec_ushort8 * threeQuads ) -{ - vec_float4 xyz0[3]; - vec_float4 xyz1[3]; - storeXYZArray( vec0, vec1, vec2, vec3, xyz0 ); - storeXYZArray( vec4, vec5, vec6, vec7, xyz1 ); - threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); - threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); - threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); -} - -inline Vector3 & Vector3::operator =( Vector3 vec ) -{ - mVec128 = vec.mVec128; - return *this; -} - -inline Vector3 & Vector3::setX( float _x ) -{ - _vmathVfSetElement(mVec128, _x, 0); - return *this; -} - -inline Vector3 & Vector3::setX( floatInVec _x ) -{ - mVec128 = _vmathVfInsert(mVec128, _x.get128(), 0); - return *this; -} - -inline const floatInVec Vector3::getX( ) const -{ - return floatInVec( mVec128, 0 ); -} - -inline Vector3 & Vector3::setY( float _y ) -{ - _vmathVfSetElement(mVec128, _y, 1); - return *this; -} - -inline Vector3 & Vector3::setY( floatInVec _y ) -{ - mVec128 = _vmathVfInsert(mVec128, _y.get128(), 1); - return *this; -} - -inline const floatInVec Vector3::getY( ) const -{ - return floatInVec( mVec128, 1 ); -} - -inline Vector3 & Vector3::setZ( float _z ) -{ - _vmathVfSetElement(mVec128, _z, 2); - return *this; -} - -inline Vector3 & Vector3::setZ( floatInVec _z ) -{ - mVec128 = _vmathVfInsert(mVec128, _z.get128(), 2); - return *this; -} - -inline const floatInVec Vector3::getZ( ) const -{ - return floatInVec( mVec128, 2 ); -} - -inline Vector3 & Vector3::setElem( int idx, float value ) -{ - _vmathVfSetElement(mVec128, value, idx); - return *this; -} - -inline Vector3 & Vector3::setElem( int idx, floatInVec value ) -{ - mVec128 = _vmathVfInsert(mVec128, value.get128(), idx); - return *this; -} - -inline const floatInVec Vector3::getElem( int idx ) const -{ - return floatInVec( mVec128, idx ); -} - -inline VecIdx Vector3::operator []( int idx ) -{ - return VecIdx( mVec128, idx ); -} - -inline const floatInVec Vector3::operator []( int idx ) const -{ - return floatInVec( mVec128, idx ); -} - -inline const Vector3 Vector3::operator +( Vector3 vec ) const -{ - return Vector3( vec_add( mVec128, vec.mVec128 ) ); -} - -inline const Vector3 Vector3::operator -( Vector3 vec ) const -{ - return Vector3( vec_sub( mVec128, vec.mVec128 ) ); -} - -inline const Point3 Vector3::operator +( Point3 pnt ) const -{ - return Point3( vec_add( mVec128, pnt.get128() ) ); -} - -inline const Vector3 Vector3::operator *( float scalar ) const -{ - return *this * floatInVec(scalar); -} - -inline const Vector3 Vector3::operator *( floatInVec scalar ) const -{ - return Vector3( vec_madd( mVec128, scalar.get128(), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); -} - -inline Vector3 & Vector3::operator +=( Vector3 vec ) -{ - *this = *this + vec; - return *this; -} - -inline Vector3 & Vector3::operator -=( Vector3 vec ) -{ - *this = *this - vec; - return *this; -} - -inline Vector3 & Vector3::operator *=( float scalar ) -{ - *this = *this * scalar; - return *this; -} - -inline Vector3 & Vector3::operator *=( floatInVec scalar ) -{ - *this = *this * scalar; - return *this; -} - -inline const Vector3 Vector3::operator /( float scalar ) const -{ - return *this / floatInVec(scalar); -} - -inline const Vector3 Vector3::operator /( floatInVec scalar ) const -{ - return Vector3( divf4( mVec128, scalar.get128() ) ); -} - -inline Vector3 & Vector3::operator /=( float scalar ) -{ - *this = *this / scalar; - return *this; -} - -inline Vector3 & Vector3::operator /=( floatInVec scalar ) -{ - *this = *this / scalar; - return *this; -} - -inline const Vector3 Vector3::operator -( ) const -{ - return Vector3( negatef4( mVec128 ) ); -} - -inline const Vector3 operator *( float scalar, Vector3 vec ) -{ - return floatInVec(scalar) * vec; -} - -inline const Vector3 operator *( floatInVec scalar, Vector3 vec ) -{ - return vec * scalar; -} - -inline const Vector3 mulPerElem( Vector3 vec0, Vector3 vec1 ) -{ - return Vector3( vec_madd( vec0.get128(), vec1.get128(), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); -} - -inline const Vector3 divPerElem( Vector3 vec0, Vector3 vec1 ) -{ - return Vector3( divf4( vec0.get128(), vec1.get128() ) ); -} - -inline const Vector3 recipPerElem( Vector3 vec ) -{ - return Vector3( recipf4( vec.get128() ) ); -} - -inline const Vector3 sqrtPerElem( Vector3 vec ) -{ - return Vector3( sqrtf4( vec.get128() ) ); -} - -inline const Vector3 rsqrtPerElem( Vector3 vec ) -{ - return Vector3( rsqrtf4( vec.get128() ) ); -} - -inline const Vector3 absPerElem( Vector3 vec ) -{ - return Vector3( fabsf4( vec.get128() ) ); -} - -inline const Vector3 copySignPerElem( Vector3 vec0, Vector3 vec1 ) -{ - return Vector3( copysignf4( vec0.get128(), vec1.get128() ) ); -} - -inline const Vector3 maxPerElem( Vector3 vec0, Vector3 vec1 ) -{ - return Vector3( fmaxf4( vec0.get128(), vec1.get128() ) ); -} - -inline const floatInVec maxElem( Vector3 vec ) -{ - vec_float4 result; - result = fmaxf4( vec_splat( vec.get128(), 1 ), vec.get128() ); - result = fmaxf4( vec_splat( vec.get128(), 2 ), result ); - return floatInVec( result, 0 ); -} - -inline const Vector3 minPerElem( Vector3 vec0, Vector3 vec1 ) -{ - return Vector3( fminf4( vec0.get128(), vec1.get128() ) ); -} - -inline const floatInVec minElem( Vector3 vec ) -{ - vec_float4 result; - result = fminf4( vec_splat( vec.get128(), 1 ), vec.get128() ); - result = fminf4( vec_splat( vec.get128(), 2 ), result ); - return floatInVec( result, 0 ); -} - -inline const floatInVec sum( Vector3 vec ) -{ - vec_float4 result; - result = vec_add( vec_splat( vec.get128(), 1 ), vec.get128() ); - result = vec_add( vec_splat( vec.get128(), 2 ), result ); - return floatInVec( result, 0 ); -} - -inline const floatInVec dot( Vector3 vec0, Vector3 vec1 ) -{ - return floatInVec( _vmathVfDot3( vec0.get128(), vec1.get128() ), 0 ); -} - -inline const floatInVec lengthSqr( Vector3 vec ) -{ - return floatInVec( _vmathVfDot3( vec.get128(), vec.get128() ), 0 ); -} - -inline const floatInVec length( Vector3 vec ) -{ - return floatInVec( sqrtf4(_vmathVfDot3( vec.get128(), vec.get128() )), 0 ); -} - -inline const Vector3 normalize( Vector3 vec ) -{ - vec_float4 dot = _vmathVfDot3( vec.get128(), vec.get128() ); - dot = vec_splat( dot, 0 ); - return Vector3( vec_madd( vec.get128(), rsqrtf4( dot ), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); -} - -inline const Vector3 cross( Vector3 vec0, Vector3 vec1 ) -{ - return Vector3( _vmathVfCross( vec0.get128(), vec1.get128() ) ); -} - -inline const Vector3 select( Vector3 vec0, Vector3 vec1, bool select1 ) -{ - return select( vec0, vec1, boolInVec(select1) ); -} - -inline const Vector3 select( Vector3 vec0, Vector3 vec1, boolInVec select1 ) -{ - return Vector3( vec_sel( vec0.get128(), vec1.get128(), select1.get128() ) ); -} - -#ifdef _VECTORMATH_DEBUG - -inline void print( Vector3 vec ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec.get128(); - printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -inline void print( Vector3 vec, const char * name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec.get128(); - printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -#endif - -inline Vector4::Vector4( float _x, float _y, float _z, float _w ) -{ - if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & - __builtin_constant_p(_z) & __builtin_constant_p(_w)) { - mVec128 = (vec_float4){_x, _y, _z, _w}; - } else { - float *pf = (float *)&mVec128; - pf[0] = _x; - pf[1] = _y; - pf[2] = _z; - pf[3] = _w; - } -} - -inline Vector4::Vector4( floatInVec _x, floatInVec _y, floatInVec _z, floatInVec _w ) -{ - vec_float4 xz = vec_mergeh( _x.get128(), _z.get128() ); - vec_float4 yw = vec_mergeh( _y.get128(), _w.get128() ); - mVec128 = vec_mergeh( xz, yw ); -} - -inline Vector4::Vector4( Vector3 xyz, float _w ) -{ - mVec128 = xyz.get128(); - _vmathVfSetElement(mVec128, _w, 3); -} - -inline Vector4::Vector4( Vector3 xyz, floatInVec _w ) -{ - mVec128 = xyz.get128(); - mVec128 = _vmathVfInsert(mVec128, _w.get128(), 3); -} - -inline Vector4::Vector4( Vector3 vec ) -{ - mVec128 = vec.get128(); - mVec128 = _vmathVfInsert(mVec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}), 3); -} - -inline Vector4::Vector4( Point3 pnt ) -{ - mVec128 = pnt.get128(); - mVec128 = _vmathVfInsert(mVec128, ((vec_float4){1.0f,1.0f,1.0f,1.0f}), 3); -} - -inline Vector4::Vector4( Quat quat ) -{ - mVec128 = quat.get128(); -} - -inline Vector4::Vector4( float scalar ) -{ - mVec128 = floatInVec(scalar).get128(); -} - -inline Vector4::Vector4( floatInVec scalar ) -{ - mVec128 = scalar.get128(); -} - -inline Vector4::Vector4( vec_float4 vf4 ) -{ - mVec128 = vf4; -} - -inline const Vector4 Vector4::xAxis( ) -{ - return Vector4( _VECTORMATH_UNIT_1000 ); -} - -inline const Vector4 Vector4::yAxis( ) -{ - return Vector4( _VECTORMATH_UNIT_0100 ); -} - -inline const Vector4 Vector4::zAxis( ) -{ - return Vector4( _VECTORMATH_UNIT_0010 ); -} - -inline const Vector4 Vector4::wAxis( ) -{ - return Vector4( _VECTORMATH_UNIT_0001 ); -} - -inline const Vector4 lerp( float t, Vector4 vec0, Vector4 vec1 ) -{ - return lerp( floatInVec(t), vec0, vec1 ); -} - -inline const Vector4 lerp( floatInVec t, Vector4 vec0, Vector4 vec1 ) -{ - return ( vec0 + ( ( vec1 - vec0 ) * t ) ); -} - -inline const Vector4 slerp( float t, Vector4 unitVec0, Vector4 unitVec1 ) -{ - return slerp( floatInVec(t), unitVec0, unitVec1 ); -} - -inline const Vector4 slerp( floatInVec t, Vector4 unitVec0, Vector4 unitVec1 ) -{ - vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; - vec_uint4 selectMask; - cosAngle = _vmathVfDot4( unitVec0.get128(), unitVec1.get128() ); - cosAngle = vec_splat( cosAngle, 0 ); - selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL}), cosAngle ); - angle = acosf4( cosAngle ); - tttt = t.get128(); - oneMinusT = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); - angles = vec_mergeh( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); - angles = vec_mergeh( angles, oneMinusT ); - angles = vec_madd( angles, angle, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); - sines = sinf4( angles ); - scales = divf4( sines, vec_splat( sines, 0 ) ); - scale0 = vec_sel( oneMinusT, vec_splat( scales, 1 ), selectMask ); - scale1 = vec_sel( tttt, vec_splat( scales, 2 ), selectMask ); - return Vector4( vec_madd( unitVec0.get128(), scale0, vec_madd( unitVec1.get128(), scale1, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ) ); -} - -inline vec_float4 Vector4::get128( ) const -{ - return mVec128; -} - -inline void storeHalfFloats( Vector4 vec0, Vector4 vec1, Vector4 vec2, Vector4 vec3, vec_ushort8 * twoQuads ) -{ - twoQuads[0] = _vmath2VfToHalfFloats(vec0.get128(), vec1.get128()); - twoQuads[1] = _vmath2VfToHalfFloats(vec2.get128(), vec3.get128()); -} - -inline Vector4 & Vector4::operator =( Vector4 vec ) -{ - mVec128 = vec.mVec128; - return *this; -} - -inline Vector4 & Vector4::setXYZ( Vector3 vec ) -{ - mVec128 = vec_sel( vec.get128(), mVec128, _VECTORMATH_MASK_0x000F ); - return *this; -} - -inline const Vector3 Vector4::getXYZ( ) const -{ - return Vector3( mVec128 ); -} - -inline Vector4 & Vector4::setX( float _x ) -{ - _vmathVfSetElement(mVec128, _x, 0); - return *this; -} - -inline Vector4 & Vector4::setX( floatInVec _x ) -{ - mVec128 = _vmathVfInsert(mVec128, _x.get128(), 0); - return *this; -} - -inline const floatInVec Vector4::getX( ) const -{ - return floatInVec( mVec128, 0 ); -} - -inline Vector4 & Vector4::setY( float _y ) -{ - _vmathVfSetElement(mVec128, _y, 1); - return *this; -} - -inline Vector4 & Vector4::setY( floatInVec _y ) -{ - mVec128 = _vmathVfInsert(mVec128, _y.get128(), 1); - return *this; -} - -inline const floatInVec Vector4::getY( ) const -{ - return floatInVec( mVec128, 1 ); -} - -inline Vector4 & Vector4::setZ( float _z ) -{ - _vmathVfSetElement(mVec128, _z, 2); - return *this; -} - -inline Vector4 & Vector4::setZ( floatInVec _z ) -{ - mVec128 = _vmathVfInsert(mVec128, _z.get128(), 2); - return *this; -} - -inline const floatInVec Vector4::getZ( ) const -{ - return floatInVec( mVec128, 2 ); -} - -inline Vector4 & Vector4::setW( float _w ) -{ - _vmathVfSetElement(mVec128, _w, 3); - return *this; -} - -inline Vector4 & Vector4::setW( floatInVec _w ) -{ - mVec128 = _vmathVfInsert(mVec128, _w.get128(), 3); - return *this; -} - -inline const floatInVec Vector4::getW( ) const -{ - return floatInVec( mVec128, 3 ); -} - -inline Vector4 & Vector4::setElem( int idx, float value ) -{ - _vmathVfSetElement(mVec128, value, idx); - return *this; -} - -inline Vector4 & Vector4::setElem( int idx, floatInVec value ) -{ - mVec128 = _vmathVfInsert(mVec128, value.get128(), idx); - return *this; -} - -inline const floatInVec Vector4::getElem( int idx ) const -{ - return floatInVec( mVec128, idx ); -} - -inline VecIdx Vector4::operator []( int idx ) -{ - return VecIdx( mVec128, idx ); -} - -inline const floatInVec Vector4::operator []( int idx ) const -{ - return floatInVec( mVec128, idx ); -} - -inline const Vector4 Vector4::operator +( Vector4 vec ) const -{ - return Vector4( vec_add( mVec128, vec.mVec128 ) ); -} - -inline const Vector4 Vector4::operator -( Vector4 vec ) const -{ - return Vector4( vec_sub( mVec128, vec.mVec128 ) ); -} - -inline const Vector4 Vector4::operator *( float scalar ) const -{ - return *this * floatInVec(scalar); -} - -inline const Vector4 Vector4::operator *( floatInVec scalar ) const -{ - return Vector4( vec_madd( mVec128, scalar.get128(), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); -} - -inline Vector4 & Vector4::operator +=( Vector4 vec ) -{ - *this = *this + vec; - return *this; -} - -inline Vector4 & Vector4::operator -=( Vector4 vec ) -{ - *this = *this - vec; - return *this; -} - -inline Vector4 & Vector4::operator *=( float scalar ) -{ - *this = *this * scalar; - return *this; -} - -inline Vector4 & Vector4::operator *=( floatInVec scalar ) -{ - *this = *this * scalar; - return *this; -} - -inline const Vector4 Vector4::operator /( float scalar ) const -{ - return *this / floatInVec(scalar); -} - -inline const Vector4 Vector4::operator /( floatInVec scalar ) const -{ - return Vector4( divf4( mVec128, scalar.get128() ) ); -} - -inline Vector4 & Vector4::operator /=( float scalar ) -{ - *this = *this / scalar; - return *this; -} - -inline Vector4 & Vector4::operator /=( floatInVec scalar ) -{ - *this = *this / scalar; - return *this; -} - -inline const Vector4 Vector4::operator -( ) const -{ - return Vector4( negatef4( mVec128 ) ); -} - -inline const Vector4 operator *( float scalar, Vector4 vec ) -{ - return floatInVec(scalar) * vec; -} - -inline const Vector4 operator *( floatInVec scalar, Vector4 vec ) -{ - return vec * scalar; -} - -inline const Vector4 mulPerElem( Vector4 vec0, Vector4 vec1 ) -{ - return Vector4( vec_madd( vec0.get128(), vec1.get128(), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); -} - -inline const Vector4 divPerElem( Vector4 vec0, Vector4 vec1 ) -{ - return Vector4( divf4( vec0.get128(), vec1.get128() ) ); -} - -inline const Vector4 recipPerElem( Vector4 vec ) -{ - return Vector4( recipf4( vec.get128() ) ); -} - -inline const Vector4 sqrtPerElem( Vector4 vec ) -{ - return Vector4( sqrtf4( vec.get128() ) ); -} - -inline const Vector4 rsqrtPerElem( Vector4 vec ) -{ - return Vector4( rsqrtf4( vec.get128() ) ); -} - -inline const Vector4 absPerElem( Vector4 vec ) -{ - return Vector4( fabsf4( vec.get128() ) ); -} - -inline const Vector4 copySignPerElem( Vector4 vec0, Vector4 vec1 ) -{ - return Vector4( copysignf4( vec0.get128(), vec1.get128() ) ); -} - -inline const Vector4 maxPerElem( Vector4 vec0, Vector4 vec1 ) -{ - return Vector4( fmaxf4( vec0.get128(), vec1.get128() ) ); -} - -inline const floatInVec maxElem( Vector4 vec ) -{ - vec_float4 result; - result = fmaxf4( vec_splat( vec.get128(), 1 ), vec.get128() ); - result = fmaxf4( vec_splat( vec.get128(), 2 ), result ); - result = fmaxf4( vec_splat( vec.get128(), 3 ), result ); - return floatInVec( result, 0 ); -} - -inline const Vector4 minPerElem( Vector4 vec0, Vector4 vec1 ) -{ - return Vector4( fminf4( vec0.get128(), vec1.get128() ) ); -} - -inline const floatInVec minElem( Vector4 vec ) -{ - vec_float4 result; - result = fminf4( vec_splat( vec.get128(), 1 ), vec.get128() ); - result = fminf4( vec_splat( vec.get128(), 2 ), result ); - result = fminf4( vec_splat( vec.get128(), 3 ), result ); - return floatInVec( result, 0 ); -} - -inline const floatInVec sum( Vector4 vec ) -{ - vec_float4 result; - result = vec_add( vec_splat( vec.get128(), 1 ), vec.get128() ); - result = vec_add( vec_splat( vec.get128(), 2 ), result ); - result = vec_add( vec_splat( vec.get128(), 3 ), result ); - return floatInVec( result, 0 ); -} - -inline const floatInVec dot( Vector4 vec0, Vector4 vec1 ) -{ - return floatInVec( _vmathVfDot4( vec0.get128(), vec1.get128() ), 0 ); -} - -inline const floatInVec lengthSqr( Vector4 vec ) -{ - return floatInVec( _vmathVfDot4( vec.get128(), vec.get128() ), 0 ); -} - -inline const floatInVec length( Vector4 vec ) -{ - return floatInVec( sqrtf4(_vmathVfDot4( vec.get128(), vec.get128() )), 0 ); -} - -inline const Vector4 normalize( Vector4 vec ) -{ - vec_float4 dot = _vmathVfDot4( vec.get128(), vec.get128() ); - return Vector4( vec_madd( vec.get128(), rsqrtf4( dot ), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); -} - -inline const Vector4 select( Vector4 vec0, Vector4 vec1, bool select1 ) -{ - return select( vec0, vec1, boolInVec(select1) ); -} - -inline const Vector4 select( Vector4 vec0, Vector4 vec1, boolInVec select1 ) -{ - return Vector4( vec_sel( vec0.get128(), vec1.get128(), select1.get128() ) ); -} - -#ifdef _VECTORMATH_DEBUG - -inline void print( Vector4 vec ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec.get128(); - printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -inline void print( Vector4 vec, const char * name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec.get128(); - printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -#endif - -inline Point3::Point3( float _x, float _y, float _z ) -{ - if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & __builtin_constant_p(_z)) { - mVec128 = (vec_float4){_x, _y, _z, 0.0f}; - } else { - float *pf = (float *)&mVec128; - pf[0] = _x; - pf[1] = _y; - pf[2] = _z; - } -} - -inline Point3::Point3( floatInVec _x, floatInVec _y, floatInVec _z ) -{ - vec_float4 xz = vec_mergeh( _x.get128(), _z.get128() ); - mVec128 = vec_mergeh( xz, _y.get128() ); -} - -inline Point3::Point3( Vector3 vec ) -{ - mVec128 = vec.get128(); -} - -inline Point3::Point3( float scalar ) -{ - mVec128 = floatInVec(scalar).get128(); -} - -inline Point3::Point3( floatInVec scalar ) -{ - mVec128 = scalar.get128(); -} - -inline Point3::Point3( vec_float4 vf4 ) -{ - mVec128 = vf4; -} - -inline const Point3 lerp( float t, Point3 pnt0, Point3 pnt1 ) -{ - return lerp( floatInVec(t), pnt0, pnt1 ); -} - -inline const Point3 lerp( floatInVec t, Point3 pnt0, Point3 pnt1 ) -{ - return ( pnt0 + ( ( pnt1 - pnt0 ) * t ) ); -} - -inline vec_float4 Point3::get128( ) const -{ - return mVec128; -} - -inline void storeXYZ( Point3 pnt, vec_float4 * quad ) -{ - vec_float4 dstVec = *quad; - vec_uint4 mask = _VECTORMATH_MASK_0x000F; - dstVec = vec_sel(pnt.get128(), dstVec, mask); - *quad = dstVec; -} - -inline void loadXYZArray( Point3 & pnt0, Point3 & pnt1, Point3 & pnt2, Point3 & pnt3, const vec_float4 * threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; - xyzx = threeQuads[0]; - yzxy = threeQuads[1]; - zxyz = threeQuads[2]; - xyz1 = vec_sld( xyzx, yzxy, 12 ); - xyz2 = vec_sld( yzxy, zxyz, 8 ); - xyz3 = vec_sld( zxyz, zxyz, 4 ); - pnt0 = Point3( xyzx ); - pnt1 = Point3( xyz1 ); - pnt2 = Point3( xyz2 ); - pnt3 = Point3( xyz3 ); -} - -inline void storeXYZArray( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, vec_float4 * threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz; - xyzx = vec_perm( pnt0.get128(), pnt1.get128(), _VECTORMATH_PERM_XYZA ); - yzxy = vec_perm( pnt1.get128(), pnt2.get128(), _VECTORMATH_PERM_YZAB ); - zxyz = vec_perm( pnt2.get128(), pnt3.get128(), _VECTORMATH_PERM_ZABC ); - threeQuads[0] = xyzx; - threeQuads[1] = yzxy; - threeQuads[2] = zxyz; -} - -inline void storeHalfFloats( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, Point3 pnt4, Point3 pnt5, Point3 pnt6, Point3 pnt7, vec_ushort8 * threeQuads ) -{ - vec_float4 xyz0[3]; - vec_float4 xyz1[3]; - storeXYZArray( pnt0, pnt1, pnt2, pnt3, xyz0 ); - storeXYZArray( pnt4, pnt5, pnt6, pnt7, xyz1 ); - threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); - threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); - threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); -} - -inline Point3 & Point3::operator =( Point3 pnt ) -{ - mVec128 = pnt.mVec128; - return *this; -} - -inline Point3 & Point3::setX( float _x ) -{ - _vmathVfSetElement(mVec128, _x, 0); - return *this; -} - -inline Point3 & Point3::setX( floatInVec _x ) -{ - mVec128 = _vmathVfInsert(mVec128, _x.get128(), 0); - return *this; -} - -inline const floatInVec Point3::getX( ) const -{ - return floatInVec( mVec128, 0 ); -} - -inline Point3 & Point3::setY( float _y ) -{ - _vmathVfSetElement(mVec128, _y, 1); - return *this; -} - -inline Point3 & Point3::setY( floatInVec _y ) -{ - mVec128 = _vmathVfInsert(mVec128, _y.get128(), 1); - return *this; -} - -inline const floatInVec Point3::getY( ) const -{ - return floatInVec( mVec128, 1 ); -} - -inline Point3 & Point3::setZ( float _z ) -{ - _vmathVfSetElement(mVec128, _z, 2); - return *this; -} - -inline Point3 & Point3::setZ( floatInVec _z ) -{ - mVec128 = _vmathVfInsert(mVec128, _z.get128(), 2); - return *this; -} - -inline const floatInVec Point3::getZ( ) const -{ - return floatInVec( mVec128, 2 ); -} - -inline Point3 & Point3::setElem( int idx, float value ) -{ - _vmathVfSetElement(mVec128, value, idx); - return *this; -} - -inline Point3 & Point3::setElem( int idx, floatInVec value ) -{ - mVec128 = _vmathVfInsert(mVec128, value.get128(), idx); - return *this; -} - -inline const floatInVec Point3::getElem( int idx ) const -{ - return floatInVec( mVec128, idx ); -} - -inline VecIdx Point3::operator []( int idx ) -{ - return VecIdx( mVec128, idx ); -} - -inline const floatInVec Point3::operator []( int idx ) const -{ - return floatInVec( mVec128, idx ); -} - -inline const Vector3 Point3::operator -( Point3 pnt ) const -{ - return Vector3( vec_sub( mVec128, pnt.mVec128 ) ); -} - -inline const Point3 Point3::operator +( Vector3 vec ) const -{ - return Point3( vec_add( mVec128, vec.get128() ) ); -} - -inline const Point3 Point3::operator -( Vector3 vec ) const -{ - return Point3( vec_sub( mVec128, vec.get128() ) ); -} - -inline Point3 & Point3::operator +=( Vector3 vec ) -{ - *this = *this + vec; - return *this; -} - -inline Point3 & Point3::operator -=( Vector3 vec ) -{ - *this = *this - vec; - return *this; -} - -inline const Point3 mulPerElem( Point3 pnt0, Point3 pnt1 ) -{ - return Point3( vec_madd( pnt0.get128(), pnt1.get128(), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); -} - -inline const Point3 divPerElem( Point3 pnt0, Point3 pnt1 ) -{ - return Point3( divf4( pnt0.get128(), pnt1.get128() ) ); -} - -inline const Point3 recipPerElem( Point3 pnt ) -{ - return Point3( recipf4( pnt.get128() ) ); -} - -inline const Point3 sqrtPerElem( Point3 pnt ) -{ - return Point3( sqrtf4( pnt.get128() ) ); -} - -inline const Point3 rsqrtPerElem( Point3 pnt ) -{ - return Point3( rsqrtf4( pnt.get128() ) ); -} - -inline const Point3 absPerElem( Point3 pnt ) -{ - return Point3( fabsf4( pnt.get128() ) ); -} - -inline const Point3 copySignPerElem( Point3 pnt0, Point3 pnt1 ) -{ - return Point3( copysignf4( pnt0.get128(), pnt1.get128() ) ); -} - -inline const Point3 maxPerElem( Point3 pnt0, Point3 pnt1 ) -{ - return Point3( fmaxf4( pnt0.get128(), pnt1.get128() ) ); -} - -inline const floatInVec maxElem( Point3 pnt ) -{ - vec_float4 result; - result = fmaxf4( vec_splat( pnt.get128(), 1 ), pnt.get128() ); - result = fmaxf4( vec_splat( pnt.get128(), 2 ), result ); - return floatInVec( result, 0 ); -} - -inline const Point3 minPerElem( Point3 pnt0, Point3 pnt1 ) -{ - return Point3( fminf4( pnt0.get128(), pnt1.get128() ) ); -} - -inline const floatInVec minElem( Point3 pnt ) -{ - vec_float4 result; - result = fminf4( vec_splat( pnt.get128(), 1 ), pnt.get128() ); - result = fminf4( vec_splat( pnt.get128(), 2 ), result ); - return floatInVec( result, 0 ); -} - -inline const floatInVec sum( Point3 pnt ) -{ - vec_float4 result; - result = vec_add( vec_splat( pnt.get128(), 1 ), pnt.get128() ); - result = vec_add( vec_splat( pnt.get128(), 2 ), result ); - return floatInVec( result, 0 ); -} - -inline const Point3 scale( Point3 pnt, float scaleVal ) -{ - return scale( pnt, floatInVec( scaleVal ) ); -} - -inline const Point3 scale( Point3 pnt, floatInVec scaleVal ) -{ - return mulPerElem( pnt, Point3( scaleVal ) ); -} - -inline const Point3 scale( Point3 pnt, Vector3 scaleVec ) -{ - return mulPerElem( pnt, Point3( scaleVec ) ); -} - -inline const floatInVec projection( Point3 pnt, Vector3 unitVec ) -{ - return floatInVec( _vmathVfDot3( pnt.get128(), unitVec.get128() ), 0 ); -} - -inline const floatInVec distSqrFromOrigin( Point3 pnt ) -{ - return lengthSqr( Vector3( pnt ) ); -} - -inline const floatInVec distFromOrigin( Point3 pnt ) -{ - return length( Vector3( pnt ) ); -} - -inline const floatInVec distSqr( Point3 pnt0, Point3 pnt1 ) -{ - return lengthSqr( ( pnt1 - pnt0 ) ); -} - -inline const floatInVec dist( Point3 pnt0, Point3 pnt1 ) -{ - return length( ( pnt1 - pnt0 ) ); -} - -inline const Point3 select( Point3 pnt0, Point3 pnt1, bool select1 ) -{ - return select( pnt0, pnt1, boolInVec(select1) ); -} - -inline const Point3 select( Point3 pnt0, Point3 pnt1, boolInVec select1 ) -{ - return Point3( vec_sel( pnt0.get128(), pnt1.get128(), select1.get128() ) ); -} - -#ifdef _VECTORMATH_DEBUG - -inline void print( Point3 pnt ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = pnt.get128(); - printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -inline void print( Point3 pnt, const char * name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = pnt.get128(); - printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -#endif - -} // namespace Aos -} // namespace Vectormath - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_VEC_AOS_CPP_H +#define _VECTORMATH_VEC_AOS_CPP_H +//----------------------------------------------------------------------------- +// Constants +// for permutes words are labeled [x,y,z,w] [a,b,c,d] + +#define _VECTORMATH_PERM_X 0x00010203 +#define _VECTORMATH_PERM_Y 0x04050607 +#define _VECTORMATH_PERM_Z 0x08090a0b +#define _VECTORMATH_PERM_W 0x0c0d0e0f +#define _VECTORMATH_PERM_A 0x10111213 +#define _VECTORMATH_PERM_B 0x14151617 +#define _VECTORMATH_PERM_C 0x18191a1b +#define _VECTORMATH_PERM_D 0x1c1d1e1f +#define _VECTORMATH_PERM_XYZA (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A } +#define _VECTORMATH_PERM_ZXYW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_W } +#define _VECTORMATH_PERM_YZXW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_X, _VECTORMATH_PERM_W } +#define _VECTORMATH_PERM_YZAB (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Y, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A, _VECTORMATH_PERM_B } +#define _VECTORMATH_PERM_ZABC (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_Z, _VECTORMATH_PERM_A, _VECTORMATH_PERM_B, _VECTORMATH_PERM_C } +#define _VECTORMATH_PERM_XYAW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_Y, _VECTORMATH_PERM_A, _VECTORMATH_PERM_W } +#define _VECTORMATH_PERM_XAZW (vec_uchar16)(vec_uint4){ _VECTORMATH_PERM_X, _VECTORMATH_PERM_A, _VECTORMATH_PERM_Z, _VECTORMATH_PERM_W } +#define _VECTORMATH_MASK_0xF000 (vec_uint4){ 0xffffffff, 0, 0, 0 } +#define _VECTORMATH_MASK_0x0F00 (vec_uint4){ 0, 0xffffffff, 0, 0 } +#define _VECTORMATH_MASK_0x00F0 (vec_uint4){ 0, 0, 0xffffffff, 0 } +#define _VECTORMATH_MASK_0x000F (vec_uint4){ 0, 0, 0, 0xffffffff } +#define _VECTORMATH_UNIT_1000 (vec_float4){ 1.0f, 0.0f, 0.0f, 0.0f } +#define _VECTORMATH_UNIT_0100 (vec_float4){ 0.0f, 1.0f, 0.0f, 0.0f } +#define _VECTORMATH_UNIT_0010 (vec_float4){ 0.0f, 0.0f, 1.0f, 0.0f } +#define _VECTORMATH_UNIT_0001 (vec_float4){ 0.0f, 0.0f, 0.0f, 1.0f } +#define _VECTORMATH_SLERP_TOL 0.999f + +//----------------------------------------------------------------------------- +// Definitions + +#ifndef _VECTORMATH_INTERNAL_FUNCTIONS +#define _VECTORMATH_INTERNAL_FUNCTIONS + +static inline vec_float4 _vmathVfDot3( vec_float4 vec0, vec_float4 vec1 ) +{ + vec_float4 result; + result = vec_madd( vec0, vec1, (vec_float4){0.0f,0.0f,0.0f,0.0f} ); + result = vec_madd( vec_sld( vec0, vec0, 4 ), vec_sld( vec1, vec1, 4 ), result ); + return vec_madd( vec_sld( vec0, vec0, 8 ), vec_sld( vec1, vec1, 8 ), result ); +} + +static inline vec_float4 _vmathVfDot4( vec_float4 vec0, vec_float4 vec1 ) +{ + vec_float4 result; + result = vec_madd( vec0, vec1, (vec_float4){0.0f,0.0f,0.0f,0.0f} ); + result = vec_madd( vec_sld( vec0, vec0, 4 ), vec_sld( vec1, vec1, 4 ), result ); + return vec_add( vec_sld( result, result, 8 ), result ); +} + +static inline vec_float4 _vmathVfCross( vec_float4 vec0, vec_float4 vec1 ) +{ + vec_float4 tmp0, tmp1, tmp2, tmp3, result; + tmp0 = vec_perm( vec0, vec0, _VECTORMATH_PERM_YZXW ); + tmp1 = vec_perm( vec1, vec1, _VECTORMATH_PERM_ZXYW ); + tmp2 = vec_perm( vec0, vec0, _VECTORMATH_PERM_ZXYW ); + tmp3 = vec_perm( vec1, vec1, _VECTORMATH_PERM_YZXW ); + result = vec_madd( tmp0, tmp1, (vec_float4){0.0f,0.0f,0.0f,0.0f} ); + result = vec_nmsub( tmp2, tmp3, result ); + return result; +} + +static inline vec_uint4 _vmathVfToHalfFloatsUnpacked(vec_float4 v) +{ + vec_int4 bexp; + vec_uint4 mant, sign, hfloat; + vec_uint4 notZero, isInf; + const vec_uint4 hfloatInf = (vec_uint4){0x00007c00u,0x00007c00u,0x00007c00u,0x00007c00u}; + const vec_uint4 mergeMant = (vec_uint4){0x000003ffu,0x000003ffu,0x000003ffu,0x000003ffu}; + const vec_uint4 mergeSign = (vec_uint4){0x00008000u,0x00008000u,0x00008000u,0x00008000u}; + + sign = vec_sr((vec_uint4)v, (vec_uint4){16,16,16,16}); + mant = vec_sr((vec_uint4)v, (vec_uint4){13,13,13,13}); + bexp = vec_and(vec_sr((vec_int4)v, (vec_uint4){23,23,23,23}), (vec_int4){0xff,0xff,0xff,0xff}); + + notZero = (vec_uint4)vec_cmpgt(bexp, (vec_int4){112,112,112,112}); + isInf = (vec_uint4)vec_cmpgt(bexp, (vec_int4){142,142,142,142}); + + bexp = vec_add(bexp, (vec_int4){-112,-112,-112,-112}); + bexp = vec_sl(bexp, (vec_uint4){10,10,10,10}); + + hfloat = vec_sel((vec_uint4)bexp, mant, mergeMant); + hfloat = vec_sel((vec_uint4){0,0,0,0}, hfloat, notZero); + hfloat = vec_sel(hfloat, hfloatInf, isInf); + hfloat = vec_sel(hfloat, sign, mergeSign); + + return hfloat; +} + +static inline vec_ushort8 _vmath2VfToHalfFloats(vec_float4 u, vec_float4 v) +{ + vec_uint4 hfloat_u, hfloat_v; + const vec_uchar16 pack = (vec_uchar16){2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31}; + hfloat_u = _vmathVfToHalfFloatsUnpacked(u); + hfloat_v = _vmathVfToHalfFloatsUnpacked(v); + return (vec_ushort8)vec_perm(hfloat_u, hfloat_v, pack); +} + +#ifndef __GNUC__ +#define __builtin_constant_p(x) 0 +#endif + +static inline vec_float4 _vmathVfInsert(vec_float4 dst, vec_float4 src, int slot) +{ +#ifdef __GNUC__ + if (__builtin_constant_p(slot)) { + dst = vec_sld(dst, dst, slot<<2); + dst = vec_sld(dst, src, 4); + if (slot != 3) dst = vec_sld(dst, dst, (3-slot)<<2); + return dst; + } else +#endif + { + vec_uchar16 shiftpattern = vec_lvsr( 0, (float *)(size_t)(slot<<2) ); + vec_uint4 selectmask = (vec_uint4)vec_perm( (vec_uint4){0,0,0,0}, _VECTORMATH_MASK_0xF000, shiftpattern ); + return vec_sel( dst, src, selectmask ); + } +} + +#define _vmathVfGetElement(vec, slot) ((float *)&(vec))[slot] +#ifdef _VECTORMATH_SET_CONSTS_IN_MEM +#define _vmathVfSetElement(vec, scalar, slot) ((float *)&(vec))[slot] = scalar +#else +#define _vmathVfSetElement(vec, scalar, slot) \ +{ \ + if (__builtin_constant_p(scalar)) { \ + (vec) = _vmathVfInsert(vec, (vec_float4){scalar, scalar, scalar, scalar}, slot); \ + } else { \ + ((float *)&(vec))[slot] = scalar; \ + } \ +} +#endif + +static inline vec_float4 _vmathVfSplatScalar(float scalar) +{ + vec_float4 result; + if (__builtin_constant_p(scalar)) { + result = (vec_float4){scalar, scalar, scalar, scalar}; + } else { + result = vec_ld(0, &scalar); + result = vec_splat(vec_perm(result, result, vec_lvsl(0, &scalar)), 0); + } + return result; +} + +static inline vec_uint4 _vmathVuiSplatScalar(unsigned int scalar) +{ + vec_uint4 result; + if (__builtin_constant_p(scalar)) { + result = (vec_uint4){scalar, scalar, scalar, scalar}; + } else { + result = vec_ld(0, &scalar); + result = vec_splat(vec_perm(result, result, vec_lvsl(0, &scalar)), 0); + } + return result; +} + +#endif + +namespace Vectormath { +namespace Aos { + +#ifdef _VECTORMATH_NO_SCALAR_CAST +inline VecIdx::operator floatInVec() const +{ + return floatInVec(ref, i); +} + +inline float VecIdx::getAsFloat() const +#else +inline VecIdx::operator float() const +#endif +{ + return _vmathVfGetElement(ref, i); +} + +inline float VecIdx::operator =( float scalar ) +{ + _vmathVfSetElement(ref, scalar, i); + return scalar; +} + +inline floatInVec VecIdx::operator =( floatInVec scalar ) +{ + ref = _vmathVfInsert(ref, scalar.get128(), i); + return scalar; +} + +inline floatInVec VecIdx::operator =( const VecIdx& scalar ) +{ + return *this = floatInVec(scalar.ref, scalar.i); +} + +inline floatInVec VecIdx::operator *=( float scalar ) +{ + return *this *= floatInVec(scalar); +} + +inline floatInVec VecIdx::operator *=( floatInVec scalar ) +{ + return *this = floatInVec(ref, i) * scalar; +} + +inline floatInVec VecIdx::operator /=( float scalar ) +{ + return *this /= floatInVec(scalar); +} + +inline floatInVec VecIdx::operator /=( floatInVec scalar ) +{ + return *this = floatInVec(ref, i) / scalar; +} + +inline floatInVec VecIdx::operator +=( float scalar ) +{ + return *this += floatInVec(scalar); +} + +inline floatInVec VecIdx::operator +=( floatInVec scalar ) +{ + return *this = floatInVec(ref, i) + scalar; +} + +inline floatInVec VecIdx::operator -=( float scalar ) +{ + return *this -= floatInVec(scalar); +} + +inline floatInVec VecIdx::operator -=( floatInVec scalar ) +{ + return *this = floatInVec(ref, i) - scalar; +} + +inline Vector3::Vector3( float _x, float _y, float _z ) +{ + if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & __builtin_constant_p(_z)) { + mVec128 = (vec_float4){_x, _y, _z, 0.0f}; + } else { + float *pf = (float *)&mVec128; + pf[0] = _x; + pf[1] = _y; + pf[2] = _z; + pf[3] = 0.0f; + } +} + +inline Vector3::Vector3( floatInVec _x, floatInVec _y, floatInVec _z ) +{ + vec_float4 xz = vec_mergeh( _x.get128(), _z.get128() ); + vec_float4 yw = vec_mergeh( _y.get128(), ((vec_float4){0.0f, 0.0f, 0.0f, 0.0f})); + mVec128 = vec_mergeh( xz, yw ); +} + +inline Vector3::Vector3( Point3 pnt ) +{ + mVec128 = pnt.get128(); +} + +inline Vector3::Vector3( float scalar ) +{ + mVec128 = floatInVec(scalar).get128(); +} + +inline Vector3::Vector3( floatInVec scalar ) +{ + mVec128 = scalar.get128(); +} + +inline Vector3::Vector3( vec_float4 vf4 ) +{ + mVec128 = vf4; +} + +inline const Vector3 Vector3::xAxis( ) +{ + return Vector3( _VECTORMATH_UNIT_1000 ); +} + +inline const Vector3 Vector3::yAxis( ) +{ + return Vector3( _VECTORMATH_UNIT_0100 ); +} + +inline const Vector3 Vector3::zAxis( ) +{ + return Vector3( _VECTORMATH_UNIT_0010 ); +} + +inline const Vector3 lerp( float t, Vector3 vec0, Vector3 vec1 ) +{ + return lerp( floatInVec(t), vec0, vec1 ); +} + +inline const Vector3 lerp( floatInVec t, Vector3 vec0, Vector3 vec1 ) +{ + return ( vec0 + ( ( vec1 - vec0 ) * t ) ); +} + +inline const Vector3 slerp( float t, Vector3 unitVec0, Vector3 unitVec1 ) +{ + return slerp( floatInVec(t), unitVec0, unitVec1 ); +} + +inline const Vector3 slerp( floatInVec t, Vector3 unitVec0, Vector3 unitVec1 ) +{ + vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; + vec_uint4 selectMask; + cosAngle = _vmathVfDot3( unitVec0.get128(), unitVec1.get128() ); + cosAngle = vec_splat( cosAngle, 0 ); + selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL}), cosAngle ); + angle = acosf4( cosAngle ); + tttt = t.get128(); + oneMinusT = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); + angles = vec_mergeh( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); + angles = vec_mergeh( angles, oneMinusT ); + angles = vec_madd( angles, angle, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + sines = sinf4( angles ); + scales = divf4( sines, vec_splat( sines, 0 ) ); + scale0 = vec_sel( oneMinusT, vec_splat( scales, 1 ), selectMask ); + scale1 = vec_sel( tttt, vec_splat( scales, 2 ), selectMask ); + return Vector3( vec_madd( unitVec0.get128(), scale0, vec_madd( unitVec1.get128(), scale1, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ) ); +} + +inline vec_float4 Vector3::get128( ) const +{ + return mVec128; +} + +inline void loadXYZ( Vector3 & vec, const vec_float4 * quad ) +{ + vec = Vector3( *quad ); +} + +inline void storeXYZ( Vector3 vec, vec_float4 * quad ) +{ + vec_float4 dstVec = *quad; + vec_uint4 mask = _VECTORMATH_MASK_0x000F; + dstVec = vec_sel(vec.get128(), dstVec, mask); + *quad = dstVec; +} + +inline void loadXYZ( Vector3 & vec, const float * fptr ) +{ + vec_float4 vec0 = vec_ld(0, fptr); + vec_float4 vec1 = vec_ld(16, fptr); + vec = Vector3( vec_perm(vec0, vec1, vec_lvsl(0, fptr)) ); +} + +inline void storeXYZ( Vector3 vec, float * fptr ) +{ + vec_float4 vsrc = vec.get128(); + vec_float4 x = vec_splat(vsrc, 0); + vec_float4 y = vec_splat(vsrc, 1); + vec_float4 z = vec_splat(vsrc, 2); + vec_ste(x, 0, fptr); + vec_ste(y, 4, fptr); + vec_ste(z, 8, fptr); +} + +inline void loadXYZArray( Vector3 & vec0, Vector3 & vec1, Vector3 & vec2, Vector3 & vec3, const vec_float4 * threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; + xyzx = threeQuads[0]; + yzxy = threeQuads[1]; + zxyz = threeQuads[2]; + xyz1 = vec_sld( xyzx, yzxy, 12 ); + xyz2 = vec_sld( yzxy, zxyz, 8 ); + xyz3 = vec_sld( zxyz, zxyz, 4 ); + vec0 = Vector3( xyzx ); + vec1 = Vector3( xyz1 ); + vec2 = Vector3( xyz2 ); + vec3 = Vector3( xyz3 ); +} + +inline void storeXYZArray( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, vec_float4 * threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz; + xyzx = vec_perm( vec0.get128(), vec1.get128(), _VECTORMATH_PERM_XYZA ); + yzxy = vec_perm( vec1.get128(), vec2.get128(), _VECTORMATH_PERM_YZAB ); + zxyz = vec_perm( vec2.get128(), vec3.get128(), _VECTORMATH_PERM_ZABC ); + threeQuads[0] = xyzx; + threeQuads[1] = yzxy; + threeQuads[2] = zxyz; +} + +inline void storeHalfFloats( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, Vector3 vec4, Vector3 vec5, Vector3 vec6, Vector3 vec7, vec_ushort8 * threeQuads ) +{ + vec_float4 xyz0[3]; + vec_float4 xyz1[3]; + storeXYZArray( vec0, vec1, vec2, vec3, xyz0 ); + storeXYZArray( vec4, vec5, vec6, vec7, xyz1 ); + threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); + threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); + threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); +} + +inline Vector3 & Vector3::operator =( Vector3 vec ) +{ + mVec128 = vec.mVec128; + return *this; +} + +inline Vector3 & Vector3::setX( float _x ) +{ + _vmathVfSetElement(mVec128, _x, 0); + return *this; +} + +inline Vector3 & Vector3::setX( floatInVec _x ) +{ + mVec128 = _vmathVfInsert(mVec128, _x.get128(), 0); + return *this; +} + +inline const floatInVec Vector3::getX( ) const +{ + return floatInVec( mVec128, 0 ); +} + +inline Vector3 & Vector3::setY( float _y ) +{ + _vmathVfSetElement(mVec128, _y, 1); + return *this; +} + +inline Vector3 & Vector3::setY( floatInVec _y ) +{ + mVec128 = _vmathVfInsert(mVec128, _y.get128(), 1); + return *this; +} + +inline const floatInVec Vector3::getY( ) const +{ + return floatInVec( mVec128, 1 ); +} + +inline Vector3 & Vector3::setZ( float _z ) +{ + _vmathVfSetElement(mVec128, _z, 2); + return *this; +} + +inline Vector3 & Vector3::setZ( floatInVec _z ) +{ + mVec128 = _vmathVfInsert(mVec128, _z.get128(), 2); + return *this; +} + +inline const floatInVec Vector3::getZ( ) const +{ + return floatInVec( mVec128, 2 ); +} + +inline Vector3 & Vector3::setElem( int idx, float value ) +{ + _vmathVfSetElement(mVec128, value, idx); + return *this; +} + +inline Vector3 & Vector3::setElem( int idx, floatInVec value ) +{ + mVec128 = _vmathVfInsert(mVec128, value.get128(), idx); + return *this; +} + +inline const floatInVec Vector3::getElem( int idx ) const +{ + return floatInVec( mVec128, idx ); +} + +inline VecIdx Vector3::operator []( int idx ) +{ + return VecIdx( mVec128, idx ); +} + +inline const floatInVec Vector3::operator []( int idx ) const +{ + return floatInVec( mVec128, idx ); +} + +inline const Vector3 Vector3::operator +( Vector3 vec ) const +{ + return Vector3( vec_add( mVec128, vec.mVec128 ) ); +} + +inline const Vector3 Vector3::operator -( Vector3 vec ) const +{ + return Vector3( vec_sub( mVec128, vec.mVec128 ) ); +} + +inline const Point3 Vector3::operator +( Point3 pnt ) const +{ + return Point3( vec_add( mVec128, pnt.get128() ) ); +} + +inline const Vector3 Vector3::operator *( float scalar ) const +{ + return *this * floatInVec(scalar); +} + +inline const Vector3 Vector3::operator *( floatInVec scalar ) const +{ + return Vector3( vec_madd( mVec128, scalar.get128(), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); +} + +inline Vector3 & Vector3::operator +=( Vector3 vec ) +{ + *this = *this + vec; + return *this; +} + +inline Vector3 & Vector3::operator -=( Vector3 vec ) +{ + *this = *this - vec; + return *this; +} + +inline Vector3 & Vector3::operator *=( float scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline Vector3 & Vector3::operator *=( floatInVec scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Vector3 Vector3::operator /( float scalar ) const +{ + return *this / floatInVec(scalar); +} + +inline const Vector3 Vector3::operator /( floatInVec scalar ) const +{ + return Vector3( divf4( mVec128, scalar.get128() ) ); +} + +inline Vector3 & Vector3::operator /=( float scalar ) +{ + *this = *this / scalar; + return *this; +} + +inline Vector3 & Vector3::operator /=( floatInVec scalar ) +{ + *this = *this / scalar; + return *this; +} + +inline bool Vector3::operator == (const Vector3& vec) const +{ + return vec_all_gt(vec_cmpeq(vec_sel(mVec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}), _VECTORMATH_MASK_0x000F), vec_sel(vec.mVec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}), _VECTORMATH_MASK_0x000F)),((vec_uint4){0,0,0,0})); +} + +inline bool Vector3::operator != (const Vector3& vec) const +{ + return !(*this == vec); +} + +inline bool Vector3::operator < (const Vector3& vec) const +{ + return vec_all_gt(vec_cmpgt(vec_sel(vec.mVec128, ((vec_float4){1.0f,1.0f,1.0f,1.0f}), _VECTORMATH_MASK_0x000F), vec_sel(mVec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}), _VECTORMATH_MASK_0x000F)), ((vec_uint4){0,0,0,0})); +} + +inline bool Vector3::operator <= (const Vector3& vec) const +{ + return !(*this > vec); +} + +inline bool Vector3::operator > (const Vector3& vec) const +{ + return vec_all_gt(vec_cmpgt(vec_sel(mVec128, ((vec_float4){1.0f,1.0f,1.0f,1.0f}), _VECTORMATH_MASK_0x000F), vec_sel(vec.mVec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}), _VECTORMATH_MASK_0x000F)), ((vec_uint4){0,0,0,0})); +} + +inline bool Vector3::operator >= (const Vector3& vec) const +{ + return !(*this < vec); +} + +inline const Vector3 Vector3::operator -( ) const +{ + return Vector3( negatef4( mVec128 ) ); +} + +inline const Vector3 operator *( float scalar, Vector3 vec ) +{ + return floatInVec(scalar) * vec; +} + +inline const Vector3 operator *( floatInVec scalar, Vector3 vec ) +{ + return vec * scalar; +} + +inline const Vector3 mulPerElem( Vector3 vec0, Vector3 vec1 ) +{ + return Vector3( vec_madd( vec0.get128(), vec1.get128(), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); +} + +inline const Vector3 divPerElem( Vector3 vec0, Vector3 vec1 ) +{ + return Vector3( divf4( vec0.get128(), vec1.get128() ) ); +} + +inline const Vector3 recipPerElem( Vector3 vec ) +{ + return Vector3( recipf4( vec.get128() ) ); +} + +inline const Vector3 sqrtPerElem( Vector3 vec ) +{ + return Vector3( sqrtf4( vec.get128() ) ); +} + +inline const Vector3 rsqrtPerElem( Vector3 vec ) +{ + return Vector3( rsqrtf4( vec.get128() ) ); +} + +inline const Vector3 absPerElem( Vector3 vec ) +{ + return Vector3( fabsf4( vec.get128() ) ); +} + +inline const Vector3 copySignPerElem( Vector3 vec0, Vector3 vec1 ) +{ + return Vector3( copysignf4( vec0.get128(), vec1.get128() ) ); +} + +inline const Vector3 maxPerElem( Vector3 vec0, Vector3 vec1 ) +{ + return Vector3( fmaxf4( vec0.get128(), vec1.get128() ) ); +} + +inline const floatInVec maxElem( Vector3 vec ) +{ + vec_float4 result; + result = fmaxf4( vec_splat( vec.get128(), 1 ), vec.get128() ); + result = fmaxf4( vec_splat( vec.get128(), 2 ), result ); + return floatInVec( result, 0 ); +} + +inline const Vector3 minPerElem( Vector3 vec0, Vector3 vec1 ) +{ + return Vector3( fminf4( vec0.get128(), vec1.get128() ) ); +} + +inline const floatInVec minElem( Vector3 vec ) +{ + vec_float4 result; + result = fminf4( vec_splat( vec.get128(), 1 ), vec.get128() ); + result = fminf4( vec_splat( vec.get128(), 2 ), result ); + return floatInVec( result, 0 ); +} + +inline const floatInVec sum( Vector3 vec ) +{ + vec_float4 result; + result = vec_add( vec_splat( vec.get128(), 1 ), vec.get128() ); + result = vec_add( vec_splat( vec.get128(), 2 ), result ); + return floatInVec( result, 0 ); +} + +inline const floatInVec dot( Vector3 vec0, Vector3 vec1 ) +{ + return floatInVec( _vmathVfDot3( vec0.get128(), vec1.get128() ), 0 ); +} + +inline const floatInVec lengthSqr( Vector3 vec ) +{ + return floatInVec( _vmathVfDot3( vec.get128(), vec.get128() ), 0 ); +} + +inline const floatInVec length( Vector3 vec ) +{ + return floatInVec( sqrtf4(_vmathVfDot3( vec.get128(), vec.get128() )), 0 ); +} + +inline const Vector3 normalize( Vector3 vec ) +{ + vec_float4 dot = _vmathVfDot3( vec.get128(), vec.get128() ); + dot = vec_splat( dot, 0 ); + return Vector3( vec_madd( vec.get128(), rsqrtf4( dot ), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); +} + +inline const Vector3 cross( Vector3 vec0, Vector3 vec1 ) +{ + return Vector3( _vmathVfCross( vec0.get128(), vec1.get128() ) ); +} + +inline const Vector3 select( Vector3 vec0, Vector3 vec1, bool select1 ) +{ + return select( vec0, vec1, boolInVec(select1) ); +} + +inline const Vector3 select( Vector3 vec0, Vector3 vec1, boolInVec select1 ) +{ + return Vector3( vec_sel( vec0.get128(), vec1.get128(), select1.get128() ) ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( Vector3 vec ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec.get128(); + printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +inline void print( Vector3 vec, const char * name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec.get128(); + printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +#endif + +inline Vector4::Vector4( float _x, float _y, float _z, float _w ) +{ + if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & + __builtin_constant_p(_z) & __builtin_constant_p(_w)) { + mVec128 = (vec_float4){_x, _y, _z, _w}; + } else { + float *pf = (float *)&mVec128; + pf[0] = _x; + pf[1] = _y; + pf[2] = _z; + pf[3] = _w; + } +} + +inline Vector4::Vector4( floatInVec _x, floatInVec _y, floatInVec _z, floatInVec _w ) +{ + vec_float4 xz = vec_mergeh( _x.get128(), _z.get128() ); + vec_float4 yw = vec_mergeh( _y.get128(), _w.get128() ); + mVec128 = vec_mergeh( xz, yw ); +} + +inline Vector4::Vector4( Vector3 xyz, float _w ) +{ + mVec128 = xyz.get128(); + _vmathVfSetElement(mVec128, _w, 3); +} + +inline Vector4::Vector4( Vector3 xyz, floatInVec _w ) +{ + mVec128 = xyz.get128(); + mVec128 = _vmathVfInsert(mVec128, _w.get128(), 3); +} + +inline Vector4::Vector4( Vector3 vec ) +{ + mVec128 = vec.get128(); + mVec128 = _vmathVfInsert(mVec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}), 3); +} + +inline Vector4::Vector4( Point3 pnt ) +{ + mVec128 = pnt.get128(); + mVec128 = _vmathVfInsert(mVec128, ((vec_float4){1.0f,1.0f,1.0f,1.0f}), 3); +} + +inline Vector4::Vector4( Quat quat ) +{ + mVec128 = quat.get128(); +} + +inline Vector4::Vector4( float scalar ) +{ + mVec128 = floatInVec(scalar).get128(); +} + +inline Vector4::Vector4( floatInVec scalar ) +{ + mVec128 = scalar.get128(); +} + +inline Vector4::Vector4( vec_float4 vf4 ) +{ + mVec128 = vf4; +} + +inline const Vector4 Vector4::xAxis( ) +{ + return Vector4( _VECTORMATH_UNIT_1000 ); +} + +inline const Vector4 Vector4::yAxis( ) +{ + return Vector4( _VECTORMATH_UNIT_0100 ); +} + +inline const Vector4 Vector4::zAxis( ) +{ + return Vector4( _VECTORMATH_UNIT_0010 ); +} + +inline const Vector4 Vector4::wAxis( ) +{ + return Vector4( _VECTORMATH_UNIT_0001 ); +} + +inline const Vector4 lerp( float t, Vector4 vec0, Vector4 vec1 ) +{ + return lerp( floatInVec(t), vec0, vec1 ); +} + +inline const Vector4 lerp( floatInVec t, Vector4 vec0, Vector4 vec1 ) +{ + return ( vec0 + ( ( vec1 - vec0 ) * t ) ); +} + +inline const Vector4 slerp( float t, Vector4 unitVec0, Vector4 unitVec1 ) +{ + return slerp( floatInVec(t), unitVec0, unitVec1 ); +} + +inline const Vector4 slerp( floatInVec t, Vector4 unitVec0, Vector4 unitVec1 ) +{ + vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; + vec_uint4 selectMask; + cosAngle = _vmathVfDot4( unitVec0.get128(), unitVec1.get128() ); + cosAngle = vec_splat( cosAngle, 0 ); + selectMask = (vec_uint4)vec_cmpgt( ((vec_float4){_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL,_VECTORMATH_SLERP_TOL}), cosAngle ); + angle = acosf4( cosAngle ); + tttt = t.get128(); + oneMinusT = vec_sub( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); + angles = vec_mergeh( ((vec_float4){1.0f,1.0f,1.0f,1.0f}), tttt ); + angles = vec_mergeh( angles, oneMinusT ); + angles = vec_madd( angles, angle, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ); + sines = sinf4( angles ); + scales = divf4( sines, vec_splat( sines, 0 ) ); + scale0 = vec_sel( oneMinusT, vec_splat( scales, 1 ), selectMask ); + scale1 = vec_sel( tttt, vec_splat( scales, 2 ), selectMask ); + return Vector4( vec_madd( unitVec0.get128(), scale0, vec_madd( unitVec1.get128(), scale1, ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ) ); +} + +inline vec_float4 Vector4::get128( ) const +{ + return mVec128; +} + +inline void storeHalfFloats( Vector4 vec0, Vector4 vec1, Vector4 vec2, Vector4 vec3, vec_ushort8 * twoQuads ) +{ + twoQuads[0] = _vmath2VfToHalfFloats(vec0.get128(), vec1.get128()); + twoQuads[1] = _vmath2VfToHalfFloats(vec2.get128(), vec3.get128()); +} + +inline Vector4 & Vector4::operator =( Vector4 vec ) +{ + mVec128 = vec.mVec128; + return *this; +} + +inline Vector4 & Vector4::setXYZ( Vector3 vec ) +{ + mVec128 = vec_sel( vec.get128(), mVec128, _VECTORMATH_MASK_0x000F ); + return *this; +} + +inline const Vector3 Vector4::getXYZ( ) const +{ + return Vector3( mVec128 ); +} + +inline Vector4 & Vector4::setX( float _x ) +{ + _vmathVfSetElement(mVec128, _x, 0); + return *this; +} + +inline Vector4 & Vector4::setX( floatInVec _x ) +{ + mVec128 = _vmathVfInsert(mVec128, _x.get128(), 0); + return *this; +} + +inline const floatInVec Vector4::getX( ) const +{ + return floatInVec( mVec128, 0 ); +} + +inline Vector4 & Vector4::setY( float _y ) +{ + _vmathVfSetElement(mVec128, _y, 1); + return *this; +} + +inline Vector4 & Vector4::setY( floatInVec _y ) +{ + mVec128 = _vmathVfInsert(mVec128, _y.get128(), 1); + return *this; +} + +inline const floatInVec Vector4::getY( ) const +{ + return floatInVec( mVec128, 1 ); +} + +inline Vector4 & Vector4::setZ( float _z ) +{ + _vmathVfSetElement(mVec128, _z, 2); + return *this; +} + +inline Vector4 & Vector4::setZ( floatInVec _z ) +{ + mVec128 = _vmathVfInsert(mVec128, _z.get128(), 2); + return *this; +} + +inline const floatInVec Vector4::getZ( ) const +{ + return floatInVec( mVec128, 2 ); +} + +inline Vector4 & Vector4::setW( float _w ) +{ + _vmathVfSetElement(mVec128, _w, 3); + return *this; +} + +inline Vector4 & Vector4::setW( floatInVec _w ) +{ + mVec128 = _vmathVfInsert(mVec128, _w.get128(), 3); + return *this; +} + +inline const floatInVec Vector4::getW( ) const +{ + return floatInVec( mVec128, 3 ); +} + +inline Vector4 & Vector4::setElem( int idx, float value ) +{ + _vmathVfSetElement(mVec128, value, idx); + return *this; +} + +inline Vector4 & Vector4::setElem( int idx, floatInVec value ) +{ + mVec128 = _vmathVfInsert(mVec128, value.get128(), idx); + return *this; +} + +inline const floatInVec Vector4::getElem( int idx ) const +{ + return floatInVec( mVec128, idx ); +} + +inline VecIdx Vector4::operator []( int idx ) +{ + return VecIdx( mVec128, idx ); +} + +inline const floatInVec Vector4::operator []( int idx ) const +{ + return floatInVec( mVec128, idx ); +} + +inline const Vector4 Vector4::operator +( Vector4 vec ) const +{ + return Vector4( vec_add( mVec128, vec.mVec128 ) ); +} + +inline const Vector4 Vector4::operator -( Vector4 vec ) const +{ + return Vector4( vec_sub( mVec128, vec.mVec128 ) ); +} + +inline const Vector4 Vector4::operator *( float scalar ) const +{ + return *this * floatInVec(scalar); +} + +inline const Vector4 Vector4::operator *( floatInVec scalar ) const +{ + return Vector4( vec_madd( mVec128, scalar.get128(), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); +} + +inline Vector4 & Vector4::operator +=( Vector4 vec ) +{ + *this = *this + vec; + return *this; +} + +inline Vector4 & Vector4::operator -=( Vector4 vec ) +{ + *this = *this - vec; + return *this; +} + +inline Vector4 & Vector4::operator *=( float scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline Vector4 & Vector4::operator *=( floatInVec scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Vector4 Vector4::operator /( float scalar ) const +{ + return *this / floatInVec(scalar); +} + +inline const Vector4 Vector4::operator /( floatInVec scalar ) const +{ + return Vector4( divf4( mVec128, scalar.get128() ) ); +} + +inline Vector4 & Vector4::operator /=( float scalar ) +{ + *this = *this / scalar; + return *this; +} + +inline Vector4 & Vector4::operator /=( floatInVec scalar ) +{ + *this = *this / scalar; + return *this; +} + +inline const Vector4 Vector4::operator -( ) const +{ + return Vector4( negatef4( mVec128 ) ); +} + +inline bool Vector4::operator == (const Vector4& vec) const +{ + return vec_all_gt(vec_cmpeq(mVec128, vec.mVec128),((vec_uint4){0,0,0,0})); +} + +inline bool Vector4::operator != (const Vector4& vec) const +{ + return !(*this == vec); +} + +inline bool Vector4::operator < (const Vector4& vec) const +{ + return vec_all_gt(vec_cmpgt(vec.mVec128, mVec128),((vec_uint4){0,0,0,0})); +} + +inline bool Vector4::operator <= (const Vector4& vec) const +{ + return !(*this > vec); +} + +inline bool Vector4::operator > (const Vector4& vec) const +{ + return vec_all_gt(vec_cmpgt(mVec128, vec.mVec128),((vec_uint4){0,0,0,0})); +} + +inline bool Vector4::operator >= (const Vector4& vec) const +{ + return !(*this < vec); +} + +inline const Vector4 operator *( float scalar, Vector4 vec ) +{ + return floatInVec(scalar) * vec; +} + +inline const Vector4 operator *( floatInVec scalar, Vector4 vec ) +{ + return vec * scalar; +} + +inline const Vector4 mulPerElem( Vector4 vec0, Vector4 vec1 ) +{ + return Vector4( vec_madd( vec0.get128(), vec1.get128(), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); +} + +inline const Vector4 divPerElem( Vector4 vec0, Vector4 vec1 ) +{ + return Vector4( divf4( vec0.get128(), vec1.get128() ) ); +} + +inline const Vector4 recipPerElem( Vector4 vec ) +{ + return Vector4( recipf4( vec.get128() ) ); +} + +inline const Vector4 sqrtPerElem( Vector4 vec ) +{ + return Vector4( sqrtf4( vec.get128() ) ); +} + +inline const Vector4 rsqrtPerElem( Vector4 vec ) +{ + return Vector4( rsqrtf4( vec.get128() ) ); +} + +inline const Vector4 absPerElem( Vector4 vec ) +{ + return Vector4( fabsf4( vec.get128() ) ); +} + +inline const Vector4 copySignPerElem( Vector4 vec0, Vector4 vec1 ) +{ + return Vector4( copysignf4( vec0.get128(), vec1.get128() ) ); +} + +inline const Vector4 maxPerElem( Vector4 vec0, Vector4 vec1 ) +{ + return Vector4( fmaxf4( vec0.get128(), vec1.get128() ) ); +} + +inline const floatInVec maxElem( Vector4 vec ) +{ + vec_float4 result; + result = fmaxf4( vec_splat( vec.get128(), 1 ), vec.get128() ); + result = fmaxf4( vec_splat( vec.get128(), 2 ), result ); + result = fmaxf4( vec_splat( vec.get128(), 3 ), result ); + return floatInVec( result, 0 ); +} + +inline const Vector4 minPerElem( Vector4 vec0, Vector4 vec1 ) +{ + return Vector4( fminf4( vec0.get128(), vec1.get128() ) ); +} + +inline const floatInVec minElem( Vector4 vec ) +{ + vec_float4 result; + result = fminf4( vec_splat( vec.get128(), 1 ), vec.get128() ); + result = fminf4( vec_splat( vec.get128(), 2 ), result ); + result = fminf4( vec_splat( vec.get128(), 3 ), result ); + return floatInVec( result, 0 ); +} + +inline const floatInVec sum( Vector4 vec ) +{ + vec_float4 result; + result = vec_add( vec_splat( vec.get128(), 1 ), vec.get128() ); + result = vec_add( vec_splat( vec.get128(), 2 ), result ); + result = vec_add( vec_splat( vec.get128(), 3 ), result ); + return floatInVec( result, 0 ); +} + +inline const floatInVec dot( Vector4 vec0, Vector4 vec1 ) +{ + return floatInVec( _vmathVfDot4( vec0.get128(), vec1.get128() ), 0 ); +} + +inline const floatInVec lengthSqr( Vector4 vec ) +{ + return floatInVec( _vmathVfDot4( vec.get128(), vec.get128() ), 0 ); +} + +inline const floatInVec length( Vector4 vec ) +{ + return floatInVec( sqrtf4(_vmathVfDot4( vec.get128(), vec.get128() )), 0 ); +} + +inline const Vector4 normalize( Vector4 vec ) +{ + vec_float4 dot = _vmathVfDot4( vec.get128(), vec.get128() ); + return Vector4( vec_madd( vec.get128(), rsqrtf4( dot ), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); +} + +inline const Vector4 select( Vector4 vec0, Vector4 vec1, bool select1 ) +{ + return select( vec0, vec1, boolInVec(select1) ); +} + +inline const Vector4 select( Vector4 vec0, Vector4 vec1, boolInVec select1 ) +{ + return Vector4( vec_sel( vec0.get128(), vec1.get128(), select1.get128() ) ); +} + +inline void loadXYZW( Vector4 & vec, const vec_float4 * quad ) +{ + vec = Vector4( *quad ); +} + +inline void loadXYZW( Vector4 & vec, const float * fptr ) +{ + vec_float4 vec0 = vec_ld(0, fptr); + vec_float4 vec1 = vec_ld(16, fptr); + vec = Vector4( vec_perm(vec0, vec1, vec_lvsl(0, fptr)) ); +} + +inline void storeXYZW( Vector4 vec, float * fptr ) +{ + vec_float4 vsrc = vec.get128(); + vec_float4 x = vec_splat(vsrc, 0); + vec_float4 y = vec_splat(vsrc, 1); + vec_float4 z = vec_splat(vsrc, 2); + vec_float4 w = vec_splat(vsrc, 3); + vec_ste(x, 0, fptr); + vec_ste(y, 4, fptr); + vec_ste(z, 8, fptr); + vec_ste(w, 12, fptr); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( Vector4 vec ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec.get128(); + printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +inline void print( Vector4 vec, const char * name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec.get128(); + printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +#endif + +inline Point3::Point3( float _x, float _y, float _z ) +{ + if (__builtin_constant_p(_x) & __builtin_constant_p(_y) & __builtin_constant_p(_z)) { + mVec128 = (vec_float4){_x, _y, _z, 0.0f}; + } else { + float *pf = (float *)&mVec128; + pf[0] = _x; + pf[1] = _y; + pf[2] = _z; + pf[3] = 0.0f; + } +} + +inline Point3::Point3( floatInVec _x, floatInVec _y, floatInVec _z ) +{ + vec_float4 xz = vec_mergeh( _x.get128(), _z.get128() ); + vec_float4 yw = vec_mergeh( _y.get128(), ((vec_float4){0.0f, 0.0f, 0.0f, 0.0f})); + mVec128 = vec_mergeh( xz, yw ); +} + +inline Point3::Point3( Vector3 vec ) +{ + mVec128 = vec.get128(); +} + +inline Point3::Point3( float scalar ) +{ + mVec128 = floatInVec(scalar).get128(); +} + +inline Point3::Point3( floatInVec scalar ) +{ + mVec128 = scalar.get128(); +} + +inline Point3::Point3( vec_float4 vf4 ) +{ + mVec128 = vf4; +} + +inline const Point3 lerp( float t, Point3 pnt0, Point3 pnt1 ) +{ + return lerp( floatInVec(t), pnt0, pnt1 ); +} + +inline const Point3 lerp( floatInVec t, Point3 pnt0, Point3 pnt1 ) +{ + return ( pnt0 + ( ( pnt1 - pnt0 ) * t ) ); +} + +inline vec_float4 Point3::get128( ) const +{ + return mVec128; +} + +inline void loadXYZ( Point3 & pnt, const float * fptr ) +{ + vec_float4 vec0 = vec_ld(0, fptr); + vec_float4 vec1 = vec_ld(16, fptr); + pnt = Point3( vec_perm(vec0, vec1, vec_lvsl(0, fptr)) ); +} + +inline void storeXYZ( Point3 pnt, vec_float4 * quad ) +{ + vec_float4 dstVec = *quad; + vec_uint4 mask = _VECTORMATH_MASK_0x000F; + dstVec = vec_sel(pnt.get128(), dstVec, mask); + *quad = dstVec; +} + +inline void storeXYZ( Point3 pnt, float * fptr ) +{ + vec_float4 vsrc = pnt.get128(); + vec_float4 x = vec_splat(vsrc, 0); + vec_float4 y = vec_splat(vsrc, 1); + vec_float4 z = vec_splat(vsrc, 2); + vec_ste(x, 0, fptr); + vec_ste(y, 4, fptr); + vec_ste(z, 8, fptr); +} + +inline void loadXYZArray( Point3 & pnt0, Point3 & pnt1, Point3 & pnt2, Point3 & pnt3, const vec_float4 * threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; + xyzx = threeQuads[0]; + yzxy = threeQuads[1]; + zxyz = threeQuads[2]; + xyz1 = vec_sld( xyzx, yzxy, 12 ); + xyz2 = vec_sld( yzxy, zxyz, 8 ); + xyz3 = vec_sld( zxyz, zxyz, 4 ); + pnt0 = Point3( xyzx ); + pnt1 = Point3( xyz1 ); + pnt2 = Point3( xyz2 ); + pnt3 = Point3( xyz3 ); +} + +inline void storeXYZArray( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, vec_float4 * threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz; + xyzx = vec_perm( pnt0.get128(), pnt1.get128(), _VECTORMATH_PERM_XYZA ); + yzxy = vec_perm( pnt1.get128(), pnt2.get128(), _VECTORMATH_PERM_YZAB ); + zxyz = vec_perm( pnt2.get128(), pnt3.get128(), _VECTORMATH_PERM_ZABC ); + threeQuads[0] = xyzx; + threeQuads[1] = yzxy; + threeQuads[2] = zxyz; +} + +inline void storeHalfFloats( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, Point3 pnt4, Point3 pnt5, Point3 pnt6, Point3 pnt7, vec_ushort8 * threeQuads ) +{ + vec_float4 xyz0[3]; + vec_float4 xyz1[3]; + storeXYZArray( pnt0, pnt1, pnt2, pnt3, xyz0 ); + storeXYZArray( pnt4, pnt5, pnt6, pnt7, xyz1 ); + threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); + threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); + threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); +} + +inline Point3 & Point3::operator =( Point3 pnt ) +{ + mVec128 = pnt.mVec128; + return *this; +} + +inline Point3 & Point3::setX( float _x ) +{ + _vmathVfSetElement(mVec128, _x, 0); + return *this; +} + +inline Point3 & Point3::setX( floatInVec _x ) +{ + mVec128 = _vmathVfInsert(mVec128, _x.get128(), 0); + return *this; +} + +inline const floatInVec Point3::getX( ) const +{ + return floatInVec( mVec128, 0 ); +} + +inline Point3 & Point3::setY( float _y ) +{ + _vmathVfSetElement(mVec128, _y, 1); + return *this; +} + +inline Point3 & Point3::setY( floatInVec _y ) +{ + mVec128 = _vmathVfInsert(mVec128, _y.get128(), 1); + return *this; +} + +inline const floatInVec Point3::getY( ) const +{ + return floatInVec( mVec128, 1 ); +} + +inline Point3 & Point3::setZ( float _z ) +{ + _vmathVfSetElement(mVec128, _z, 2); + return *this; +} + +inline Point3 & Point3::setZ( floatInVec _z ) +{ + mVec128 = _vmathVfInsert(mVec128, _z.get128(), 2); + return *this; +} + +inline const floatInVec Point3::getZ( ) const +{ + return floatInVec( mVec128, 2 ); +} + +inline Point3 & Point3::setElem( int idx, float value ) +{ + _vmathVfSetElement(mVec128, value, idx); + return *this; +} + +inline Point3 & Point3::setElem( int idx, floatInVec value ) +{ + mVec128 = _vmathVfInsert(mVec128, value.get128(), idx); + return *this; +} + +inline const floatInVec Point3::getElem( int idx ) const +{ + return floatInVec( mVec128, idx ); +} + +inline VecIdx Point3::operator []( int idx ) +{ + return VecIdx( mVec128, idx ); +} + +inline const floatInVec Point3::operator []( int idx ) const +{ + return floatInVec( mVec128, idx ); +} + +inline const Vector3 Point3::operator -( Point3 pnt ) const +{ + return Vector3( vec_sub( mVec128, pnt.mVec128 ) ); +} + +inline const Point3 Point3::operator +( Vector3 vec ) const +{ + return Point3( vec_add( mVec128, vec.get128() ) ); +} + +inline const Point3 Point3::operator -( Vector3 vec ) const +{ + return Point3( vec_sub( mVec128, vec.get128() ) ); +} + +inline Point3 & Point3::operator +=( Vector3 vec ) +{ + *this = *this + vec; + return *this; +} + +inline Point3 & Point3::operator -=( Vector3 vec ) +{ + *this = *this - vec; + return *this; +} + +inline bool Point3::operator == (const Point3& pnt) const +{ + return vec_all_gt(vec_cmpeq(vec_sel(mVec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}), _VECTORMATH_MASK_0x000F), vec_sel(pnt.mVec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}), _VECTORMATH_MASK_0x000F)),((vec_uint4){0,0,0,0})); +} + +inline bool Point3::operator != (const Point3& pnt) const +{ + return !(*this == pnt); +} + +inline bool Point3::operator < (const Point3& pnt) const +{ + return vec_all_gt(vec_cmpgt(vec_sel(pnt.mVec128, ((vec_float4){1.0f,1.0f,1.0f,1.0f}), _VECTORMATH_MASK_0x000F), vec_sel(mVec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}), _VECTORMATH_MASK_0x000F)), ((vec_uint4){0,0,0,0})); +} + +inline bool Point3::operator <= (const Point3& pnt) const +{ + return !(*this > pnt); +} + +inline bool Point3::operator > (const Point3& pnt) const +{ + return vec_all_gt(vec_cmpgt(vec_sel(mVec128, ((vec_float4){1.0f,1.0f,1.0f,1.0f}), _VECTORMATH_MASK_0x000F), vec_sel(pnt.mVec128, ((vec_float4){0.0f,0.0f,0.0f,0.0f}), _VECTORMATH_MASK_0x000F)), ((vec_uint4){0,0,0,0})); +} + +inline bool Point3::operator >= (const Point3& pnt) const +{ + return !(*this < pnt); +} + +inline const Point3 mulPerElem( Point3 pnt0, Point3 pnt1 ) +{ + return Point3( vec_madd( pnt0.get128(), pnt1.get128(), ((vec_float4){0.0f,0.0f,0.0f,0.0f}) ) ); +} + +inline const Point3 divPerElem( Point3 pnt0, Point3 pnt1 ) +{ + return Point3( divf4( pnt0.get128(), pnt1.get128() ) ); +} + +inline const Point3 recipPerElem( Point3 pnt ) +{ + return Point3( recipf4( pnt.get128() ) ); +} + +inline const Point3 sqrtPerElem( Point3 pnt ) +{ + return Point3( sqrtf4( pnt.get128() ) ); +} + +inline const Point3 rsqrtPerElem( Point3 pnt ) +{ + return Point3( rsqrtf4( pnt.get128() ) ); +} + +inline const Point3 absPerElem( Point3 pnt ) +{ + return Point3( fabsf4( pnt.get128() ) ); +} + +inline const Point3 copySignPerElem( Point3 pnt0, Point3 pnt1 ) +{ + return Point3( copysignf4( pnt0.get128(), pnt1.get128() ) ); +} + +inline const Point3 maxPerElem( Point3 pnt0, Point3 pnt1 ) +{ + return Point3( fmaxf4( pnt0.get128(), pnt1.get128() ) ); +} + +inline const floatInVec maxElem( Point3 pnt ) +{ + vec_float4 result; + result = fmaxf4( vec_splat( pnt.get128(), 1 ), pnt.get128() ); + result = fmaxf4( vec_splat( pnt.get128(), 2 ), result ); + return floatInVec( result, 0 ); +} + +inline const Point3 minPerElem( Point3 pnt0, Point3 pnt1 ) +{ + return Point3( fminf4( pnt0.get128(), pnt1.get128() ) ); +} + +inline const floatInVec minElem( Point3 pnt ) +{ + vec_float4 result; + result = fminf4( vec_splat( pnt.get128(), 1 ), pnt.get128() ); + result = fminf4( vec_splat( pnt.get128(), 2 ), result ); + return floatInVec( result, 0 ); +} + +inline const floatInVec sum( Point3 pnt ) +{ + vec_float4 result; + result = vec_add( vec_splat( pnt.get128(), 1 ), pnt.get128() ); + result = vec_add( vec_splat( pnt.get128(), 2 ), result ); + return floatInVec( result, 0 ); +} + +inline const Point3 scale( Point3 pnt, float scaleVal ) +{ + return scale( pnt, floatInVec( scaleVal ) ); +} + +inline const Point3 scale( Point3 pnt, floatInVec scaleVal ) +{ + return mulPerElem( pnt, Point3( scaleVal ) ); +} + +inline const Point3 scale( Point3 pnt, Vector3 scaleVec ) +{ + return mulPerElem( pnt, Point3( scaleVec ) ); +} + +inline const floatInVec projection( Point3 pnt, Vector3 unitVec ) +{ + return floatInVec( _vmathVfDot3( pnt.get128(), unitVec.get128() ), 0 ); +} + +inline const floatInVec distSqrFromOrigin( Point3 pnt ) +{ + return lengthSqr( Vector3( pnt ) ); +} + +inline const floatInVec distFromOrigin( Point3 pnt ) +{ + return length( Vector3( pnt ) ); +} + +inline const floatInVec distSqr( Point3 pnt0, Point3 pnt1 ) +{ + return lengthSqr( ( pnt1 - pnt0 ) ); +} + +inline const floatInVec dist( Point3 pnt0, Point3 pnt1 ) +{ + return length( ( pnt1 - pnt0 ) ); +} + +inline const Point3 select( Point3 pnt0, Point3 pnt1, bool select1 ) +{ + return select( pnt0, pnt1, boolInVec(select1) ); +} + +inline const Point3 select( Point3 pnt0, Point3 pnt1, boolInVec select1 ) +{ + return Point3( vec_sel( pnt0.get128(), pnt1.get128(), select1.get128() ) ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( Point3 pnt ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = pnt.get128(); + printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +inline void print( Point3 pnt, const char * name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = pnt.get128(); + printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +#endif + +} // namespace Aos +} // namespace Vectormath + +#endif diff --git a/common/vectormath/ppu/cpp/vecidx_aos.h b/common/vectormath/ppu/cpp/vecidx_aos.h index df335757..86ddf84f 100644 --- a/common/vectormath/ppu/cpp/vecidx_aos.h +++ b/common/vectormath/ppu/cpp/vecidx_aos.h @@ -1,80 +1,80 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_VECIDX_AOS_H -#define _VECTORMATH_VECIDX_AOS_H - -#include "floatInVec.h" - -namespace Vectormath { -namespace Aos { - -//----------------------------------------------------------------------------- -// VecIdx -// Used in setting elements of Vector3, Vector4, Point3, or Quat with the -// subscripting operator. -// - -class VecIdx -{ -private: - typedef vec_float4 vec_float4_t; - vec_float4_t &ref __attribute__ ((aligned(16))); - int i __attribute__ ((aligned(16))); -public: - inline VecIdx( vec_float4_t& vec, int idx ): ref(vec) { i = idx; } - - // implicitly casts to float unless _VECTORMATH_NO_SCALAR_CAST defined - // in which case, implicitly casts to floatInVec, and one must call - // getAsFloat to convert to float. - // -#ifdef _VECTORMATH_NO_SCALAR_CAST - inline operator floatInVec() const; - inline float getAsFloat() const; -#else - inline operator float() const; -#endif - - inline float operator =( float scalar ); - inline floatInVec operator =( floatInVec scalar ); - inline floatInVec operator =( const VecIdx& scalar ); - inline floatInVec operator *=( float scalar ); - inline floatInVec operator *=( floatInVec scalar ); - inline floatInVec operator /=( float scalar ); - inline floatInVec operator /=( floatInVec scalar ); - inline floatInVec operator +=( float scalar ); - inline floatInVec operator +=( floatInVec scalar ); - inline floatInVec operator -=( float scalar ); - inline floatInVec operator -=( floatInVec scalar ); -}; - -} // namespace Aos -} // namespace Vectormath - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_VECIDX_AOS_H +#define _VECTORMATH_VECIDX_AOS_H + +#include "floatInVec.h" + +namespace Vectormath { +namespace Aos { + +//----------------------------------------------------------------------------- +// VecIdx +// Used in setting elements of Vector3, Vector4, Point3, or Quat with the +// subscripting operator. +// + +class VecIdx +{ +private: + typedef vec_float4 vec_float4_t; + vec_float4_t &ref __attribute__ ((aligned(16))); + int i __attribute__ ((aligned(16))); +public: + inline VecIdx( vec_float4_t& vec, int idx ): ref(vec) { i = idx; } + + // implicitly casts to float unless _VECTORMATH_NO_SCALAR_CAST defined + // in which case, implicitly casts to floatInVec, and one must call + // getAsFloat to convert to float. + // +#ifdef _VECTORMATH_NO_SCALAR_CAST + inline operator floatInVec() const; + inline float getAsFloat() const; +#else + inline operator float() const; +#endif + + inline float operator =( float scalar ); + inline floatInVec operator =( floatInVec scalar ); + inline floatInVec operator =( const VecIdx& scalar ); + inline floatInVec operator *=( float scalar ); + inline floatInVec operator *=( floatInVec scalar ); + inline floatInVec operator /=( float scalar ); + inline floatInVec operator /=( floatInVec scalar ); + inline floatInVec operator +=( float scalar ); + inline floatInVec operator +=( floatInVec scalar ); + inline floatInVec operator -=( float scalar ); + inline floatInVec operator -=( floatInVec scalar ); +}; + +} // namespace Aos +} // namespace Vectormath + +#endif diff --git a/common/vectormath/ppu/cpp/vectormath_aos.h b/common/vectormath/ppu/cpp/vectormath_aos.h index 0a7fa173..2e73f034 100644 --- a/common/vectormath/ppu/cpp/vectormath_aos.h +++ b/common/vectormath/ppu/cpp/vectormath_aos.h @@ -1,2244 +1,2369 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_AOS_CPP_PPU_H -#define _VECTORMATH_AOS_CPP_PPU_H - -#include -#include -#include "vecidx_aos.h" -#include "floatInVec.h" -#include "boolInVec.h" - -#ifdef _VECTORMATH_DEBUG -#include -#endif - -namespace Vectormath { - -namespace Aos { - -//----------------------------------------------------------------------------- -// Forward Declarations -// - -class Vector3; -class Vector4; -class Point3; -class Quat; -class Matrix3; -class Matrix4; -class Transform3; - -// A 3-D vector in array-of-structures format -// -class Vector3 -{ - vec_float4 mVec128; - -public: - // Default constructor; does no initialization - // - inline Vector3( ) { }; - - // Construct a 3-D vector from x, y, and z elements - // - inline Vector3( float x, float y, float z ); - - // Construct a 3-D vector from x, y, and z elements (scalar data contained in vector data type) - // - inline Vector3( floatInVec x, floatInVec y, floatInVec z ); - - // Copy elements from a 3-D point into a 3-D vector - // - explicit inline Vector3( Point3 pnt ); - - // Set all elements of a 3-D vector to the same scalar value - // - explicit inline Vector3( float scalar ); - - // Set all elements of a 3-D vector to the same scalar value (scalar data contained in vector data type) - // - explicit inline Vector3( floatInVec scalar ); - - // Set vector float data in a 3-D vector - // - explicit inline Vector3( vec_float4 vf4 ); - - // Get vector float data from a 3-D vector - // - inline vec_float4 get128( ) const; - - // Assign one 3-D vector to another - // - inline Vector3 & operator =( Vector3 vec ); - - // Set the x element of a 3-D vector - // - inline Vector3 & setX( float x ); - - // Set the y element of a 3-D vector - // - inline Vector3 & setY( float y ); - - // Set the z element of a 3-D vector - // - inline Vector3 & setZ( float z ); - - // Set the x element of a 3-D vector (scalar data contained in vector data type) - // - inline Vector3 & setX( floatInVec x ); - - // Set the y element of a 3-D vector (scalar data contained in vector data type) - // - inline Vector3 & setY( floatInVec y ); - - // Set the z element of a 3-D vector (scalar data contained in vector data type) - // - inline Vector3 & setZ( floatInVec z ); - - // Get the x element of a 3-D vector - // - inline const floatInVec getX( ) const; - - // Get the y element of a 3-D vector - // - inline const floatInVec getY( ) const; - - // Get the z element of a 3-D vector - // - inline const floatInVec getZ( ) const; - - // Set an x, y, or z element of a 3-D vector by index - // - inline Vector3 & setElem( int idx, float value ); - - // Set an x, y, or z element of a 3-D vector by index (scalar data contained in vector data type) - // - inline Vector3 & setElem( int idx, floatInVec value ); - - // Get an x, y, or z element of a 3-D vector by index - // - inline const floatInVec getElem( int idx ) const; - - // Subscripting operator to set or get an element - // - inline VecIdx operator []( int idx ); - - // Subscripting operator to get an element - // - inline const floatInVec operator []( int idx ) const; - - // Add two 3-D vectors - // - inline const Vector3 operator +( Vector3 vec ) const; - - // Subtract a 3-D vector from another 3-D vector - // - inline const Vector3 operator -( Vector3 vec ) const; - - // Add a 3-D vector to a 3-D point - // - inline const Point3 operator +( Point3 pnt ) const; - - // Multiply a 3-D vector by a scalar - // - inline const Vector3 operator *( float scalar ) const; - - // Divide a 3-D vector by a scalar - // - inline const Vector3 operator /( float scalar ) const; - - // Multiply a 3-D vector by a scalar (scalar data contained in vector data type) - // - inline const Vector3 operator *( floatInVec scalar ) const; - - // Divide a 3-D vector by a scalar (scalar data contained in vector data type) - // - inline const Vector3 operator /( floatInVec scalar ) const; - - // Perform compound assignment and addition with a 3-D vector - // - inline Vector3 & operator +=( Vector3 vec ); - - // Perform compound assignment and subtraction by a 3-D vector - // - inline Vector3 & operator -=( Vector3 vec ); - - // Perform compound assignment and multiplication by a scalar - // - inline Vector3 & operator *=( float scalar ); - - // Perform compound assignment and division by a scalar - // - inline Vector3 & operator /=( float scalar ); - - // Perform compound assignment and multiplication by a scalar (scalar data contained in vector data type) - // - inline Vector3 & operator *=( floatInVec scalar ); - - // Perform compound assignment and division by a scalar (scalar data contained in vector data type) - // - inline Vector3 & operator /=( floatInVec scalar ); - - // Negate all elements of a 3-D vector - // - inline const Vector3 operator -( ) const; - - // Construct x axis - // - static inline const Vector3 xAxis( ); - - // Construct y axis - // - static inline const Vector3 yAxis( ); - - // Construct z axis - // - static inline const Vector3 zAxis( ); - -}; - -// Multiply a 3-D vector by a scalar -// -inline const Vector3 operator *( float scalar, Vector3 vec ); - -// Multiply a 3-D vector by a scalar (scalar data contained in vector data type) -// -inline const Vector3 operator *( floatInVec scalar, Vector3 vec ); - -// Multiply two 3-D vectors per element -// -inline const Vector3 mulPerElem( Vector3 vec0, Vector3 vec1 ); - -// Divide two 3-D vectors per element -// NOTE: -// Floating-point behavior matches standard library function divf4. -// -inline const Vector3 divPerElem( Vector3 vec0, Vector3 vec1 ); - -// Compute the reciprocal of a 3-D vector per element -// NOTE: -// Floating-point behavior matches standard library function recipf4. -// -inline const Vector3 recipPerElem( Vector3 vec ); - -// Compute the square root of a 3-D vector per element -// NOTE: -// Floating-point behavior matches standard library function sqrtf4. -// -inline const Vector3 sqrtPerElem( Vector3 vec ); - -// Compute the reciprocal square root of a 3-D vector per element -// NOTE: -// Floating-point behavior matches standard library function rsqrtf4. -// -inline const Vector3 rsqrtPerElem( Vector3 vec ); - -// Compute the absolute value of a 3-D vector per element -// -inline const Vector3 absPerElem( Vector3 vec ); - -// Copy sign from one 3-D vector to another, per element -// -inline const Vector3 copySignPerElem( Vector3 vec0, Vector3 vec1 ); - -// Maximum of two 3-D vectors per element -// -inline const Vector3 maxPerElem( Vector3 vec0, Vector3 vec1 ); - -// Minimum of two 3-D vectors per element -// -inline const Vector3 minPerElem( Vector3 vec0, Vector3 vec1 ); - -// Maximum element of a 3-D vector -// -inline const floatInVec maxElem( Vector3 vec ); - -// Minimum element of a 3-D vector -// -inline const floatInVec minElem( Vector3 vec ); - -// Compute the sum of all elements of a 3-D vector -// -inline const floatInVec sum( Vector3 vec ); - -// Compute the dot product of two 3-D vectors -// -inline const floatInVec dot( Vector3 vec0, Vector3 vec1 ); - -// Compute the square of the length of a 3-D vector -// -inline const floatInVec lengthSqr( Vector3 vec ); - -// Compute the length of a 3-D vector -// -inline const floatInVec length( Vector3 vec ); - -// Normalize a 3-D vector -// NOTE: -// The result is unpredictable when all elements of vec are at or near zero. -// -inline const Vector3 normalize( Vector3 vec ); - -// Compute cross product of two 3-D vectors -// -inline const Vector3 cross( Vector3 vec0, Vector3 vec1 ); - -// Outer product of two 3-D vectors -// -inline const Matrix3 outer( Vector3 vec0, Vector3 vec1 ); - -// Pre-multiply a row vector by a 3x3 matrix -// NOTE: -// Slower than column post-multiply. -// -inline const Vector3 rowMul( Vector3 vec, const Matrix3 & mat ); - -// Cross-product matrix of a 3-D vector -// -inline const Matrix3 crossMatrix( Vector3 vec ); - -// Create cross-product matrix and multiply -// NOTE: -// Faster than separately creating a cross-product matrix and multiplying. -// -inline const Matrix3 crossMatrixMul( Vector3 vec, const Matrix3 & mat ); - -// Linear interpolation between two 3-D vectors -// NOTE: -// Does not clamp t between 0 and 1. -// -inline const Vector3 lerp( float t, Vector3 vec0, Vector3 vec1 ); - -// Linear interpolation between two 3-D vectors (scalar data contained in vector data type) -// NOTE: -// Does not clamp t between 0 and 1. -// -inline const Vector3 lerp( floatInVec t, Vector3 vec0, Vector3 vec1 ); - -// Spherical linear interpolation between two 3-D vectors -// NOTE: -// The result is unpredictable if the vectors point in opposite directions. -// Does not clamp t between 0 and 1. -// -inline const Vector3 slerp( float t, Vector3 unitVec0, Vector3 unitVec1 ); - -// Spherical linear interpolation between two 3-D vectors (scalar data contained in vector data type) -// NOTE: -// The result is unpredictable if the vectors point in opposite directions. -// Does not clamp t between 0 and 1. -// -inline const Vector3 slerp( floatInVec t, Vector3 unitVec0, Vector3 unitVec1 ); - -// Conditionally select between two 3-D vectors -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// However, the transfer of select1 to a VMX register may use more processing time than a branch. -// Use the boolInVec version for better performance. -// -inline const Vector3 select( Vector3 vec0, Vector3 vec1, bool select1 ); - -// Conditionally select between two 3-D vectors (scalar data contained in vector data type) -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// -inline const Vector3 select( Vector3 vec0, Vector3 vec1, boolInVec select1 ); - -// Store x, y, and z elements of a 3-D vector in the first three words of a quadword. -// The value of the fourth word (the word with the highest address) remains unchanged -// -inline void storeXYZ( Vector3 vec, vec_float4 * quad ); - -// Load four three-float 3-D vectors, stored in three quadwords -// -inline void loadXYZArray( Vector3 & vec0, Vector3 & vec1, Vector3 & vec2, Vector3 & vec3, const vec_float4 * threeQuads ); - -// Store four 3-D vectors in three quadwords -// -inline void storeXYZArray( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, vec_float4 * threeQuads ); - -// Store eight 3-D vectors as half-floats -// -inline void storeHalfFloats( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, Vector3 vec4, Vector3 vec5, Vector3 vec6, Vector3 vec7, vec_ushort8 * threeQuads ); - -#ifdef _VECTORMATH_DEBUG - -// Print a 3-D vector -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Vector3 vec ); - -// Print a 3-D vector and an associated string identifier -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Vector3 vec, const char * name ); - -#endif - -// A 4-D vector in array-of-structures format -// -class Vector4 -{ - vec_float4 mVec128; - -public: - // Default constructor; does no initialization - // - inline Vector4( ) { }; - - // Construct a 4-D vector from x, y, z, and w elements - // - inline Vector4( float x, float y, float z, float w ); - - // Construct a 4-D vector from x, y, z, and w elements (scalar data contained in vector data type) - // - inline Vector4( floatInVec x, floatInVec y, floatInVec z, floatInVec w ); - - // Construct a 4-D vector from a 3-D vector and a scalar - // - inline Vector4( Vector3 xyz, float w ); - - // Construct a 4-D vector from a 3-D vector and a scalar (scalar data contained in vector data type) - // - inline Vector4( Vector3 xyz, floatInVec w ); - - // Copy x, y, and z from a 3-D vector into a 4-D vector, and set w to 0 - // - explicit inline Vector4( Vector3 vec ); - - // Copy x, y, and z from a 3-D point into a 4-D vector, and set w to 1 - // - explicit inline Vector4( Point3 pnt ); - - // Copy elements from a quaternion into a 4-D vector - // - explicit inline Vector4( Quat quat ); - - // Set all elements of a 4-D vector to the same scalar value - // - explicit inline Vector4( float scalar ); - - // Set all elements of a 4-D vector to the same scalar value (scalar data contained in vector data type) - // - explicit inline Vector4( floatInVec scalar ); - - // Set vector float data in a 4-D vector - // - explicit inline Vector4( vec_float4 vf4 ); - - // Get vector float data from a 4-D vector - // - inline vec_float4 get128( ) const; - - // Assign one 4-D vector to another - // - inline Vector4 & operator =( Vector4 vec ); - - // Set the x, y, and z elements of a 4-D vector - // NOTE: - // This function does not change the w element. - // - inline Vector4 & setXYZ( Vector3 vec ); - - // Get the x, y, and z elements of a 4-D vector - // - inline const Vector3 getXYZ( ) const; - - // Set the x element of a 4-D vector - // - inline Vector4 & setX( float x ); - - // Set the y element of a 4-D vector - // - inline Vector4 & setY( float y ); - - // Set the z element of a 4-D vector - // - inline Vector4 & setZ( float z ); - - // Set the w element of a 4-D vector - // - inline Vector4 & setW( float w ); - - // Set the x element of a 4-D vector (scalar data contained in vector data type) - // - inline Vector4 & setX( floatInVec x ); - - // Set the y element of a 4-D vector (scalar data contained in vector data type) - // - inline Vector4 & setY( floatInVec y ); - - // Set the z element of a 4-D vector (scalar data contained in vector data type) - // - inline Vector4 & setZ( floatInVec z ); - - // Set the w element of a 4-D vector (scalar data contained in vector data type) - // - inline Vector4 & setW( floatInVec w ); - - // Get the x element of a 4-D vector - // - inline const floatInVec getX( ) const; - - // Get the y element of a 4-D vector - // - inline const floatInVec getY( ) const; - - // Get the z element of a 4-D vector - // - inline const floatInVec getZ( ) const; - - // Get the w element of a 4-D vector - // - inline const floatInVec getW( ) const; - - // Set an x, y, z, or w element of a 4-D vector by index - // - inline Vector4 & setElem( int idx, float value ); - - // Set an x, y, z, or w element of a 4-D vector by index (scalar data contained in vector data type) - // - inline Vector4 & setElem( int idx, floatInVec value ); - - // Get an x, y, z, or w element of a 4-D vector by index - // - inline const floatInVec getElem( int idx ) const; - - // Subscripting operator to set or get an element - // - inline VecIdx operator []( int idx ); - - // Subscripting operator to get an element - // - inline const floatInVec operator []( int idx ) const; - - // Add two 4-D vectors - // - inline const Vector4 operator +( Vector4 vec ) const; - - // Subtract a 4-D vector from another 4-D vector - // - inline const Vector4 operator -( Vector4 vec ) const; - - // Multiply a 4-D vector by a scalar - // - inline const Vector4 operator *( float scalar ) const; - - // Divide a 4-D vector by a scalar - // - inline const Vector4 operator /( float scalar ) const; - - // Multiply a 4-D vector by a scalar (scalar data contained in vector data type) - // - inline const Vector4 operator *( floatInVec scalar ) const; - - // Divide a 4-D vector by a scalar (scalar data contained in vector data type) - // - inline const Vector4 operator /( floatInVec scalar ) const; - - // Perform compound assignment and addition with a 4-D vector - // - inline Vector4 & operator +=( Vector4 vec ); - - // Perform compound assignment and subtraction by a 4-D vector - // - inline Vector4 & operator -=( Vector4 vec ); - - // Perform compound assignment and multiplication by a scalar - // - inline Vector4 & operator *=( float scalar ); - - // Perform compound assignment and division by a scalar - // - inline Vector4 & operator /=( float scalar ); - - // Perform compound assignment and multiplication by a scalar (scalar data contained in vector data type) - // - inline Vector4 & operator *=( floatInVec scalar ); - - // Perform compound assignment and division by a scalar (scalar data contained in vector data type) - // - inline Vector4 & operator /=( floatInVec scalar ); - - // Negate all elements of a 4-D vector - // - inline const Vector4 operator -( ) const; - - // Construct x axis - // - static inline const Vector4 xAxis( ); - - // Construct y axis - // - static inline const Vector4 yAxis( ); - - // Construct z axis - // - static inline const Vector4 zAxis( ); - - // Construct w axis - // - static inline const Vector4 wAxis( ); - -}; - -// Multiply a 4-D vector by a scalar -// -inline const Vector4 operator *( float scalar, Vector4 vec ); - -// Multiply a 4-D vector by a scalar (scalar data contained in vector data type) -// -inline const Vector4 operator *( floatInVec scalar, Vector4 vec ); - -// Multiply two 4-D vectors per element -// -inline const Vector4 mulPerElem( Vector4 vec0, Vector4 vec1 ); - -// Divide two 4-D vectors per element -// NOTE: -// Floating-point behavior matches standard library function divf4. -// -inline const Vector4 divPerElem( Vector4 vec0, Vector4 vec1 ); - -// Compute the reciprocal of a 4-D vector per element -// NOTE: -// Floating-point behavior matches standard library function recipf4. -// -inline const Vector4 recipPerElem( Vector4 vec ); - -// Compute the square root of a 4-D vector per element -// NOTE: -// Floating-point behavior matches standard library function sqrtf4. -// -inline const Vector4 sqrtPerElem( Vector4 vec ); - -// Compute the reciprocal square root of a 4-D vector per element -// NOTE: -// Floating-point behavior matches standard library function rsqrtf4. -// -inline const Vector4 rsqrtPerElem( Vector4 vec ); - -// Compute the absolute value of a 4-D vector per element -// -inline const Vector4 absPerElem( Vector4 vec ); - -// Copy sign from one 4-D vector to another, per element -// -inline const Vector4 copySignPerElem( Vector4 vec0, Vector4 vec1 ); - -// Maximum of two 4-D vectors per element -// -inline const Vector4 maxPerElem( Vector4 vec0, Vector4 vec1 ); - -// Minimum of two 4-D vectors per element -// -inline const Vector4 minPerElem( Vector4 vec0, Vector4 vec1 ); - -// Maximum element of a 4-D vector -// -inline const floatInVec maxElem( Vector4 vec ); - -// Minimum element of a 4-D vector -// -inline const floatInVec minElem( Vector4 vec ); - -// Compute the sum of all elements of a 4-D vector -// -inline const floatInVec sum( Vector4 vec ); - -// Compute the dot product of two 4-D vectors -// -inline const floatInVec dot( Vector4 vec0, Vector4 vec1 ); - -// Compute the square of the length of a 4-D vector -// -inline const floatInVec lengthSqr( Vector4 vec ); - -// Compute the length of a 4-D vector -// -inline const floatInVec length( Vector4 vec ); - -// Normalize a 4-D vector -// NOTE: -// The result is unpredictable when all elements of vec are at or near zero. -// -inline const Vector4 normalize( Vector4 vec ); - -// Outer product of two 4-D vectors -// -inline const Matrix4 outer( Vector4 vec0, Vector4 vec1 ); - -// Linear interpolation between two 4-D vectors -// NOTE: -// Does not clamp t between 0 and 1. -// -inline const Vector4 lerp( float t, Vector4 vec0, Vector4 vec1 ); - -// Linear interpolation between two 4-D vectors (scalar data contained in vector data type) -// NOTE: -// Does not clamp t between 0 and 1. -// -inline const Vector4 lerp( floatInVec t, Vector4 vec0, Vector4 vec1 ); - -// Spherical linear interpolation between two 4-D vectors -// NOTE: -// The result is unpredictable if the vectors point in opposite directions. -// Does not clamp t between 0 and 1. -// -inline const Vector4 slerp( float t, Vector4 unitVec0, Vector4 unitVec1 ); - -// Spherical linear interpolation between two 4-D vectors (scalar data contained in vector data type) -// NOTE: -// The result is unpredictable if the vectors point in opposite directions. -// Does not clamp t between 0 and 1. -// -inline const Vector4 slerp( floatInVec t, Vector4 unitVec0, Vector4 unitVec1 ); - -// Conditionally select between two 4-D vectors -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// However, the transfer of select1 to a VMX register may use more processing time than a branch. -// Use the boolInVec version for better performance. -// -inline const Vector4 select( Vector4 vec0, Vector4 vec1, bool select1 ); - -// Conditionally select between two 4-D vectors (scalar data contained in vector data type) -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// -inline const Vector4 select( Vector4 vec0, Vector4 vec1, boolInVec select1 ); - -// Store four 4-D vectors as half-floats -// -inline void storeHalfFloats( Vector4 vec0, Vector4 vec1, Vector4 vec2, Vector4 vec3, vec_ushort8 * twoQuads ); - -#ifdef _VECTORMATH_DEBUG - -// Print a 4-D vector -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Vector4 vec ); - -// Print a 4-D vector and an associated string identifier -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Vector4 vec, const char * name ); - -#endif - -// A 3-D point in array-of-structures format -// -class Point3 -{ - vec_float4 mVec128; - -public: - // Default constructor; does no initialization - // - inline Point3( ) { }; - - // Construct a 3-D point from x, y, and z elements - // - inline Point3( float x, float y, float z ); - - // Construct a 3-D point from x, y, and z elements (scalar data contained in vector data type) - // - inline Point3( floatInVec x, floatInVec y, floatInVec z ); - - // Copy elements from a 3-D vector into a 3-D point - // - explicit inline Point3( Vector3 vec ); - - // Set all elements of a 3-D point to the same scalar value - // - explicit inline Point3( float scalar ); - - // Set all elements of a 3-D point to the same scalar value (scalar data contained in vector data type) - // - explicit inline Point3( floatInVec scalar ); - - // Set vector float data in a 3-D point - // - explicit inline Point3( vec_float4 vf4 ); - - // Get vector float data from a 3-D point - // - inline vec_float4 get128( ) const; - - // Assign one 3-D point to another - // - inline Point3 & operator =( Point3 pnt ); - - // Set the x element of a 3-D point - // - inline Point3 & setX( float x ); - - // Set the y element of a 3-D point - // - inline Point3 & setY( float y ); - - // Set the z element of a 3-D point - // - inline Point3 & setZ( float z ); - - // Set the x element of a 3-D point (scalar data contained in vector data type) - // - inline Point3 & setX( floatInVec x ); - - // Set the y element of a 3-D point (scalar data contained in vector data type) - // - inline Point3 & setY( floatInVec y ); - - // Set the z element of a 3-D point (scalar data contained in vector data type) - // - inline Point3 & setZ( floatInVec z ); - - // Get the x element of a 3-D point - // - inline const floatInVec getX( ) const; - - // Get the y element of a 3-D point - // - inline const floatInVec getY( ) const; - - // Get the z element of a 3-D point - // - inline const floatInVec getZ( ) const; - - // Set an x, y, or z element of a 3-D point by index - // - inline Point3 & setElem( int idx, float value ); - - // Set an x, y, or z element of a 3-D point by index (scalar data contained in vector data type) - // - inline Point3 & setElem( int idx, floatInVec value ); - - // Get an x, y, or z element of a 3-D point by index - // - inline const floatInVec getElem( int idx ) const; - - // Subscripting operator to set or get an element - // - inline VecIdx operator []( int idx ); - - // Subscripting operator to get an element - // - inline const floatInVec operator []( int idx ) const; - - // Subtract a 3-D point from another 3-D point - // - inline const Vector3 operator -( Point3 pnt ) const; - - // Add a 3-D point to a 3-D vector - // - inline const Point3 operator +( Vector3 vec ) const; - - // Subtract a 3-D vector from a 3-D point - // - inline const Point3 operator -( Vector3 vec ) const; - - // Perform compound assignment and addition with a 3-D vector - // - inline Point3 & operator +=( Vector3 vec ); - - // Perform compound assignment and subtraction by a 3-D vector - // - inline Point3 & operator -=( Vector3 vec ); - -}; - -// Multiply two 3-D points per element -// -inline const Point3 mulPerElem( Point3 pnt0, Point3 pnt1 ); - -// Divide two 3-D points per element -// NOTE: -// Floating-point behavior matches standard library function divf4. -// -inline const Point3 divPerElem( Point3 pnt0, Point3 pnt1 ); - -// Compute the reciprocal of a 3-D point per element -// NOTE: -// Floating-point behavior matches standard library function recipf4. -// -inline const Point3 recipPerElem( Point3 pnt ); - -// Compute the square root of a 3-D point per element -// NOTE: -// Floating-point behavior matches standard library function sqrtf4. -// -inline const Point3 sqrtPerElem( Point3 pnt ); - -// Compute the reciprocal square root of a 3-D point per element -// NOTE: -// Floating-point behavior matches standard library function rsqrtf4. -// -inline const Point3 rsqrtPerElem( Point3 pnt ); - -// Compute the absolute value of a 3-D point per element -// -inline const Point3 absPerElem( Point3 pnt ); - -// Copy sign from one 3-D point to another, per element -// -inline const Point3 copySignPerElem( Point3 pnt0, Point3 pnt1 ); - -// Maximum of two 3-D points per element -// -inline const Point3 maxPerElem( Point3 pnt0, Point3 pnt1 ); - -// Minimum of two 3-D points per element -// -inline const Point3 minPerElem( Point3 pnt0, Point3 pnt1 ); - -// Maximum element of a 3-D point -// -inline const floatInVec maxElem( Point3 pnt ); - -// Minimum element of a 3-D point -// -inline const floatInVec minElem( Point3 pnt ); - -// Compute the sum of all elements of a 3-D point -// -inline const floatInVec sum( Point3 pnt ); - -// Apply uniform scale to a 3-D point -// -inline const Point3 scale( Point3 pnt, float scaleVal ); - -// Apply uniform scale to a 3-D point (scalar data contained in vector data type) -// -inline const Point3 scale( Point3 pnt, floatInVec scaleVal ); - -// Apply non-uniform scale to a 3-D point -// -inline const Point3 scale( Point3 pnt, Vector3 scaleVec ); - -// Scalar projection of a 3-D point on a unit-length 3-D vector -// -inline const floatInVec projection( Point3 pnt, Vector3 unitVec ); - -// Compute the square of the distance of a 3-D point from the coordinate-system origin -// -inline const floatInVec distSqrFromOrigin( Point3 pnt ); - -// Compute the distance of a 3-D point from the coordinate-system origin -// -inline const floatInVec distFromOrigin( Point3 pnt ); - -// Compute the square of the distance between two 3-D points -// -inline const floatInVec distSqr( Point3 pnt0, Point3 pnt1 ); - -// Compute the distance between two 3-D points -// -inline const floatInVec dist( Point3 pnt0, Point3 pnt1 ); - -// Linear interpolation between two 3-D points -// NOTE: -// Does not clamp t between 0 and 1. -// -inline const Point3 lerp( float t, Point3 pnt0, Point3 pnt1 ); - -// Linear interpolation between two 3-D points (scalar data contained in vector data type) -// NOTE: -// Does not clamp t between 0 and 1. -// -inline const Point3 lerp( floatInVec t, Point3 pnt0, Point3 pnt1 ); - -// Conditionally select between two 3-D points -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// However, the transfer of select1 to a VMX register may use more processing time than a branch. -// Use the boolInVec version for better performance. -// -inline const Point3 select( Point3 pnt0, Point3 pnt1, bool select1 ); - -// Conditionally select between two 3-D points (scalar data contained in vector data type) -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// -inline const Point3 select( Point3 pnt0, Point3 pnt1, boolInVec select1 ); - -// Store x, y, and z elements of a 3-D point in the first three words of a quadword. -// The value of the fourth word (the word with the highest address) remains unchanged -// -inline void storeXYZ( Point3 pnt, vec_float4 * quad ); - -// Load four three-float 3-D points, stored in three quadwords -// -inline void loadXYZArray( Point3 & pnt0, Point3 & pnt1, Point3 & pnt2, Point3 & pnt3, const vec_float4 * threeQuads ); - -// Store four 3-D points in three quadwords -// -inline void storeXYZArray( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, vec_float4 * threeQuads ); - -// Store eight 3-D points as half-floats -// -inline void storeHalfFloats( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, Point3 pnt4, Point3 pnt5, Point3 pnt6, Point3 pnt7, vec_ushort8 * threeQuads ); - -#ifdef _VECTORMATH_DEBUG - -// Print a 3-D point -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Point3 pnt ); - -// Print a 3-D point and an associated string identifier -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Point3 pnt, const char * name ); - -#endif - -// A quaternion in array-of-structures format -// -class Quat -{ - vec_float4 mVec128; - -public: - // Default constructor; does no initialization - // - inline Quat( ) { }; - - // Construct a quaternion from x, y, z, and w elements - // - inline Quat( float x, float y, float z, float w ); - - // Construct a quaternion from x, y, z, and w elements (scalar data contained in vector data type) - // - inline Quat( floatInVec x, floatInVec y, floatInVec z, floatInVec w ); - - // Construct a quaternion from a 3-D vector and a scalar - // - inline Quat( Vector3 xyz, float w ); - - // Construct a quaternion from a 3-D vector and a scalar (scalar data contained in vector data type) - // - inline Quat( Vector3 xyz, floatInVec w ); - - // Copy elements from a 4-D vector into a quaternion - // - explicit inline Quat( Vector4 vec ); - - // Convert a rotation matrix to a unit-length quaternion - // - explicit inline Quat( const Matrix3 & rotMat ); - - // Set all elements of a quaternion to the same scalar value - // - explicit inline Quat( float scalar ); - - // Set all elements of a quaternion to the same scalar value (scalar data contained in vector data type) - // - explicit inline Quat( floatInVec scalar ); - - // Set vector float data in a quaternion - // - explicit inline Quat( vec_float4 vf4 ); - - // Get vector float data from a quaternion - // - inline vec_float4 get128( ) const; - - // Assign one quaternion to another - // - inline Quat & operator =( Quat quat ); - - // Set the x, y, and z elements of a quaternion - // NOTE: - // This function does not change the w element. - // - inline Quat & setXYZ( Vector3 vec ); - - // Get the x, y, and z elements of a quaternion - // - inline const Vector3 getXYZ( ) const; - - // Set the x element of a quaternion - // - inline Quat & setX( float x ); - - // Set the y element of a quaternion - // - inline Quat & setY( float y ); - - // Set the z element of a quaternion - // - inline Quat & setZ( float z ); - - // Set the w element of a quaternion - // - inline Quat & setW( float w ); - - // Set the x element of a quaternion (scalar data contained in vector data type) - // - inline Quat & setX( floatInVec x ); - - // Set the y element of a quaternion (scalar data contained in vector data type) - // - inline Quat & setY( floatInVec y ); - - // Set the z element of a quaternion (scalar data contained in vector data type) - // - inline Quat & setZ( floatInVec z ); - - // Set the w element of a quaternion (scalar data contained in vector data type) - // - inline Quat & setW( floatInVec w ); - - // Get the x element of a quaternion - // - inline const floatInVec getX( ) const; - - // Get the y element of a quaternion - // - inline const floatInVec getY( ) const; - - // Get the z element of a quaternion - // - inline const floatInVec getZ( ) const; - - // Get the w element of a quaternion - // - inline const floatInVec getW( ) const; - - // Set an x, y, z, or w element of a quaternion by index - // - inline Quat & setElem( int idx, float value ); - - // Set an x, y, z, or w element of a quaternion by index (scalar data contained in vector data type) - // - inline Quat & setElem( int idx, floatInVec value ); - - // Get an x, y, z, or w element of a quaternion by index - // - inline const floatInVec getElem( int idx ) const; - - // Subscripting operator to set or get an element - // - inline VecIdx operator []( int idx ); - - // Subscripting operator to get an element - // - inline const floatInVec operator []( int idx ) const; - - // Add two quaternions - // - inline const Quat operator +( Quat quat ) const; - - // Subtract a quaternion from another quaternion - // - inline const Quat operator -( Quat quat ) const; - - // Multiply two quaternions - // - inline const Quat operator *( Quat quat ) const; - - // Multiply a quaternion by a scalar - // - inline const Quat operator *( float scalar ) const; - - // Divide a quaternion by a scalar - // - inline const Quat operator /( float scalar ) const; - - // Multiply a quaternion by a scalar (scalar data contained in vector data type) - // - inline const Quat operator *( floatInVec scalar ) const; - - // Divide a quaternion by a scalar (scalar data contained in vector data type) - // - inline const Quat operator /( floatInVec scalar ) const; - - // Perform compound assignment and addition with a quaternion - // - inline Quat & operator +=( Quat quat ); - - // Perform compound assignment and subtraction by a quaternion - // - inline Quat & operator -=( Quat quat ); - - // Perform compound assignment and multiplication by a quaternion - // - inline Quat & operator *=( Quat quat ); - - // Perform compound assignment and multiplication by a scalar - // - inline Quat & operator *=( float scalar ); - - // Perform compound assignment and division by a scalar - // - inline Quat & operator /=( float scalar ); - - // Perform compound assignment and multiplication by a scalar (scalar data contained in vector data type) - // - inline Quat & operator *=( floatInVec scalar ); - - // Perform compound assignment and division by a scalar (scalar data contained in vector data type) - // - inline Quat & operator /=( floatInVec scalar ); - - // Negate all elements of a quaternion - // - inline const Quat operator -( ) const; - - // Construct an identity quaternion - // - static inline const Quat identity( ); - - // Construct a quaternion to rotate between two unit-length 3-D vectors - // NOTE: - // The result is unpredictable if unitVec0 and unitVec1 point in opposite directions. - // - static inline const Quat rotation( Vector3 unitVec0, Vector3 unitVec1 ); - - // Construct a quaternion to rotate around a unit-length 3-D vector - // - static inline const Quat rotation( float radians, Vector3 unitVec ); - - // Construct a quaternion to rotate around a unit-length 3-D vector (scalar data contained in vector data type) - // - static inline const Quat rotation( floatInVec radians, Vector3 unitVec ); - - // Construct a quaternion to rotate around the x axis - // - static inline const Quat rotationX( float radians ); - - // Construct a quaternion to rotate around the y axis - // - static inline const Quat rotationY( float radians ); - - // Construct a quaternion to rotate around the z axis - // - static inline const Quat rotationZ( float radians ); - - // Construct a quaternion to rotate around the x axis (scalar data contained in vector data type) - // - static inline const Quat rotationX( floatInVec radians ); - - // Construct a quaternion to rotate around the y axis (scalar data contained in vector data type) - // - static inline const Quat rotationY( floatInVec radians ); - - // Construct a quaternion to rotate around the z axis (scalar data contained in vector data type) - // - static inline const Quat rotationZ( floatInVec radians ); - -}; - -// Multiply a quaternion by a scalar -// -inline const Quat operator *( float scalar, Quat quat ); - -// Multiply a quaternion by a scalar (scalar data contained in vector data type) -// -inline const Quat operator *( floatInVec scalar, Quat quat ); - -// Compute the conjugate of a quaternion -// -inline const Quat conj( Quat quat ); - -// Use a unit-length quaternion to rotate a 3-D vector -// -inline const Vector3 rotate( Quat unitQuat, Vector3 vec ); - -// Compute the dot product of two quaternions -// -inline const floatInVec dot( Quat quat0, Quat quat1 ); - -// Compute the norm of a quaternion -// -inline const floatInVec norm( Quat quat ); - -// Compute the length of a quaternion -// -inline const floatInVec length( Quat quat ); - -// Normalize a quaternion -// NOTE: -// The result is unpredictable when all elements of quat are at or near zero. -// -inline const Quat normalize( Quat quat ); - -// Linear interpolation between two quaternions -// NOTE: -// Does not clamp t between 0 and 1. -// -inline const Quat lerp( float t, Quat quat0, Quat quat1 ); - -// Linear interpolation between two quaternions (scalar data contained in vector data type) -// NOTE: -// Does not clamp t between 0 and 1. -// -inline const Quat lerp( floatInVec t, Quat quat0, Quat quat1 ); - -// Spherical linear interpolation between two quaternions -// NOTE: -// Interpolates along the shortest path between orientations. -// Does not clamp t between 0 and 1. -// -inline const Quat slerp( float t, Quat unitQuat0, Quat unitQuat1 ); - -// Spherical linear interpolation between two quaternions (scalar data contained in vector data type) -// NOTE: -// Interpolates along the shortest path between orientations. -// Does not clamp t between 0 and 1. -// -inline const Quat slerp( floatInVec t, Quat unitQuat0, Quat unitQuat1 ); - -// Spherical quadrangle interpolation -// -inline const Quat squad( float t, Quat unitQuat0, Quat unitQuat1, Quat unitQuat2, Quat unitQuat3 ); - -// Spherical quadrangle interpolation (scalar data contained in vector data type) -// -inline const Quat squad( floatInVec t, Quat unitQuat0, Quat unitQuat1, Quat unitQuat2, Quat unitQuat3 ); - -// Conditionally select between two quaternions -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// However, the transfer of select1 to a VMX register may use more processing time than a branch. -// Use the boolInVec version for better performance. -// -inline const Quat select( Quat quat0, Quat quat1, bool select1 ); - -// Conditionally select between two quaternions (scalar data contained in vector data type) -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// -inline const Quat select( Quat quat0, Quat quat1, boolInVec select1 ); - -#ifdef _VECTORMATH_DEBUG - -// Print a quaternion -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Quat quat ); - -// Print a quaternion and an associated string identifier -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Quat quat, const char * name ); - -#endif - -// A 3x3 matrix in array-of-structures format -// -class Matrix3 -{ - Vector3 mCol0; - Vector3 mCol1; - Vector3 mCol2; - -public: - // Default constructor; does no initialization - // - inline Matrix3( ) { }; - - // Copy a 3x3 matrix - // - inline Matrix3( const Matrix3 & mat ); - - // Construct a 3x3 matrix containing the specified columns - // - inline Matrix3( Vector3 col0, Vector3 col1, Vector3 col2 ); - - // Construct a 3x3 rotation matrix from a unit-length quaternion - // - explicit inline Matrix3( Quat unitQuat ); - - // Set all elements of a 3x3 matrix to the same scalar value - // - explicit inline Matrix3( float scalar ); - - // Set all elements of a 3x3 matrix to the same scalar value (scalar data contained in vector data type) - // - explicit inline Matrix3( floatInVec scalar ); - - // Assign one 3x3 matrix to another - // - inline Matrix3 & operator =( const Matrix3 & mat ); - - // Set column 0 of a 3x3 matrix - // - inline Matrix3 & setCol0( Vector3 col0 ); - - // Set column 1 of a 3x3 matrix - // - inline Matrix3 & setCol1( Vector3 col1 ); - - // Set column 2 of a 3x3 matrix - // - inline Matrix3 & setCol2( Vector3 col2 ); - - // Get column 0 of a 3x3 matrix - // - inline const Vector3 getCol0( ) const; - - // Get column 1 of a 3x3 matrix - // - inline const Vector3 getCol1( ) const; - - // Get column 2 of a 3x3 matrix - // - inline const Vector3 getCol2( ) const; - - // Set the column of a 3x3 matrix referred to by the specified index - // - inline Matrix3 & setCol( int col, Vector3 vec ); - - // Set the row of a 3x3 matrix referred to by the specified index - // - inline Matrix3 & setRow( int row, Vector3 vec ); - - // Get the column of a 3x3 matrix referred to by the specified index - // - inline const Vector3 getCol( int col ) const; - - // Get the row of a 3x3 matrix referred to by the specified index - // - inline const Vector3 getRow( int row ) const; - - // Subscripting operator to set or get a column - // - inline Vector3 & operator []( int col ); - - // Subscripting operator to get a column - // - inline const Vector3 operator []( int col ) const; - - // Set the element of a 3x3 matrix referred to by column and row indices - // - inline Matrix3 & setElem( int col, int row, float val ); - - // Set the element of a 3x3 matrix referred to by column and row indices (scalar data contained in vector data type) - // - inline Matrix3 & setElem( int col, int row, floatInVec val ); - - // Get the element of a 3x3 matrix referred to by column and row indices - // - inline const floatInVec getElem( int col, int row ) const; - - // Add two 3x3 matrices - // - inline const Matrix3 operator +( const Matrix3 & mat ) const; - - // Subtract a 3x3 matrix from another 3x3 matrix - // - inline const Matrix3 operator -( const Matrix3 & mat ) const; - - // Negate all elements of a 3x3 matrix - // - inline const Matrix3 operator -( ) const; - - // Multiply a 3x3 matrix by a scalar - // - inline const Matrix3 operator *( float scalar ) const; - - // Multiply a 3x3 matrix by a scalar (scalar data contained in vector data type) - // - inline const Matrix3 operator *( floatInVec scalar ) const; - - // Multiply a 3x3 matrix by a 3-D vector - // - inline const Vector3 operator *( Vector3 vec ) const; - - // Multiply two 3x3 matrices - // - inline const Matrix3 operator *( const Matrix3 & mat ) const; - - // Perform compound assignment and addition with a 3x3 matrix - // - inline Matrix3 & operator +=( const Matrix3 & mat ); - - // Perform compound assignment and subtraction by a 3x3 matrix - // - inline Matrix3 & operator -=( const Matrix3 & mat ); - - // Perform compound assignment and multiplication by a scalar - // - inline Matrix3 & operator *=( float scalar ); - - // Perform compound assignment and multiplication by a scalar (scalar data contained in vector data type) - // - inline Matrix3 & operator *=( floatInVec scalar ); - - // Perform compound assignment and multiplication by a 3x3 matrix - // - inline Matrix3 & operator *=( const Matrix3 & mat ); - - // Construct an identity 3x3 matrix - // - static inline const Matrix3 identity( ); - - // Construct a 3x3 matrix to rotate around the x axis - // - static inline const Matrix3 rotationX( float radians ); - - // Construct a 3x3 matrix to rotate around the y axis - // - static inline const Matrix3 rotationY( float radians ); - - // Construct a 3x3 matrix to rotate around the z axis - // - static inline const Matrix3 rotationZ( float radians ); - - // Construct a 3x3 matrix to rotate around the x axis (scalar data contained in vector data type) - // - static inline const Matrix3 rotationX( floatInVec radians ); - - // Construct a 3x3 matrix to rotate around the y axis (scalar data contained in vector data type) - // - static inline const Matrix3 rotationY( floatInVec radians ); - - // Construct a 3x3 matrix to rotate around the z axis (scalar data contained in vector data type) - // - static inline const Matrix3 rotationZ( floatInVec radians ); - - // Construct a 3x3 matrix to rotate around the x, y, and z axes - // - static inline const Matrix3 rotationZYX( Vector3 radiansXYZ ); - - // Construct a 3x3 matrix to rotate around a unit-length 3-D vector - // - static inline const Matrix3 rotation( float radians, Vector3 unitVec ); - - // Construct a 3x3 matrix to rotate around a unit-length 3-D vector (scalar data contained in vector data type) - // - static inline const Matrix3 rotation( floatInVec radians, Vector3 unitVec ); - - // Construct a rotation matrix from a unit-length quaternion - // - static inline const Matrix3 rotation( Quat unitQuat ); - - // Construct a 3x3 matrix to perform scaling - // - static inline const Matrix3 scale( Vector3 scaleVec ); - -}; -// Multiply a 3x3 matrix by a scalar -// -inline const Matrix3 operator *( float scalar, const Matrix3 & mat ); - -// Multiply a 3x3 matrix by a scalar (scalar data contained in vector data type) -// -inline const Matrix3 operator *( floatInVec scalar, const Matrix3 & mat ); - -// Append (post-multiply) a scale transformation to a 3x3 matrix -// NOTE: -// Faster than creating and multiplying a scale transformation matrix. -// -inline const Matrix3 appendScale( const Matrix3 & mat, Vector3 scaleVec ); - -// Prepend (pre-multiply) a scale transformation to a 3x3 matrix -// NOTE: -// Faster than creating and multiplying a scale transformation matrix. -// -inline const Matrix3 prependScale( Vector3 scaleVec, const Matrix3 & mat ); - -// Multiply two 3x3 matrices per element -// -inline const Matrix3 mulPerElem( const Matrix3 & mat0, const Matrix3 & mat1 ); - -// Compute the absolute value of a 3x3 matrix per element -// -inline const Matrix3 absPerElem( const Matrix3 & mat ); - -// Transpose of a 3x3 matrix -// -inline const Matrix3 transpose( const Matrix3 & mat ); - -// Compute the inverse of a 3x3 matrix -// NOTE: -// Result is unpredictable when the determinant of mat is equal to or near 0. -// -inline const Matrix3 inverse( const Matrix3 & mat ); - -// Determinant of a 3x3 matrix -// -inline const floatInVec determinant( const Matrix3 & mat ); - -// Conditionally select between two 3x3 matrices -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// However, the transfer of select1 to a VMX register may use more processing time than a branch. -// Use the boolInVec version for better performance. -// -inline const Matrix3 select( const Matrix3 & mat0, const Matrix3 & mat1, bool select1 ); - -// Conditionally select between two 3x3 matrices (scalar data contained in vector data type) -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// -inline const Matrix3 select( const Matrix3 & mat0, const Matrix3 & mat1, boolInVec select1 ); - -#ifdef _VECTORMATH_DEBUG - -// Print a 3x3 matrix -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( const Matrix3 & mat ); - -// Print a 3x3 matrix and an associated string identifier -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( const Matrix3 & mat, const char * name ); - -#endif - -// A 4x4 matrix in array-of-structures format -// -class Matrix4 -{ - Vector4 mCol0; - Vector4 mCol1; - Vector4 mCol2; - Vector4 mCol3; - -public: - // Default constructor; does no initialization - // - inline Matrix4( ) { }; - - // Copy a 4x4 matrix - // - inline Matrix4( const Matrix4 & mat ); - - // Construct a 4x4 matrix containing the specified columns - // - inline Matrix4( Vector4 col0, Vector4 col1, Vector4 col2, Vector4 col3 ); - - // Construct a 4x4 matrix from a 3x4 transformation matrix - // - explicit inline Matrix4( const Transform3 & mat ); - - // Construct a 4x4 matrix from a 3x3 matrix and a 3-D vector - // - inline Matrix4( const Matrix3 & mat, Vector3 translateVec ); - - // Construct a 4x4 matrix from a unit-length quaternion and a 3-D vector - // - inline Matrix4( Quat unitQuat, Vector3 translateVec ); - - // Set all elements of a 4x4 matrix to the same scalar value - // - explicit inline Matrix4( float scalar ); - - // Set all elements of a 4x4 matrix to the same scalar value (scalar data contained in vector data type) - // - explicit inline Matrix4( floatInVec scalar ); - - // Assign one 4x4 matrix to another - // - inline Matrix4 & operator =( const Matrix4 & mat ); - - // Set the upper-left 3x3 submatrix - // NOTE: - // This function does not change the bottom row elements. - // - inline Matrix4 & setUpper3x3( const Matrix3 & mat3 ); - - // Get the upper-left 3x3 submatrix of a 4x4 matrix - // - inline const Matrix3 getUpper3x3( ) const; - - // Set translation component - // NOTE: - // This function does not change the bottom row elements. - // - inline Matrix4 & setTranslation( Vector3 translateVec ); - - // Get the translation component of a 4x4 matrix - // - inline const Vector3 getTranslation( ) const; - - // Set column 0 of a 4x4 matrix - // - inline Matrix4 & setCol0( Vector4 col0 ); - - // Set column 1 of a 4x4 matrix - // - inline Matrix4 & setCol1( Vector4 col1 ); - - // Set column 2 of a 4x4 matrix - // - inline Matrix4 & setCol2( Vector4 col2 ); - - // Set column 3 of a 4x4 matrix - // - inline Matrix4 & setCol3( Vector4 col3 ); - - // Get column 0 of a 4x4 matrix - // - inline const Vector4 getCol0( ) const; - - // Get column 1 of a 4x4 matrix - // - inline const Vector4 getCol1( ) const; - - // Get column 2 of a 4x4 matrix - // - inline const Vector4 getCol2( ) const; - - // Get column 3 of a 4x4 matrix - // - inline const Vector4 getCol3( ) const; - - // Set the column of a 4x4 matrix referred to by the specified index - // - inline Matrix4 & setCol( int col, Vector4 vec ); - - // Set the row of a 4x4 matrix referred to by the specified index - // - inline Matrix4 & setRow( int row, Vector4 vec ); - - // Get the column of a 4x4 matrix referred to by the specified index - // - inline const Vector4 getCol( int col ) const; - - // Get the row of a 4x4 matrix referred to by the specified index - // - inline const Vector4 getRow( int row ) const; - - // Subscripting operator to set or get a column - // - inline Vector4 & operator []( int col ); - - // Subscripting operator to get a column - // - inline const Vector4 operator []( int col ) const; - - // Set the element of a 4x4 matrix referred to by column and row indices - // - inline Matrix4 & setElem( int col, int row, float val ); - - // Set the element of a 4x4 matrix referred to by column and row indices (scalar data contained in vector data type) - // - inline Matrix4 & setElem( int col, int row, floatInVec val ); - - // Get the element of a 4x4 matrix referred to by column and row indices - // - inline const floatInVec getElem( int col, int row ) const; - - // Add two 4x4 matrices - // - inline const Matrix4 operator +( const Matrix4 & mat ) const; - - // Subtract a 4x4 matrix from another 4x4 matrix - // - inline const Matrix4 operator -( const Matrix4 & mat ) const; - - // Negate all elements of a 4x4 matrix - // - inline const Matrix4 operator -( ) const; - - // Multiply a 4x4 matrix by a scalar - // - inline const Matrix4 operator *( float scalar ) const; - - // Multiply a 4x4 matrix by a scalar (scalar data contained in vector data type) - // - inline const Matrix4 operator *( floatInVec scalar ) const; - - // Multiply a 4x4 matrix by a 4-D vector - // - inline const Vector4 operator *( Vector4 vec ) const; - - // Multiply a 4x4 matrix by a 3-D vector - // - inline const Vector4 operator *( Vector3 vec ) const; - - // Multiply a 4x4 matrix by a 3-D point - // - inline const Vector4 operator *( Point3 pnt ) const; - - // Multiply two 4x4 matrices - // - inline const Matrix4 operator *( const Matrix4 & mat ) const; - - // Multiply a 4x4 matrix by a 3x4 transformation matrix - // - inline const Matrix4 operator *( const Transform3 & tfrm ) const; - - // Perform compound assignment and addition with a 4x4 matrix - // - inline Matrix4 & operator +=( const Matrix4 & mat ); - - // Perform compound assignment and subtraction by a 4x4 matrix - // - inline Matrix4 & operator -=( const Matrix4 & mat ); - - // Perform compound assignment and multiplication by a scalar - // - inline Matrix4 & operator *=( float scalar ); - - // Perform compound assignment and multiplication by a scalar (scalar data contained in vector data type) - // - inline Matrix4 & operator *=( floatInVec scalar ); - - // Perform compound assignment and multiplication by a 4x4 matrix - // - inline Matrix4 & operator *=( const Matrix4 & mat ); - - // Perform compound assignment and multiplication by a 3x4 transformation matrix - // - inline Matrix4 & operator *=( const Transform3 & tfrm ); - - // Construct an identity 4x4 matrix - // - static inline const Matrix4 identity( ); - - // Construct a 4x4 matrix to rotate around the x axis - // - static inline const Matrix4 rotationX( float radians ); - - // Construct a 4x4 matrix to rotate around the y axis - // - static inline const Matrix4 rotationY( float radians ); - - // Construct a 4x4 matrix to rotate around the z axis - // - static inline const Matrix4 rotationZ( float radians ); - - // Construct a 4x4 matrix to rotate around the x axis (scalar data contained in vector data type) - // - static inline const Matrix4 rotationX( floatInVec radians ); - - // Construct a 4x4 matrix to rotate around the y axis (scalar data contained in vector data type) - // - static inline const Matrix4 rotationY( floatInVec radians ); - - // Construct a 4x4 matrix to rotate around the z axis (scalar data contained in vector data type) - // - static inline const Matrix4 rotationZ( floatInVec radians ); - - // Construct a 4x4 matrix to rotate around the x, y, and z axes - // - static inline const Matrix4 rotationZYX( Vector3 radiansXYZ ); - - // Construct a 4x4 matrix to rotate around a unit-length 3-D vector - // - static inline const Matrix4 rotation( float radians, Vector3 unitVec ); - - // Construct a 4x4 matrix to rotate around a unit-length 3-D vector (scalar data contained in vector data type) - // - static inline const Matrix4 rotation( floatInVec radians, Vector3 unitVec ); - - // Construct a rotation matrix from a unit-length quaternion - // - static inline const Matrix4 rotation( Quat unitQuat ); - - // Construct a 4x4 matrix to perform scaling - // - static inline const Matrix4 scale( Vector3 scaleVec ); - - // Construct a 4x4 matrix to perform translation - // - static inline const Matrix4 translation( Vector3 translateVec ); - - // Construct viewing matrix based on eye position, position looked at, and up direction - // - static inline const Matrix4 lookAt( Point3 eyePos, Point3 lookAtPos, Vector3 upVec ); - - // Construct a perspective projection matrix - // - static inline const Matrix4 perspective( float fovyRadians, float aspect, float zNear, float zFar ); - - // Construct a perspective projection matrix based on frustum - // - static inline const Matrix4 frustum( float left, float right, float bottom, float top, float zNear, float zFar ); - - // Construct an orthographic projection matrix - // - static inline const Matrix4 orthographic( float left, float right, float bottom, float top, float zNear, float zFar ); - -}; -// Multiply a 4x4 matrix by a scalar -// -inline const Matrix4 operator *( float scalar, const Matrix4 & mat ); - -// Multiply a 4x4 matrix by a scalar (scalar data contained in vector data type) -// -inline const Matrix4 operator *( floatInVec scalar, const Matrix4 & mat ); - -// Append (post-multiply) a scale transformation to a 4x4 matrix -// NOTE: -// Faster than creating and multiplying a scale transformation matrix. -// -inline const Matrix4 appendScale( const Matrix4 & mat, Vector3 scaleVec ); - -// Prepend (pre-multiply) a scale transformation to a 4x4 matrix -// NOTE: -// Faster than creating and multiplying a scale transformation matrix. -// -inline const Matrix4 prependScale( Vector3 scaleVec, const Matrix4 & mat ); - -// Multiply two 4x4 matrices per element -// -inline const Matrix4 mulPerElem( const Matrix4 & mat0, const Matrix4 & mat1 ); - -// Compute the absolute value of a 4x4 matrix per element -// -inline const Matrix4 absPerElem( const Matrix4 & mat ); - -// Transpose of a 4x4 matrix -// -inline const Matrix4 transpose( const Matrix4 & mat ); - -// Compute the inverse of a 4x4 matrix -// NOTE: -// Result is unpredictable when the determinant of mat is equal to or near 0. -// -inline const Matrix4 inverse( const Matrix4 & mat ); - -// Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix -// NOTE: -// This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. The result is unpredictable when the determinant of mat is equal to or near 0. -// -inline const Matrix4 affineInverse( const Matrix4 & mat ); - -// Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix with an orthogonal upper-left 3x3 submatrix -// NOTE: -// This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. -// -inline const Matrix4 orthoInverse( const Matrix4 & mat ); - -// Determinant of a 4x4 matrix -// -inline const floatInVec determinant( const Matrix4 & mat ); - -// Conditionally select between two 4x4 matrices -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// However, the transfer of select1 to a VMX register may use more processing time than a branch. -// Use the boolInVec version for better performance. -// -inline const Matrix4 select( const Matrix4 & mat0, const Matrix4 & mat1, bool select1 ); - -// Conditionally select between two 4x4 matrices (scalar data contained in vector data type) -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// -inline const Matrix4 select( const Matrix4 & mat0, const Matrix4 & mat1, boolInVec select1 ); - -#ifdef _VECTORMATH_DEBUG - -// Print a 4x4 matrix -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( const Matrix4 & mat ); - -// Print a 4x4 matrix and an associated string identifier -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( const Matrix4 & mat, const char * name ); - -#endif - -// A 3x4 transformation matrix in array-of-structures format -// -class Transform3 -{ - Vector3 mCol0; - Vector3 mCol1; - Vector3 mCol2; - Vector3 mCol3; - -public: - // Default constructor; does no initialization - // - inline Transform3( ) { }; - - // Copy a 3x4 transformation matrix - // - inline Transform3( const Transform3 & tfrm ); - - // Construct a 3x4 transformation matrix containing the specified columns - // - inline Transform3( Vector3 col0, Vector3 col1, Vector3 col2, Vector3 col3 ); - - // Construct a 3x4 transformation matrix from a 3x3 matrix and a 3-D vector - // - inline Transform3( const Matrix3 & tfrm, Vector3 translateVec ); - - // Construct a 3x4 transformation matrix from a unit-length quaternion and a 3-D vector - // - inline Transform3( Quat unitQuat, Vector3 translateVec ); - - // Set all elements of a 3x4 transformation matrix to the same scalar value - // - explicit inline Transform3( float scalar ); - - // Set all elements of a 3x4 transformation matrix to the same scalar value (scalar data contained in vector data type) - // - explicit inline Transform3( floatInVec scalar ); - - // Assign one 3x4 transformation matrix to another - // - inline Transform3 & operator =( const Transform3 & tfrm ); - - // Set the upper-left 3x3 submatrix - // - inline Transform3 & setUpper3x3( const Matrix3 & mat3 ); - - // Get the upper-left 3x3 submatrix of a 3x4 transformation matrix - // - inline const Matrix3 getUpper3x3( ) const; - - // Set translation component - // - inline Transform3 & setTranslation( Vector3 translateVec ); - - // Get the translation component of a 3x4 transformation matrix - // - inline const Vector3 getTranslation( ) const; - - // Set column 0 of a 3x4 transformation matrix - // - inline Transform3 & setCol0( Vector3 col0 ); - - // Set column 1 of a 3x4 transformation matrix - // - inline Transform3 & setCol1( Vector3 col1 ); - - // Set column 2 of a 3x4 transformation matrix - // - inline Transform3 & setCol2( Vector3 col2 ); - - // Set column 3 of a 3x4 transformation matrix - // - inline Transform3 & setCol3( Vector3 col3 ); - - // Get column 0 of a 3x4 transformation matrix - // - inline const Vector3 getCol0( ) const; - - // Get column 1 of a 3x4 transformation matrix - // - inline const Vector3 getCol1( ) const; - - // Get column 2 of a 3x4 transformation matrix - // - inline const Vector3 getCol2( ) const; - - // Get column 3 of a 3x4 transformation matrix - // - inline const Vector3 getCol3( ) const; - - // Set the column of a 3x4 transformation matrix referred to by the specified index - // - inline Transform3 & setCol( int col, Vector3 vec ); - - // Set the row of a 3x4 transformation matrix referred to by the specified index - // - inline Transform3 & setRow( int row, Vector4 vec ); - - // Get the column of a 3x4 transformation matrix referred to by the specified index - // - inline const Vector3 getCol( int col ) const; - - // Get the row of a 3x4 transformation matrix referred to by the specified index - // - inline const Vector4 getRow( int row ) const; - - // Subscripting operator to set or get a column - // - inline Vector3 & operator []( int col ); - - // Subscripting operator to get a column - // - inline const Vector3 operator []( int col ) const; - - // Set the element of a 3x4 transformation matrix referred to by column and row indices - // - inline Transform3 & setElem( int col, int row, float val ); - - // Set the element of a 3x4 transformation matrix referred to by column and row indices (scalar data contained in vector data type) - // - inline Transform3 & setElem( int col, int row, floatInVec val ); - - // Get the element of a 3x4 transformation matrix referred to by column and row indices - // - inline const floatInVec getElem( int col, int row ) const; - - // Multiply a 3x4 transformation matrix by a 3-D vector - // - inline const Vector3 operator *( Vector3 vec ) const; - - // Multiply a 3x4 transformation matrix by a 3-D point - // - inline const Point3 operator *( Point3 pnt ) const; - - // Multiply two 3x4 transformation matrices - // - inline const Transform3 operator *( const Transform3 & tfrm ) const; - - // Perform compound assignment and multiplication by a 3x4 transformation matrix - // - inline Transform3 & operator *=( const Transform3 & tfrm ); - - // Construct an identity 3x4 transformation matrix - // - static inline const Transform3 identity( ); - - // Construct a 3x4 transformation matrix to rotate around the x axis - // - static inline const Transform3 rotationX( float radians ); - - // Construct a 3x4 transformation matrix to rotate around the y axis - // - static inline const Transform3 rotationY( float radians ); - - // Construct a 3x4 transformation matrix to rotate around the z axis - // - static inline const Transform3 rotationZ( float radians ); - - // Construct a 3x4 transformation matrix to rotate around the x axis (scalar data contained in vector data type) - // - static inline const Transform3 rotationX( floatInVec radians ); - - // Construct a 3x4 transformation matrix to rotate around the y axis (scalar data contained in vector data type) - // - static inline const Transform3 rotationY( floatInVec radians ); - - // Construct a 3x4 transformation matrix to rotate around the z axis (scalar data contained in vector data type) - // - static inline const Transform3 rotationZ( floatInVec radians ); - - // Construct a 3x4 transformation matrix to rotate around the x, y, and z axes - // - static inline const Transform3 rotationZYX( Vector3 radiansXYZ ); - - // Construct a 3x4 transformation matrix to rotate around a unit-length 3-D vector - // - static inline const Transform3 rotation( float radians, Vector3 unitVec ); - - // Construct a 3x4 transformation matrix to rotate around a unit-length 3-D vector (scalar data contained in vector data type) - // - static inline const Transform3 rotation( floatInVec radians, Vector3 unitVec ); - - // Construct a rotation matrix from a unit-length quaternion - // - static inline const Transform3 rotation( Quat unitQuat ); - - // Construct a 3x4 transformation matrix to perform scaling - // - static inline const Transform3 scale( Vector3 scaleVec ); - - // Construct a 3x4 transformation matrix to perform translation - // - static inline const Transform3 translation( Vector3 translateVec ); - -}; -// Append (post-multiply) a scale transformation to a 3x4 transformation matrix -// NOTE: -// Faster than creating and multiplying a scale transformation matrix. -// -inline const Transform3 appendScale( const Transform3 & tfrm, Vector3 scaleVec ); - -// Prepend (pre-multiply) a scale transformation to a 3x4 transformation matrix -// NOTE: -// Faster than creating and multiplying a scale transformation matrix. -// -inline const Transform3 prependScale( Vector3 scaleVec, const Transform3 & tfrm ); - -// Multiply two 3x4 transformation matrices per element -// -inline const Transform3 mulPerElem( const Transform3 & tfrm0, const Transform3 & tfrm1 ); - -// Compute the absolute value of a 3x4 transformation matrix per element -// -inline const Transform3 absPerElem( const Transform3 & tfrm ); - -// Inverse of a 3x4 transformation matrix -// NOTE: -// Result is unpredictable when the determinant of the left 3x3 submatrix is equal to or near 0. -// -inline const Transform3 inverse( const Transform3 & tfrm ); - -// Compute the inverse of a 3x4 transformation matrix, expected to have an orthogonal upper-left 3x3 submatrix -// NOTE: -// This can be used to achieve better performance than a general inverse when the specified 3x4 transformation matrix meets the given restrictions. -// -inline const Transform3 orthoInverse( const Transform3 & tfrm ); - -// Conditionally select between two 3x4 transformation matrices -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// However, the transfer of select1 to a VMX register may use more processing time than a branch. -// Use the boolInVec version for better performance. -// -inline const Transform3 select( const Transform3 & tfrm0, const Transform3 & tfrm1, bool select1 ); - -// Conditionally select between two 3x4 transformation matrices (scalar data contained in vector data type) -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// -inline const Transform3 select( const Transform3 & tfrm0, const Transform3 & tfrm1, boolInVec select1 ); - -#ifdef _VECTORMATH_DEBUG - -// Print a 3x4 transformation matrix -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( const Transform3 & tfrm ); - -// Print a 3x4 transformation matrix and an associated string identifier -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( const Transform3 & tfrm, const char * name ); - -#endif - -} // namespace Aos -} // namespace Vectormath - -#include "vec_aos.h" -#include "quat_aos.h" -#include "mat_aos.h" - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_AOS_CPP_PPU_H +#define _VECTORMATH_AOS_CPP_PPU_H + +#include +#include +#include "vecidx_aos.h" +#include "floatInVec.h" +#include "boolInVec.h" + +#ifdef _VECTORMATH_DEBUG +#include +#endif + +namespace Vectormath { + +namespace Aos { + +//----------------------------------------------------------------------------- +// Forward Declarations +// + +class Vector3; +class Vector4; +class Point3; +class Quat; +class Matrix3; +class Matrix4; +class Transform3; + +// A 3-D vector in array-of-structures format +// +class Vector3 +{ + vec_float4 mVec128; + +public: + // Default constructor; does no initialization + // + inline Vector3( ) { }; + + // Construct a 3-D vector from x, y, and z elements + // + inline Vector3( float x, float y, float z ); + + // Construct a 3-D vector from x, y, and z elements (scalar data contained in vector data type) + // + inline Vector3( floatInVec x, floatInVec y, floatInVec z ); + + // Copy elements from a 3-D point into a 3-D vector + // + explicit inline Vector3( Point3 pnt ); + + // Set all elements of a 3-D vector to the same scalar value + // + explicit inline Vector3( float scalar ); + + // Set all elements of a 3-D vector to the same scalar value (scalar data contained in vector data type) + // + explicit inline Vector3( floatInVec scalar ); + + // Set vector float data in a 3-D vector + // + explicit inline Vector3( vec_float4 vf4 ); + + // Get vector float data from a 3-D vector + // + inline vec_float4 get128( ) const; + + // Assign one 3-D vector to another + // + inline Vector3 & operator =( Vector3 vec ); + + // Set the x element of a 3-D vector + // + inline Vector3 & setX( float x ); + + // Set the y element of a 3-D vector + // + inline Vector3 & setY( float y ); + + // Set the z element of a 3-D vector + // + inline Vector3 & setZ( float z ); + + // Set the x element of a 3-D vector (scalar data contained in vector data type) + // + inline Vector3 & setX( floatInVec x ); + + // Set the y element of a 3-D vector (scalar data contained in vector data type) + // + inline Vector3 & setY( floatInVec y ); + + // Set the z element of a 3-D vector (scalar data contained in vector data type) + // + inline Vector3 & setZ( floatInVec z ); + + // Get the x element of a 3-D vector + // + inline const floatInVec getX( ) const; + + // Get the y element of a 3-D vector + // + inline const floatInVec getY( ) const; + + // Get the z element of a 3-D vector + // + inline const floatInVec getZ( ) const; + + // Set an x, y, or z element of a 3-D vector by index + // + inline Vector3 & setElem( int idx, float value ); + + // Set an x, y, or z element of a 3-D vector by index (scalar data contained in vector data type) + // + inline Vector3 & setElem( int idx, floatInVec value ); + + // Get an x, y, or z element of a 3-D vector by index + // + inline const floatInVec getElem( int idx ) const; + + // Subscripting operator to set or get an element + // + inline VecIdx operator []( int idx ); + + // Subscripting operator to get an element + // + inline const floatInVec operator []( int idx ) const; + + // Add two 3-D vectors + // + inline const Vector3 operator +( Vector3 vec ) const; + + // Subtract a 3-D vector from another 3-D vector + // + inline const Vector3 operator -( Vector3 vec ) const; + + // Add a 3-D vector to a 3-D point + // + inline const Point3 operator +( Point3 pnt ) const; + + // Multiply a 3-D vector by a scalar + // + inline const Vector3 operator *( float scalar ) const; + + // Divide a 3-D vector by a scalar + // + inline const Vector3 operator /( float scalar ) const; + + // Multiply a 3-D vector by a scalar (scalar data contained in vector data type) + // + inline const Vector3 operator *( floatInVec scalar ) const; + + // Divide a 3-D vector by a scalar (scalar data contained in vector data type) + // + inline const Vector3 operator /( floatInVec scalar ) const; + + // Perform compound assignment and addition with a 3-D vector + // + inline Vector3 & operator +=( Vector3 vec ); + + // Perform compound assignment and subtraction by a 3-D vector + // + inline Vector3 & operator -=( Vector3 vec ); + + // Perform compound assignment and multiplication by a scalar + // + inline Vector3 & operator *=( float scalar ); + + // Perform compound assignment and division by a scalar + // + inline Vector3 & operator /=( float scalar ); + + // Perform compound assignment and multiplication by a scalar (scalar data contained in vector data type) + // + inline Vector3 & operator *=( floatInVec scalar ); + + // Perform compound assignment and division by a scalar (scalar data contained in vector data type) + // + inline Vector3 & operator /=( floatInVec scalar ); + + // Negate all elements of a 3-D vector + // + inline const Vector3 operator -( ) const; + + // Perform equality comparsion of two 3-D vector + // + inline bool operator == (const Vector3& vec) const; + + // Perform equality comparsion of two 3-D vector + // + inline bool operator != (const Vector3& vec) const; + + // Perform lower than comparsion of two 3-D vector + // + inline bool operator < (const Vector3& vec) const; + + // Perform lower or equal comparsion of two 3-D vector + // + inline bool operator <= (const Vector3& vec) const; + + // Perform greater than comparsion of two 3-D vector + // + inline bool operator > (const Vector3& vec) const; + + // Perform greater or equal comparsion of two 3-D vector + // + inline bool operator >= (const Vector3& vec) const; + + // Construct x axis + // + static inline const Vector3 xAxis( ); + + // Construct y axis + // + static inline const Vector3 yAxis( ); + + // Construct z axis + // + static inline const Vector3 zAxis( ); + +}; + +// Multiply a 3-D vector by a scalar +// +inline const Vector3 operator *( float scalar, Vector3 vec ); + +// Multiply a 3-D vector by a scalar (scalar data contained in vector data type) +// +inline const Vector3 operator *( floatInVec scalar, Vector3 vec ); + +// Multiply two 3-D vectors per element +// +inline const Vector3 mulPerElem( Vector3 vec0, Vector3 vec1 ); + +// Divide two 3-D vectors per element +// NOTE: +// Floating-point behavior matches standard library function divf4. +// +inline const Vector3 divPerElem( Vector3 vec0, Vector3 vec1 ); + +// Compute the reciprocal of a 3-D vector per element +// NOTE: +// Floating-point behavior matches standard library function recipf4. +// +inline const Vector3 recipPerElem( Vector3 vec ); + +// Compute the square root of a 3-D vector per element +// NOTE: +// Floating-point behavior matches standard library function sqrtf4. +// +inline const Vector3 sqrtPerElem( Vector3 vec ); + +// Compute the reciprocal square root of a 3-D vector per element +// NOTE: +// Floating-point behavior matches standard library function rsqrtf4. +// +inline const Vector3 rsqrtPerElem( Vector3 vec ); + +// Compute the absolute value of a 3-D vector per element +// +inline const Vector3 absPerElem( Vector3 vec ); + +// Copy sign from one 3-D vector to another, per element +// +inline const Vector3 copySignPerElem( Vector3 vec0, Vector3 vec1 ); + +// Maximum of two 3-D vectors per element +// +inline const Vector3 maxPerElem( Vector3 vec0, Vector3 vec1 ); + +// Minimum of two 3-D vectors per element +// +inline const Vector3 minPerElem( Vector3 vec0, Vector3 vec1 ); + +// Maximum element of a 3-D vector +// +inline const floatInVec maxElem( Vector3 vec ); + +// Minimum element of a 3-D vector +// +inline const floatInVec minElem( Vector3 vec ); + +// Compute the sum of all elements of a 3-D vector +// +inline const floatInVec sum( Vector3 vec ); + +// Compute the dot product of two 3-D vectors +// +inline const floatInVec dot( Vector3 vec0, Vector3 vec1 ); + +// Compute the square of the length of a 3-D vector +// +inline const floatInVec lengthSqr( Vector3 vec ); + +// Compute the length of a 3-D vector +// +inline const floatInVec length( Vector3 vec ); + +// Normalize a 3-D vector +// NOTE: +// The result is unpredictable when all elements of vec are at or near zero. +// +inline const Vector3 normalize( Vector3 vec ); + +// Compute cross product of two 3-D vectors +// +inline const Vector3 cross( Vector3 vec0, Vector3 vec1 ); + +// Outer product of two 3-D vectors +// +inline const Matrix3 outer( Vector3 vec0, Vector3 vec1 ); + +// Pre-multiply a row vector by a 3x3 matrix +// NOTE: +// Slower than column post-multiply. +// +inline const Vector3 rowMul( Vector3 vec, const Matrix3 & mat ); + +// Cross-product matrix of a 3-D vector +// +inline const Matrix3 crossMatrix( Vector3 vec ); + +// Create cross-product matrix and multiply +// NOTE: +// Faster than separately creating a cross-product matrix and multiplying. +// +inline const Matrix3 crossMatrixMul( Vector3 vec, const Matrix3 & mat ); + +// Linear interpolation between two 3-D vectors +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Vector3 lerp( float t, Vector3 vec0, Vector3 vec1 ); + +// Linear interpolation between two 3-D vectors (scalar data contained in vector data type) +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Vector3 lerp( floatInVec t, Vector3 vec0, Vector3 vec1 ); + +// Spherical linear interpolation between two 3-D vectors +// NOTE: +// The result is unpredictable if the vectors point in opposite directions. +// Does not clamp t between 0 and 1. +// +inline const Vector3 slerp( float t, Vector3 unitVec0, Vector3 unitVec1 ); + +// Spherical linear interpolation between two 3-D vectors (scalar data contained in vector data type) +// NOTE: +// The result is unpredictable if the vectors point in opposite directions. +// Does not clamp t between 0 and 1. +// +inline const Vector3 slerp( floatInVec t, Vector3 unitVec0, Vector3 unitVec1 ); + +// Conditionally select between two 3-D vectors +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// However, the transfer of select1 to a VMX register may use more processing time than a branch. +// Use the boolInVec version for better performance. +// +inline const Vector3 select( Vector3 vec0, Vector3 vec1, bool select1 ); + +// Conditionally select between two 3-D vectors (scalar data contained in vector data type) +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// +inline const Vector3 select( Vector3 vec0, Vector3 vec1, boolInVec select1 ); + +// Load x, y, and z elements from the first three words of a quadword. +// +// +inline void loadXYZ( Vector3 & vec, const vec_float4 * quad ); + +// Load x, y, and z elements from the first three words of a float array. +// +// +inline void loadXYZ( Vector3 & vec, const float * fptr ); + +// Store x, y, and z elements of a 3-D vector in the first three words of a quadword. +// The value of the fourth word (the word with the highest address) remains unchanged +// +inline void storeXYZ( Vector3 vec, vec_float4 * quad ); + +// Store x, y, and z elements of a 3-D vector in the first three words of a float array. +// Memory area of previous 16 bytes and next 32 bytes from fptr might be accessed +// +inline void storeXYZ( Vector3 vec, float * fptr ); + +// Load four three-float 3-D vectors, stored in three quadwords +// +inline void loadXYZArray( Vector3 & vec0, Vector3 & vec1, Vector3 & vec2, Vector3 & vec3, const vec_float4 * threeQuads ); + +// Store four 3-D vectors in three quadwords +// +inline void storeXYZArray( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, vec_float4 * threeQuads ); + +// Store eight 3-D vectors as half-floats +// +inline void storeHalfFloats( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, Vector3 vec4, Vector3 vec5, Vector3 vec6, Vector3 vec7, vec_ushort8 * threeQuads ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 3-D vector +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Vector3 vec ); + +// Print a 3-D vector and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Vector3 vec, const char * name ); + +#endif + +// A 4-D vector in array-of-structures format +// +class Vector4 +{ + vec_float4 mVec128; + +public: + // Default constructor; does no initialization + // + inline Vector4( ) { }; + + // Construct a 4-D vector from x, y, z, and w elements + // + inline Vector4( float x, float y, float z, float w ); + + // Construct a 4-D vector from x, y, z, and w elements (scalar data contained in vector data type) + // + inline Vector4( floatInVec x, floatInVec y, floatInVec z, floatInVec w ); + + // Construct a 4-D vector from a 3-D vector and a scalar + // + inline Vector4( Vector3 xyz, float w ); + + // Construct a 4-D vector from a 3-D vector and a scalar (scalar data contained in vector data type) + // + inline Vector4( Vector3 xyz, floatInVec w ); + + // Copy x, y, and z from a 3-D vector into a 4-D vector, and set w to 0 + // + explicit inline Vector4( Vector3 vec ); + + // Copy x, y, and z from a 3-D point into a 4-D vector, and set w to 1 + // + explicit inline Vector4( Point3 pnt ); + + // Copy elements from a quaternion into a 4-D vector + // + explicit inline Vector4( Quat quat ); + + // Set all elements of a 4-D vector to the same scalar value + // + explicit inline Vector4( float scalar ); + + // Set all elements of a 4-D vector to the same scalar value (scalar data contained in vector data type) + // + explicit inline Vector4( floatInVec scalar ); + + // Set vector float data in a 4-D vector + // + explicit inline Vector4( vec_float4 vf4 ); + + // Get vector float data from a 4-D vector + // + inline vec_float4 get128( ) const; + + // Assign one 4-D vector to another + // + inline Vector4 & operator =( Vector4 vec ); + + // Set the x, y, and z elements of a 4-D vector + // NOTE: + // This function does not change the w element. + // + inline Vector4 & setXYZ( Vector3 vec ); + + // Get the x, y, and z elements of a 4-D vector + // + inline const Vector3 getXYZ( ) const; + + // Set the x element of a 4-D vector + // + inline Vector4 & setX( float x ); + + // Set the y element of a 4-D vector + // + inline Vector4 & setY( float y ); + + // Set the z element of a 4-D vector + // + inline Vector4 & setZ( float z ); + + // Set the w element of a 4-D vector + // + inline Vector4 & setW( float w ); + + // Set the x element of a 4-D vector (scalar data contained in vector data type) + // + inline Vector4 & setX( floatInVec x ); + + // Set the y element of a 4-D vector (scalar data contained in vector data type) + // + inline Vector4 & setY( floatInVec y ); + + // Set the z element of a 4-D vector (scalar data contained in vector data type) + // + inline Vector4 & setZ( floatInVec z ); + + // Set the w element of a 4-D vector (scalar data contained in vector data type) + // + inline Vector4 & setW( floatInVec w ); + + // Get the x element of a 4-D vector + // + inline const floatInVec getX( ) const; + + // Get the y element of a 4-D vector + // + inline const floatInVec getY( ) const; + + // Get the z element of a 4-D vector + // + inline const floatInVec getZ( ) const; + + // Get the w element of a 4-D vector + // + inline const floatInVec getW( ) const; + + // Set an x, y, z, or w element of a 4-D vector by index + // + inline Vector4 & setElem( int idx, float value ); + + // Set an x, y, z, or w element of a 4-D vector by index (scalar data contained in vector data type) + // + inline Vector4 & setElem( int idx, floatInVec value ); + + // Get an x, y, z, or w element of a 4-D vector by index + // + inline const floatInVec getElem( int idx ) const; + + // Subscripting operator to set or get an element + // + inline VecIdx operator []( int idx ); + + // Subscripting operator to get an element + // + inline const floatInVec operator []( int idx ) const; + + // Add two 4-D vectors + // + inline const Vector4 operator +( Vector4 vec ) const; + + // Subtract a 4-D vector from another 4-D vector + // + inline const Vector4 operator -( Vector4 vec ) const; + + // Multiply a 4-D vector by a scalar + // + inline const Vector4 operator *( float scalar ) const; + + // Divide a 4-D vector by a scalar + // + inline const Vector4 operator /( float scalar ) const; + + // Multiply a 4-D vector by a scalar (scalar data contained in vector data type) + // + inline const Vector4 operator *( floatInVec scalar ) const; + + // Divide a 4-D vector by a scalar (scalar data contained in vector data type) + // + inline const Vector4 operator /( floatInVec scalar ) const; + + // Perform compound assignment and addition with a 4-D vector + // + inline Vector4 & operator +=( Vector4 vec ); + + // Perform compound assignment and subtraction by a 4-D vector + // + inline Vector4 & operator -=( Vector4 vec ); + + // Perform compound assignment and multiplication by a scalar + // + inline Vector4 & operator *=( float scalar ); + + // Perform compound assignment and division by a scalar + // + inline Vector4 & operator /=( float scalar ); + + // Perform compound assignment and multiplication by a scalar (scalar data contained in vector data type) + // + inline Vector4 & operator *=( floatInVec scalar ); + + // Perform compound assignment and division by a scalar (scalar data contained in vector data type) + // + inline Vector4 & operator /=( floatInVec scalar ); + + // Negate all elements of a 4-D vector + // + inline const Vector4 operator -( ) const; + + // Perform equality comparsion of two 4-D vector + // + inline bool operator == (const Vector4& vec) const; + + // Perform equality comparsion of two 4-D vector + // + inline bool operator != (const Vector4& vec) const; + + // Perform lower than comparsion of two 4-D vector + // + inline bool operator < (const Vector4& vec) const; + + // Perform lower or equal comparsion of two 4-D vector + // + inline bool operator <= (const Vector4& vec) const; + + // Perform greater than comparsion of two 4-D vector + // + inline bool operator > (const Vector4& vec) const; + + // Perform greater or equal comparsion of two 4-D vector + // + inline bool operator >= (const Vector4& vec) const; + + // Construct x axis + // + static inline const Vector4 xAxis( ); + + // Construct y axis + // + static inline const Vector4 yAxis( ); + + // Construct z axis + // + static inline const Vector4 zAxis( ); + + // Construct w axis + // + static inline const Vector4 wAxis( ); + +}; + +// Multiply a 4-D vector by a scalar +// +inline const Vector4 operator *( float scalar, Vector4 vec ); + +// Multiply a 4-D vector by a scalar (scalar data contained in vector data type) +// +inline const Vector4 operator *( floatInVec scalar, Vector4 vec ); + +// Multiply two 4-D vectors per element +// +inline const Vector4 mulPerElem( Vector4 vec0, Vector4 vec1 ); + +// Divide two 4-D vectors per element +// NOTE: +// Floating-point behavior matches standard library function divf4. +// +inline const Vector4 divPerElem( Vector4 vec0, Vector4 vec1 ); + +// Compute the reciprocal of a 4-D vector per element +// NOTE: +// Floating-point behavior matches standard library function recipf4. +// +inline const Vector4 recipPerElem( Vector4 vec ); + +// Compute the square root of a 4-D vector per element +// NOTE: +// Floating-point behavior matches standard library function sqrtf4. +// +inline const Vector4 sqrtPerElem( Vector4 vec ); + +// Compute the reciprocal square root of a 4-D vector per element +// NOTE: +// Floating-point behavior matches standard library function rsqrtf4. +// +inline const Vector4 rsqrtPerElem( Vector4 vec ); + +// Compute the absolute value of a 4-D vector per element +// +inline const Vector4 absPerElem( Vector4 vec ); + +// Copy sign from one 4-D vector to another, per element +// +inline const Vector4 copySignPerElem( Vector4 vec0, Vector4 vec1 ); + +// Maximum of two 4-D vectors per element +// +inline const Vector4 maxPerElem( Vector4 vec0, Vector4 vec1 ); + +// Minimum of two 4-D vectors per element +// +inline const Vector4 minPerElem( Vector4 vec0, Vector4 vec1 ); + +// Maximum element of a 4-D vector +// +inline const floatInVec maxElem( Vector4 vec ); + +// Minimum element of a 4-D vector +// +inline const floatInVec minElem( Vector4 vec ); + +// Compute the sum of all elements of a 4-D vector +// +inline const floatInVec sum( Vector4 vec ); + +// Compute the dot product of two 4-D vectors +// +inline const floatInVec dot( Vector4 vec0, Vector4 vec1 ); + +// Compute the square of the length of a 4-D vector +// +inline const floatInVec lengthSqr( Vector4 vec ); + +// Compute the length of a 4-D vector +// +inline const floatInVec length( Vector4 vec ); + +// Normalize a 4-D vector +// NOTE: +// The result is unpredictable when all elements of vec are at or near zero. +// +inline const Vector4 normalize( Vector4 vec ); + +// Outer product of two 4-D vectors +// +inline const Matrix4 outer( Vector4 vec0, Vector4 vec1 ); + +// Linear interpolation between two 4-D vectors +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Vector4 lerp( float t, Vector4 vec0, Vector4 vec1 ); + +// Linear interpolation between two 4-D vectors (scalar data contained in vector data type) +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Vector4 lerp( floatInVec t, Vector4 vec0, Vector4 vec1 ); + +// Spherical linear interpolation between two 4-D vectors +// NOTE: +// The result is unpredictable if the vectors point in opposite directions. +// Does not clamp t between 0 and 1. +// +inline const Vector4 slerp( float t, Vector4 unitVec0, Vector4 unitVec1 ); + +// Spherical linear interpolation between two 4-D vectors (scalar data contained in vector data type) +// NOTE: +// The result is unpredictable if the vectors point in opposite directions. +// Does not clamp t between 0 and 1. +// +inline const Vector4 slerp( floatInVec t, Vector4 unitVec0, Vector4 unitVec1 ); + +// Conditionally select between two 4-D vectors +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// However, the transfer of select1 to a VMX register may use more processing time than a branch. +// Use the boolInVec version for better performance. +// +inline const Vector4 select( Vector4 vec0, Vector4 vec1, bool select1 ); + +// Conditionally select between two 4-D vectors (scalar data contained in vector data type) +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// +inline const Vector4 select( Vector4 vec0, Vector4 vec1, boolInVec select1 ); + +// Store four 4-D vectors as half-floats +// +inline void storeHalfFloats( Vector4 vec0, Vector4 vec1, Vector4 vec2, Vector4 vec3, vec_ushort8 * twoQuads ); + +// Load x, y, z, and w elements from the first four words of a float array. +// +// +inline void loadXYZW( Vector4 & vec, const float * fptr ); + +// Store x, y, z, and w elements of a 4-D vector in the first four words of a float array. +// Memory area of previous 16 bytes and next 32 bytes from fptr might be accessed +// +inline void storeXYZW( Vector4 vec, float * fptr ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 4-D vector +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Vector4 vec ); + +// Print a 4-D vector and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Vector4 vec, const char * name ); + +#endif + +// A 3-D point in array-of-structures format +// +class Point3 +{ + vec_float4 mVec128; + +public: + // Default constructor; does no initialization + // + inline Point3( ) { }; + + // Construct a 3-D point from x, y, and z elements + // + inline Point3( float x, float y, float z ); + + // Construct a 3-D point from x, y, and z elements (scalar data contained in vector data type) + // + inline Point3( floatInVec x, floatInVec y, floatInVec z ); + + // Copy elements from a 3-D vector into a 3-D point + // + explicit inline Point3( Vector3 vec ); + + // Set all elements of a 3-D point to the same scalar value + // + explicit inline Point3( float scalar ); + + // Set all elements of a 3-D point to the same scalar value (scalar data contained in vector data type) + // + explicit inline Point3( floatInVec scalar ); + + // Set vector float data in a 3-D point + // + explicit inline Point3( vec_float4 vf4 ); + + // Get vector float data from a 3-D point + // + inline vec_float4 get128( ) const; + + // Assign one 3-D point to another + // + inline Point3 & operator =( Point3 pnt ); + + // Set the x element of a 3-D point + // + inline Point3 & setX( float x ); + + // Set the y element of a 3-D point + // + inline Point3 & setY( float y ); + + // Set the z element of a 3-D point + // + inline Point3 & setZ( float z ); + + // Set the x element of a 3-D point (scalar data contained in vector data type) + // + inline Point3 & setX( floatInVec x ); + + // Set the y element of a 3-D point (scalar data contained in vector data type) + // + inline Point3 & setY( floatInVec y ); + + // Set the z element of a 3-D point (scalar data contained in vector data type) + // + inline Point3 & setZ( floatInVec z ); + + // Get the x element of a 3-D point + // + inline const floatInVec getX( ) const; + + // Get the y element of a 3-D point + // + inline const floatInVec getY( ) const; + + // Get the z element of a 3-D point + // + inline const floatInVec getZ( ) const; + + // Set an x, y, or z element of a 3-D point by index + // + inline Point3 & setElem( int idx, float value ); + + // Set an x, y, or z element of a 3-D point by index (scalar data contained in vector data type) + // + inline Point3 & setElem( int idx, floatInVec value ); + + // Get an x, y, or z element of a 3-D point by index + // + inline const floatInVec getElem( int idx ) const; + + // Subscripting operator to set or get an element + // + inline VecIdx operator []( int idx ); + + // Subscripting operator to get an element + // + inline const floatInVec operator []( int idx ) const; + + // Subtract a 3-D point from another 3-D point + // + inline const Vector3 operator -( Point3 pnt ) const; + + // Add a 3-D point to a 3-D vector + // + inline const Point3 operator +( Vector3 vec ) const; + + // Subtract a 3-D vector from a 3-D point + // + inline const Point3 operator -( Vector3 vec ) const; + + // Perform compound assignment and addition with a 3-D vector + // + inline Point3 & operator +=( Vector3 vec ); + + // Perform compound assignment and subtraction by a 3-D vector + // + inline Point3 & operator -=( Vector3 vec ); + + // Perform equality comparsion of two 3-D point + // + inline bool operator == (const Point3& vec) const; + + // Perform equality comparsion of two 3-D point + // + inline bool operator != (const Point3& vec) const; + + // Perform lower than comparsion of two 3-D point + // + inline bool operator < (const Point3& vec) const; + + // Perform lower or equal comparsion of two 3-D point + // + inline bool operator <= (const Point3& vec) const; + + // Perform greater than comparsion of two 3-D point + // + inline bool operator > (const Point3& vec) const; + + // Perform greater or equal comparsion of two 3-D point + // + inline bool operator >= (const Point3& vec) const; +}; + +// Multiply two 3-D points per element +// +inline const Point3 mulPerElem( Point3 pnt0, Point3 pnt1 ); + +// Divide two 3-D points per element +// NOTE: +// Floating-point behavior matches standard library function divf4. +// +inline const Point3 divPerElem( Point3 pnt0, Point3 pnt1 ); + +// Compute the reciprocal of a 3-D point per element +// NOTE: +// Floating-point behavior matches standard library function recipf4. +// +inline const Point3 recipPerElem( Point3 pnt ); + +// Compute the square root of a 3-D point per element +// NOTE: +// Floating-point behavior matches standard library function sqrtf4. +// +inline const Point3 sqrtPerElem( Point3 pnt ); + +// Compute the reciprocal square root of a 3-D point per element +// NOTE: +// Floating-point behavior matches standard library function rsqrtf4. +// +inline const Point3 rsqrtPerElem( Point3 pnt ); + +// Compute the absolute value of a 3-D point per element +// +inline const Point3 absPerElem( Point3 pnt ); + +// Copy sign from one 3-D point to another, per element +// +inline const Point3 copySignPerElem( Point3 pnt0, Point3 pnt1 ); + +// Maximum of two 3-D points per element +// +inline const Point3 maxPerElem( Point3 pnt0, Point3 pnt1 ); + +// Minimum of two 3-D points per element +// +inline const Point3 minPerElem( Point3 pnt0, Point3 pnt1 ); + +// Maximum element of a 3-D point +// +inline const floatInVec maxElem( Point3 pnt ); + +// Minimum element of a 3-D point +// +inline const floatInVec minElem( Point3 pnt ); + +// Compute the sum of all elements of a 3-D point +// +inline const floatInVec sum( Point3 pnt ); + +// Apply uniform scale to a 3-D point +// +inline const Point3 scale( Point3 pnt, float scaleVal ); + +// Apply uniform scale to a 3-D point (scalar data contained in vector data type) +// +inline const Point3 scale( Point3 pnt, floatInVec scaleVal ); + +// Apply non-uniform scale to a 3-D point +// +inline const Point3 scale( Point3 pnt, Vector3 scaleVec ); + +// Scalar projection of a 3-D point on a unit-length 3-D vector +// +inline const floatInVec projection( Point3 pnt, Vector3 unitVec ); + +// Compute the square of the distance of a 3-D point from the coordinate-system origin +// +inline const floatInVec distSqrFromOrigin( Point3 pnt ); + +// Compute the distance of a 3-D point from the coordinate-system origin +// +inline const floatInVec distFromOrigin( Point3 pnt ); + +// Compute the square of the distance between two 3-D points +// +inline const floatInVec distSqr( Point3 pnt0, Point3 pnt1 ); + +// Compute the distance between two 3-D points +// +inline const floatInVec dist( Point3 pnt0, Point3 pnt1 ); + +// Linear interpolation between two 3-D points +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Point3 lerp( float t, Point3 pnt0, Point3 pnt1 ); + +// Linear interpolation between two 3-D points (scalar data contained in vector data type) +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Point3 lerp( floatInVec t, Point3 pnt0, Point3 pnt1 ); + +// Conditionally select between two 3-D points +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// However, the transfer of select1 to a VMX register may use more processing time than a branch. +// Use the boolInVec version for better performance. +// +inline const Point3 select( Point3 pnt0, Point3 pnt1, bool select1 ); + +// Conditionally select between two 3-D points (scalar data contained in vector data type) +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// +inline const Point3 select( Point3 pnt0, Point3 pnt1, boolInVec select1 ); + +// Load x, y, and z elements from the first three words of a quadword. +// +// +inline void loadXYZ( Point3 & pnt, const vec_float4 * quad ); + +// Load x, y, and z elements from the first three words of a float array. +// +// +inline void loadXYZ( Point3 & pnt, const float * fptr ); + +// Store x, y, and z elements of a 3-D point in the first three words of a quadword. +// The value of the fourth word (the word with the highest address) remains unchanged +// +inline void storeXYZ( Point3 pnt, vec_float4 * quad ); + +// Store x, y, and z elements of a 3-D point in the first three words of a float array. +// Memory area of previous 16 bytes and next 32 bytes from fptr might be accessed +// +inline void storeXYZ( Point3 pnt, float * fptr ); + +// Load four three-float 3-D points, stored in three quadwords +// +inline void loadXYZArray( Point3 & pnt0, Point3 & pnt1, Point3 & pnt2, Point3 & pnt3, const vec_float4 * threeQuads ); + +// Store four 3-D points in three quadwords +// +inline void storeXYZArray( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, vec_float4 * threeQuads ); + +// Store eight 3-D points as half-floats +// +inline void storeHalfFloats( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, Point3 pnt4, Point3 pnt5, Point3 pnt6, Point3 pnt7, vec_ushort8 * threeQuads ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 3-D point +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Point3 pnt ); + +// Print a 3-D point and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Point3 pnt, const char * name ); + +#endif + +// A quaternion in array-of-structures format +// +class Quat +{ + vec_float4 mVec128; + +public: + // Default constructor; does no initialization + // + inline Quat( ) { }; + + // Construct a quaternion from x, y, z, and w elements + // + inline Quat( float x, float y, float z, float w ); + + // Construct a quaternion from x, y, z, and w elements (scalar data contained in vector data type) + // + inline Quat( floatInVec x, floatInVec y, floatInVec z, floatInVec w ); + + // Construct a quaternion from a 3-D vector and a scalar + // + inline Quat( Vector3 xyz, float w ); + + // Construct a quaternion from a 3-D vector and a scalar (scalar data contained in vector data type) + // + inline Quat( Vector3 xyz, floatInVec w ); + + // Copy elements from a 4-D vector into a quaternion + // + explicit inline Quat( Vector4 vec ); + + // Convert a rotation matrix to a unit-length quaternion + // + explicit inline Quat( const Matrix3 & rotMat ); + + // Set all elements of a quaternion to the same scalar value + // + explicit inline Quat( float scalar ); + + // Set all elements of a quaternion to the same scalar value (scalar data contained in vector data type) + // + explicit inline Quat( floatInVec scalar ); + + // Set vector float data in a quaternion + // + explicit inline Quat( vec_float4 vf4 ); + + // Get vector float data from a quaternion + // + inline vec_float4 get128( ) const; + + // Assign one quaternion to another + // + inline Quat & operator =( Quat quat ); + + // Set the x, y, and z elements of a quaternion + // NOTE: + // This function does not change the w element. + // + inline Quat & setXYZ( Vector3 vec ); + + // Get the x, y, and z elements of a quaternion + // + inline const Vector3 getXYZ( ) const; + + // Set the x element of a quaternion + // + inline Quat & setX( float x ); + + // Set the y element of a quaternion + // + inline Quat & setY( float y ); + + // Set the z element of a quaternion + // + inline Quat & setZ( float z ); + + // Set the w element of a quaternion + // + inline Quat & setW( float w ); + + // Set the x element of a quaternion (scalar data contained in vector data type) + // + inline Quat & setX( floatInVec x ); + + // Set the y element of a quaternion (scalar data contained in vector data type) + // + inline Quat & setY( floatInVec y ); + + // Set the z element of a quaternion (scalar data contained in vector data type) + // + inline Quat & setZ( floatInVec z ); + + // Set the w element of a quaternion (scalar data contained in vector data type) + // + inline Quat & setW( floatInVec w ); + + // Get the x element of a quaternion + // + inline const floatInVec getX( ) const; + + // Get the y element of a quaternion + // + inline const floatInVec getY( ) const; + + // Get the z element of a quaternion + // + inline const floatInVec getZ( ) const; + + // Get the w element of a quaternion + // + inline const floatInVec getW( ) const; + + // Set an x, y, z, or w element of a quaternion by index + // + inline Quat & setElem( int idx, float value ); + + // Set an x, y, z, or w element of a quaternion by index (scalar data contained in vector data type) + // + inline Quat & setElem( int idx, floatInVec value ); + + // Get an x, y, z, or w element of a quaternion by index + // + inline const floatInVec getElem( int idx ) const; + + // Subscripting operator to set or get an element + // + inline VecIdx operator []( int idx ); + + // Subscripting operator to get an element + // + inline const floatInVec operator []( int idx ) const; + + // Add two quaternions + // + inline const Quat operator +( Quat quat ) const; + + // Subtract a quaternion from another quaternion + // + inline const Quat operator -( Quat quat ) const; + + // Multiply two quaternions + // + inline const Quat operator *( Quat quat ) const; + + // Multiply a quaternion by a scalar + // + inline const Quat operator *( float scalar ) const; + + // Divide a quaternion by a scalar + // + inline const Quat operator /( float scalar ) const; + + // Multiply a quaternion by a scalar (scalar data contained in vector data type) + // + inline const Quat operator *( floatInVec scalar ) const; + + // Divide a quaternion by a scalar (scalar data contained in vector data type) + // + inline const Quat operator /( floatInVec scalar ) const; + + // Perform compound assignment and addition with a quaternion + // + inline Quat & operator +=( Quat quat ); + + // Perform compound assignment and subtraction by a quaternion + // + inline Quat & operator -=( Quat quat ); + + // Perform compound assignment and multiplication by a quaternion + // + inline Quat & operator *=( Quat quat ); + + // Perform compound assignment and multiplication by a scalar + // + inline Quat & operator *=( float scalar ); + + // Perform compound assignment and division by a scalar + // + inline Quat & operator /=( float scalar ); + + // Perform compound assignment and multiplication by a scalar (scalar data contained in vector data type) + // + inline Quat & operator *=( floatInVec scalar ); + + // Perform compound assignment and division by a scalar (scalar data contained in vector data type) + // + inline Quat & operator /=( floatInVec scalar ); + + // Negate all elements of a quaternion + // + inline const Quat operator -( ) const; + + // Construct an identity quaternion + // + static inline const Quat identity( ); + + // Construct a quaternion to rotate between two unit-length 3-D vectors + // NOTE: + // The result is unpredictable if unitVec0 and unitVec1 point in opposite directions. + // + static inline const Quat rotation( Vector3 unitVec0, Vector3 unitVec1 ); + + // Construct a quaternion to rotate around a unit-length 3-D vector + // + static inline const Quat rotation( float radians, Vector3 unitVec ); + + // Construct a quaternion to rotate around a unit-length 3-D vector (scalar data contained in vector data type) + // + static inline const Quat rotation( floatInVec radians, Vector3 unitVec ); + + // Construct a quaternion to rotate around the x axis + // + static inline const Quat rotationX( float radians ); + + // Construct a quaternion to rotate around the y axis + // + static inline const Quat rotationY( float radians ); + + // Construct a quaternion to rotate around the z axis + // + static inline const Quat rotationZ( float radians ); + + // Construct a quaternion to rotate around the x axis (scalar data contained in vector data type) + // + static inline const Quat rotationX( floatInVec radians ); + + // Construct a quaternion to rotate around the y axis (scalar data contained in vector data type) + // + static inline const Quat rotationY( floatInVec radians ); + + // Construct a quaternion to rotate around the z axis (scalar data contained in vector data type) + // + static inline const Quat rotationZ( floatInVec radians ); + +}; + +// Multiply a quaternion by a scalar +// +inline const Quat operator *( float scalar, Quat quat ); + +// Multiply a quaternion by a scalar (scalar data contained in vector data type) +// +inline const Quat operator *( floatInVec scalar, Quat quat ); + +// Compute the conjugate of a quaternion +// +inline const Quat conj( Quat quat ); + +// Use a unit-length quaternion to rotate a 3-D vector +// +inline const Vector3 rotate( Quat unitQuat, Vector3 vec ); + +// Compute the dot product of two quaternions +// +inline const floatInVec dot( Quat quat0, Quat quat1 ); + +// Compute the norm of a quaternion +// +inline const floatInVec norm( Quat quat ); + +// Compute the length of a quaternion +// +inline const floatInVec length( Quat quat ); + +// Normalize a quaternion +// NOTE: +// The result is unpredictable when all elements of quat are at or near zero. +// +inline const Quat normalize( Quat quat ); + +// Linear interpolation between two quaternions +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Quat lerp( float t, Quat quat0, Quat quat1 ); + +// Linear interpolation between two quaternions (scalar data contained in vector data type) +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Quat lerp( floatInVec t, Quat quat0, Quat quat1 ); + +// Spherical linear interpolation between two quaternions +// NOTE: +// Interpolates along the shortest path between orientations. +// Does not clamp t between 0 and 1. +// +inline const Quat slerp( float t, Quat unitQuat0, Quat unitQuat1 ); + +// Spherical linear interpolation between two quaternions (scalar data contained in vector data type) +// NOTE: +// Interpolates along the shortest path between orientations. +// Does not clamp t between 0 and 1. +// +inline const Quat slerp( floatInVec t, Quat unitQuat0, Quat unitQuat1 ); + +// Spherical quadrangle interpolation +// +inline const Quat squad( float t, Quat unitQuat0, Quat unitQuat1, Quat unitQuat2, Quat unitQuat3 ); + +// Spherical quadrangle interpolation (scalar data contained in vector data type) +// +inline const Quat squad( floatInVec t, Quat unitQuat0, Quat unitQuat1, Quat unitQuat2, Quat unitQuat3 ); + +// Load x, y, z, and w elements from the first four words of a float array. +// +// +inline void loadXYZW( Quat & quat, const float * fptr ); + +// Store x, y, z, and w elements of a quaternion in the first four words of a float array. +// Memory area of previous 16 bytes and next 32 bytes from fptr might be accessed +// +inline void storeXYZW( Quat quat, float * fptr ); + +// Conditionally select between two quaternions +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// However, the transfer of select1 to a VMX register may use more processing time than a branch. +// Use the boolInVec version for better performance. +// +inline const Quat select( Quat quat0, Quat quat1, bool select1 ); + +// Conditionally select between two quaternions (scalar data contained in vector data type) +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// +inline const Quat select( Quat quat0, Quat quat1, boolInVec select1 ); + +#ifdef _VECTORMATH_DEBUG + +// Print a quaternion +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Quat quat ); + +// Print a quaternion and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Quat quat, const char * name ); + +#endif + +// A 3x3 matrix in array-of-structures format +// +class Matrix3 +{ + Vector3 mCol0; + Vector3 mCol1; + Vector3 mCol2; + +public: + // Default constructor; does no initialization + // + inline Matrix3( ) { }; + + // Copy a 3x3 matrix + // + inline Matrix3( const Matrix3 & mat ); + + // Construct a 3x3 matrix containing the specified columns + // + inline Matrix3( Vector3 col0, Vector3 col1, Vector3 col2 ); + + // Construct a 3x3 rotation matrix from a unit-length quaternion + // + explicit inline Matrix3( Quat unitQuat ); + + // Set all elements of a 3x3 matrix to the same scalar value + // + explicit inline Matrix3( float scalar ); + + // Set all elements of a 3x3 matrix to the same scalar value (scalar data contained in vector data type) + // + explicit inline Matrix3( floatInVec scalar ); + + // Assign one 3x3 matrix to another + // + inline Matrix3 & operator =( const Matrix3 & mat ); + + // Set column 0 of a 3x3 matrix + // + inline Matrix3 & setCol0( Vector3 col0 ); + + // Set column 1 of a 3x3 matrix + // + inline Matrix3 & setCol1( Vector3 col1 ); + + // Set column 2 of a 3x3 matrix + // + inline Matrix3 & setCol2( Vector3 col2 ); + + // Get column 0 of a 3x3 matrix + // + inline const Vector3 getCol0( ) const; + + // Get column 1 of a 3x3 matrix + // + inline const Vector3 getCol1( ) const; + + // Get column 2 of a 3x3 matrix + // + inline const Vector3 getCol2( ) const; + + // Set the column of a 3x3 matrix referred to by the specified index + // + inline Matrix3 & setCol( int col, Vector3 vec ); + + // Set the row of a 3x3 matrix referred to by the specified index + // + inline Matrix3 & setRow( int row, Vector3 vec ); + + // Get the column of a 3x3 matrix referred to by the specified index + // + inline const Vector3 getCol( int col ) const; + + // Get the row of a 3x3 matrix referred to by the specified index + // + inline const Vector3 getRow( int row ) const; + + // Subscripting operator to set or get a column + // + inline Vector3 & operator []( int col ); + + // Subscripting operator to get a column + // + inline const Vector3 operator []( int col ) const; + + // Set the element of a 3x3 matrix referred to by column and row indices + // + inline Matrix3 & setElem( int col, int row, float val ); + + // Set the element of a 3x3 matrix referred to by column and row indices (scalar data contained in vector data type) + // + inline Matrix3 & setElem( int col, int row, floatInVec val ); + + // Get the element of a 3x3 matrix referred to by column and row indices + // + inline const floatInVec getElem( int col, int row ) const; + + // Add two 3x3 matrices + // + inline const Matrix3 operator +( const Matrix3 & mat ) const; + + // Subtract a 3x3 matrix from another 3x3 matrix + // + inline const Matrix3 operator -( const Matrix3 & mat ) const; + + // Negate all elements of a 3x3 matrix + // + inline const Matrix3 operator -( ) const; + + // Multiply a 3x3 matrix by a scalar + // + inline const Matrix3 operator *( float scalar ) const; + + // Multiply a 3x3 matrix by a scalar (scalar data contained in vector data type) + // + inline const Matrix3 operator *( floatInVec scalar ) const; + + // Multiply a 3x3 matrix by a 3-D vector + // + inline const Vector3 operator *( Vector3 vec ) const; + + // Multiply two 3x3 matrices + // + inline const Matrix3 operator *( const Matrix3 & mat ) const; + + // Perform compound assignment and addition with a 3x3 matrix + // + inline Matrix3 & operator +=( const Matrix3 & mat ); + + // Perform compound assignment and subtraction by a 3x3 matrix + // + inline Matrix3 & operator -=( const Matrix3 & mat ); + + // Perform compound assignment and multiplication by a scalar + // + inline Matrix3 & operator *=( float scalar ); + + // Perform compound assignment and multiplication by a scalar (scalar data contained in vector data type) + // + inline Matrix3 & operator *=( floatInVec scalar ); + + // Perform compound assignment and multiplication by a 3x3 matrix + // + inline Matrix3 & operator *=( const Matrix3 & mat ); + + // Construct an identity 3x3 matrix + // + static inline const Matrix3 identity( ); + + // Construct a 3x3 matrix to rotate around the x axis + // + static inline const Matrix3 rotationX( float radians ); + + // Construct a 3x3 matrix to rotate around the y axis + // + static inline const Matrix3 rotationY( float radians ); + + // Construct a 3x3 matrix to rotate around the z axis + // + static inline const Matrix3 rotationZ( float radians ); + + // Construct a 3x3 matrix to rotate around the x axis (scalar data contained in vector data type) + // + static inline const Matrix3 rotationX( floatInVec radians ); + + // Construct a 3x3 matrix to rotate around the y axis (scalar data contained in vector data type) + // + static inline const Matrix3 rotationY( floatInVec radians ); + + // Construct a 3x3 matrix to rotate around the z axis (scalar data contained in vector data type) + // + static inline const Matrix3 rotationZ( floatInVec radians ); + + // Construct a 3x3 matrix to rotate around the x, y, and z axes + // + static inline const Matrix3 rotationZYX( Vector3 radiansXYZ ); + + // Construct a 3x3 matrix to rotate around a unit-length 3-D vector + // + static inline const Matrix3 rotation( float radians, Vector3 unitVec ); + + // Construct a 3x3 matrix to rotate around a unit-length 3-D vector (scalar data contained in vector data type) + // + static inline const Matrix3 rotation( floatInVec radians, Vector3 unitVec ); + + // Construct a rotation matrix from a unit-length quaternion + // + static inline const Matrix3 rotation( Quat unitQuat ); + + // Construct a 3x3 matrix to perform scaling + // + static inline const Matrix3 scale( Vector3 scaleVec ); + +}; +// Multiply a 3x3 matrix by a scalar +// +inline const Matrix3 operator *( float scalar, const Matrix3 & mat ); + +// Multiply a 3x3 matrix by a scalar (scalar data contained in vector data type) +// +inline const Matrix3 operator *( floatInVec scalar, const Matrix3 & mat ); + +// Append (post-multiply) a scale transformation to a 3x3 matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Matrix3 appendScale( const Matrix3 & mat, Vector3 scaleVec ); + +// Prepend (pre-multiply) a scale transformation to a 3x3 matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Matrix3 prependScale( Vector3 scaleVec, const Matrix3 & mat ); + +// Multiply two 3x3 matrices per element +// +inline const Matrix3 mulPerElem( const Matrix3 & mat0, const Matrix3 & mat1 ); + +// Compute the absolute value of a 3x3 matrix per element +// +inline const Matrix3 absPerElem( const Matrix3 & mat ); + +// Transpose of a 3x3 matrix +// +inline const Matrix3 transpose( const Matrix3 & mat ); + +// Compute the inverse of a 3x3 matrix +// NOTE: +// Result is unpredictable when the determinant of mat is equal to or near 0. +// +inline const Matrix3 inverse( const Matrix3 & mat ); + +// Determinant of a 3x3 matrix +// +inline const floatInVec determinant( const Matrix3 & mat ); + +// Conditionally select between two 3x3 matrices +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// However, the transfer of select1 to a VMX register may use more processing time than a branch. +// Use the boolInVec version for better performance. +// +inline const Matrix3 select( const Matrix3 & mat0, const Matrix3 & mat1, bool select1 ); + +// Conditionally select between two 3x3 matrices (scalar data contained in vector data type) +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// +inline const Matrix3 select( const Matrix3 & mat0, const Matrix3 & mat1, boolInVec select1 ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 3x3 matrix +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Matrix3 & mat ); + +// Print a 3x3 matrix and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Matrix3 & mat, const char * name ); + +#endif + +// A 4x4 matrix in array-of-structures format +// +class Matrix4 +{ + Vector4 mCol0; + Vector4 mCol1; + Vector4 mCol2; + Vector4 mCol3; + +public: + // Default constructor; does no initialization + // + inline Matrix4( ) { }; + + // Copy a 4x4 matrix + // + inline Matrix4( const Matrix4 & mat ); + + // Construct a 4x4 matrix containing the specified columns + // + inline Matrix4( Vector4 col0, Vector4 col1, Vector4 col2, Vector4 col3 ); + + // Construct a 4x4 matrix from a 3x4 transformation matrix + // + explicit inline Matrix4( const Transform3 & mat ); + + // Construct a 4x4 matrix from a 3x3 matrix and a 3-D vector + // + inline Matrix4( const Matrix3 & mat, Vector3 translateVec ); + + // Construct a 4x4 matrix from a unit-length quaternion and a 3-D vector + // + inline Matrix4( Quat unitQuat, Vector3 translateVec ); + + // Set all elements of a 4x4 matrix to the same scalar value + // + explicit inline Matrix4( float scalar ); + + // Set all elements of a 4x4 matrix to the same scalar value (scalar data contained in vector data type) + // + explicit inline Matrix4( floatInVec scalar ); + + // Assign one 4x4 matrix to another + // + inline Matrix4 & operator =( const Matrix4 & mat ); + + // Set the upper-left 3x3 submatrix + // NOTE: + // This function does not change the bottom row elements. + // + inline Matrix4 & setUpper3x3( const Matrix3 & mat3 ); + + // Get the upper-left 3x3 submatrix of a 4x4 matrix + // + inline const Matrix3 getUpper3x3( ) const; + + // Set translation component + // NOTE: + // This function does not change the bottom row elements. + // + inline Matrix4 & setTranslation( Vector3 translateVec ); + + // Get the translation component of a 4x4 matrix + // + inline const Vector3 getTranslation( ) const; + + // Set column 0 of a 4x4 matrix + // + inline Matrix4 & setCol0( Vector4 col0 ); + + // Set column 1 of a 4x4 matrix + // + inline Matrix4 & setCol1( Vector4 col1 ); + + // Set column 2 of a 4x4 matrix + // + inline Matrix4 & setCol2( Vector4 col2 ); + + // Set column 3 of a 4x4 matrix + // + inline Matrix4 & setCol3( Vector4 col3 ); + + // Get column 0 of a 4x4 matrix + // + inline const Vector4 getCol0( ) const; + + // Get column 1 of a 4x4 matrix + // + inline const Vector4 getCol1( ) const; + + // Get column 2 of a 4x4 matrix + // + inline const Vector4 getCol2( ) const; + + // Get column 3 of a 4x4 matrix + // + inline const Vector4 getCol3( ) const; + + // Set the column of a 4x4 matrix referred to by the specified index + // + inline Matrix4 & setCol( int col, Vector4 vec ); + + // Set the row of a 4x4 matrix referred to by the specified index + // + inline Matrix4 & setRow( int row, Vector4 vec ); + + // Get the column of a 4x4 matrix referred to by the specified index + // + inline const Vector4 getCol( int col ) const; + + // Get the row of a 4x4 matrix referred to by the specified index + // + inline const Vector4 getRow( int row ) const; + + // Subscripting operator to set or get a column + // + inline Vector4 & operator []( int col ); + + // Subscripting operator to get a column + // + inline const Vector4 operator []( int col ) const; + + // Set the element of a 4x4 matrix referred to by column and row indices + // + inline Matrix4 & setElem( int col, int row, float val ); + + // Set the element of a 4x4 matrix referred to by column and row indices (scalar data contained in vector data type) + // + inline Matrix4 & setElem( int col, int row, floatInVec val ); + + // Get the element of a 4x4 matrix referred to by column and row indices + // + inline const floatInVec getElem( int col, int row ) const; + + // Add two 4x4 matrices + // + inline const Matrix4 operator +( const Matrix4 & mat ) const; + + // Subtract a 4x4 matrix from another 4x4 matrix + // + inline const Matrix4 operator -( const Matrix4 & mat ) const; + + // Negate all elements of a 4x4 matrix + // + inline const Matrix4 operator -( ) const; + + // Multiply a 4x4 matrix by a scalar + // + inline const Matrix4 operator *( float scalar ) const; + + // Multiply a 4x4 matrix by a scalar (scalar data contained in vector data type) + // + inline const Matrix4 operator *( floatInVec scalar ) const; + + // Multiply a 4x4 matrix by a 4-D vector + // + inline const Vector4 operator *( Vector4 vec ) const; + + // Multiply a 4x4 matrix by a 3-D vector + // + inline const Vector4 operator *( Vector3 vec ) const; + + // Multiply a 4x4 matrix by a 3-D point + // + inline const Vector4 operator *( Point3 pnt ) const; + + // Multiply two 4x4 matrices + // + inline const Matrix4 operator *( const Matrix4 & mat ) const; + + // Multiply a 4x4 matrix by a 3x4 transformation matrix + // + inline const Matrix4 operator *( const Transform3 & tfrm ) const; + + // Perform compound assignment and addition with a 4x4 matrix + // + inline Matrix4 & operator +=( const Matrix4 & mat ); + + // Perform compound assignment and subtraction by a 4x4 matrix + // + inline Matrix4 & operator -=( const Matrix4 & mat ); + + // Perform compound assignment and multiplication by a scalar + // + inline Matrix4 & operator *=( float scalar ); + + // Perform compound assignment and multiplication by a scalar (scalar data contained in vector data type) + // + inline Matrix4 & operator *=( floatInVec scalar ); + + // Perform compound assignment and multiplication by a 4x4 matrix + // + inline Matrix4 & operator *=( const Matrix4 & mat ); + + // Perform compound assignment and multiplication by a 3x4 transformation matrix + // + inline Matrix4 & operator *=( const Transform3 & tfrm ); + + inline bool operator == (const Matrix4& mat) const; + + inline bool operator != (const Matrix4& mat) const; + + // Construct an identity 4x4 matrix + // + static inline const Matrix4 identity( ); + + // Construct a 4x4 matrix to rotate around the x axis + // + static inline const Matrix4 rotationX( float radians ); + + // Construct a 4x4 matrix to rotate around the y axis + // + static inline const Matrix4 rotationY( float radians ); + + // Construct a 4x4 matrix to rotate around the z axis + // + static inline const Matrix4 rotationZ( float radians ); + + // Construct a 4x4 matrix to rotate around the x axis (scalar data contained in vector data type) + // + static inline const Matrix4 rotationX( floatInVec radians ); + + // Construct a 4x4 matrix to rotate around the y axis (scalar data contained in vector data type) + // + static inline const Matrix4 rotationY( floatInVec radians ); + + // Construct a 4x4 matrix to rotate around the z axis (scalar data contained in vector data type) + // + static inline const Matrix4 rotationZ( floatInVec radians ); + + // Construct a 4x4 matrix to rotate around the x, y, and z axes + // + static inline const Matrix4 rotationZYX( Vector3 radiansXYZ ); + + // Construct a 4x4 matrix to rotate around a unit-length 3-D vector + // + static inline const Matrix4 rotation( float radians, Vector3 unitVec ); + + // Construct a 4x4 matrix to rotate around a unit-length 3-D vector (scalar data contained in vector data type) + // + static inline const Matrix4 rotation( floatInVec radians, Vector3 unitVec ); + + // Construct a rotation matrix from a unit-length quaternion + // + static inline const Matrix4 rotation( Quat unitQuat ); + + // Construct a 4x4 matrix to perform scaling + // + static inline const Matrix4 scale( Vector3 scaleVec ); + + // Construct a 4x4 matrix to perform translation + // + static inline const Matrix4 translation( Vector3 translateVec ); + + // Construct viewing matrix based on eye position, position looked at, and up direction + // + static inline const Matrix4 lookAt( Point3 eyePos, Point3 lookAtPos, Vector3 upVec ); + + // Construct a perspective projection matrix + // + static inline const Matrix4 perspective( float fovyRadians, float aspect, float zNear, float zFar ); + + // Construct a perspective projection matrix based on frustum + // + static inline const Matrix4 frustum( float left, float right, float bottom, float top, float zNear, float zFar ); + + // Construct an orthographic projection matrix + // + static inline const Matrix4 orthographic( float left, float right, float bottom, float top, float zNear, float zFar ); + +}; +// Multiply a 4x4 matrix by a scalar +// +inline const Matrix4 operator *( float scalar, const Matrix4 & mat ); + +// Multiply a 4x4 matrix by a scalar (scalar data contained in vector data type) +// +inline const Matrix4 operator *( floatInVec scalar, const Matrix4 & mat ); + +// Append (post-multiply) a scale transformation to a 4x4 matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Matrix4 appendScale( const Matrix4 & mat, Vector3 scaleVec ); + +// Prepend (pre-multiply) a scale transformation to a 4x4 matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Matrix4 prependScale( Vector3 scaleVec, const Matrix4 & mat ); + +// Multiply two 4x4 matrices per element +// +inline const Matrix4 mulPerElem( const Matrix4 & mat0, const Matrix4 & mat1 ); + +// Compute the absolute value of a 4x4 matrix per element +// +inline const Matrix4 absPerElem( const Matrix4 & mat ); + +// Transpose of a 4x4 matrix +// +inline const Matrix4 transpose( const Matrix4 & mat ); + +// Compute the inverse of a 4x4 matrix +// NOTE: +// Result is unpredictable when the determinant of mat is equal to or near 0. +// +inline const Matrix4 inverse( const Matrix4 & mat ); + +// Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix +// NOTE: +// This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. The result is unpredictable when the determinant of mat is equal to or near 0. +// +inline const Matrix4 affineInverse( const Matrix4 & mat ); + +// Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix with an orthogonal upper-left 3x3 submatrix +// NOTE: +// This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. +// +inline const Matrix4 orthoInverse( const Matrix4 & mat ); + +// Determinant of a 4x4 matrix +// +inline const floatInVec determinant( const Matrix4 & mat ); + +// Conditionally select between two 4x4 matrices +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// However, the transfer of select1 to a VMX register may use more processing time than a branch. +// Use the boolInVec version for better performance. +// +inline const Matrix4 select( const Matrix4 & mat0, const Matrix4 & mat1, bool select1 ); + +// Conditionally select between two 4x4 matrices (scalar data contained in vector data type) +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// +inline const Matrix4 select( const Matrix4 & mat0, const Matrix4 & mat1, boolInVec select1 ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 4x4 matrix +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Matrix4 & mat ); + +// Print a 4x4 matrix and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Matrix4 & mat, const char * name ); + +#endif + +// A 3x4 transformation matrix in array-of-structures format +// +class Transform3 +{ + Vector3 mCol0; + Vector3 mCol1; + Vector3 mCol2; + Vector3 mCol3; + +public: + // Default constructor; does no initialization + // + inline Transform3( ) { }; + + // Copy a 3x4 transformation matrix + // + inline Transform3( const Transform3 & tfrm ); + + // Construct a 3x4 transformation matrix containing the specified columns + // + inline Transform3( Vector3 col0, Vector3 col1, Vector3 col2, Vector3 col3 ); + + // Construct a 3x4 transformation matrix from a 3x3 matrix and a 3-D vector + // + inline Transform3( const Matrix3 & tfrm, Vector3 translateVec ); + + // Construct a 3x4 transformation matrix from a unit-length quaternion and a 3-D vector + // + inline Transform3( Quat unitQuat, Vector3 translateVec ); + + // Set all elements of a 3x4 transformation matrix to the same scalar value + // + explicit inline Transform3( float scalar ); + + // Set all elements of a 3x4 transformation matrix to the same scalar value (scalar data contained in vector data type) + // + explicit inline Transform3( floatInVec scalar ); + + // Assign one 3x4 transformation matrix to another + // + inline Transform3 & operator =( const Transform3 & tfrm ); + + // Set the upper-left 3x3 submatrix + // + inline Transform3 & setUpper3x3( const Matrix3 & mat3 ); + + // Get the upper-left 3x3 submatrix of a 3x4 transformation matrix + // + inline const Matrix3 getUpper3x3( ) const; + + // Set translation component + // + inline Transform3 & setTranslation( Vector3 translateVec ); + + // Get the translation component of a 3x4 transformation matrix + // + inline const Vector3 getTranslation( ) const; + + // Set column 0 of a 3x4 transformation matrix + // + inline Transform3 & setCol0( Vector3 col0 ); + + // Set column 1 of a 3x4 transformation matrix + // + inline Transform3 & setCol1( Vector3 col1 ); + + // Set column 2 of a 3x4 transformation matrix + // + inline Transform3 & setCol2( Vector3 col2 ); + + // Set column 3 of a 3x4 transformation matrix + // + inline Transform3 & setCol3( Vector3 col3 ); + + // Get column 0 of a 3x4 transformation matrix + // + inline const Vector3 getCol0( ) const; + + // Get column 1 of a 3x4 transformation matrix + // + inline const Vector3 getCol1( ) const; + + // Get column 2 of a 3x4 transformation matrix + // + inline const Vector3 getCol2( ) const; + + // Get column 3 of a 3x4 transformation matrix + // + inline const Vector3 getCol3( ) const; + + // Set the column of a 3x4 transformation matrix referred to by the specified index + // + inline Transform3 & setCol( int col, Vector3 vec ); + + // Set the row of a 3x4 transformation matrix referred to by the specified index + // + inline Transform3 & setRow( int row, Vector4 vec ); + + // Get the column of a 3x4 transformation matrix referred to by the specified index + // + inline const Vector3 getCol( int col ) const; + + // Get the row of a 3x4 transformation matrix referred to by the specified index + // + inline const Vector4 getRow( int row ) const; + + // Subscripting operator to set or get a column + // + inline Vector3 & operator []( int col ); + + // Subscripting operator to get a column + // + inline const Vector3 operator []( int col ) const; + + // Set the element of a 3x4 transformation matrix referred to by column and row indices + // + inline Transform3 & setElem( int col, int row, float val ); + + // Set the element of a 3x4 transformation matrix referred to by column and row indices (scalar data contained in vector data type) + // + inline Transform3 & setElem( int col, int row, floatInVec val ); + + // Get the element of a 3x4 transformation matrix referred to by column and row indices + // + inline const floatInVec getElem( int col, int row ) const; + + // Multiply a 3x4 transformation matrix by a 3-D vector + // + inline const Vector3 operator *( Vector3 vec ) const; + + // Multiply a 3x4 transformation matrix by a 3-D point + // + inline const Point3 operator *( Point3 pnt ) const; + + // Multiply two 3x4 transformation matrices + // + inline const Transform3 operator *( const Transform3 & tfrm ) const; + + // Perform compound assignment and multiplication by a 3x4 transformation matrix + // + inline Transform3 & operator *=( const Transform3 & tfrm ); + + // Construct an identity 3x4 transformation matrix + // + static inline const Transform3 identity( ); + + // Construct a 3x4 transformation matrix to rotate around the x axis + // + static inline const Transform3 rotationX( float radians ); + + // Construct a 3x4 transformation matrix to rotate around the y axis + // + static inline const Transform3 rotationY( float radians ); + + // Construct a 3x4 transformation matrix to rotate around the z axis + // + static inline const Transform3 rotationZ( float radians ); + + // Construct a 3x4 transformation matrix to rotate around the x axis (scalar data contained in vector data type) + // + static inline const Transform3 rotationX( floatInVec radians ); + + // Construct a 3x4 transformation matrix to rotate around the y axis (scalar data contained in vector data type) + // + static inline const Transform3 rotationY( floatInVec radians ); + + // Construct a 3x4 transformation matrix to rotate around the z axis (scalar data contained in vector data type) + // + static inline const Transform3 rotationZ( floatInVec radians ); + + // Construct a 3x4 transformation matrix to rotate around the x, y, and z axes + // + static inline const Transform3 rotationZYX( Vector3 radiansXYZ ); + + // Construct a 3x4 transformation matrix to rotate around a unit-length 3-D vector + // + static inline const Transform3 rotation( float radians, Vector3 unitVec ); + + // Construct a 3x4 transformation matrix to rotate around a unit-length 3-D vector (scalar data contained in vector data type) + // + static inline const Transform3 rotation( floatInVec radians, Vector3 unitVec ); + + // Construct a rotation matrix from a unit-length quaternion + // + static inline const Transform3 rotation( Quat unitQuat ); + + // Construct a 3x4 transformation matrix to perform scaling + // + static inline const Transform3 scale( Vector3 scaleVec ); + + // Construct a 3x4 transformation matrix to perform translation + // + static inline const Transform3 translation( Vector3 translateVec ); + +}; +// Append (post-multiply) a scale transformation to a 3x4 transformation matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Transform3 appendScale( const Transform3 & tfrm, Vector3 scaleVec ); + +// Prepend (pre-multiply) a scale transformation to a 3x4 transformation matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Transform3 prependScale( Vector3 scaleVec, const Transform3 & tfrm ); + +// Multiply two 3x4 transformation matrices per element +// +inline const Transform3 mulPerElem( const Transform3 & tfrm0, const Transform3 & tfrm1 ); + +// Compute the absolute value of a 3x4 transformation matrix per element +// +inline const Transform3 absPerElem( const Transform3 & tfrm ); + +// Inverse of a 3x4 transformation matrix +// NOTE: +// Result is unpredictable when the determinant of the left 3x3 submatrix is equal to or near 0. +// +inline const Transform3 inverse( const Transform3 & tfrm ); + +// Compute the inverse of a 3x4 transformation matrix, expected to have an orthogonal upper-left 3x3 submatrix +// NOTE: +// This can be used to achieve better performance than a general inverse when the specified 3x4 transformation matrix meets the given restrictions. +// +inline const Transform3 orthoInverse( const Transform3 & tfrm ); + +// Conditionally select between two 3x4 transformation matrices +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// However, the transfer of select1 to a VMX register may use more processing time than a branch. +// Use the boolInVec version for better performance. +// +inline const Transform3 select( const Transform3 & tfrm0, const Transform3 & tfrm1, bool select1 ); + +// Conditionally select between two 3x4 transformation matrices (scalar data contained in vector data type) +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// +inline const Transform3 select( const Transform3 & tfrm0, const Transform3 & tfrm1, boolInVec select1 ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 3x4 transformation matrix +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Transform3 & tfrm ); + +// Print a 3x4 transformation matrix and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Transform3 & tfrm, const char * name ); + +#endif + +} // namespace Aos +} // namespace Vectormath + +#include "vec_aos.h" +#include "quat_aos.h" +#include "mat_aos.h" + +#endif diff --git a/common/vectormath/spu/c/mat_aos.h b/common/vectormath/spu/c/mat_aos.h index d6b4cb0b..f738e880 100644 --- a/common/vectormath/spu/c/mat_aos.h +++ b/common/vectormath/spu/c/mat_aos.h @@ -1,1833 +1,1833 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_MAT_AOS_C_H -#define _VECTORMATH_MAT_AOS_C_H - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/*----------------------------------------------------------------------------- - * Constants - * for shuffles, words are labeled [x,y,z,w] [a,b,c,d] - */ -#define _VECTORMATH_SHUF_XAYB ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_B }) -#define _VECTORMATH_SHUF_ZCWD ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_D }) -#define _VECTORMATH_SHUF_ZBW0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_XCY0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_XYAB ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B }) -#define _VECTORMATH_SHUF_ZWCD ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_D }) -#define _VECTORMATH_SHUF_0ZB0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_C0X0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_YA00 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_XAZC ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_C }) -#define _VECTORMATH_SHUF_YXWZ ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_Z }) -#define _VECTORMATH_SHUF_YBWD ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_D }) -#define _VECTORMATH_SHUF_XYCX ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_X }) -#define _VECTORMATH_SHUF_YCXY ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y }) -#define _VECTORMATH_SHUF_CXYC ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_C }) -#define _VECTORMATH_SHUF_ZAY0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_BZX0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_0ZYA ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_A }) -#define _VECTORMATH_SHUF_Z0XB ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_B }) -#define _VECTORMATH_SHUF_YX0C ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_C }) -#define _VECTORMATH_SHUF_CZD0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_D, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_BBY0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_PI_OVER_2 1.570796327f - -/*----------------------------------------------------------------------------- - * Definitions - */ -static inline void vmathM3Copy( VmathMatrix3 *result, const VmathMatrix3 *mat ) -{ - vmathV3Copy( &result->col0, &mat->col0 ); - vmathV3Copy( &result->col1, &mat->col1 ); - vmathV3Copy( &result->col2, &mat->col2 ); -} - -static inline void vmathM3MakeFromScalar( VmathMatrix3 *result, float scalar ) -{ - vmathV3MakeFromScalar( &result->col0, scalar ); - vmathV3MakeFromScalar( &result->col1, scalar ); - vmathV3MakeFromScalar( &result->col2, scalar ); -} - -static inline void vmathM3MakeFromQ( VmathMatrix3 *result, const VmathQuat *unitQuat ) -{ - vec_float4 xyzw_2, wwww, yzxw, zxyw, yzxw_2, zxyw_2; - vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; - vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); - vec_uint4 select_x = (vec_uint4)spu_maskb(0xf000); - vec_uint4 select_z = (vec_uint4)spu_maskb(0x00f0); - xyzw_2 = spu_add( unitQuat->vec128, unitQuat->vec128 ); - wwww = spu_shuffle( unitQuat->vec128, unitQuat->vec128, shuffle_wwww ); - yzxw = spu_shuffle( unitQuat->vec128, unitQuat->vec128, _VECTORMATH_SHUF_YZXW ); - zxyw = spu_shuffle( unitQuat->vec128, unitQuat->vec128, _VECTORMATH_SHUF_ZXYW ); - yzxw_2 = spu_shuffle( xyzw_2, xyzw_2, _VECTORMATH_SHUF_YZXW ); - zxyw_2 = spu_shuffle( xyzw_2, xyzw_2, _VECTORMATH_SHUF_ZXYW ); - tmp0 = spu_mul( yzxw_2, wwww ); - tmp1 = spu_nmsub( yzxw, yzxw_2, spu_splats(1.0f) ); - tmp2 = spu_mul( yzxw, xyzw_2 ); - tmp0 = spu_madd( zxyw, xyzw_2, tmp0 ); - tmp1 = spu_nmsub( zxyw, zxyw_2, tmp1 ); - tmp2 = spu_nmsub( zxyw_2, wwww, tmp2 ); - tmp3 = spu_sel( tmp0, tmp1, select_x ); - tmp4 = spu_sel( tmp1, tmp2, select_x ); - tmp5 = spu_sel( tmp2, tmp0, select_x ); - result->col0.vec128 = spu_sel( tmp3, tmp2, select_z ); - result->col1.vec128 = spu_sel( tmp4, tmp0, select_z ); - result->col2.vec128 = spu_sel( tmp5, tmp1, select_z ); -} - -static inline void vmathM3MakeFromCols( VmathMatrix3 *result, const VmathVector3 *_col0, const VmathVector3 *_col1, const VmathVector3 *_col2 ) -{ - vmathV3Copy( &result->col0, _col0 ); - vmathV3Copy( &result->col1, _col1 ); - vmathV3Copy( &result->col2, _col2 ); -} - -static inline void vmathM3SetCol0( VmathMatrix3 *result, const VmathVector3 *_col0 ) -{ - vmathV3Copy( &result->col0, _col0 ); -} - -static inline void vmathM3SetCol1( VmathMatrix3 *result, const VmathVector3 *_col1 ) -{ - vmathV3Copy( &result->col1, _col1 ); -} - -static inline void vmathM3SetCol2( VmathMatrix3 *result, const VmathVector3 *_col2 ) -{ - vmathV3Copy( &result->col2, _col2 ); -} - -static inline void vmathM3SetCol( VmathMatrix3 *result, int col, const VmathVector3 *vec ) -{ - vmathV3Copy( (&result->col0 + col), vec ); -} - -static inline void vmathM3SetRow( VmathMatrix3 *result, int row, const VmathVector3 *vec ) -{ - vmathV3SetElem( &result->col0, row, vmathV3GetElem( vec, 0 ) ); - vmathV3SetElem( &result->col1, row, vmathV3GetElem( vec, 1 ) ); - vmathV3SetElem( &result->col2, row, vmathV3GetElem( vec, 2 ) ); -} - -static inline void vmathM3SetElem( VmathMatrix3 *result, int col, int row, float val ) -{ - VmathVector3 tmpV3_0; - vmathM3GetCol( &tmpV3_0, result, col ); - vmathV3SetElem( &tmpV3_0, row, val ); - vmathM3SetCol( result, col, &tmpV3_0 ); -} - -static inline float vmathM3GetElem( const VmathMatrix3 *mat, int col, int row ) -{ - VmathVector3 tmpV3_0; - vmathM3GetCol( &tmpV3_0, mat, col ); - return vmathV3GetElem( &tmpV3_0, row ); -} - -static inline void vmathM3GetCol0( VmathVector3 *result, const VmathMatrix3 *mat ) -{ - vmathV3Copy( result, &mat->col0 ); -} - -static inline void vmathM3GetCol1( VmathVector3 *result, const VmathMatrix3 *mat ) -{ - vmathV3Copy( result, &mat->col1 ); -} - -static inline void vmathM3GetCol2( VmathVector3 *result, const VmathMatrix3 *mat ) -{ - vmathV3Copy( result, &mat->col2 ); -} - -static inline void vmathM3GetCol( VmathVector3 *result, const VmathMatrix3 *mat, int col ) -{ - vmathV3Copy( result, (&mat->col0 + col) ); -} - -static inline void vmathM3GetRow( VmathVector3 *result, const VmathMatrix3 *mat, int row ) -{ - vmathV3MakeFromElems( result, vmathV3GetElem( &mat->col0, row ), vmathV3GetElem( &mat->col1, row ), vmathV3GetElem( &mat->col2, row ) ); -} - -static inline void vmathM3Transpose( VmathMatrix3 *result, const VmathMatrix3 *mat ) -{ - vec_float4 tmp0, tmp1, res0, res1, res2; - tmp0 = spu_shuffle( mat->col0.vec128, mat->col2.vec128, _VECTORMATH_SHUF_XAYB ); - tmp1 = spu_shuffle( mat->col0.vec128, mat->col2.vec128, _VECTORMATH_SHUF_ZCWD ); - res0 = spu_shuffle( tmp0, mat->col1.vec128, _VECTORMATH_SHUF_XAYB ); - res1 = spu_shuffle( tmp0, mat->col1.vec128, _VECTORMATH_SHUF_ZBW0 ); - res2 = spu_shuffle( tmp1, mat->col1.vec128, _VECTORMATH_SHUF_XCY0 ); - result->col0.vec128 = res0; - result->col1.vec128 = res1; - result->col2.vec128 = res2; -} - -static inline void vmathM3Inverse( VmathMatrix3 *result, const VmathMatrix3 *mat ) -{ - vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet, inv0, inv1, inv2; - tmp2 = _vmathVfCross( mat->col0.vec128, mat->col1.vec128 ); - tmp0 = _vmathVfCross( mat->col1.vec128, mat->col2.vec128 ); - tmp1 = _vmathVfCross( mat->col2.vec128, mat->col0.vec128 ); - dot = _vmathVfDot3( tmp2, mat->col2.vec128 ); - dot = spu_shuffle( dot, dot, (vec_uchar16)spu_splats(0x00010203) ); - invdet = recipf4( dot ); - tmp3 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_XAYB ); - tmp4 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_ZCWD ); - inv0 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_XAYB ); - inv1 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_ZBW0 ); - inv2 = spu_shuffle( tmp4, tmp1, _VECTORMATH_SHUF_XCY0 ); - inv0 = spu_mul( inv0, invdet ); - inv1 = spu_mul( inv1, invdet ); - inv2 = spu_mul( inv2, invdet ); - result->col0.vec128 = inv0; - result->col1.vec128 = inv1; - result->col2.vec128 = inv2; -} - -static inline float vmathM3Determinant( const VmathMatrix3 *mat ) -{ - VmathVector3 tmpV3_0; - vmathV3Cross( &tmpV3_0, &mat->col0, &mat->col1 ); - return vmathV3Dot( &mat->col2, &tmpV3_0 ); -} - -static inline void vmathM3Add( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) -{ - vmathV3Add( &result->col0, &mat0->col0, &mat1->col0 ); - vmathV3Add( &result->col1, &mat0->col1, &mat1->col1 ); - vmathV3Add( &result->col2, &mat0->col2, &mat1->col2 ); -} - -static inline void vmathM3Sub( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) -{ - vmathV3Sub( &result->col0, &mat0->col0, &mat1->col0 ); - vmathV3Sub( &result->col1, &mat0->col1, &mat1->col1 ); - vmathV3Sub( &result->col2, &mat0->col2, &mat1->col2 ); -} - -static inline void vmathM3Neg( VmathMatrix3 *result, const VmathMatrix3 *mat ) -{ - vmathV3Neg( &result->col0, &mat->col0 ); - vmathV3Neg( &result->col1, &mat->col1 ); - vmathV3Neg( &result->col2, &mat->col2 ); -} - -static inline void vmathM3AbsPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat ) -{ - vmathV3AbsPerElem( &result->col0, &mat->col0 ); - vmathV3AbsPerElem( &result->col1, &mat->col1 ); - vmathV3AbsPerElem( &result->col2, &mat->col2 ); -} - -static inline void vmathM3ScalarMul( VmathMatrix3 *result, const VmathMatrix3 *mat, float scalar ) -{ - vmathV3ScalarMul( &result->col0, &mat->col0, scalar ); - vmathV3ScalarMul( &result->col1, &mat->col1, scalar ); - vmathV3ScalarMul( &result->col2, &mat->col2, scalar ); -} - -static inline void vmathM3MulV3( VmathVector3 *result, const VmathMatrix3 *mat, const VmathVector3 *vec ) -{ - vec_float4 res; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - xxxx = spu_shuffle( vec->vec128, vec->vec128, shuffle_xxxx ); - yyyy = spu_shuffle( vec->vec128, vec->vec128, shuffle_yyyy ); - zzzz = spu_shuffle( vec->vec128, vec->vec128, shuffle_zzzz ); - res = spu_mul( mat->col0.vec128, xxxx ); - res = spu_madd( mat->col1.vec128, yyyy, res ); - res = spu_madd( mat->col2.vec128, zzzz, res ); - result->vec128 = res; -} - -static inline void vmathM3Mul( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) -{ - VmathMatrix3 tmpResult; - vmathM3MulV3( &tmpResult.col0, mat0, &mat1->col0 ); - vmathM3MulV3( &tmpResult.col1, mat0, &mat1->col1 ); - vmathM3MulV3( &tmpResult.col2, mat0, &mat1->col2 ); - vmathM3Copy( result, &tmpResult ); -} - -static inline void vmathM3MulPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) -{ - vmathV3MulPerElem( &result->col0, &mat0->col0, &mat1->col0 ); - vmathV3MulPerElem( &result->col1, &mat0->col1, &mat1->col1 ); - vmathV3MulPerElem( &result->col2, &mat0->col2, &mat1->col2 ); -} - -static inline void vmathM3MakeIdentity( VmathMatrix3 *result ) -{ - vmathV3MakeXAxis( &result->col0 ); - vmathV3MakeYAxis( &result->col1 ); - vmathV3MakeZAxis( &result->col2 ); -} - -static inline void vmathM3MakeRotationX( VmathMatrix3 *result, float radians ) -{ - vec_float4 s, c, res1, res2; - vec_uint4 select_y, select_z; - vec_float4 zero; - select_y = (vec_uint4)spu_maskb(0x0f00); - select_z = (vec_uint4)spu_maskb(0x00f0); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res1 = spu_sel( zero, c, select_y ); - res1 = spu_sel( res1, s, select_z ); - res2 = spu_sel( zero, negatef4(s), select_y ); - res2 = spu_sel( res2, c, select_z ); - vmathV3MakeXAxis( &result->col0 ); - result->col1.vec128 = res1; - result->col2.vec128 = res2; -} - -static inline void vmathM3MakeRotationY( VmathMatrix3 *result, float radians ) -{ - vec_float4 s, c, res0, res2; - vec_uint4 select_x, select_z; - vec_float4 zero; - select_x = (vec_uint4)spu_maskb(0xf000); - select_z = (vec_uint4)spu_maskb(0x00f0); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res0 = spu_sel( zero, c, select_x ); - res0 = spu_sel( res0, negatef4(s), select_z ); - res2 = spu_sel( zero, s, select_x ); - res2 = spu_sel( res2, c, select_z ); - result->col0.vec128 = res0; - vmathV3MakeYAxis( &result->col1 ); - result->col2.vec128 = res2; -} - -static inline void vmathM3MakeRotationZ( VmathMatrix3 *result, float radians ) -{ - vec_float4 s, c, res0, res1; - vec_uint4 select_x, select_y; - vec_float4 zero; - select_x = (vec_uint4)spu_maskb(0xf000); - select_y = (vec_uint4)spu_maskb(0x0f00); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res0 = spu_sel( zero, c, select_x ); - res0 = spu_sel( res0, s, select_y ); - res1 = spu_sel( zero, negatef4(s), select_x ); - res1 = spu_sel( res1, c, select_y ); - result->col0.vec128 = res0; - result->col1.vec128 = res1; - vmathV3MakeZAxis( &result->col2 ); -} - -static inline void vmathM3MakeRotationZYX( VmathMatrix3 *result, const VmathVector3 *radiansXYZ ) -{ - vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - angles = radiansXYZ->vec128; - angles = spu_insert( 0.0f, angles, 3 ); - sincosf4( angles, &s, &c ); - negS = negatef4( s ); - Z0 = spu_shuffle( s, c, _VECTORMATH_SHUF_CZD0 ); - Z1 = spu_shuffle( c, negS, _VECTORMATH_SHUF_CZD0 ); - Y0 = spu_shuffle( negS, c, _VECTORMATH_SHUF_BBY0 ); - Y1 = spu_shuffle( c, s, _VECTORMATH_SHUF_BBY0 ); - X0 = spu_shuffle( s, s, shuffle_xxxx ); - X1 = spu_shuffle( c, c, shuffle_xxxx ); - tmp = spu_mul( Z0, Y1 ); - result->col0.vec128 = spu_mul( Z0, Y0 ); - result->col1.vec128 = spu_madd( Z1, X1, spu_mul( tmp, X0 ) ); - result->col2.vec128 = spu_nmsub( Z1, X0, spu_mul( tmp, X1 ) ); -} - -static inline void vmathM3MakeRotationAxis( VmathMatrix3 *result, float radians, const VmathVector3 *unitVec ) -{ - vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - axis = unitVec->vec128; - sincosf4( spu_splats( radians ), &s, &c ); - xxxx = spu_shuffle( axis, axis, shuffle_xxxx ); - yyyy = spu_shuffle( axis, axis, shuffle_yyyy ); - zzzz = spu_shuffle( axis, axis, shuffle_zzzz ); - oneMinusC = spu_sub( spu_splats(1.0f), c ); - axisS = spu_mul( axis, s ); - negAxisS = negatef4( axisS ); - tmp0 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_0ZB0 ); - tmp1 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_C0X0 ); - tmp2 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_YA00 ); - tmp0 = spu_sel( tmp0, c, (vec_uint4)spu_maskb(0xf000) ); - tmp1 = spu_sel( tmp1, c, (vec_uint4)spu_maskb(0x0f00) ); - tmp2 = spu_sel( tmp2, c, (vec_uint4)spu_maskb(0x00f0) ); - result->col0.vec128 = spu_madd( spu_mul( axis, xxxx ), oneMinusC, tmp0 ); - result->col1.vec128 = spu_madd( spu_mul( axis, yyyy ), oneMinusC, tmp1 ); - result->col2.vec128 = spu_madd( spu_mul( axis, zzzz ), oneMinusC, tmp2 ); -} - -static inline void vmathM3MakeRotationQ( VmathMatrix3 *result, const VmathQuat *unitQuat ) -{ - vmathM3MakeFromQ( result, unitQuat ); -} - -static inline void vmathM3MakeScale( VmathMatrix3 *result, const VmathVector3 *scaleVec ) -{ - vec_float4 zero = spu_splats(0.0f); - result->col0.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0xf000) ); - result->col1.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0x0f00) ); - result->col2.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0x00f0) ); -} - -static inline void vmathM3AppendScale( VmathMatrix3 *result, const VmathMatrix3 *mat, const VmathVector3 *scaleVec ) -{ - vmathV3ScalarMul( &result->col0, &mat->col0, vmathV3GetX( scaleVec ) ); - vmathV3ScalarMul( &result->col1, &mat->col1, vmathV3GetY( scaleVec ) ); - vmathV3ScalarMul( &result->col2, &mat->col2, vmathV3GetZ( scaleVec ) ); -} - -static inline void vmathM3PrependScale( VmathMatrix3 *result, const VmathVector3 *scaleVec, const VmathMatrix3 *mat ) -{ - vmathV3MulPerElem( &result->col0, &mat->col0, scaleVec ); - vmathV3MulPerElem( &result->col1, &mat->col1, scaleVec ); - vmathV3MulPerElem( &result->col2, &mat->col2, scaleVec ); -} - -static inline void vmathM3Select( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1, unsigned int select1 ) -{ - vmathV3Select( &result->col0, &mat0->col0, &mat1->col0, select1 ); - vmathV3Select( &result->col1, &mat0->col1, &mat1->col1, select1 ); - vmathV3Select( &result->col2, &mat0->col2, &mat1->col2, select1 ); -} - -#ifdef _VECTORMATH_DEBUG - -static inline void vmathM3Print( const VmathMatrix3 *mat ) -{ - VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2; - vmathM3GetRow( &tmpV3_0, mat, 0 ); - vmathV3Print( &tmpV3_0 ); - vmathM3GetRow( &tmpV3_1, mat, 1 ); - vmathV3Print( &tmpV3_1 ); - vmathM3GetRow( &tmpV3_2, mat, 2 ); - vmathV3Print( &tmpV3_2 ); -} - -static inline void vmathM3Prints( const VmathMatrix3 *mat, const char *name ) -{ - printf("%s:\n", name); - vmathM3Print( mat ); -} - -#endif - -static inline void vmathM4Copy( VmathMatrix4 *result, const VmathMatrix4 *mat ) -{ - vmathV4Copy( &result->col0, &mat->col0 ); - vmathV4Copy( &result->col1, &mat->col1 ); - vmathV4Copy( &result->col2, &mat->col2 ); - vmathV4Copy( &result->col3, &mat->col3 ); -} - -static inline void vmathM4MakeFromScalar( VmathMatrix4 *result, float scalar ) -{ - vmathV4MakeFromScalar( &result->col0, scalar ); - vmathV4MakeFromScalar( &result->col1, scalar ); - vmathV4MakeFromScalar( &result->col2, scalar ); - vmathV4MakeFromScalar( &result->col3, scalar ); -} - -static inline void vmathM4MakeFromT3( VmathMatrix4 *result, const VmathTransform3 *mat ) -{ - vmathV4MakeFromV3Scalar( &result->col0, &mat->col0, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col1, &mat->col1, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col2, &mat->col2, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col3, &mat->col3, 1.0f ); -} - -static inline void vmathM4MakeFromCols( VmathMatrix4 *result, const VmathVector4 *_col0, const VmathVector4 *_col1, const VmathVector4 *_col2, const VmathVector4 *_col3 ) -{ - vmathV4Copy( &result->col0, _col0 ); - vmathV4Copy( &result->col1, _col1 ); - vmathV4Copy( &result->col2, _col2 ); - vmathV4Copy( &result->col3, _col3 ); -} - -static inline void vmathM4MakeFromM3V3( VmathMatrix4 *result, const VmathMatrix3 *mat, const VmathVector3 *translateVec ) -{ - vmathV4MakeFromV3Scalar( &result->col0, &mat->col0, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col1, &mat->col1, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col2, &mat->col2, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col3, translateVec, 1.0f ); -} - -static inline void vmathM4MakeFromQV3( VmathMatrix4 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ) -{ - VmathMatrix3 mat; - vmathM3MakeFromQ( &mat, unitQuat ); - vmathV4MakeFromV3Scalar( &result->col0, &mat.col0, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col1, &mat.col1, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col2, &mat.col2, 0.0f ); - vmathV4MakeFromV3Scalar( &result->col3, translateVec, 1.0f ); -} - -static inline void vmathM4SetCol0( VmathMatrix4 *result, const VmathVector4 *_col0 ) -{ - vmathV4Copy( &result->col0, _col0 ); -} - -static inline void vmathM4SetCol1( VmathMatrix4 *result, const VmathVector4 *_col1 ) -{ - vmathV4Copy( &result->col1, _col1 ); -} - -static inline void vmathM4SetCol2( VmathMatrix4 *result, const VmathVector4 *_col2 ) -{ - vmathV4Copy( &result->col2, _col2 ); -} - -static inline void vmathM4SetCol3( VmathMatrix4 *result, const VmathVector4 *_col3 ) -{ - vmathV4Copy( &result->col3, _col3 ); -} - -static inline void vmathM4SetCol( VmathMatrix4 *result, int col, const VmathVector4 *vec ) -{ - vmathV4Copy( (&result->col0 + col), vec ); -} - -static inline void vmathM4SetRow( VmathMatrix4 *result, int row, const VmathVector4 *vec ) -{ - vmathV4SetElem( &result->col0, row, vmathV4GetElem( vec, 0 ) ); - vmathV4SetElem( &result->col1, row, vmathV4GetElem( vec, 1 ) ); - vmathV4SetElem( &result->col2, row, vmathV4GetElem( vec, 2 ) ); - vmathV4SetElem( &result->col3, row, vmathV4GetElem( vec, 3 ) ); -} - -static inline void vmathM4SetElem( VmathMatrix4 *result, int col, int row, float val ) -{ - VmathVector4 tmpV3_0; - vmathM4GetCol( &tmpV3_0, result, col ); - vmathV4SetElem( &tmpV3_0, row, val ); - vmathM4SetCol( result, col, &tmpV3_0 ); -} - -static inline float vmathM4GetElem( const VmathMatrix4 *mat, int col, int row ) -{ - VmathVector4 tmpV4_0; - vmathM4GetCol( &tmpV4_0, mat, col ); - return vmathV4GetElem( &tmpV4_0, row ); -} - -static inline void vmathM4GetCol0( VmathVector4 *result, const VmathMatrix4 *mat ) -{ - vmathV4Copy( result, &mat->col0 ); -} - -static inline void vmathM4GetCol1( VmathVector4 *result, const VmathMatrix4 *mat ) -{ - vmathV4Copy( result, &mat->col1 ); -} - -static inline void vmathM4GetCol2( VmathVector4 *result, const VmathMatrix4 *mat ) -{ - vmathV4Copy( result, &mat->col2 ); -} - -static inline void vmathM4GetCol3( VmathVector4 *result, const VmathMatrix4 *mat ) -{ - vmathV4Copy( result, &mat->col3 ); -} - -static inline void vmathM4GetCol( VmathVector4 *result, const VmathMatrix4 *mat, int col ) -{ - vmathV4Copy( result, (&mat->col0 + col) ); -} - -static inline void vmathM4GetRow( VmathVector4 *result, const VmathMatrix4 *mat, int row ) -{ - vmathV4MakeFromElems( result, vmathV4GetElem( &mat->col0, row ), vmathV4GetElem( &mat->col1, row ), vmathV4GetElem( &mat->col2, row ), vmathV4GetElem( &mat->col3, row ) ); -} - -static inline void vmathM4Transpose( VmathMatrix4 *result, const VmathMatrix4 *mat ) -{ - vec_float4 tmp0, tmp1, tmp2, tmp3, res0, res1, res2, res3; - tmp0 = spu_shuffle( mat->col0.vec128, mat->col2.vec128, _VECTORMATH_SHUF_XAYB ); - tmp1 = spu_shuffle( mat->col1.vec128, mat->col3.vec128, _VECTORMATH_SHUF_XAYB ); - tmp2 = spu_shuffle( mat->col0.vec128, mat->col2.vec128, _VECTORMATH_SHUF_ZCWD ); - tmp3 = spu_shuffle( mat->col1.vec128, mat->col3.vec128, _VECTORMATH_SHUF_ZCWD ); - res0 = spu_shuffle( tmp0, tmp1, _VECTORMATH_SHUF_XAYB ); - res1 = spu_shuffle( tmp0, tmp1, _VECTORMATH_SHUF_ZCWD ); - res2 = spu_shuffle( tmp2, tmp3, _VECTORMATH_SHUF_XAYB ); - res3 = spu_shuffle( tmp2, tmp3, _VECTORMATH_SHUF_ZCWD ); - result->col0.vec128 = res0; - result->col1.vec128 = res1; - result->col2.vec128 = res2; - result->col3.vec128 = res3; -} - -static inline void vmathM4Inverse( VmathMatrix4 *result, const VmathMatrix4 *mat ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vec_float4 in0, in1, in2, in3; - vec_float4 tmp0, tmp1, tmp2, tmp3; - vec_float4 cof0, cof1, cof2, cof3; - vec_float4 t0, t1, t2, t3; - vec_float4 t01, t02, t03, t12, t23; - vec_float4 t1r, t2r; - vec_float4 t01r, t02r, t03r, t12r, t23r; - vec_float4 t1r3, t1r3r; - vec_float4 det, det1, det2, det3, invdet; - in0 = mat->col0.vec128; - in1 = mat->col1.vec128; - in2 = mat->col2.vec128; - in3 = mat->col3.vec128; - /* Perform transform of the input matrix of the form: - * A B C D - * E F G H - * I J K L - * M N O P - * - * The pseudo transpose of the input matrix is trans: - * A E I M - * J N B F - * C G K O - * L P D H - */ - tmp0 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_XAZC); /* A E C G */ - tmp1 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_XAZC); /* I M K O */ - tmp2 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_YBWD); /* B F D H */ - tmp3 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_YBWD); /* J N L P */ - t0 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_XYAB); /* A E I M */ - t1 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_XYAB); /* J N B F */ - t2 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_ZWCD); /* C G K O */ - t3 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_ZWCD); /* L P D H */ - /* Generate a cofactor matrix. The computed cofactors reside in - * cof0, cof1, cof2, cof3. - */ - t23 = spu_mul(t2, t3); /* CL GP KD OH */ - t23 = spu_shuffle(t23, t23, _VECTORMATH_SHUF_YXWZ); /* GP CL OH KD */ - cof0 = spu_mul(t1, t23); /* JGP NCL BOH FKD */ - cof1 = spu_mul(t0, t23); /* AGP ECL IOH MKD */ - t23r = spu_rlqwbyte(t23, 8); /* OH KD GP CL */ - cof0 = spu_msub(t1, t23r, cof0); /* JOH NKD BGP FCL - cof0 */ - cof1 = spu_msub(t0, t23r, cof1); /* AOH EKD IGP MCL - cof1 */ - cof1 = spu_rlqwbyte(cof1, 8); /* IGP MCL AOH EKD - IOH MKD AGP ECL */ - - t12 = spu_mul(t1, t2); /* JC NG BK FO */ - t12 = spu_shuffle(t12, t12, _VECTORMATH_SHUF_YXWZ); /* NG JC FO BK */ - cof0 = spu_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ - cof3 = spu_mul(t0, t12); /* ANG EJC IFO MBK */ - t12r = spu_rlqwbyte(t12, 8); /* FO BK NG JC */ - cof0 = spu_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ - cof3 = spu_msub(t0, t12r, cof3); /* AFO EBK ING MJC - cof3 */ - cof3 = spu_rlqwbyte(cof3, 8); /* ING MJC AFO EBK - IFO MBK ANG EJC */ - t1r = spu_rlqwbyte(t1, 8); /* B F J N */ - t2r = spu_rlqwbyte(t2, 8); /* K O C G */ - t1r3 = spu_mul(t1r, t3); /* BL FP JD NH */ - t1r3 = spu_shuffle(t1r3, t1r3, _VECTORMATH_SHUF_YXWZ); /* FP BL NH JD */ - cof0 = spu_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ - cof2 = spu_mul(t0, t1r3); /* AFP EBL INH MJD */ - t1r3r = spu_rlqwbyte(t1r3, 8); /* NH JD FP BL */ - cof0 = spu_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ - cof2 = spu_msub(t0, t1r3r, cof2); /* ANH EJD IFP MBL - cof2 */ - cof2 = spu_rlqwbyte(cof2, 8); /* IFP MBL ANH EJD - INH MJD AFP EBL */ - t01 = spu_mul(t0, t1); /* AJ EN IB MF */ - t01 = spu_shuffle(t01, t01, _VECTORMATH_SHUF_YXWZ); /* EN AJ MF IB */ - cof2 = spu_madd(t3, t01, cof2); /* LEN PAJ DMF HIB + cof2 */ - cof3 = spu_msub(t2r, t01, cof3); /* KEN OAJ CMF GIB - cof3 */ - t01r = spu_rlqwbyte(t01, 8); /* MF IB EN AJ */ - cof2 = spu_msub(t3, t01r, cof2); /* LMF PIB DEN HAJ - cof2 */ - cof3 = spu_nmsub(t2r, t01r, cof3); /* cof3 - KMF OIB CEN GAJ */ - t03 = spu_mul(t0, t3); /* AL EP ID MH */ - t03 = spu_shuffle(t03, t03, _VECTORMATH_SHUF_YXWZ); /* EP AL MH ID */ - cof1 = spu_nmsub(t2r, t03, cof1); /* cof1 - KEP OAL CMH GID */ - cof2 = spu_madd(t1, t03, cof2); /* JEP NAL BMH FID + cof2 */ - t03r = spu_rlqwbyte(t03, 8); /* MH ID EP AL */ - cof1 = spu_madd(t2r, t03r, cof1); /* KMH OID CEP GAL + cof1 */ - cof2 = spu_nmsub(t1, t03r, cof2); /* cof2 - JMH NID BEP FAL */ - t02 = spu_mul(t0, t2r); /* AK EO IC MG */ - t02 = spu_shuffle(t02, t02, _VECTORMATH_SHUF_YXWZ); /* E0 AK MG IC */ - cof1 = spu_madd(t3, t02, cof1); /* LEO PAK DMG HIC + cof1 */ - cof3 = spu_nmsub(t1, t02, cof3); /* cof3 - JEO NAK BMG FIC */ - t02r = spu_rlqwbyte(t02, 8); /* MG IC EO AK */ - cof1 = spu_nmsub(t3, t02r, cof1); /* cof1 - LMG PIC DEO HAK */ - cof3 = spu_madd(t1, t02r, cof3); /* JMG NIC BEO FAK + cof3 */ - /* Compute the determinant of the matrix - * - * det = sum_across(t0 * cof0); - * - * We perform a sum across the entire vector so that - * we don't have to splat the result when multiplying the - * cofactors by the inverse of the determinant. - */ - det = spu_mul(t0, cof0); - det1 = spu_rlqwbyte(det, 4); - det2 = spu_rlqwbyte(det, 8); - det3 = spu_rlqwbyte(det, 12); - det = spu_add(det, det1); - det2 = spu_add(det2, det3); - det = spu_add(det, det2); - /* Compute the reciprocal of the determinant. - */ - invdet = recipf4(det); - /* Multiply the cofactors by the reciprocal of the determinant. - */ - result->col0.vec128 = spu_mul(cof0, invdet); - result->col1.vec128 = spu_mul(cof1, invdet); - result->col2.vec128 = spu_mul(cof2, invdet); - result->col3.vec128 = spu_mul(cof3, invdet); -} - -static inline void vmathM4AffineInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ) -{ - VmathTransform3 affineMat, tmpT3_0; - VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2, tmpV3_3; - vmathV4GetXYZ( &tmpV3_0, &mat->col0 ); - vmathT3SetCol0( &affineMat, &tmpV3_0 ); - vmathV4GetXYZ( &tmpV3_1, &mat->col1 ); - vmathT3SetCol1( &affineMat, &tmpV3_1 ); - vmathV4GetXYZ( &tmpV3_2, &mat->col2 ); - vmathT3SetCol2( &affineMat, &tmpV3_2 ); - vmathV4GetXYZ( &tmpV3_3, &mat->col3 ); - vmathT3SetCol3( &affineMat, &tmpV3_3 ); - vmathT3Inverse( &tmpT3_0, &affineMat ); - vmathM4MakeFromT3( result, &tmpT3_0 ); -} - -static inline void vmathM4OrthoInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ) -{ - VmathTransform3 affineMat, tmpT3_0; - VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2, tmpV3_3; - vmathV4GetXYZ( &tmpV3_0, &mat->col0 ); - vmathT3SetCol0( &affineMat, &tmpV3_0 ); - vmathV4GetXYZ( &tmpV3_1, &mat->col1 ); - vmathT3SetCol1( &affineMat, &tmpV3_1 ); - vmathV4GetXYZ( &tmpV3_2, &mat->col2 ); - vmathT3SetCol2( &affineMat, &tmpV3_2 ); - vmathV4GetXYZ( &tmpV3_3, &mat->col3 ); - vmathT3SetCol3( &affineMat, &tmpV3_3 ); - vmathT3OrthoInverse( &tmpT3_0, &affineMat ); - vmathM4MakeFromT3( result, &tmpT3_0 ); -} - -static inline float vmathM4Determinant( const VmathMatrix4 *mat ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vec_float4 in0, in1, in2, in3; - vec_float4 tmp0, tmp1, tmp2, tmp3; - vec_float4 cof0; - vec_float4 t0, t1, t2, t3; - vec_float4 t12, t23; - vec_float4 t1r, t2r; - vec_float4 t12r, t23r; - vec_float4 t1r3, t1r3r; - in0 = mat->col0.vec128; - in1 = mat->col1.vec128; - in2 = mat->col2.vec128; - in3 = mat->col3.vec128; - /* Perform transform of the input matrix of the form: - * A B C D - * E F G H - * I J K L - * M N O P - * - * The pseudo transpose of the input matrix is trans: - * A E I M - * J N B F - * C G K O - * L P D H - */ - tmp0 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_XAZC); /* A E C G */ - tmp1 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_XAZC); /* I M K O */ - tmp2 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_YBWD); /* B F D H */ - tmp3 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_YBWD); /* J N L P */ - t0 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_XYAB); /* A E I M */ - t1 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_XYAB); /* J N B F */ - t2 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_ZWCD); /* C G K O */ - t3 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_ZWCD); /* L P D H */ - /* Generate a cofactor matrix. The computed cofactors reside in - * cof0, cof1, cof2, cof3. - */ - t23 = spu_mul(t2, t3); /* CL GP KD OH */ - t23 = spu_shuffle(t23, t23, _VECTORMATH_SHUF_YXWZ); /* GP CL OH KD */ - cof0 = spu_mul(t1, t23); /* JGP NCL BOH FKD */ - t23r = spu_rlqwbyte(t23, 8); /* OH KD GP CL */ - cof0 = spu_msub(t1, t23r, cof0); /* JOH NKD BGP FCL - cof0 */ - - t12 = spu_mul(t1, t2); /* JC NG BK FO */ - t12 = spu_shuffle(t12, t12, _VECTORMATH_SHUF_YXWZ); /* NG JC FO BK */ - cof0 = spu_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ - t12r = spu_rlqwbyte(t12, 8); /* FO BK NG JC */ - cof0 = spu_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ - t1r = spu_rlqwbyte(t1, 8); /* B F J N */ - t2r = spu_rlqwbyte(t2, 8); /* K O C G */ - t1r3 = spu_mul(t1r, t3); /* BL FP JD NH */ - t1r3 = spu_shuffle(t1r3, t1r3, _VECTORMATH_SHUF_YXWZ); /* FP BL NH JD */ - cof0 = spu_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ - t1r3r = spu_rlqwbyte(t1r3, 8); /* NH JD FP BL */ - cof0 = spu_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ - return spu_extract( _vmathVfDot4(t0,cof0), 0 ); -} - -static inline void vmathM4Add( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) -{ - vmathV4Add( &result->col0, &mat0->col0, &mat1->col0 ); - vmathV4Add( &result->col1, &mat0->col1, &mat1->col1 ); - vmathV4Add( &result->col2, &mat0->col2, &mat1->col2 ); - vmathV4Add( &result->col3, &mat0->col3, &mat1->col3 ); -} - -static inline void vmathM4Sub( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) -{ - vmathV4Sub( &result->col0, &mat0->col0, &mat1->col0 ); - vmathV4Sub( &result->col1, &mat0->col1, &mat1->col1 ); - vmathV4Sub( &result->col2, &mat0->col2, &mat1->col2 ); - vmathV4Sub( &result->col3, &mat0->col3, &mat1->col3 ); -} - -static inline void vmathM4Neg( VmathMatrix4 *result, const VmathMatrix4 *mat ) -{ - vmathV4Neg( &result->col0, &mat->col0 ); - vmathV4Neg( &result->col1, &mat->col1 ); - vmathV4Neg( &result->col2, &mat->col2 ); - vmathV4Neg( &result->col3, &mat->col3 ); -} - -static inline void vmathM4AbsPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat ) -{ - vmathV4AbsPerElem( &result->col0, &mat->col0 ); - vmathV4AbsPerElem( &result->col1, &mat->col1 ); - vmathV4AbsPerElem( &result->col2, &mat->col2 ); - vmathV4AbsPerElem( &result->col3, &mat->col3 ); -} - -static inline void vmathM4ScalarMul( VmathMatrix4 *result, const VmathMatrix4 *mat, float scalar ) -{ - vmathV4ScalarMul( &result->col0, &mat->col0, scalar ); - vmathV4ScalarMul( &result->col1, &mat->col1, scalar ); - vmathV4ScalarMul( &result->col2, &mat->col2, scalar ); - vmathV4ScalarMul( &result->col3, &mat->col3, scalar ); -} - -static inline void vmathM4MulV4( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector4 *vec ) -{ - vec_float4 tmp0, tmp1, res; - vec_float4 xxxx, yyyy, zzzz, wwww; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); - xxxx = spu_shuffle( vec->vec128, vec->vec128, shuffle_xxxx ); - yyyy = spu_shuffle( vec->vec128, vec->vec128, shuffle_yyyy ); - zzzz = spu_shuffle( vec->vec128, vec->vec128, shuffle_zzzz ); - wwww = spu_shuffle( vec->vec128, vec->vec128, shuffle_wwww ); - tmp0 = spu_mul( mat->col0.vec128, xxxx ); - tmp1 = spu_mul( mat->col1.vec128, yyyy ); - tmp0 = spu_madd( mat->col2.vec128, zzzz, tmp0 ); - tmp1 = spu_madd( mat->col3.vec128, wwww, tmp1 ); - res = spu_add( tmp0, tmp1 ); - result->vec128 = res; -} - -static inline void vmathM4MulV3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector3 *vec ) -{ - vec_float4 res; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - xxxx = spu_shuffle( vec->vec128, vec->vec128, shuffle_xxxx ); - yyyy = spu_shuffle( vec->vec128, vec->vec128, shuffle_yyyy ); - zzzz = spu_shuffle( vec->vec128, vec->vec128, shuffle_zzzz ); - res = spu_mul( mat->col0.vec128, xxxx ); - res = spu_madd( mat->col1.vec128, yyyy, res ); - res = spu_madd( mat->col2.vec128, zzzz, res ); - result->vec128 = res; -} - -static inline void vmathM4MulP3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathPoint3 *pnt ) -{ - vec_float4 tmp0, tmp1, res; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - xxxx = spu_shuffle( pnt->vec128, pnt->vec128, shuffle_xxxx ); - yyyy = spu_shuffle( pnt->vec128, pnt->vec128, shuffle_yyyy ); - zzzz = spu_shuffle( pnt->vec128, pnt->vec128, shuffle_zzzz ); - tmp0 = spu_mul( mat->col0.vec128, xxxx ); - tmp1 = spu_mul( mat->col1.vec128, yyyy ); - tmp0 = spu_madd( mat->col2.vec128, zzzz, tmp0 ); - tmp1 = spu_add( mat->col3.vec128, tmp1 ); - res = spu_add( tmp0, tmp1 ); - result->vec128 = res; -} - -static inline void vmathM4Mul( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) -{ - VmathMatrix4 tmpResult; - vmathM4MulV4( &tmpResult.col0, mat0, &mat1->col0 ); - vmathM4MulV4( &tmpResult.col1, mat0, &mat1->col1 ); - vmathM4MulV4( &tmpResult.col2, mat0, &mat1->col2 ); - vmathM4MulV4( &tmpResult.col3, mat0, &mat1->col3 ); - vmathM4Copy( result, &tmpResult ); -} - -static inline void vmathM4MulT3( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathTransform3 *tfrm1 ) -{ - VmathMatrix4 tmpResult; - VmathPoint3 tmpP3_0; - vmathM4MulV3( &tmpResult.col0, mat, &tfrm1->col0 ); - vmathM4MulV3( &tmpResult.col1, mat, &tfrm1->col1 ); - vmathM4MulV3( &tmpResult.col2, mat, &tfrm1->col2 ); - vmathP3MakeFromV3( &tmpP3_0, &tfrm1->col3 ); - vmathM4MulP3( &tmpResult.col3, mat, &tmpP3_0 ); - vmathM4Copy( result, &tmpResult ); -} - -static inline void vmathM4MulPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) -{ - vmathV4MulPerElem( &result->col0, &mat0->col0, &mat1->col0 ); - vmathV4MulPerElem( &result->col1, &mat0->col1, &mat1->col1 ); - vmathV4MulPerElem( &result->col2, &mat0->col2, &mat1->col2 ); - vmathV4MulPerElem( &result->col3, &mat0->col3, &mat1->col3 ); -} - -static inline void vmathM4MakeIdentity( VmathMatrix4 *result ) -{ - vmathV4MakeXAxis( &result->col0 ); - vmathV4MakeYAxis( &result->col1 ); - vmathV4MakeZAxis( &result->col2 ); - vmathV4MakeWAxis( &result->col3 ); -} - -static inline void vmathM4SetUpper3x3( VmathMatrix4 *result, const VmathMatrix3 *mat3 ) -{ - vmathV4SetXYZ( &result->col0, &mat3->col0 ); - vmathV4SetXYZ( &result->col1, &mat3->col1 ); - vmathV4SetXYZ( &result->col2, &mat3->col2 ); -} - -static inline void vmathM4GetUpper3x3( VmathMatrix3 *result, const VmathMatrix4 *mat ) -{ - vmathV4GetXYZ( &result->col0, &mat->col0 ); - vmathV4GetXYZ( &result->col1, &mat->col1 ); - vmathV4GetXYZ( &result->col2, &mat->col2 ); -} - -static inline void vmathM4SetTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ) -{ - vmathV4SetXYZ( &result->col3, translateVec ); -} - -static inline void vmathM4GetTranslation( VmathVector3 *result, const VmathMatrix4 *mat ) -{ - vmathV4GetXYZ( result, &mat->col3 ); -} - -static inline void vmathM4MakeRotationX( VmathMatrix4 *result, float radians ) -{ - vec_float4 s, c, res1, res2; - vec_uint4 select_y, select_z; - vec_float4 zero; - select_y = (vec_uint4)spu_maskb(0x0f00); - select_z = (vec_uint4)spu_maskb(0x00f0); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res1 = spu_sel( zero, c, select_y ); - res1 = spu_sel( res1, s, select_z ); - res2 = spu_sel( zero, negatef4(s), select_y ); - res2 = spu_sel( res2, c, select_z ); - vmathV4MakeXAxis( &result->col0 ); - result->col1.vec128 = res1; - result->col2.vec128 = res2; - vmathV4MakeWAxis( &result->col3 ); -} - -static inline void vmathM4MakeRotationY( VmathMatrix4 *result, float radians ) -{ - vec_float4 s, c, res0, res2; - vec_uint4 select_x, select_z; - vec_float4 zero; - select_x = (vec_uint4)spu_maskb(0xf000); - select_z = (vec_uint4)spu_maskb(0x00f0); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res0 = spu_sel( zero, c, select_x ); - res0 = spu_sel( res0, negatef4(s), select_z ); - res2 = spu_sel( zero, s, select_x ); - res2 = spu_sel( res2, c, select_z ); - result->col0.vec128 = res0; - vmathV4MakeYAxis( &result->col1 ); - result->col2.vec128 = res2; - vmathV4MakeWAxis( &result->col3 ); -} - -static inline void vmathM4MakeRotationZ( VmathMatrix4 *result, float radians ) -{ - vec_float4 s, c, res0, res1; - vec_uint4 select_x, select_y; - vec_float4 zero; - select_x = (vec_uint4)spu_maskb(0xf000); - select_y = (vec_uint4)spu_maskb(0x0f00); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res0 = spu_sel( zero, c, select_x ); - res0 = spu_sel( res0, s, select_y ); - res1 = spu_sel( zero, negatef4(s), select_x ); - res1 = spu_sel( res1, c, select_y ); - result->col0.vec128 = res0; - result->col1.vec128 = res1; - vmathV4MakeZAxis( &result->col2 ); - vmathV4MakeWAxis( &result->col3 ); -} - -static inline void vmathM4MakeRotationZYX( VmathMatrix4 *result, const VmathVector3 *radiansXYZ ) -{ - vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - angles = radiansXYZ->vec128; - angles = spu_insert( 0.0f, angles, 3 ); - sincosf4( angles, &s, &c ); - negS = negatef4( s ); - Z0 = spu_shuffle( s, c, _VECTORMATH_SHUF_CZD0 ); - Z1 = spu_shuffle( c, negS, _VECTORMATH_SHUF_CZD0 ); - Y0 = spu_shuffle( negS, c, _VECTORMATH_SHUF_BBY0 ); - Y1 = spu_shuffle( c, s, _VECTORMATH_SHUF_BBY0 ); - X0 = spu_shuffle( s, s, shuffle_xxxx ); - X1 = spu_shuffle( c, c, shuffle_xxxx ); - tmp = spu_mul( Z0, Y1 ); - result->col0.vec128 = spu_mul( Z0, Y0 ); - result->col1.vec128 = spu_madd( Z1, X1, spu_mul( tmp, X0 ) ); - result->col2.vec128 = spu_nmsub( Z1, X0, spu_mul( tmp, X1 ) ); - vmathV4MakeWAxis( &result->col3 ); -} - -static inline void vmathM4MakeRotationAxis( VmathMatrix4 *result, float radians, const VmathVector3 *unitVec ) -{ - vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2, zeroW; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - axis = unitVec->vec128; - sincosf4( spu_splats( radians ), &s, &c ); - xxxx = spu_shuffle( axis, axis, shuffle_xxxx ); - yyyy = spu_shuffle( axis, axis, shuffle_yyyy ); - zzzz = spu_shuffle( axis, axis, shuffle_zzzz ); - oneMinusC = spu_sub( spu_splats(1.0f), c ); - axisS = spu_mul( axis, s ); - negAxisS = negatef4( axisS ); - tmp0 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_0ZB0 ); - tmp1 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_C0X0 ); - tmp2 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_YA00 ); - tmp0 = spu_sel( tmp0, c, (vec_uint4)spu_maskb(0xf000) ); - tmp1 = spu_sel( tmp1, c, (vec_uint4)spu_maskb(0x0f00) ); - tmp2 = spu_sel( tmp2, c, (vec_uint4)spu_maskb(0x00f0) ); - zeroW = (vec_float4)spu_maskb(0x000f); - axis = spu_andc( axis, zeroW ); - result->col0.vec128 = spu_madd( spu_mul( axis, xxxx ), oneMinusC, tmp0 ); - result->col1.vec128 = spu_madd( spu_mul( axis, yyyy ), oneMinusC, tmp1 ); - result->col2.vec128 = spu_madd( spu_mul( axis, zzzz ), oneMinusC, tmp2 ); - vmathV4MakeWAxis( &result->col3 ); -} - -static inline void vmathM4MakeRotationQ( VmathMatrix4 *result, const VmathQuat *unitQuat ) -{ - VmathTransform3 tmpT3_0; - vmathT3MakeRotationQ( &tmpT3_0, unitQuat ); - vmathM4MakeFromT3( result, &tmpT3_0 ); -} - -static inline void vmathM4MakeScale( VmathMatrix4 *result, const VmathVector3 *scaleVec ) -{ - vec_float4 zero = spu_splats(0.0f); - result->col0.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0xf000) ); - result->col1.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0x0f00) ); - result->col2.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0x00f0) ); - vmathV4MakeWAxis( &result->col3 ); -} - -static inline void vmathM4AppendScale( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathVector3 *scaleVec ) -{ - vmathV4ScalarMul( &result->col0, &mat->col0, vmathV3GetX( scaleVec ) ); - vmathV4ScalarMul( &result->col1, &mat->col1, vmathV3GetY( scaleVec ) ); - vmathV4ScalarMul( &result->col2, &mat->col2, vmathV3GetZ( scaleVec ) ); - vmathV4Copy( &result->col3, &mat->col3 ); -} - -static inline void vmathM4PrependScale( VmathMatrix4 *result, const VmathVector3 *scaleVec, const VmathMatrix4 *mat ) -{ - VmathVector4 scale4; - vmathV4MakeFromV3Scalar( &scale4, scaleVec, 1.0f ); - vmathV4MulPerElem( &result->col0, &mat->col0, &scale4 ); - vmathV4MulPerElem( &result->col1, &mat->col1, &scale4 ); - vmathV4MulPerElem( &result->col2, &mat->col2, &scale4 ); - vmathV4MulPerElem( &result->col3, &mat->col3, &scale4 ); -} - -static inline void vmathM4MakeTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ) -{ - vmathV4MakeXAxis( &result->col0 ); - vmathV4MakeYAxis( &result->col1 ); - vmathV4MakeZAxis( &result->col2 ); - vmathV4MakeFromV3Scalar( &result->col3, translateVec, 1.0f ); -} - -static inline void vmathM4MakeLookAt( VmathMatrix4 *result, const VmathPoint3 *eyePos, const VmathPoint3 *lookAtPos, const VmathVector3 *upVec ) -{ - VmathMatrix4 m4EyeFrame; - VmathVector3 v3X, v3Y, v3Z, tmpV3_0, tmpV3_1; - VmathVector4 tmpV4_0, tmpV4_1, tmpV4_2, tmpV4_3; - vmathV3Normalize( &v3Y, upVec ); - vmathP3Sub( &tmpV3_0, eyePos, lookAtPos ); - vmathV3Normalize( &v3Z, &tmpV3_0 ); - vmathV3Cross( &tmpV3_1, &v3Y, &v3Z ); - vmathV3Normalize( &v3X, &tmpV3_1 ); - vmathV3Cross( &v3Y, &v3Z, &v3X ); - vmathV4MakeFromV3( &tmpV4_0, &v3X ); - vmathV4MakeFromV3( &tmpV4_1, &v3Y ); - vmathV4MakeFromV3( &tmpV4_2, &v3Z ); - vmathV4MakeFromP3( &tmpV4_3, eyePos ); - vmathM4MakeFromCols( &m4EyeFrame, &tmpV4_0, &tmpV4_1, &tmpV4_2, &tmpV4_3 ); - vmathM4OrthoInverse( result, &m4EyeFrame ); -} - -static inline void vmathM4MakePerspective( VmathMatrix4 *result, float fovyRadians, float aspect, float zNear, float zFar ) -{ - float f, rangeInv; - vec_float4 zero, col0, col1, col2, col3; - f = tanf( _VECTORMATH_PI_OVER_2 - fovyRadians * 0.5f ); - rangeInv = 1.0f / ( zNear - zFar ); - zero = spu_splats(0.0f); - col0 = zero; - col1 = zero; - col2 = zero; - col3 = zero; - col0 = spu_insert( f / aspect, col0, 0 ); - col1 = spu_insert( f, col1, 1 ); - col2 = spu_insert( ( zNear + zFar ) * rangeInv, col2, 2 ); - col2 = spu_insert( -1.0f, col2, 3 ); - col3 = spu_insert( zNear * zFar * rangeInv * 2.0f, col3, 2 ); - result->col0.vec128 = col0; - result->col1.vec128 = col1; - result->col2.vec128 = col2; - result->col3.vec128 = col3; -} - -static inline void vmathM4MakeFrustum( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vec_float4 lbf, rtn; - vec_float4 diff, sum, inv_diff; - vec_float4 diagonal, column, near2; - vec_float4 zero = spu_splats(0.0f); - lbf = spu_shuffle( spu_promote(left,0), spu_promote(zFar,0), _VECTORMATH_SHUF_XAYB ); - rtn = spu_shuffle( spu_promote(right,0), spu_promote(zNear,0), _VECTORMATH_SHUF_XAYB ); - lbf = spu_shuffle( lbf, spu_promote(bottom,0), _VECTORMATH_SHUF_XAYB ); - rtn = spu_shuffle( rtn, spu_promote(top,0), _VECTORMATH_SHUF_XAYB ); - diff = spu_sub( rtn, lbf ); - sum = spu_add( rtn, lbf ); - inv_diff = recipf4( diff ); - near2 = spu_splats( zNear ); - near2 = spu_add( near2, near2 ); - diagonal = spu_mul( near2, inv_diff ); - column = spu_mul( sum, inv_diff ); - result->col0.vec128 = spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0xf000) ); - result->col1.vec128 = spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0x0f00) ); - result->col2.vec128 = spu_sel( column, spu_splats(-1.0f), (vec_uint4)spu_maskb(0x000f) ); - result->col3.vec128 = spu_sel( zero, spu_mul( diagonal, spu_splats(zFar) ), (vec_uint4)spu_maskb(0x00f0) ); -} - -static inline void vmathM4MakeOrthographic( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vec_float4 lbf, rtn; - vec_float4 diff, sum, inv_diff, neg_inv_diff; - vec_float4 diagonal, column; - vec_float4 zero = spu_splats(0.0f); - lbf = spu_shuffle( spu_promote(left,0), spu_promote(zFar,0), _VECTORMATH_SHUF_XAYB ); - rtn = spu_shuffle( spu_promote(right,0), spu_promote(zNear,0), _VECTORMATH_SHUF_XAYB ); - lbf = spu_shuffle( lbf, spu_promote(bottom,0), _VECTORMATH_SHUF_XAYB ); - rtn = spu_shuffle( rtn, spu_promote(top,0), _VECTORMATH_SHUF_XAYB ); - diff = spu_sub( rtn, lbf ); - sum = spu_add( rtn, lbf ); - inv_diff = recipf4( diff ); - neg_inv_diff = negatef4( inv_diff ); - diagonal = spu_add( inv_diff, inv_diff ); - column = spu_mul( sum, spu_sel( neg_inv_diff, inv_diff, (vec_uint4)spu_maskb(0x00f0) ) ); - result->col0.vec128 = spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0xf000) ); - result->col1.vec128 = spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0x0f00) ); - result->col2.vec128 = spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0x00f0) ); - result->col3.vec128 = spu_sel( column, spu_splats(1.0f), (vec_uint4)spu_maskb(0x000f) ); -} - -static inline void vmathM4Select( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1, unsigned int select1 ) -{ - vmathV4Select( &result->col0, &mat0->col0, &mat1->col0, select1 ); - vmathV4Select( &result->col1, &mat0->col1, &mat1->col1, select1 ); - vmathV4Select( &result->col2, &mat0->col2, &mat1->col2, select1 ); - vmathV4Select( &result->col3, &mat0->col3, &mat1->col3, select1 ); -} - -#ifdef _VECTORMATH_DEBUG - -static inline void vmathM4Print( const VmathMatrix4 *mat ) -{ - VmathVector4 tmpV4_0, tmpV4_1, tmpV4_2, tmpV4_3; - vmathM4GetRow( &tmpV4_0, mat, 0 ); - vmathV4Print( &tmpV4_0 ); - vmathM4GetRow( &tmpV4_1, mat, 1 ); - vmathV4Print( &tmpV4_1 ); - vmathM4GetRow( &tmpV4_2, mat, 2 ); - vmathV4Print( &tmpV4_2 ); - vmathM4GetRow( &tmpV4_3, mat, 3 ); - vmathV4Print( &tmpV4_3 ); -} - -static inline void vmathM4Prints( const VmathMatrix4 *mat, const char *name ) -{ - printf("%s:\n", name); - vmathM4Print( mat ); -} - -#endif - -static inline void vmathT3Copy( VmathTransform3 *result, const VmathTransform3 *tfrm ) -{ - vmathV3Copy( &result->col0, &tfrm->col0 ); - vmathV3Copy( &result->col1, &tfrm->col1 ); - vmathV3Copy( &result->col2, &tfrm->col2 ); - vmathV3Copy( &result->col3, &tfrm->col3 ); -} - -static inline void vmathT3MakeFromScalar( VmathTransform3 *result, float scalar ) -{ - vmathV3MakeFromScalar( &result->col0, scalar ); - vmathV3MakeFromScalar( &result->col1, scalar ); - vmathV3MakeFromScalar( &result->col2, scalar ); - vmathV3MakeFromScalar( &result->col3, scalar ); -} - -static inline void vmathT3MakeFromCols( VmathTransform3 *result, const VmathVector3 *_col0, const VmathVector3 *_col1, const VmathVector3 *_col2, const VmathVector3 *_col3 ) -{ - vmathV3Copy( &result->col0, _col0 ); - vmathV3Copy( &result->col1, _col1 ); - vmathV3Copy( &result->col2, _col2 ); - vmathV3Copy( &result->col3, _col3 ); -} - -static inline void vmathT3MakeFromM3V3( VmathTransform3 *result, const VmathMatrix3 *tfrm, const VmathVector3 *translateVec ) -{ - vmathT3SetUpper3x3( result, tfrm ); - vmathT3SetTranslation( result, translateVec ); -} - -static inline void vmathT3MakeFromQV3( VmathTransform3 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ) -{ - VmathMatrix3 tmpM3_0; - vmathM3MakeFromQ( &tmpM3_0, unitQuat ); - vmathT3SetUpper3x3( result, &tmpM3_0 ); - vmathT3SetTranslation( result, translateVec ); -} - -static inline void vmathT3SetCol0( VmathTransform3 *result, const VmathVector3 *_col0 ) -{ - vmathV3Copy( &result->col0, _col0 ); -} - -static inline void vmathT3SetCol1( VmathTransform3 *result, const VmathVector3 *_col1 ) -{ - vmathV3Copy( &result->col1, _col1 ); -} - -static inline void vmathT3SetCol2( VmathTransform3 *result, const VmathVector3 *_col2 ) -{ - vmathV3Copy( &result->col2, _col2 ); -} - -static inline void vmathT3SetCol3( VmathTransform3 *result, const VmathVector3 *_col3 ) -{ - vmathV3Copy( &result->col3, _col3 ); -} - -static inline void vmathT3SetCol( VmathTransform3 *result, int col, const VmathVector3 *vec ) -{ - vmathV3Copy( (&result->col0 + col), vec ); -} - -static inline void vmathT3SetRow( VmathTransform3 *result, int row, const VmathVector4 *vec ) -{ - vmathV3SetElem( &result->col0, row, vmathV4GetElem( vec, 0 ) ); - vmathV3SetElem( &result->col1, row, vmathV4GetElem( vec, 1 ) ); - vmathV3SetElem( &result->col2, row, vmathV4GetElem( vec, 2 ) ); - vmathV3SetElem( &result->col3, row, vmathV4GetElem( vec, 3 ) ); -} - -static inline void vmathT3SetElem( VmathTransform3 *result, int col, int row, float val ) -{ - VmathVector3 tmpV3_0; - vmathT3GetCol( &tmpV3_0, result, col ); - vmathV3SetElem( &tmpV3_0, row, val ); - vmathT3SetCol( result, col, &tmpV3_0 ); -} - -static inline float vmathT3GetElem( const VmathTransform3 *tfrm, int col, int row ) -{ - VmathVector3 tmpV3_0; - vmathT3GetCol( &tmpV3_0, tfrm, col ); - return vmathV3GetElem( &tmpV3_0, row ); -} - -static inline void vmathT3GetCol0( VmathVector3 *result, const VmathTransform3 *tfrm ) -{ - vmathV3Copy( result, &tfrm->col0 ); -} - -static inline void vmathT3GetCol1( VmathVector3 *result, const VmathTransform3 *tfrm ) -{ - vmathV3Copy( result, &tfrm->col1 ); -} - -static inline void vmathT3GetCol2( VmathVector3 *result, const VmathTransform3 *tfrm ) -{ - vmathV3Copy( result, &tfrm->col2 ); -} - -static inline void vmathT3GetCol3( VmathVector3 *result, const VmathTransform3 *tfrm ) -{ - vmathV3Copy( result, &tfrm->col3 ); -} - -static inline void vmathT3GetCol( VmathVector3 *result, const VmathTransform3 *tfrm, int col ) -{ - vmathV3Copy( result, (&tfrm->col0 + col) ); -} - -static inline void vmathT3GetRow( VmathVector4 *result, const VmathTransform3 *tfrm, int row ) -{ - vmathV4MakeFromElems( result, vmathV3GetElem( &tfrm->col0, row ), vmathV3GetElem( &tfrm->col1, row ), vmathV3GetElem( &tfrm->col2, row ), vmathV3GetElem( &tfrm->col3, row ) ); -} - -static inline void vmathT3Inverse( VmathTransform3 *result, const VmathTransform3 *tfrm ) -{ - vec_float4 inv0, inv1, inv2, inv3; - vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - tmp2 = _vmathVfCross( tfrm->col0.vec128, tfrm->col1.vec128 ); - tmp0 = _vmathVfCross( tfrm->col1.vec128, tfrm->col2.vec128 ); - tmp1 = _vmathVfCross( tfrm->col2.vec128, tfrm->col0.vec128 ); - inv3 = negatef4( tfrm->col3.vec128 ); - dot = _vmathVfDot3( tmp2, tfrm->col2.vec128 ); - dot = spu_shuffle( dot, dot, shuffle_xxxx ); - invdet = recipf4( dot ); - tmp3 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_XAYB ); - tmp4 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_ZCWD ); - inv0 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_XAYB ); - xxxx = spu_shuffle( inv3, inv3, shuffle_xxxx ); - inv1 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_ZBW0 ); - inv2 = spu_shuffle( tmp4, tmp1, _VECTORMATH_SHUF_XCY0 ); - yyyy = spu_shuffle( inv3, inv3, shuffle_yyyy ); - zzzz = spu_shuffle( inv3, inv3, shuffle_zzzz ); - inv3 = spu_mul( inv0, xxxx ); - inv3 = spu_madd( inv1, yyyy, inv3 ); - inv3 = spu_madd( inv2, zzzz, inv3 ); - inv0 = spu_mul( inv0, invdet ); - inv1 = spu_mul( inv1, invdet ); - inv2 = spu_mul( inv2, invdet ); - inv3 = spu_mul( inv3, invdet ); - result->col0.vec128 = inv0; - result->col1.vec128 = inv1; - result->col2.vec128 = inv2; - result->col3.vec128 = inv3; -} - -static inline void vmathT3OrthoInverse( VmathTransform3 *result, const VmathTransform3 *tfrm ) -{ - vec_float4 inv0, inv1, inv2, inv3; - vec_float4 tmp0, tmp1; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - tmp0 = spu_shuffle( tfrm->col0.vec128, tfrm->col2.vec128, _VECTORMATH_SHUF_XAYB ); - tmp1 = spu_shuffle( tfrm->col0.vec128, tfrm->col2.vec128, _VECTORMATH_SHUF_ZCWD ); - inv3 = negatef4( tfrm->col3.vec128 ); - inv0 = spu_shuffle( tmp0, tfrm->col1.vec128, _VECTORMATH_SHUF_XAYB ); - xxxx = spu_shuffle( inv3, inv3, shuffle_xxxx ); - inv1 = spu_shuffle( tmp0, tfrm->col1.vec128, _VECTORMATH_SHUF_ZBW0 ); - inv2 = spu_shuffle( tmp1, tfrm->col1.vec128, _VECTORMATH_SHUF_XCY0 ); - yyyy = spu_shuffle( inv3, inv3, shuffle_yyyy ); - zzzz = spu_shuffle( inv3, inv3, shuffle_zzzz ); - inv3 = spu_mul( inv0, xxxx ); - inv3 = spu_madd( inv1, yyyy, inv3 ); - inv3 = spu_madd( inv2, zzzz, inv3 ); - result->col0.vec128 = inv0; - result->col1.vec128 = inv1; - result->col2.vec128 = inv2; - result->col3.vec128 = inv3; -} - -static inline void vmathT3AbsPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm ) -{ - vmathV3AbsPerElem( &result->col0, &tfrm->col0 ); - vmathV3AbsPerElem( &result->col1, &tfrm->col1 ); - vmathV3AbsPerElem( &result->col2, &tfrm->col2 ); - vmathV3AbsPerElem( &result->col3, &tfrm->col3 ); -} - -static inline void vmathT3MulV3( VmathVector3 *result, const VmathTransform3 *tfrm, const VmathVector3 *vec ) -{ - vec_float4 res; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - xxxx = spu_shuffle( vec->vec128, vec->vec128, shuffle_xxxx ); - yyyy = spu_shuffle( vec->vec128, vec->vec128, shuffle_yyyy ); - zzzz = spu_shuffle( vec->vec128, vec->vec128, shuffle_zzzz ); - res = spu_mul( tfrm->col0.vec128, xxxx ); - res = spu_madd( tfrm->col1.vec128, yyyy, res ); - res = spu_madd( tfrm->col2.vec128, zzzz, res ); - result->vec128 = res; -} - -static inline void vmathT3MulP3( VmathPoint3 *result, const VmathTransform3 *tfrm, const VmathPoint3 *pnt ) -{ - vec_float4 tmp0, tmp1, res; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - xxxx = spu_shuffle( pnt->vec128, pnt->vec128, shuffle_xxxx ); - yyyy = spu_shuffle( pnt->vec128, pnt->vec128, shuffle_yyyy ); - zzzz = spu_shuffle( pnt->vec128, pnt->vec128, shuffle_zzzz ); - tmp0 = spu_mul( tfrm->col0.vec128, xxxx ); - tmp1 = spu_mul( tfrm->col1.vec128, yyyy ); - tmp0 = spu_madd( tfrm->col2.vec128, zzzz, tmp0 ); - tmp1 = spu_add( tfrm->col3.vec128, tmp1 ); - res = spu_add( tmp0, tmp1 ); - result->vec128 = res; -} - -static inline void vmathT3Mul( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ) -{ - VmathTransform3 tmpResult; - VmathPoint3 tmpP3_0, tmpP3_1; - vmathT3MulV3( &tmpResult.col0, tfrm0, &tfrm1->col0 ); - vmathT3MulV3( &tmpResult.col1, tfrm0, &tfrm1->col1 ); - vmathT3MulV3( &tmpResult.col2, tfrm0, &tfrm1->col2 ); - vmathP3MakeFromV3( &tmpP3_0, &tfrm1->col3 ); - vmathT3MulP3( &tmpP3_1, tfrm0, &tmpP3_0 ); - vmathV3MakeFromP3( &tmpResult.col3, &tmpP3_1 ); - vmathT3Copy( result, &tmpResult ); -} - -static inline void vmathT3MulPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ) -{ - vmathV3MulPerElem( &result->col0, &tfrm0->col0, &tfrm1->col0 ); - vmathV3MulPerElem( &result->col1, &tfrm0->col1, &tfrm1->col1 ); - vmathV3MulPerElem( &result->col2, &tfrm0->col2, &tfrm1->col2 ); - vmathV3MulPerElem( &result->col3, &tfrm0->col3, &tfrm1->col3 ); -} - -static inline void vmathT3MakeIdentity( VmathTransform3 *result ) -{ - vmathV3MakeXAxis( &result->col0 ); - vmathV3MakeYAxis( &result->col1 ); - vmathV3MakeZAxis( &result->col2 ); - vmathV3MakeFromScalar( &result->col3, 0.0f ); -} - -static inline void vmathT3SetUpper3x3( VmathTransform3 *result, const VmathMatrix3 *tfrm ) -{ - vmathV3Copy( &result->col0, &tfrm->col0 ); - vmathV3Copy( &result->col1, &tfrm->col1 ); - vmathV3Copy( &result->col2, &tfrm->col2 ); -} - -static inline void vmathT3GetUpper3x3( VmathMatrix3 *result, const VmathTransform3 *tfrm ) -{ - vmathM3MakeFromCols( result, &tfrm->col0, &tfrm->col1, &tfrm->col2 ); -} - -static inline void vmathT3SetTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ) -{ - vmathV3Copy( &result->col3, translateVec ); -} - -static inline void vmathT3GetTranslation( VmathVector3 *result, const VmathTransform3 *tfrm ) -{ - vmathV3Copy( result, &tfrm->col3 ); -} - -static inline void vmathT3MakeRotationX( VmathTransform3 *result, float radians ) -{ - vec_float4 s, c, res1, res2; - vec_uint4 select_y, select_z; - vec_float4 zero; - select_y = (vec_uint4)spu_maskb(0x0f00); - select_z = (vec_uint4)spu_maskb(0x00f0); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res1 = spu_sel( zero, c, select_y ); - res1 = spu_sel( res1, s, select_z ); - res2 = spu_sel( zero, negatef4(s), select_y ); - res2 = spu_sel( res2, c, select_z ); - vmathV3MakeXAxis( &result->col0 ); - result->col1.vec128 = res1; - result->col2.vec128 = res2; - vmathV3MakeFromScalar( &result->col3, 0.0f ); -} - -static inline void vmathT3MakeRotationY( VmathTransform3 *result, float radians ) -{ - vec_float4 s, c, res0, res2; - vec_uint4 select_x, select_z; - vec_float4 zero; - select_x = (vec_uint4)spu_maskb(0xf000); - select_z = (vec_uint4)spu_maskb(0x00f0); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res0 = spu_sel( zero, c, select_x ); - res0 = spu_sel( res0, negatef4(s), select_z ); - res2 = spu_sel( zero, s, select_x ); - res2 = spu_sel( res2, c, select_z ); - result->col0.vec128 = res0; - vmathV3MakeYAxis( &result->col1 ); - result->col2.vec128 = res2; - vmathV3MakeFromScalar( &result->col3, 0.0f ); -} - -static inline void vmathT3MakeRotationZ( VmathTransform3 *result, float radians ) -{ - vec_float4 s, c, res0, res1; - vec_uint4 select_x, select_y; - vec_float4 zero; - select_x = (vec_uint4)spu_maskb(0xf000); - select_y = (vec_uint4)spu_maskb(0x0f00); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res0 = spu_sel( zero, c, select_x ); - res0 = spu_sel( res0, s, select_y ); - res1 = spu_sel( zero, negatef4(s), select_x ); - res1 = spu_sel( res1, c, select_y ); - result->col0.vec128 = res0; - result->col1.vec128 = res1; - vmathV3MakeZAxis( &result->col2 ); - vmathV3MakeFromScalar( &result->col3, 0.0f ); -} - -static inline void vmathT3MakeRotationZYX( VmathTransform3 *result, const VmathVector3 *radiansXYZ ) -{ - vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - angles = radiansXYZ->vec128; - angles = spu_insert( 0.0f, angles, 3 ); - sincosf4( angles, &s, &c ); - negS = negatef4( s ); - Z0 = spu_shuffle( s, c, _VECTORMATH_SHUF_CZD0 ); - Z1 = spu_shuffle( c, negS, _VECTORMATH_SHUF_CZD0 ); - Y0 = spu_shuffle( negS, c, _VECTORMATH_SHUF_BBY0 ); - Y1 = spu_shuffle( c, s, _VECTORMATH_SHUF_BBY0 ); - X0 = spu_shuffle( s, s, shuffle_xxxx ); - X1 = spu_shuffle( c, c, shuffle_xxxx ); - tmp = spu_mul( Z0, Y1 ); - result->col0.vec128 = spu_mul( Z0, Y0 ); - result->col1.vec128 = spu_madd( Z1, X1, spu_mul( tmp, X0 ) ); - result->col2.vec128 = spu_nmsub( Z1, X0, spu_mul( tmp, X1 ) ); - vmathV3MakeFromScalar( &result->col3, 0.0f ); -} - -static inline void vmathT3MakeRotationAxis( VmathTransform3 *result, float radians, const VmathVector3 *unitVec ) -{ - VmathMatrix3 tmpM3_0; - VmathVector3 tmpV3_0; - vmathM3MakeRotationAxis( &tmpM3_0, radians, unitVec ); - vmathV3MakeFromScalar( &tmpV3_0, 0.0f ); - vmathT3MakeFromM3V3( result, &tmpM3_0, &tmpV3_0 ); -} - -static inline void vmathT3MakeRotationQ( VmathTransform3 *result, const VmathQuat *unitQuat ) -{ - VmathMatrix3 tmpM3_0; - VmathVector3 tmpV3_0; - vmathM3MakeFromQ( &tmpM3_0, unitQuat ); - vmathV3MakeFromScalar( &tmpV3_0, 0.0f ); - vmathT3MakeFromM3V3( result, &tmpM3_0, &tmpV3_0 ); -} - -static inline void vmathT3MakeScale( VmathTransform3 *result, const VmathVector3 *scaleVec ) -{ - vec_float4 zero = spu_splats(0.0f); - result->col0.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0xf000) ); - result->col1.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0x0f00) ); - result->col2.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0x00f0) ); - vmathV3MakeFromScalar( &result->col3, 0.0f ); -} - -static inline void vmathT3AppendScale( VmathTransform3 *result, const VmathTransform3 *tfrm, const VmathVector3 *scaleVec ) -{ - vmathV3ScalarMul( &result->col0, &tfrm->col0, vmathV3GetX( scaleVec ) ); - vmathV3ScalarMul( &result->col1, &tfrm->col1, vmathV3GetY( scaleVec ) ); - vmathV3ScalarMul( &result->col2, &tfrm->col2, vmathV3GetZ( scaleVec ) ); - vmathV3Copy( &result->col3, &tfrm->col3 ); -} - -static inline void vmathT3PrependScale( VmathTransform3 *result, const VmathVector3 *scaleVec, const VmathTransform3 *tfrm ) -{ - vmathV3MulPerElem( &result->col0, &tfrm->col0, scaleVec ); - vmathV3MulPerElem( &result->col1, &tfrm->col1, scaleVec ); - vmathV3MulPerElem( &result->col2, &tfrm->col2, scaleVec ); - vmathV3MulPerElem( &result->col3, &tfrm->col3, scaleVec ); -} - -static inline void vmathT3MakeTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ) -{ - vmathV3MakeXAxis( &result->col0 ); - vmathV3MakeYAxis( &result->col1 ); - vmathV3MakeZAxis( &result->col2 ); - vmathV3Copy( &result->col3, translateVec ); -} - -static inline void vmathT3Select( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1, unsigned int select1 ) -{ - vmathV3Select( &result->col0, &tfrm0->col0, &tfrm1->col0, select1 ); - vmathV3Select( &result->col1, &tfrm0->col1, &tfrm1->col1, select1 ); - vmathV3Select( &result->col2, &tfrm0->col2, &tfrm1->col2, select1 ); - vmathV3Select( &result->col3, &tfrm0->col3, &tfrm1->col3, select1 ); -} - -#ifdef _VECTORMATH_DEBUG - -static inline void vmathT3Print( const VmathTransform3 *tfrm ) -{ - VmathVector4 tmpV4_0, tmpV4_1, tmpV4_2; - vmathT3GetRow( &tmpV4_0, tfrm, 0 ); - vmathV4Print( &tmpV4_0 ); - vmathT3GetRow( &tmpV4_1, tfrm, 1 ); - vmathV4Print( &tmpV4_1 ); - vmathT3GetRow( &tmpV4_2, tfrm, 2 ); - vmathV4Print( &tmpV4_2 ); -} - -static inline void vmathT3Prints( const VmathTransform3 *tfrm, const char *name ) -{ - printf("%s:\n", name); - vmathT3Print( tfrm ); -} - -#endif - -static inline void vmathQMakeFromM3( VmathQuat *result, const VmathMatrix3 *tfrm ) -{ - vec_float4 res; - vec_float4 col0, col1, col2; - vec_float4 xx_yy, xx_yy_zz_xx, yy_zz_xx_yy, zz_xx_yy_zz, diagSum, diagDiff; - vec_float4 zy_xz_yx, yz_zx_xy, sum, diff; - vec_float4 radicand, invSqrt, scale; - vec_float4 res0, res1, res2, res3; - vec_float4 xx, yy, zz; - vec_uint4 select_x = (vec_uint4)spu_maskb( 0xf000 ); - vec_uint4 select_y = (vec_uint4)spu_maskb( 0x0f00 ); - vec_uint4 select_z = (vec_uint4)spu_maskb( 0x00f0 ); - vec_uint4 select_w = (vec_uint4)spu_maskb( 0x000f ); - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((unsigned int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((unsigned int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((unsigned int)0x08090a0b); - vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((unsigned int)0x0c0d0e0f); - - col0 = tfrm->col0.vec128; - col1 = tfrm->col1.vec128; - col2 = tfrm->col2.vec128; - - /* four cases: */ - /* trace > 0 */ - /* else */ - /* xx largest diagonal element */ - /* yy largest diagonal element */ - /* zz largest diagonal element */ - - /* compute quaternion for each case */ - - xx_yy = spu_sel( col0, col1, select_y ); - xx_yy_zz_xx = spu_shuffle( xx_yy, col2, _VECTORMATH_SHUF_XYCX ); - yy_zz_xx_yy = spu_shuffle( xx_yy, col2, _VECTORMATH_SHUF_YCXY ); - zz_xx_yy_zz = spu_shuffle( xx_yy, col2, _VECTORMATH_SHUF_CXYC ); - - diagSum = spu_add( spu_add( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); - diagDiff = spu_sub( spu_sub( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); - radicand = spu_add( spu_sel( diagDiff, diagSum, select_w ), spu_splats(1.0f) ); - invSqrt = rsqrtf4( radicand ); - - zy_xz_yx = spu_sel( col0, col1, select_z ); - zy_xz_yx = spu_shuffle( zy_xz_yx, col2, _VECTORMATH_SHUF_ZAY0 ); - yz_zx_xy = spu_sel( col0, col1, select_x ); - yz_zx_xy = spu_shuffle( yz_zx_xy, col2, _VECTORMATH_SHUF_BZX0 ); - - sum = spu_add( zy_xz_yx, yz_zx_xy ); - diff = spu_sub( zy_xz_yx, yz_zx_xy ); - - scale = spu_mul( invSqrt, spu_splats(0.5f) ); - res0 = spu_shuffle( sum, diff, _VECTORMATH_SHUF_0ZYA ); - res1 = spu_shuffle( sum, diff, _VECTORMATH_SHUF_Z0XB ); - res2 = spu_shuffle( sum, diff, _VECTORMATH_SHUF_YX0C ); - res3 = diff; - res0 = spu_sel( res0, radicand, select_x ); - res1 = spu_sel( res1, radicand, select_y ); - res2 = spu_sel( res2, radicand, select_z ); - res3 = spu_sel( res3, radicand, select_w ); - res0 = spu_mul( res0, spu_shuffle( scale, scale, shuffle_xxxx ) ); - res1 = spu_mul( res1, spu_shuffle( scale, scale, shuffle_yyyy ) ); - res2 = spu_mul( res2, spu_shuffle( scale, scale, shuffle_zzzz ) ); - res3 = spu_mul( res3, spu_shuffle( scale, scale, shuffle_wwww ) ); - - /* determine case and select answer */ - - xx = spu_shuffle( col0, col0, shuffle_xxxx ); - yy = spu_shuffle( col1, col1, shuffle_yyyy ); - zz = spu_shuffle( col2, col2, shuffle_zzzz ); - res = spu_sel( res0, res1, spu_cmpgt( yy, xx ) ); - res = spu_sel( res, res2, spu_and( spu_cmpgt( zz, xx ), spu_cmpgt( zz, yy ) ) ); - res = spu_sel( res, res3, spu_cmpgt( spu_shuffle( diagSum, diagSum, shuffle_xxxx ), spu_splats(0.0f) ) ); - result->vec128 = res; -} - -static inline void vmathV3Outer( VmathMatrix3 *result, const VmathVector3 *tfrm0, const VmathVector3 *tfrm1 ) -{ - vmathV3ScalarMul( &result->col0, tfrm0, vmathV3GetX( tfrm1 ) ); - vmathV3ScalarMul( &result->col1, tfrm0, vmathV3GetY( tfrm1 ) ); - vmathV3ScalarMul( &result->col2, tfrm0, vmathV3GetZ( tfrm1 ) ); -} - -static inline void vmathV4Outer( VmathMatrix4 *result, const VmathVector4 *tfrm0, const VmathVector4 *tfrm1 ) -{ - vmathV4ScalarMul( &result->col0, tfrm0, vmathV4GetX( tfrm1 ) ); - vmathV4ScalarMul( &result->col1, tfrm0, vmathV4GetY( tfrm1 ) ); - vmathV4ScalarMul( &result->col2, tfrm0, vmathV4GetZ( tfrm1 ) ); - vmathV4ScalarMul( &result->col3, tfrm0, vmathV4GetW( tfrm1 ) ); -} - -static inline void vmathV3RowMul( VmathVector3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ) -{ - vec_float4 tmp0, tmp1, mcol0, mcol1, mcol2, res; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - tmp0 = spu_shuffle( mat->col0.vec128, mat->col2.vec128, _VECTORMATH_SHUF_XAYB ); - tmp1 = spu_shuffle( mat->col0.vec128, mat->col2.vec128, _VECTORMATH_SHUF_ZCWD ); - xxxx = spu_shuffle( vec->vec128, vec->vec128, shuffle_xxxx ); - mcol0 = spu_shuffle( tmp0, mat->col1.vec128, _VECTORMATH_SHUF_XAYB ); - mcol1 = spu_shuffle( tmp0, mat->col1.vec128, _VECTORMATH_SHUF_ZBW0 ); - mcol2 = spu_shuffle( tmp1, mat->col1.vec128, _VECTORMATH_SHUF_XCY0 ); - yyyy = spu_shuffle( vec->vec128, vec->vec128, shuffle_yyyy ); - res = spu_mul( mcol0, xxxx ); - zzzz = spu_shuffle( vec->vec128, vec->vec128, shuffle_zzzz ); - res = spu_madd( mcol1, yyyy, res ); - res = spu_madd( mcol2, zzzz, res ); - result->vec128 = res; -} - -static inline void vmathV3CrossMatrix( VmathMatrix3 *result, const VmathVector3 *vec ) -{ - vec_float4 neg, res0, res1, res2; - neg = negatef4( vec->vec128 ); - res0 = spu_shuffle( vec->vec128, neg, _VECTORMATH_SHUF_0ZB0 ); - res1 = spu_shuffle( vec->vec128, neg, _VECTORMATH_SHUF_C0X0 ); - res2 = spu_shuffle( vec->vec128, neg, _VECTORMATH_SHUF_YA00 ); - result->col0.vec128 = res0; - result->col1.vec128 = res1; - result->col2.vec128 = res2; -} - -static inline void vmathV3CrossMatrixMul( VmathMatrix3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ) -{ - VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2; - vmathV3Cross( &tmpV3_0, vec, &mat->col0 ); - vmathV3Cross( &tmpV3_1, vec, &mat->col1 ); - vmathV3Cross( &tmpV3_2, vec, &mat->col2 ); - vmathM3MakeFromCols( result, &tmpV3_0, &tmpV3_1, &tmpV3_2 ); -} - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_MAT_AOS_C_H +#define _VECTORMATH_MAT_AOS_C_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*----------------------------------------------------------------------------- + * Constants + * for shuffles, words are labeled [x,y,z,w] [a,b,c,d] + */ +#define _VECTORMATH_SHUF_XAYB ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_B }) +#define _VECTORMATH_SHUF_ZCWD ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_D }) +#define _VECTORMATH_SHUF_ZBW0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_XCY0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_XYAB ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B }) +#define _VECTORMATH_SHUF_ZWCD ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_D }) +#define _VECTORMATH_SHUF_0ZB0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_C0X0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_YA00 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_XAZC ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_C }) +#define _VECTORMATH_SHUF_YXWZ ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_Z }) +#define _VECTORMATH_SHUF_YBWD ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_D }) +#define _VECTORMATH_SHUF_XYCX ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_X }) +#define _VECTORMATH_SHUF_YCXY ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y }) +#define _VECTORMATH_SHUF_CXYC ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_C }) +#define _VECTORMATH_SHUF_ZAY0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_BZX0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_0ZYA ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_A }) +#define _VECTORMATH_SHUF_Z0XB ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_B }) +#define _VECTORMATH_SHUF_YX0C ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_C }) +#define _VECTORMATH_SHUF_CZD0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_D, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_BBY0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_PI_OVER_2 1.570796327f + +/*----------------------------------------------------------------------------- + * Definitions + */ +static inline void vmathM3Copy( VmathMatrix3 *result, const VmathMatrix3 *mat ) +{ + vmathV3Copy( &result->col0, &mat->col0 ); + vmathV3Copy( &result->col1, &mat->col1 ); + vmathV3Copy( &result->col2, &mat->col2 ); +} + +static inline void vmathM3MakeFromScalar( VmathMatrix3 *result, float scalar ) +{ + vmathV3MakeFromScalar( &result->col0, scalar ); + vmathV3MakeFromScalar( &result->col1, scalar ); + vmathV3MakeFromScalar( &result->col2, scalar ); +} + +static inline void vmathM3MakeFromQ( VmathMatrix3 *result, const VmathQuat *unitQuat ) +{ + vec_float4 xyzw_2, wwww, yzxw, zxyw, yzxw_2, zxyw_2; + vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); + vec_uint4 select_x = (vec_uint4)spu_maskb(0xf000); + vec_uint4 select_z = (vec_uint4)spu_maskb(0x00f0); + xyzw_2 = spu_add( unitQuat->vec128, unitQuat->vec128 ); + wwww = spu_shuffle( unitQuat->vec128, unitQuat->vec128, shuffle_wwww ); + yzxw = spu_shuffle( unitQuat->vec128, unitQuat->vec128, _VECTORMATH_SHUF_YZXW ); + zxyw = spu_shuffle( unitQuat->vec128, unitQuat->vec128, _VECTORMATH_SHUF_ZXYW ); + yzxw_2 = spu_shuffle( xyzw_2, xyzw_2, _VECTORMATH_SHUF_YZXW ); + zxyw_2 = spu_shuffle( xyzw_2, xyzw_2, _VECTORMATH_SHUF_ZXYW ); + tmp0 = spu_mul( yzxw_2, wwww ); + tmp1 = spu_nmsub( yzxw, yzxw_2, spu_splats(1.0f) ); + tmp2 = spu_mul( yzxw, xyzw_2 ); + tmp0 = spu_madd( zxyw, xyzw_2, tmp0 ); + tmp1 = spu_nmsub( zxyw, zxyw_2, tmp1 ); + tmp2 = spu_nmsub( zxyw_2, wwww, tmp2 ); + tmp3 = spu_sel( tmp0, tmp1, select_x ); + tmp4 = spu_sel( tmp1, tmp2, select_x ); + tmp5 = spu_sel( tmp2, tmp0, select_x ); + result->col0.vec128 = spu_sel( tmp3, tmp2, select_z ); + result->col1.vec128 = spu_sel( tmp4, tmp0, select_z ); + result->col2.vec128 = spu_sel( tmp5, tmp1, select_z ); +} + +static inline void vmathM3MakeFromCols( VmathMatrix3 *result, const VmathVector3 *_col0, const VmathVector3 *_col1, const VmathVector3 *_col2 ) +{ + vmathV3Copy( &result->col0, _col0 ); + vmathV3Copy( &result->col1, _col1 ); + vmathV3Copy( &result->col2, _col2 ); +} + +static inline void vmathM3SetCol0( VmathMatrix3 *result, const VmathVector3 *_col0 ) +{ + vmathV3Copy( &result->col0, _col0 ); +} + +static inline void vmathM3SetCol1( VmathMatrix3 *result, const VmathVector3 *_col1 ) +{ + vmathV3Copy( &result->col1, _col1 ); +} + +static inline void vmathM3SetCol2( VmathMatrix3 *result, const VmathVector3 *_col2 ) +{ + vmathV3Copy( &result->col2, _col2 ); +} + +static inline void vmathM3SetCol( VmathMatrix3 *result, int col, const VmathVector3 *vec ) +{ + vmathV3Copy( (&result->col0 + col), vec ); +} + +static inline void vmathM3SetRow( VmathMatrix3 *result, int row, const VmathVector3 *vec ) +{ + vmathV3SetElem( &result->col0, row, vmathV3GetElem( vec, 0 ) ); + vmathV3SetElem( &result->col1, row, vmathV3GetElem( vec, 1 ) ); + vmathV3SetElem( &result->col2, row, vmathV3GetElem( vec, 2 ) ); +} + +static inline void vmathM3SetElem( VmathMatrix3 *result, int col, int row, float val ) +{ + VmathVector3 tmpV3_0; + vmathM3GetCol( &tmpV3_0, result, col ); + vmathV3SetElem( &tmpV3_0, row, val ); + vmathM3SetCol( result, col, &tmpV3_0 ); +} + +static inline float vmathM3GetElem( const VmathMatrix3 *mat, int col, int row ) +{ + VmathVector3 tmpV3_0; + vmathM3GetCol( &tmpV3_0, mat, col ); + return vmathV3GetElem( &tmpV3_0, row ); +} + +static inline void vmathM3GetCol0( VmathVector3 *result, const VmathMatrix3 *mat ) +{ + vmathV3Copy( result, &mat->col0 ); +} + +static inline void vmathM3GetCol1( VmathVector3 *result, const VmathMatrix3 *mat ) +{ + vmathV3Copy( result, &mat->col1 ); +} + +static inline void vmathM3GetCol2( VmathVector3 *result, const VmathMatrix3 *mat ) +{ + vmathV3Copy( result, &mat->col2 ); +} + +static inline void vmathM3GetCol( VmathVector3 *result, const VmathMatrix3 *mat, int col ) +{ + vmathV3Copy( result, (&mat->col0 + col) ); +} + +static inline void vmathM3GetRow( VmathVector3 *result, const VmathMatrix3 *mat, int row ) +{ + vmathV3MakeFromElems( result, vmathV3GetElem( &mat->col0, row ), vmathV3GetElem( &mat->col1, row ), vmathV3GetElem( &mat->col2, row ) ); +} + +static inline void vmathM3Transpose( VmathMatrix3 *result, const VmathMatrix3 *mat ) +{ + vec_float4 tmp0, tmp1, res0, res1, res2; + tmp0 = spu_shuffle( mat->col0.vec128, mat->col2.vec128, _VECTORMATH_SHUF_XAYB ); + tmp1 = spu_shuffle( mat->col0.vec128, mat->col2.vec128, _VECTORMATH_SHUF_ZCWD ); + res0 = spu_shuffle( tmp0, mat->col1.vec128, _VECTORMATH_SHUF_XAYB ); + res1 = spu_shuffle( tmp0, mat->col1.vec128, _VECTORMATH_SHUF_ZBW0 ); + res2 = spu_shuffle( tmp1, mat->col1.vec128, _VECTORMATH_SHUF_XCY0 ); + result->col0.vec128 = res0; + result->col1.vec128 = res1; + result->col2.vec128 = res2; +} + +static inline void vmathM3Inverse( VmathMatrix3 *result, const VmathMatrix3 *mat ) +{ + vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet, inv0, inv1, inv2; + tmp2 = _vmathVfCross( mat->col0.vec128, mat->col1.vec128 ); + tmp0 = _vmathVfCross( mat->col1.vec128, mat->col2.vec128 ); + tmp1 = _vmathVfCross( mat->col2.vec128, mat->col0.vec128 ); + dot = _vmathVfDot3( tmp2, mat->col2.vec128 ); + dot = spu_shuffle( dot, dot, (vec_uchar16)spu_splats(0x00010203) ); + invdet = recipf4( dot ); + tmp3 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_XAYB ); + tmp4 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_ZCWD ); + inv0 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_XAYB ); + inv1 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_ZBW0 ); + inv2 = spu_shuffle( tmp4, tmp1, _VECTORMATH_SHUF_XCY0 ); + inv0 = spu_mul( inv0, invdet ); + inv1 = spu_mul( inv1, invdet ); + inv2 = spu_mul( inv2, invdet ); + result->col0.vec128 = inv0; + result->col1.vec128 = inv1; + result->col2.vec128 = inv2; +} + +static inline float vmathM3Determinant( const VmathMatrix3 *mat ) +{ + VmathVector3 tmpV3_0; + vmathV3Cross( &tmpV3_0, &mat->col0, &mat->col1 ); + return vmathV3Dot( &mat->col2, &tmpV3_0 ); +} + +static inline void vmathM3Add( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) +{ + vmathV3Add( &result->col0, &mat0->col0, &mat1->col0 ); + vmathV3Add( &result->col1, &mat0->col1, &mat1->col1 ); + vmathV3Add( &result->col2, &mat0->col2, &mat1->col2 ); +} + +static inline void vmathM3Sub( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) +{ + vmathV3Sub( &result->col0, &mat0->col0, &mat1->col0 ); + vmathV3Sub( &result->col1, &mat0->col1, &mat1->col1 ); + vmathV3Sub( &result->col2, &mat0->col2, &mat1->col2 ); +} + +static inline void vmathM3Neg( VmathMatrix3 *result, const VmathMatrix3 *mat ) +{ + vmathV3Neg( &result->col0, &mat->col0 ); + vmathV3Neg( &result->col1, &mat->col1 ); + vmathV3Neg( &result->col2, &mat->col2 ); +} + +static inline void vmathM3AbsPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat ) +{ + vmathV3AbsPerElem( &result->col0, &mat->col0 ); + vmathV3AbsPerElem( &result->col1, &mat->col1 ); + vmathV3AbsPerElem( &result->col2, &mat->col2 ); +} + +static inline void vmathM3ScalarMul( VmathMatrix3 *result, const VmathMatrix3 *mat, float scalar ) +{ + vmathV3ScalarMul( &result->col0, &mat->col0, scalar ); + vmathV3ScalarMul( &result->col1, &mat->col1, scalar ); + vmathV3ScalarMul( &result->col2, &mat->col2, scalar ); +} + +static inline void vmathM3MulV3( VmathVector3 *result, const VmathMatrix3 *mat, const VmathVector3 *vec ) +{ + vec_float4 res; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + xxxx = spu_shuffle( vec->vec128, vec->vec128, shuffle_xxxx ); + yyyy = spu_shuffle( vec->vec128, vec->vec128, shuffle_yyyy ); + zzzz = spu_shuffle( vec->vec128, vec->vec128, shuffle_zzzz ); + res = spu_mul( mat->col0.vec128, xxxx ); + res = spu_madd( mat->col1.vec128, yyyy, res ); + res = spu_madd( mat->col2.vec128, zzzz, res ); + result->vec128 = res; +} + +static inline void vmathM3Mul( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) +{ + VmathMatrix3 tmpResult; + vmathM3MulV3( &tmpResult.col0, mat0, &mat1->col0 ); + vmathM3MulV3( &tmpResult.col1, mat0, &mat1->col1 ); + vmathM3MulV3( &tmpResult.col2, mat0, &mat1->col2 ); + vmathM3Copy( result, &tmpResult ); +} + +static inline void vmathM3MulPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ) +{ + vmathV3MulPerElem( &result->col0, &mat0->col0, &mat1->col0 ); + vmathV3MulPerElem( &result->col1, &mat0->col1, &mat1->col1 ); + vmathV3MulPerElem( &result->col2, &mat0->col2, &mat1->col2 ); +} + +static inline void vmathM3MakeIdentity( VmathMatrix3 *result ) +{ + vmathV3MakeXAxis( &result->col0 ); + vmathV3MakeYAxis( &result->col1 ); + vmathV3MakeZAxis( &result->col2 ); +} + +static inline void vmathM3MakeRotationX( VmathMatrix3 *result, float radians ) +{ + vec_float4 s, c, res1, res2; + vec_uint4 select_y, select_z; + vec_float4 zero; + select_y = (vec_uint4)spu_maskb(0x0f00); + select_z = (vec_uint4)spu_maskb(0x00f0); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res1 = spu_sel( zero, c, select_y ); + res1 = spu_sel( res1, s, select_z ); + res2 = spu_sel( zero, negatef4(s), select_y ); + res2 = spu_sel( res2, c, select_z ); + vmathV3MakeXAxis( &result->col0 ); + result->col1.vec128 = res1; + result->col2.vec128 = res2; +} + +static inline void vmathM3MakeRotationY( VmathMatrix3 *result, float radians ) +{ + vec_float4 s, c, res0, res2; + vec_uint4 select_x, select_z; + vec_float4 zero; + select_x = (vec_uint4)spu_maskb(0xf000); + select_z = (vec_uint4)spu_maskb(0x00f0); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res0 = spu_sel( zero, c, select_x ); + res0 = spu_sel( res0, negatef4(s), select_z ); + res2 = spu_sel( zero, s, select_x ); + res2 = spu_sel( res2, c, select_z ); + result->col0.vec128 = res0; + vmathV3MakeYAxis( &result->col1 ); + result->col2.vec128 = res2; +} + +static inline void vmathM3MakeRotationZ( VmathMatrix3 *result, float radians ) +{ + vec_float4 s, c, res0, res1; + vec_uint4 select_x, select_y; + vec_float4 zero; + select_x = (vec_uint4)spu_maskb(0xf000); + select_y = (vec_uint4)spu_maskb(0x0f00); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res0 = spu_sel( zero, c, select_x ); + res0 = spu_sel( res0, s, select_y ); + res1 = spu_sel( zero, negatef4(s), select_x ); + res1 = spu_sel( res1, c, select_y ); + result->col0.vec128 = res0; + result->col1.vec128 = res1; + vmathV3MakeZAxis( &result->col2 ); +} + +static inline void vmathM3MakeRotationZYX( VmathMatrix3 *result, const VmathVector3 *radiansXYZ ) +{ + vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + angles = radiansXYZ->vec128; + angles = spu_insert( 0.0f, angles, 3 ); + sincosf4( angles, &s, &c ); + negS = negatef4( s ); + Z0 = spu_shuffle( s, c, _VECTORMATH_SHUF_CZD0 ); + Z1 = spu_shuffle( c, negS, _VECTORMATH_SHUF_CZD0 ); + Y0 = spu_shuffle( negS, c, _VECTORMATH_SHUF_BBY0 ); + Y1 = spu_shuffle( c, s, _VECTORMATH_SHUF_BBY0 ); + X0 = spu_shuffle( s, s, shuffle_xxxx ); + X1 = spu_shuffle( c, c, shuffle_xxxx ); + tmp = spu_mul( Z0, Y1 ); + result->col0.vec128 = spu_mul( Z0, Y0 ); + result->col1.vec128 = spu_madd( Z1, X1, spu_mul( tmp, X0 ) ); + result->col2.vec128 = spu_nmsub( Z1, X0, spu_mul( tmp, X1 ) ); +} + +static inline void vmathM3MakeRotationAxis( VmathMatrix3 *result, float radians, const VmathVector3 *unitVec ) +{ + vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + axis = unitVec->vec128; + sincosf4( spu_splats( radians ), &s, &c ); + xxxx = spu_shuffle( axis, axis, shuffle_xxxx ); + yyyy = spu_shuffle( axis, axis, shuffle_yyyy ); + zzzz = spu_shuffle( axis, axis, shuffle_zzzz ); + oneMinusC = spu_sub( spu_splats(1.0f), c ); + axisS = spu_mul( axis, s ); + negAxisS = negatef4( axisS ); + tmp0 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_0ZB0 ); + tmp1 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_C0X0 ); + tmp2 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_YA00 ); + tmp0 = spu_sel( tmp0, c, (vec_uint4)spu_maskb(0xf000) ); + tmp1 = spu_sel( tmp1, c, (vec_uint4)spu_maskb(0x0f00) ); + tmp2 = spu_sel( tmp2, c, (vec_uint4)spu_maskb(0x00f0) ); + result->col0.vec128 = spu_madd( spu_mul( axis, xxxx ), oneMinusC, tmp0 ); + result->col1.vec128 = spu_madd( spu_mul( axis, yyyy ), oneMinusC, tmp1 ); + result->col2.vec128 = spu_madd( spu_mul( axis, zzzz ), oneMinusC, tmp2 ); +} + +static inline void vmathM3MakeRotationQ( VmathMatrix3 *result, const VmathQuat *unitQuat ) +{ + vmathM3MakeFromQ( result, unitQuat ); +} + +static inline void vmathM3MakeScale( VmathMatrix3 *result, const VmathVector3 *scaleVec ) +{ + vec_float4 zero = spu_splats(0.0f); + result->col0.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0xf000) ); + result->col1.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0x0f00) ); + result->col2.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0x00f0) ); +} + +static inline void vmathM3AppendScale( VmathMatrix3 *result, const VmathMatrix3 *mat, const VmathVector3 *scaleVec ) +{ + vmathV3ScalarMul( &result->col0, &mat->col0, vmathV3GetX( scaleVec ) ); + vmathV3ScalarMul( &result->col1, &mat->col1, vmathV3GetY( scaleVec ) ); + vmathV3ScalarMul( &result->col2, &mat->col2, vmathV3GetZ( scaleVec ) ); +} + +static inline void vmathM3PrependScale( VmathMatrix3 *result, const VmathVector3 *scaleVec, const VmathMatrix3 *mat ) +{ + vmathV3MulPerElem( &result->col0, &mat->col0, scaleVec ); + vmathV3MulPerElem( &result->col1, &mat->col1, scaleVec ); + vmathV3MulPerElem( &result->col2, &mat->col2, scaleVec ); +} + +static inline void vmathM3Select( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1, unsigned int select1 ) +{ + vmathV3Select( &result->col0, &mat0->col0, &mat1->col0, select1 ); + vmathV3Select( &result->col1, &mat0->col1, &mat1->col1, select1 ); + vmathV3Select( &result->col2, &mat0->col2, &mat1->col2, select1 ); +} + +#ifdef _VECTORMATH_DEBUG + +static inline void vmathM3Print( const VmathMatrix3 *mat ) +{ + VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2; + vmathM3GetRow( &tmpV3_0, mat, 0 ); + vmathV3Print( &tmpV3_0 ); + vmathM3GetRow( &tmpV3_1, mat, 1 ); + vmathV3Print( &tmpV3_1 ); + vmathM3GetRow( &tmpV3_2, mat, 2 ); + vmathV3Print( &tmpV3_2 ); +} + +static inline void vmathM3Prints( const VmathMatrix3 *mat, const char *name ) +{ + printf("%s:\n", name); + vmathM3Print( mat ); +} + +#endif + +static inline void vmathM4Copy( VmathMatrix4 *result, const VmathMatrix4 *mat ) +{ + vmathV4Copy( &result->col0, &mat->col0 ); + vmathV4Copy( &result->col1, &mat->col1 ); + vmathV4Copy( &result->col2, &mat->col2 ); + vmathV4Copy( &result->col3, &mat->col3 ); +} + +static inline void vmathM4MakeFromScalar( VmathMatrix4 *result, float scalar ) +{ + vmathV4MakeFromScalar( &result->col0, scalar ); + vmathV4MakeFromScalar( &result->col1, scalar ); + vmathV4MakeFromScalar( &result->col2, scalar ); + vmathV4MakeFromScalar( &result->col3, scalar ); +} + +static inline void vmathM4MakeFromT3( VmathMatrix4 *result, const VmathTransform3 *mat ) +{ + vmathV4MakeFromV3Scalar( &result->col0, &mat->col0, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col1, &mat->col1, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col2, &mat->col2, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col3, &mat->col3, 1.0f ); +} + +static inline void vmathM4MakeFromCols( VmathMatrix4 *result, const VmathVector4 *_col0, const VmathVector4 *_col1, const VmathVector4 *_col2, const VmathVector4 *_col3 ) +{ + vmathV4Copy( &result->col0, _col0 ); + vmathV4Copy( &result->col1, _col1 ); + vmathV4Copy( &result->col2, _col2 ); + vmathV4Copy( &result->col3, _col3 ); +} + +static inline void vmathM4MakeFromM3V3( VmathMatrix4 *result, const VmathMatrix3 *mat, const VmathVector3 *translateVec ) +{ + vmathV4MakeFromV3Scalar( &result->col0, &mat->col0, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col1, &mat->col1, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col2, &mat->col2, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col3, translateVec, 1.0f ); +} + +static inline void vmathM4MakeFromQV3( VmathMatrix4 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ) +{ + VmathMatrix3 mat; + vmathM3MakeFromQ( &mat, unitQuat ); + vmathV4MakeFromV3Scalar( &result->col0, &mat.col0, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col1, &mat.col1, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col2, &mat.col2, 0.0f ); + vmathV4MakeFromV3Scalar( &result->col3, translateVec, 1.0f ); +} + +static inline void vmathM4SetCol0( VmathMatrix4 *result, const VmathVector4 *_col0 ) +{ + vmathV4Copy( &result->col0, _col0 ); +} + +static inline void vmathM4SetCol1( VmathMatrix4 *result, const VmathVector4 *_col1 ) +{ + vmathV4Copy( &result->col1, _col1 ); +} + +static inline void vmathM4SetCol2( VmathMatrix4 *result, const VmathVector4 *_col2 ) +{ + vmathV4Copy( &result->col2, _col2 ); +} + +static inline void vmathM4SetCol3( VmathMatrix4 *result, const VmathVector4 *_col3 ) +{ + vmathV4Copy( &result->col3, _col3 ); +} + +static inline void vmathM4SetCol( VmathMatrix4 *result, int col, const VmathVector4 *vec ) +{ + vmathV4Copy( (&result->col0 + col), vec ); +} + +static inline void vmathM4SetRow( VmathMatrix4 *result, int row, const VmathVector4 *vec ) +{ + vmathV4SetElem( &result->col0, row, vmathV4GetElem( vec, 0 ) ); + vmathV4SetElem( &result->col1, row, vmathV4GetElem( vec, 1 ) ); + vmathV4SetElem( &result->col2, row, vmathV4GetElem( vec, 2 ) ); + vmathV4SetElem( &result->col3, row, vmathV4GetElem( vec, 3 ) ); +} + +static inline void vmathM4SetElem( VmathMatrix4 *result, int col, int row, float val ) +{ + VmathVector4 tmpV3_0; + vmathM4GetCol( &tmpV3_0, result, col ); + vmathV4SetElem( &tmpV3_0, row, val ); + vmathM4SetCol( result, col, &tmpV3_0 ); +} + +static inline float vmathM4GetElem( const VmathMatrix4 *mat, int col, int row ) +{ + VmathVector4 tmpV4_0; + vmathM4GetCol( &tmpV4_0, mat, col ); + return vmathV4GetElem( &tmpV4_0, row ); +} + +static inline void vmathM4GetCol0( VmathVector4 *result, const VmathMatrix4 *mat ) +{ + vmathV4Copy( result, &mat->col0 ); +} + +static inline void vmathM4GetCol1( VmathVector4 *result, const VmathMatrix4 *mat ) +{ + vmathV4Copy( result, &mat->col1 ); +} + +static inline void vmathM4GetCol2( VmathVector4 *result, const VmathMatrix4 *mat ) +{ + vmathV4Copy( result, &mat->col2 ); +} + +static inline void vmathM4GetCol3( VmathVector4 *result, const VmathMatrix4 *mat ) +{ + vmathV4Copy( result, &mat->col3 ); +} + +static inline void vmathM4GetCol( VmathVector4 *result, const VmathMatrix4 *mat, int col ) +{ + vmathV4Copy( result, (&mat->col0 + col) ); +} + +static inline void vmathM4GetRow( VmathVector4 *result, const VmathMatrix4 *mat, int row ) +{ + vmathV4MakeFromElems( result, vmathV4GetElem( &mat->col0, row ), vmathV4GetElem( &mat->col1, row ), vmathV4GetElem( &mat->col2, row ), vmathV4GetElem( &mat->col3, row ) ); +} + +static inline void vmathM4Transpose( VmathMatrix4 *result, const VmathMatrix4 *mat ) +{ + vec_float4 tmp0, tmp1, tmp2, tmp3, res0, res1, res2, res3; + tmp0 = spu_shuffle( mat->col0.vec128, mat->col2.vec128, _VECTORMATH_SHUF_XAYB ); + tmp1 = spu_shuffle( mat->col1.vec128, mat->col3.vec128, _VECTORMATH_SHUF_XAYB ); + tmp2 = spu_shuffle( mat->col0.vec128, mat->col2.vec128, _VECTORMATH_SHUF_ZCWD ); + tmp3 = spu_shuffle( mat->col1.vec128, mat->col3.vec128, _VECTORMATH_SHUF_ZCWD ); + res0 = spu_shuffle( tmp0, tmp1, _VECTORMATH_SHUF_XAYB ); + res1 = spu_shuffle( tmp0, tmp1, _VECTORMATH_SHUF_ZCWD ); + res2 = spu_shuffle( tmp2, tmp3, _VECTORMATH_SHUF_XAYB ); + res3 = spu_shuffle( tmp2, tmp3, _VECTORMATH_SHUF_ZCWD ); + result->col0.vec128 = res0; + result->col1.vec128 = res1; + result->col2.vec128 = res2; + result->col3.vec128 = res3; +} + +static inline void vmathM4Inverse( VmathMatrix4 *result, const VmathMatrix4 *mat ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vec_float4 in0, in1, in2, in3; + vec_float4 tmp0, tmp1, tmp2, tmp3; + vec_float4 cof0, cof1, cof2, cof3; + vec_float4 t0, t1, t2, t3; + vec_float4 t01, t02, t03, t12, t23; + vec_float4 t1r, t2r; + vec_float4 t01r, t02r, t03r, t12r, t23r; + vec_float4 t1r3, t1r3r; + vec_float4 det, det1, det2, det3, invdet; + in0 = mat->col0.vec128; + in1 = mat->col1.vec128; + in2 = mat->col2.vec128; + in3 = mat->col3.vec128; + /* Perform transform of the input matrix of the form: + * A B C D + * E F G H + * I J K L + * M N O P + * + * The pseudo transpose of the input matrix is trans: + * A E I M + * J N B F + * C G K O + * L P D H + */ + tmp0 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_XAZC); /* A E C G */ + tmp1 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_XAZC); /* I M K O */ + tmp2 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_YBWD); /* B F D H */ + tmp3 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_YBWD); /* J N L P */ + t0 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_XYAB); /* A E I M */ + t1 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_XYAB); /* J N B F */ + t2 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_ZWCD); /* C G K O */ + t3 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_ZWCD); /* L P D H */ + /* Generate a cofactor matrix. The computed cofactors reside in + * cof0, cof1, cof2, cof3. + */ + t23 = spu_mul(t2, t3); /* CL GP KD OH */ + t23 = spu_shuffle(t23, t23, _VECTORMATH_SHUF_YXWZ); /* GP CL OH KD */ + cof0 = spu_mul(t1, t23); /* JGP NCL BOH FKD */ + cof1 = spu_mul(t0, t23); /* AGP ECL IOH MKD */ + t23r = spu_rlqwbyte(t23, 8); /* OH KD GP CL */ + cof0 = spu_msub(t1, t23r, cof0); /* JOH NKD BGP FCL - cof0 */ + cof1 = spu_msub(t0, t23r, cof1); /* AOH EKD IGP MCL - cof1 */ + cof1 = spu_rlqwbyte(cof1, 8); /* IGP MCL AOH EKD - IOH MKD AGP ECL */ + + t12 = spu_mul(t1, t2); /* JC NG BK FO */ + t12 = spu_shuffle(t12, t12, _VECTORMATH_SHUF_YXWZ); /* NG JC FO BK */ + cof0 = spu_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ + cof3 = spu_mul(t0, t12); /* ANG EJC IFO MBK */ + t12r = spu_rlqwbyte(t12, 8); /* FO BK NG JC */ + cof0 = spu_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ + cof3 = spu_msub(t0, t12r, cof3); /* AFO EBK ING MJC - cof3 */ + cof3 = spu_rlqwbyte(cof3, 8); /* ING MJC AFO EBK - IFO MBK ANG EJC */ + t1r = spu_rlqwbyte(t1, 8); /* B F J N */ + t2r = spu_rlqwbyte(t2, 8); /* K O C G */ + t1r3 = spu_mul(t1r, t3); /* BL FP JD NH */ + t1r3 = spu_shuffle(t1r3, t1r3, _VECTORMATH_SHUF_YXWZ); /* FP BL NH JD */ + cof0 = spu_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ + cof2 = spu_mul(t0, t1r3); /* AFP EBL INH MJD */ + t1r3r = spu_rlqwbyte(t1r3, 8); /* NH JD FP BL */ + cof0 = spu_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ + cof2 = spu_msub(t0, t1r3r, cof2); /* ANH EJD IFP MBL - cof2 */ + cof2 = spu_rlqwbyte(cof2, 8); /* IFP MBL ANH EJD - INH MJD AFP EBL */ + t01 = spu_mul(t0, t1); /* AJ EN IB MF */ + t01 = spu_shuffle(t01, t01, _VECTORMATH_SHUF_YXWZ); /* EN AJ MF IB */ + cof2 = spu_madd(t3, t01, cof2); /* LEN PAJ DMF HIB + cof2 */ + cof3 = spu_msub(t2r, t01, cof3); /* KEN OAJ CMF GIB - cof3 */ + t01r = spu_rlqwbyte(t01, 8); /* MF IB EN AJ */ + cof2 = spu_msub(t3, t01r, cof2); /* LMF PIB DEN HAJ - cof2 */ + cof3 = spu_nmsub(t2r, t01r, cof3); /* cof3 - KMF OIB CEN GAJ */ + t03 = spu_mul(t0, t3); /* AL EP ID MH */ + t03 = spu_shuffle(t03, t03, _VECTORMATH_SHUF_YXWZ); /* EP AL MH ID */ + cof1 = spu_nmsub(t2r, t03, cof1); /* cof1 - KEP OAL CMH GID */ + cof2 = spu_madd(t1, t03, cof2); /* JEP NAL BMH FID + cof2 */ + t03r = spu_rlqwbyte(t03, 8); /* MH ID EP AL */ + cof1 = spu_madd(t2r, t03r, cof1); /* KMH OID CEP GAL + cof1 */ + cof2 = spu_nmsub(t1, t03r, cof2); /* cof2 - JMH NID BEP FAL */ + t02 = spu_mul(t0, t2r); /* AK EO IC MG */ + t02 = spu_shuffle(t02, t02, _VECTORMATH_SHUF_YXWZ); /* E0 AK MG IC */ + cof1 = spu_madd(t3, t02, cof1); /* LEO PAK DMG HIC + cof1 */ + cof3 = spu_nmsub(t1, t02, cof3); /* cof3 - JEO NAK BMG FIC */ + t02r = spu_rlqwbyte(t02, 8); /* MG IC EO AK */ + cof1 = spu_nmsub(t3, t02r, cof1); /* cof1 - LMG PIC DEO HAK */ + cof3 = spu_madd(t1, t02r, cof3); /* JMG NIC BEO FAK + cof3 */ + /* Compute the determinant of the matrix + * + * det = sum_across(t0 * cof0); + * + * We perform a sum across the entire vector so that + * we don't have to splat the result when multiplying the + * cofactors by the inverse of the determinant. + */ + det = spu_mul(t0, cof0); + det1 = spu_rlqwbyte(det, 4); + det2 = spu_rlqwbyte(det, 8); + det3 = spu_rlqwbyte(det, 12); + det = spu_add(det, det1); + det2 = spu_add(det2, det3); + det = spu_add(det, det2); + /* Compute the reciprocal of the determinant. + */ + invdet = recipf4(det); + /* Multiply the cofactors by the reciprocal of the determinant. + */ + result->col0.vec128 = spu_mul(cof0, invdet); + result->col1.vec128 = spu_mul(cof1, invdet); + result->col2.vec128 = spu_mul(cof2, invdet); + result->col3.vec128 = spu_mul(cof3, invdet); +} + +static inline void vmathM4AffineInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ) +{ + VmathTransform3 affineMat, tmpT3_0; + VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2, tmpV3_3; + vmathV4GetXYZ( &tmpV3_0, &mat->col0 ); + vmathT3SetCol0( &affineMat, &tmpV3_0 ); + vmathV4GetXYZ( &tmpV3_1, &mat->col1 ); + vmathT3SetCol1( &affineMat, &tmpV3_1 ); + vmathV4GetXYZ( &tmpV3_2, &mat->col2 ); + vmathT3SetCol2( &affineMat, &tmpV3_2 ); + vmathV4GetXYZ( &tmpV3_3, &mat->col3 ); + vmathT3SetCol3( &affineMat, &tmpV3_3 ); + vmathT3Inverse( &tmpT3_0, &affineMat ); + vmathM4MakeFromT3( result, &tmpT3_0 ); +} + +static inline void vmathM4OrthoInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ) +{ + VmathTransform3 affineMat, tmpT3_0; + VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2, tmpV3_3; + vmathV4GetXYZ( &tmpV3_0, &mat->col0 ); + vmathT3SetCol0( &affineMat, &tmpV3_0 ); + vmathV4GetXYZ( &tmpV3_1, &mat->col1 ); + vmathT3SetCol1( &affineMat, &tmpV3_1 ); + vmathV4GetXYZ( &tmpV3_2, &mat->col2 ); + vmathT3SetCol2( &affineMat, &tmpV3_2 ); + vmathV4GetXYZ( &tmpV3_3, &mat->col3 ); + vmathT3SetCol3( &affineMat, &tmpV3_3 ); + vmathT3OrthoInverse( &tmpT3_0, &affineMat ); + vmathM4MakeFromT3( result, &tmpT3_0 ); +} + +static inline float vmathM4Determinant( const VmathMatrix4 *mat ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vec_float4 in0, in1, in2, in3; + vec_float4 tmp0, tmp1, tmp2, tmp3; + vec_float4 cof0; + vec_float4 t0, t1, t2, t3; + vec_float4 t12, t23; + vec_float4 t1r, t2r; + vec_float4 t12r, t23r; + vec_float4 t1r3, t1r3r; + in0 = mat->col0.vec128; + in1 = mat->col1.vec128; + in2 = mat->col2.vec128; + in3 = mat->col3.vec128; + /* Perform transform of the input matrix of the form: + * A B C D + * E F G H + * I J K L + * M N O P + * + * The pseudo transpose of the input matrix is trans: + * A E I M + * J N B F + * C G K O + * L P D H + */ + tmp0 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_XAZC); /* A E C G */ + tmp1 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_XAZC); /* I M K O */ + tmp2 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_YBWD); /* B F D H */ + tmp3 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_YBWD); /* J N L P */ + t0 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_XYAB); /* A E I M */ + t1 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_XYAB); /* J N B F */ + t2 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_ZWCD); /* C G K O */ + t3 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_ZWCD); /* L P D H */ + /* Generate a cofactor matrix. The computed cofactors reside in + * cof0, cof1, cof2, cof3. + */ + t23 = spu_mul(t2, t3); /* CL GP KD OH */ + t23 = spu_shuffle(t23, t23, _VECTORMATH_SHUF_YXWZ); /* GP CL OH KD */ + cof0 = spu_mul(t1, t23); /* JGP NCL BOH FKD */ + t23r = spu_rlqwbyte(t23, 8); /* OH KD GP CL */ + cof0 = spu_msub(t1, t23r, cof0); /* JOH NKD BGP FCL - cof0 */ + + t12 = spu_mul(t1, t2); /* JC NG BK FO */ + t12 = spu_shuffle(t12, t12, _VECTORMATH_SHUF_YXWZ); /* NG JC FO BK */ + cof0 = spu_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ + t12r = spu_rlqwbyte(t12, 8); /* FO BK NG JC */ + cof0 = spu_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ + t1r = spu_rlqwbyte(t1, 8); /* B F J N */ + t2r = spu_rlqwbyte(t2, 8); /* K O C G */ + t1r3 = spu_mul(t1r, t3); /* BL FP JD NH */ + t1r3 = spu_shuffle(t1r3, t1r3, _VECTORMATH_SHUF_YXWZ); /* FP BL NH JD */ + cof0 = spu_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ + t1r3r = spu_rlqwbyte(t1r3, 8); /* NH JD FP BL */ + cof0 = spu_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ + return spu_extract( _vmathVfDot4(t0,cof0), 0 ); +} + +static inline void vmathM4Add( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) +{ + vmathV4Add( &result->col0, &mat0->col0, &mat1->col0 ); + vmathV4Add( &result->col1, &mat0->col1, &mat1->col1 ); + vmathV4Add( &result->col2, &mat0->col2, &mat1->col2 ); + vmathV4Add( &result->col3, &mat0->col3, &mat1->col3 ); +} + +static inline void vmathM4Sub( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) +{ + vmathV4Sub( &result->col0, &mat0->col0, &mat1->col0 ); + vmathV4Sub( &result->col1, &mat0->col1, &mat1->col1 ); + vmathV4Sub( &result->col2, &mat0->col2, &mat1->col2 ); + vmathV4Sub( &result->col3, &mat0->col3, &mat1->col3 ); +} + +static inline void vmathM4Neg( VmathMatrix4 *result, const VmathMatrix4 *mat ) +{ + vmathV4Neg( &result->col0, &mat->col0 ); + vmathV4Neg( &result->col1, &mat->col1 ); + vmathV4Neg( &result->col2, &mat->col2 ); + vmathV4Neg( &result->col3, &mat->col3 ); +} + +static inline void vmathM4AbsPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat ) +{ + vmathV4AbsPerElem( &result->col0, &mat->col0 ); + vmathV4AbsPerElem( &result->col1, &mat->col1 ); + vmathV4AbsPerElem( &result->col2, &mat->col2 ); + vmathV4AbsPerElem( &result->col3, &mat->col3 ); +} + +static inline void vmathM4ScalarMul( VmathMatrix4 *result, const VmathMatrix4 *mat, float scalar ) +{ + vmathV4ScalarMul( &result->col0, &mat->col0, scalar ); + vmathV4ScalarMul( &result->col1, &mat->col1, scalar ); + vmathV4ScalarMul( &result->col2, &mat->col2, scalar ); + vmathV4ScalarMul( &result->col3, &mat->col3, scalar ); +} + +static inline void vmathM4MulV4( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector4 *vec ) +{ + vec_float4 tmp0, tmp1, res; + vec_float4 xxxx, yyyy, zzzz, wwww; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); + xxxx = spu_shuffle( vec->vec128, vec->vec128, shuffle_xxxx ); + yyyy = spu_shuffle( vec->vec128, vec->vec128, shuffle_yyyy ); + zzzz = spu_shuffle( vec->vec128, vec->vec128, shuffle_zzzz ); + wwww = spu_shuffle( vec->vec128, vec->vec128, shuffle_wwww ); + tmp0 = spu_mul( mat->col0.vec128, xxxx ); + tmp1 = spu_mul( mat->col1.vec128, yyyy ); + tmp0 = spu_madd( mat->col2.vec128, zzzz, tmp0 ); + tmp1 = spu_madd( mat->col3.vec128, wwww, tmp1 ); + res = spu_add( tmp0, tmp1 ); + result->vec128 = res; +} + +static inline void vmathM4MulV3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector3 *vec ) +{ + vec_float4 res; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + xxxx = spu_shuffle( vec->vec128, vec->vec128, shuffle_xxxx ); + yyyy = spu_shuffle( vec->vec128, vec->vec128, shuffle_yyyy ); + zzzz = spu_shuffle( vec->vec128, vec->vec128, shuffle_zzzz ); + res = spu_mul( mat->col0.vec128, xxxx ); + res = spu_madd( mat->col1.vec128, yyyy, res ); + res = spu_madd( mat->col2.vec128, zzzz, res ); + result->vec128 = res; +} + +static inline void vmathM4MulP3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathPoint3 *pnt ) +{ + vec_float4 tmp0, tmp1, res; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + xxxx = spu_shuffle( pnt->vec128, pnt->vec128, shuffle_xxxx ); + yyyy = spu_shuffle( pnt->vec128, pnt->vec128, shuffle_yyyy ); + zzzz = spu_shuffle( pnt->vec128, pnt->vec128, shuffle_zzzz ); + tmp0 = spu_mul( mat->col0.vec128, xxxx ); + tmp1 = spu_mul( mat->col1.vec128, yyyy ); + tmp0 = spu_madd( mat->col2.vec128, zzzz, tmp0 ); + tmp1 = spu_add( mat->col3.vec128, tmp1 ); + res = spu_add( tmp0, tmp1 ); + result->vec128 = res; +} + +static inline void vmathM4Mul( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) +{ + VmathMatrix4 tmpResult; + vmathM4MulV4( &tmpResult.col0, mat0, &mat1->col0 ); + vmathM4MulV4( &tmpResult.col1, mat0, &mat1->col1 ); + vmathM4MulV4( &tmpResult.col2, mat0, &mat1->col2 ); + vmathM4MulV4( &tmpResult.col3, mat0, &mat1->col3 ); + vmathM4Copy( result, &tmpResult ); +} + +static inline void vmathM4MulT3( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathTransform3 *tfrm1 ) +{ + VmathMatrix4 tmpResult; + VmathPoint3 tmpP3_0; + vmathM4MulV3( &tmpResult.col0, mat, &tfrm1->col0 ); + vmathM4MulV3( &tmpResult.col1, mat, &tfrm1->col1 ); + vmathM4MulV3( &tmpResult.col2, mat, &tfrm1->col2 ); + vmathP3MakeFromV3( &tmpP3_0, &tfrm1->col3 ); + vmathM4MulP3( &tmpResult.col3, mat, &tmpP3_0 ); + vmathM4Copy( result, &tmpResult ); +} + +static inline void vmathM4MulPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ) +{ + vmathV4MulPerElem( &result->col0, &mat0->col0, &mat1->col0 ); + vmathV4MulPerElem( &result->col1, &mat0->col1, &mat1->col1 ); + vmathV4MulPerElem( &result->col2, &mat0->col2, &mat1->col2 ); + vmathV4MulPerElem( &result->col3, &mat0->col3, &mat1->col3 ); +} + +static inline void vmathM4MakeIdentity( VmathMatrix4 *result ) +{ + vmathV4MakeXAxis( &result->col0 ); + vmathV4MakeYAxis( &result->col1 ); + vmathV4MakeZAxis( &result->col2 ); + vmathV4MakeWAxis( &result->col3 ); +} + +static inline void vmathM4SetUpper3x3( VmathMatrix4 *result, const VmathMatrix3 *mat3 ) +{ + vmathV4SetXYZ( &result->col0, &mat3->col0 ); + vmathV4SetXYZ( &result->col1, &mat3->col1 ); + vmathV4SetXYZ( &result->col2, &mat3->col2 ); +} + +static inline void vmathM4GetUpper3x3( VmathMatrix3 *result, const VmathMatrix4 *mat ) +{ + vmathV4GetXYZ( &result->col0, &mat->col0 ); + vmathV4GetXYZ( &result->col1, &mat->col1 ); + vmathV4GetXYZ( &result->col2, &mat->col2 ); +} + +static inline void vmathM4SetTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ) +{ + vmathV4SetXYZ( &result->col3, translateVec ); +} + +static inline void vmathM4GetTranslation( VmathVector3 *result, const VmathMatrix4 *mat ) +{ + vmathV4GetXYZ( result, &mat->col3 ); +} + +static inline void vmathM4MakeRotationX( VmathMatrix4 *result, float radians ) +{ + vec_float4 s, c, res1, res2; + vec_uint4 select_y, select_z; + vec_float4 zero; + select_y = (vec_uint4)spu_maskb(0x0f00); + select_z = (vec_uint4)spu_maskb(0x00f0); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res1 = spu_sel( zero, c, select_y ); + res1 = spu_sel( res1, s, select_z ); + res2 = spu_sel( zero, negatef4(s), select_y ); + res2 = spu_sel( res2, c, select_z ); + vmathV4MakeXAxis( &result->col0 ); + result->col1.vec128 = res1; + result->col2.vec128 = res2; + vmathV4MakeWAxis( &result->col3 ); +} + +static inline void vmathM4MakeRotationY( VmathMatrix4 *result, float radians ) +{ + vec_float4 s, c, res0, res2; + vec_uint4 select_x, select_z; + vec_float4 zero; + select_x = (vec_uint4)spu_maskb(0xf000); + select_z = (vec_uint4)spu_maskb(0x00f0); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res0 = spu_sel( zero, c, select_x ); + res0 = spu_sel( res0, negatef4(s), select_z ); + res2 = spu_sel( zero, s, select_x ); + res2 = spu_sel( res2, c, select_z ); + result->col0.vec128 = res0; + vmathV4MakeYAxis( &result->col1 ); + result->col2.vec128 = res2; + vmathV4MakeWAxis( &result->col3 ); +} + +static inline void vmathM4MakeRotationZ( VmathMatrix4 *result, float radians ) +{ + vec_float4 s, c, res0, res1; + vec_uint4 select_x, select_y; + vec_float4 zero; + select_x = (vec_uint4)spu_maskb(0xf000); + select_y = (vec_uint4)spu_maskb(0x0f00); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res0 = spu_sel( zero, c, select_x ); + res0 = spu_sel( res0, s, select_y ); + res1 = spu_sel( zero, negatef4(s), select_x ); + res1 = spu_sel( res1, c, select_y ); + result->col0.vec128 = res0; + result->col1.vec128 = res1; + vmathV4MakeZAxis( &result->col2 ); + vmathV4MakeWAxis( &result->col3 ); +} + +static inline void vmathM4MakeRotationZYX( VmathMatrix4 *result, const VmathVector3 *radiansXYZ ) +{ + vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + angles = radiansXYZ->vec128; + angles = spu_insert( 0.0f, angles, 3 ); + sincosf4( angles, &s, &c ); + negS = negatef4( s ); + Z0 = spu_shuffle( s, c, _VECTORMATH_SHUF_CZD0 ); + Z1 = spu_shuffle( c, negS, _VECTORMATH_SHUF_CZD0 ); + Y0 = spu_shuffle( negS, c, _VECTORMATH_SHUF_BBY0 ); + Y1 = spu_shuffle( c, s, _VECTORMATH_SHUF_BBY0 ); + X0 = spu_shuffle( s, s, shuffle_xxxx ); + X1 = spu_shuffle( c, c, shuffle_xxxx ); + tmp = spu_mul( Z0, Y1 ); + result->col0.vec128 = spu_mul( Z0, Y0 ); + result->col1.vec128 = spu_madd( Z1, X1, spu_mul( tmp, X0 ) ); + result->col2.vec128 = spu_nmsub( Z1, X0, spu_mul( tmp, X1 ) ); + vmathV4MakeWAxis( &result->col3 ); +} + +static inline void vmathM4MakeRotationAxis( VmathMatrix4 *result, float radians, const VmathVector3 *unitVec ) +{ + vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2, zeroW; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + axis = unitVec->vec128; + sincosf4( spu_splats( radians ), &s, &c ); + xxxx = spu_shuffle( axis, axis, shuffle_xxxx ); + yyyy = spu_shuffle( axis, axis, shuffle_yyyy ); + zzzz = spu_shuffle( axis, axis, shuffle_zzzz ); + oneMinusC = spu_sub( spu_splats(1.0f), c ); + axisS = spu_mul( axis, s ); + negAxisS = negatef4( axisS ); + tmp0 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_0ZB0 ); + tmp1 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_C0X0 ); + tmp2 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_YA00 ); + tmp0 = spu_sel( tmp0, c, (vec_uint4)spu_maskb(0xf000) ); + tmp1 = spu_sel( tmp1, c, (vec_uint4)spu_maskb(0x0f00) ); + tmp2 = spu_sel( tmp2, c, (vec_uint4)spu_maskb(0x00f0) ); + zeroW = (vec_float4)spu_maskb(0x000f); + axis = spu_andc( axis, zeroW ); + result->col0.vec128 = spu_madd( spu_mul( axis, xxxx ), oneMinusC, tmp0 ); + result->col1.vec128 = spu_madd( spu_mul( axis, yyyy ), oneMinusC, tmp1 ); + result->col2.vec128 = spu_madd( spu_mul( axis, zzzz ), oneMinusC, tmp2 ); + vmathV4MakeWAxis( &result->col3 ); +} + +static inline void vmathM4MakeRotationQ( VmathMatrix4 *result, const VmathQuat *unitQuat ) +{ + VmathTransform3 tmpT3_0; + vmathT3MakeRotationQ( &tmpT3_0, unitQuat ); + vmathM4MakeFromT3( result, &tmpT3_0 ); +} + +static inline void vmathM4MakeScale( VmathMatrix4 *result, const VmathVector3 *scaleVec ) +{ + vec_float4 zero = spu_splats(0.0f); + result->col0.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0xf000) ); + result->col1.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0x0f00) ); + result->col2.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0x00f0) ); + vmathV4MakeWAxis( &result->col3 ); +} + +static inline void vmathM4AppendScale( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathVector3 *scaleVec ) +{ + vmathV4ScalarMul( &result->col0, &mat->col0, vmathV3GetX( scaleVec ) ); + vmathV4ScalarMul( &result->col1, &mat->col1, vmathV3GetY( scaleVec ) ); + vmathV4ScalarMul( &result->col2, &mat->col2, vmathV3GetZ( scaleVec ) ); + vmathV4Copy( &result->col3, &mat->col3 ); +} + +static inline void vmathM4PrependScale( VmathMatrix4 *result, const VmathVector3 *scaleVec, const VmathMatrix4 *mat ) +{ + VmathVector4 scale4; + vmathV4MakeFromV3Scalar( &scale4, scaleVec, 1.0f ); + vmathV4MulPerElem( &result->col0, &mat->col0, &scale4 ); + vmathV4MulPerElem( &result->col1, &mat->col1, &scale4 ); + vmathV4MulPerElem( &result->col2, &mat->col2, &scale4 ); + vmathV4MulPerElem( &result->col3, &mat->col3, &scale4 ); +} + +static inline void vmathM4MakeTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ) +{ + vmathV4MakeXAxis( &result->col0 ); + vmathV4MakeYAxis( &result->col1 ); + vmathV4MakeZAxis( &result->col2 ); + vmathV4MakeFromV3Scalar( &result->col3, translateVec, 1.0f ); +} + +static inline void vmathM4MakeLookAt( VmathMatrix4 *result, const VmathPoint3 *eyePos, const VmathPoint3 *lookAtPos, const VmathVector3 *upVec ) +{ + VmathMatrix4 m4EyeFrame; + VmathVector3 v3X, v3Y, v3Z, tmpV3_0, tmpV3_1; + VmathVector4 tmpV4_0, tmpV4_1, tmpV4_2, tmpV4_3; + vmathV3Normalize( &v3Y, upVec ); + vmathP3Sub( &tmpV3_0, eyePos, lookAtPos ); + vmathV3Normalize( &v3Z, &tmpV3_0 ); + vmathV3Cross( &tmpV3_1, &v3Y, &v3Z ); + vmathV3Normalize( &v3X, &tmpV3_1 ); + vmathV3Cross( &v3Y, &v3Z, &v3X ); + vmathV4MakeFromV3( &tmpV4_0, &v3X ); + vmathV4MakeFromV3( &tmpV4_1, &v3Y ); + vmathV4MakeFromV3( &tmpV4_2, &v3Z ); + vmathV4MakeFromP3( &tmpV4_3, eyePos ); + vmathM4MakeFromCols( &m4EyeFrame, &tmpV4_0, &tmpV4_1, &tmpV4_2, &tmpV4_3 ); + vmathM4OrthoInverse( result, &m4EyeFrame ); +} + +static inline void vmathM4MakePerspective( VmathMatrix4 *result, float fovyRadians, float aspect, float zNear, float zFar ) +{ + float f, rangeInv; + vec_float4 zero, col0, col1, col2, col3; + f = tanf( _VECTORMATH_PI_OVER_2 - fovyRadians * 0.5f ); + rangeInv = 1.0f / ( zNear - zFar ); + zero = spu_splats(0.0f); + col0 = zero; + col1 = zero; + col2 = zero; + col3 = zero; + col0 = spu_insert( f / aspect, col0, 0 ); + col1 = spu_insert( f, col1, 1 ); + col2 = spu_insert( ( zNear + zFar ) * rangeInv, col2, 2 ); + col2 = spu_insert( -1.0f, col2, 3 ); + col3 = spu_insert( zNear * zFar * rangeInv * 2.0f, col3, 2 ); + result->col0.vec128 = col0; + result->col1.vec128 = col1; + result->col2.vec128 = col2; + result->col3.vec128 = col3; +} + +static inline void vmathM4MakeFrustum( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vec_float4 lbf, rtn; + vec_float4 diff, sum, inv_diff; + vec_float4 diagonal, column, near2; + vec_float4 zero = spu_splats(0.0f); + lbf = spu_shuffle( spu_promote(left,0), spu_promote(zFar,0), _VECTORMATH_SHUF_XAYB ); + rtn = spu_shuffle( spu_promote(right,0), spu_promote(zNear,0), _VECTORMATH_SHUF_XAYB ); + lbf = spu_shuffle( lbf, spu_promote(bottom,0), _VECTORMATH_SHUF_XAYB ); + rtn = spu_shuffle( rtn, spu_promote(top,0), _VECTORMATH_SHUF_XAYB ); + diff = spu_sub( rtn, lbf ); + sum = spu_add( rtn, lbf ); + inv_diff = recipf4( diff ); + near2 = spu_splats( zNear ); + near2 = spu_add( near2, near2 ); + diagonal = spu_mul( near2, inv_diff ); + column = spu_mul( sum, inv_diff ); + result->col0.vec128 = spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0xf000) ); + result->col1.vec128 = spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0x0f00) ); + result->col2.vec128 = spu_sel( column, spu_splats(-1.0f), (vec_uint4)spu_maskb(0x000f) ); + result->col3.vec128 = spu_sel( zero, spu_mul( diagonal, spu_splats(zFar) ), (vec_uint4)spu_maskb(0x00f0) ); +} + +static inline void vmathM4MakeOrthographic( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vec_float4 lbf, rtn; + vec_float4 diff, sum, inv_diff, neg_inv_diff; + vec_float4 diagonal, column; + vec_float4 zero = spu_splats(0.0f); + lbf = spu_shuffle( spu_promote(left,0), spu_promote(zFar,0), _VECTORMATH_SHUF_XAYB ); + rtn = spu_shuffle( spu_promote(right,0), spu_promote(zNear,0), _VECTORMATH_SHUF_XAYB ); + lbf = spu_shuffle( lbf, spu_promote(bottom,0), _VECTORMATH_SHUF_XAYB ); + rtn = spu_shuffle( rtn, spu_promote(top,0), _VECTORMATH_SHUF_XAYB ); + diff = spu_sub( rtn, lbf ); + sum = spu_add( rtn, lbf ); + inv_diff = recipf4( diff ); + neg_inv_diff = negatef4( inv_diff ); + diagonal = spu_add( inv_diff, inv_diff ); + column = spu_mul( sum, spu_sel( neg_inv_diff, inv_diff, (vec_uint4)spu_maskb(0x00f0) ) ); + result->col0.vec128 = spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0xf000) ); + result->col1.vec128 = spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0x0f00) ); + result->col2.vec128 = spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0x00f0) ); + result->col3.vec128 = spu_sel( column, spu_splats(1.0f), (vec_uint4)spu_maskb(0x000f) ); +} + +static inline void vmathM4Select( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1, unsigned int select1 ) +{ + vmathV4Select( &result->col0, &mat0->col0, &mat1->col0, select1 ); + vmathV4Select( &result->col1, &mat0->col1, &mat1->col1, select1 ); + vmathV4Select( &result->col2, &mat0->col2, &mat1->col2, select1 ); + vmathV4Select( &result->col3, &mat0->col3, &mat1->col3, select1 ); +} + +#ifdef _VECTORMATH_DEBUG + +static inline void vmathM4Print( const VmathMatrix4 *mat ) +{ + VmathVector4 tmpV4_0, tmpV4_1, tmpV4_2, tmpV4_3; + vmathM4GetRow( &tmpV4_0, mat, 0 ); + vmathV4Print( &tmpV4_0 ); + vmathM4GetRow( &tmpV4_1, mat, 1 ); + vmathV4Print( &tmpV4_1 ); + vmathM4GetRow( &tmpV4_2, mat, 2 ); + vmathV4Print( &tmpV4_2 ); + vmathM4GetRow( &tmpV4_3, mat, 3 ); + vmathV4Print( &tmpV4_3 ); +} + +static inline void vmathM4Prints( const VmathMatrix4 *mat, const char *name ) +{ + printf("%s:\n", name); + vmathM4Print( mat ); +} + +#endif + +static inline void vmathT3Copy( VmathTransform3 *result, const VmathTransform3 *tfrm ) +{ + vmathV3Copy( &result->col0, &tfrm->col0 ); + vmathV3Copy( &result->col1, &tfrm->col1 ); + vmathV3Copy( &result->col2, &tfrm->col2 ); + vmathV3Copy( &result->col3, &tfrm->col3 ); +} + +static inline void vmathT3MakeFromScalar( VmathTransform3 *result, float scalar ) +{ + vmathV3MakeFromScalar( &result->col0, scalar ); + vmathV3MakeFromScalar( &result->col1, scalar ); + vmathV3MakeFromScalar( &result->col2, scalar ); + vmathV3MakeFromScalar( &result->col3, scalar ); +} + +static inline void vmathT3MakeFromCols( VmathTransform3 *result, const VmathVector3 *_col0, const VmathVector3 *_col1, const VmathVector3 *_col2, const VmathVector3 *_col3 ) +{ + vmathV3Copy( &result->col0, _col0 ); + vmathV3Copy( &result->col1, _col1 ); + vmathV3Copy( &result->col2, _col2 ); + vmathV3Copy( &result->col3, _col3 ); +} + +static inline void vmathT3MakeFromM3V3( VmathTransform3 *result, const VmathMatrix3 *tfrm, const VmathVector3 *translateVec ) +{ + vmathT3SetUpper3x3( result, tfrm ); + vmathT3SetTranslation( result, translateVec ); +} + +static inline void vmathT3MakeFromQV3( VmathTransform3 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ) +{ + VmathMatrix3 tmpM3_0; + vmathM3MakeFromQ( &tmpM3_0, unitQuat ); + vmathT3SetUpper3x3( result, &tmpM3_0 ); + vmathT3SetTranslation( result, translateVec ); +} + +static inline void vmathT3SetCol0( VmathTransform3 *result, const VmathVector3 *_col0 ) +{ + vmathV3Copy( &result->col0, _col0 ); +} + +static inline void vmathT3SetCol1( VmathTransform3 *result, const VmathVector3 *_col1 ) +{ + vmathV3Copy( &result->col1, _col1 ); +} + +static inline void vmathT3SetCol2( VmathTransform3 *result, const VmathVector3 *_col2 ) +{ + vmathV3Copy( &result->col2, _col2 ); +} + +static inline void vmathT3SetCol3( VmathTransform3 *result, const VmathVector3 *_col3 ) +{ + vmathV3Copy( &result->col3, _col3 ); +} + +static inline void vmathT3SetCol( VmathTransform3 *result, int col, const VmathVector3 *vec ) +{ + vmathV3Copy( (&result->col0 + col), vec ); +} + +static inline void vmathT3SetRow( VmathTransform3 *result, int row, const VmathVector4 *vec ) +{ + vmathV3SetElem( &result->col0, row, vmathV4GetElem( vec, 0 ) ); + vmathV3SetElem( &result->col1, row, vmathV4GetElem( vec, 1 ) ); + vmathV3SetElem( &result->col2, row, vmathV4GetElem( vec, 2 ) ); + vmathV3SetElem( &result->col3, row, vmathV4GetElem( vec, 3 ) ); +} + +static inline void vmathT3SetElem( VmathTransform3 *result, int col, int row, float val ) +{ + VmathVector3 tmpV3_0; + vmathT3GetCol( &tmpV3_0, result, col ); + vmathV3SetElem( &tmpV3_0, row, val ); + vmathT3SetCol( result, col, &tmpV3_0 ); +} + +static inline float vmathT3GetElem( const VmathTransform3 *tfrm, int col, int row ) +{ + VmathVector3 tmpV3_0; + vmathT3GetCol( &tmpV3_0, tfrm, col ); + return vmathV3GetElem( &tmpV3_0, row ); +} + +static inline void vmathT3GetCol0( VmathVector3 *result, const VmathTransform3 *tfrm ) +{ + vmathV3Copy( result, &tfrm->col0 ); +} + +static inline void vmathT3GetCol1( VmathVector3 *result, const VmathTransform3 *tfrm ) +{ + vmathV3Copy( result, &tfrm->col1 ); +} + +static inline void vmathT3GetCol2( VmathVector3 *result, const VmathTransform3 *tfrm ) +{ + vmathV3Copy( result, &tfrm->col2 ); +} + +static inline void vmathT3GetCol3( VmathVector3 *result, const VmathTransform3 *tfrm ) +{ + vmathV3Copy( result, &tfrm->col3 ); +} + +static inline void vmathT3GetCol( VmathVector3 *result, const VmathTransform3 *tfrm, int col ) +{ + vmathV3Copy( result, (&tfrm->col0 + col) ); +} + +static inline void vmathT3GetRow( VmathVector4 *result, const VmathTransform3 *tfrm, int row ) +{ + vmathV4MakeFromElems( result, vmathV3GetElem( &tfrm->col0, row ), vmathV3GetElem( &tfrm->col1, row ), vmathV3GetElem( &tfrm->col2, row ), vmathV3GetElem( &tfrm->col3, row ) ); +} + +static inline void vmathT3Inverse( VmathTransform3 *result, const VmathTransform3 *tfrm ) +{ + vec_float4 inv0, inv1, inv2, inv3; + vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + tmp2 = _vmathVfCross( tfrm->col0.vec128, tfrm->col1.vec128 ); + tmp0 = _vmathVfCross( tfrm->col1.vec128, tfrm->col2.vec128 ); + tmp1 = _vmathVfCross( tfrm->col2.vec128, tfrm->col0.vec128 ); + inv3 = negatef4( tfrm->col3.vec128 ); + dot = _vmathVfDot3( tmp2, tfrm->col2.vec128 ); + dot = spu_shuffle( dot, dot, shuffle_xxxx ); + invdet = recipf4( dot ); + tmp3 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_XAYB ); + tmp4 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_ZCWD ); + inv0 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_XAYB ); + xxxx = spu_shuffle( inv3, inv3, shuffle_xxxx ); + inv1 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_ZBW0 ); + inv2 = spu_shuffle( tmp4, tmp1, _VECTORMATH_SHUF_XCY0 ); + yyyy = spu_shuffle( inv3, inv3, shuffle_yyyy ); + zzzz = spu_shuffle( inv3, inv3, shuffle_zzzz ); + inv3 = spu_mul( inv0, xxxx ); + inv3 = spu_madd( inv1, yyyy, inv3 ); + inv3 = spu_madd( inv2, zzzz, inv3 ); + inv0 = spu_mul( inv0, invdet ); + inv1 = spu_mul( inv1, invdet ); + inv2 = spu_mul( inv2, invdet ); + inv3 = spu_mul( inv3, invdet ); + result->col0.vec128 = inv0; + result->col1.vec128 = inv1; + result->col2.vec128 = inv2; + result->col3.vec128 = inv3; +} + +static inline void vmathT3OrthoInverse( VmathTransform3 *result, const VmathTransform3 *tfrm ) +{ + vec_float4 inv0, inv1, inv2, inv3; + vec_float4 tmp0, tmp1; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + tmp0 = spu_shuffle( tfrm->col0.vec128, tfrm->col2.vec128, _VECTORMATH_SHUF_XAYB ); + tmp1 = spu_shuffle( tfrm->col0.vec128, tfrm->col2.vec128, _VECTORMATH_SHUF_ZCWD ); + inv3 = negatef4( tfrm->col3.vec128 ); + inv0 = spu_shuffle( tmp0, tfrm->col1.vec128, _VECTORMATH_SHUF_XAYB ); + xxxx = spu_shuffle( inv3, inv3, shuffle_xxxx ); + inv1 = spu_shuffle( tmp0, tfrm->col1.vec128, _VECTORMATH_SHUF_ZBW0 ); + inv2 = spu_shuffle( tmp1, tfrm->col1.vec128, _VECTORMATH_SHUF_XCY0 ); + yyyy = spu_shuffle( inv3, inv3, shuffle_yyyy ); + zzzz = spu_shuffle( inv3, inv3, shuffle_zzzz ); + inv3 = spu_mul( inv0, xxxx ); + inv3 = spu_madd( inv1, yyyy, inv3 ); + inv3 = spu_madd( inv2, zzzz, inv3 ); + result->col0.vec128 = inv0; + result->col1.vec128 = inv1; + result->col2.vec128 = inv2; + result->col3.vec128 = inv3; +} + +static inline void vmathT3AbsPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm ) +{ + vmathV3AbsPerElem( &result->col0, &tfrm->col0 ); + vmathV3AbsPerElem( &result->col1, &tfrm->col1 ); + vmathV3AbsPerElem( &result->col2, &tfrm->col2 ); + vmathV3AbsPerElem( &result->col3, &tfrm->col3 ); +} + +static inline void vmathT3MulV3( VmathVector3 *result, const VmathTransform3 *tfrm, const VmathVector3 *vec ) +{ + vec_float4 res; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + xxxx = spu_shuffle( vec->vec128, vec->vec128, shuffle_xxxx ); + yyyy = spu_shuffle( vec->vec128, vec->vec128, shuffle_yyyy ); + zzzz = spu_shuffle( vec->vec128, vec->vec128, shuffle_zzzz ); + res = spu_mul( tfrm->col0.vec128, xxxx ); + res = spu_madd( tfrm->col1.vec128, yyyy, res ); + res = spu_madd( tfrm->col2.vec128, zzzz, res ); + result->vec128 = res; +} + +static inline void vmathT3MulP3( VmathPoint3 *result, const VmathTransform3 *tfrm, const VmathPoint3 *pnt ) +{ + vec_float4 tmp0, tmp1, res; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + xxxx = spu_shuffle( pnt->vec128, pnt->vec128, shuffle_xxxx ); + yyyy = spu_shuffle( pnt->vec128, pnt->vec128, shuffle_yyyy ); + zzzz = spu_shuffle( pnt->vec128, pnt->vec128, shuffle_zzzz ); + tmp0 = spu_mul( tfrm->col0.vec128, xxxx ); + tmp1 = spu_mul( tfrm->col1.vec128, yyyy ); + tmp0 = spu_madd( tfrm->col2.vec128, zzzz, tmp0 ); + tmp1 = spu_add( tfrm->col3.vec128, tmp1 ); + res = spu_add( tmp0, tmp1 ); + result->vec128 = res; +} + +static inline void vmathT3Mul( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ) +{ + VmathTransform3 tmpResult; + VmathPoint3 tmpP3_0, tmpP3_1; + vmathT3MulV3( &tmpResult.col0, tfrm0, &tfrm1->col0 ); + vmathT3MulV3( &tmpResult.col1, tfrm0, &tfrm1->col1 ); + vmathT3MulV3( &tmpResult.col2, tfrm0, &tfrm1->col2 ); + vmathP3MakeFromV3( &tmpP3_0, &tfrm1->col3 ); + vmathT3MulP3( &tmpP3_1, tfrm0, &tmpP3_0 ); + vmathV3MakeFromP3( &tmpResult.col3, &tmpP3_1 ); + vmathT3Copy( result, &tmpResult ); +} + +static inline void vmathT3MulPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ) +{ + vmathV3MulPerElem( &result->col0, &tfrm0->col0, &tfrm1->col0 ); + vmathV3MulPerElem( &result->col1, &tfrm0->col1, &tfrm1->col1 ); + vmathV3MulPerElem( &result->col2, &tfrm0->col2, &tfrm1->col2 ); + vmathV3MulPerElem( &result->col3, &tfrm0->col3, &tfrm1->col3 ); +} + +static inline void vmathT3MakeIdentity( VmathTransform3 *result ) +{ + vmathV3MakeXAxis( &result->col0 ); + vmathV3MakeYAxis( &result->col1 ); + vmathV3MakeZAxis( &result->col2 ); + vmathV3MakeFromScalar( &result->col3, 0.0f ); +} + +static inline void vmathT3SetUpper3x3( VmathTransform3 *result, const VmathMatrix3 *tfrm ) +{ + vmathV3Copy( &result->col0, &tfrm->col0 ); + vmathV3Copy( &result->col1, &tfrm->col1 ); + vmathV3Copy( &result->col2, &tfrm->col2 ); +} + +static inline void vmathT3GetUpper3x3( VmathMatrix3 *result, const VmathTransform3 *tfrm ) +{ + vmathM3MakeFromCols( result, &tfrm->col0, &tfrm->col1, &tfrm->col2 ); +} + +static inline void vmathT3SetTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ) +{ + vmathV3Copy( &result->col3, translateVec ); +} + +static inline void vmathT3GetTranslation( VmathVector3 *result, const VmathTransform3 *tfrm ) +{ + vmathV3Copy( result, &tfrm->col3 ); +} + +static inline void vmathT3MakeRotationX( VmathTransform3 *result, float radians ) +{ + vec_float4 s, c, res1, res2; + vec_uint4 select_y, select_z; + vec_float4 zero; + select_y = (vec_uint4)spu_maskb(0x0f00); + select_z = (vec_uint4)spu_maskb(0x00f0); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res1 = spu_sel( zero, c, select_y ); + res1 = spu_sel( res1, s, select_z ); + res2 = spu_sel( zero, negatef4(s), select_y ); + res2 = spu_sel( res2, c, select_z ); + vmathV3MakeXAxis( &result->col0 ); + result->col1.vec128 = res1; + result->col2.vec128 = res2; + vmathV3MakeFromScalar( &result->col3, 0.0f ); +} + +static inline void vmathT3MakeRotationY( VmathTransform3 *result, float radians ) +{ + vec_float4 s, c, res0, res2; + vec_uint4 select_x, select_z; + vec_float4 zero; + select_x = (vec_uint4)spu_maskb(0xf000); + select_z = (vec_uint4)spu_maskb(0x00f0); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res0 = spu_sel( zero, c, select_x ); + res0 = spu_sel( res0, negatef4(s), select_z ); + res2 = spu_sel( zero, s, select_x ); + res2 = spu_sel( res2, c, select_z ); + result->col0.vec128 = res0; + vmathV3MakeYAxis( &result->col1 ); + result->col2.vec128 = res2; + vmathV3MakeFromScalar( &result->col3, 0.0f ); +} + +static inline void vmathT3MakeRotationZ( VmathTransform3 *result, float radians ) +{ + vec_float4 s, c, res0, res1; + vec_uint4 select_x, select_y; + vec_float4 zero; + select_x = (vec_uint4)spu_maskb(0xf000); + select_y = (vec_uint4)spu_maskb(0x0f00); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res0 = spu_sel( zero, c, select_x ); + res0 = spu_sel( res0, s, select_y ); + res1 = spu_sel( zero, negatef4(s), select_x ); + res1 = spu_sel( res1, c, select_y ); + result->col0.vec128 = res0; + result->col1.vec128 = res1; + vmathV3MakeZAxis( &result->col2 ); + vmathV3MakeFromScalar( &result->col3, 0.0f ); +} + +static inline void vmathT3MakeRotationZYX( VmathTransform3 *result, const VmathVector3 *radiansXYZ ) +{ + vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + angles = radiansXYZ->vec128; + angles = spu_insert( 0.0f, angles, 3 ); + sincosf4( angles, &s, &c ); + negS = negatef4( s ); + Z0 = spu_shuffle( s, c, _VECTORMATH_SHUF_CZD0 ); + Z1 = spu_shuffle( c, negS, _VECTORMATH_SHUF_CZD0 ); + Y0 = spu_shuffle( negS, c, _VECTORMATH_SHUF_BBY0 ); + Y1 = spu_shuffle( c, s, _VECTORMATH_SHUF_BBY0 ); + X0 = spu_shuffle( s, s, shuffle_xxxx ); + X1 = spu_shuffle( c, c, shuffle_xxxx ); + tmp = spu_mul( Z0, Y1 ); + result->col0.vec128 = spu_mul( Z0, Y0 ); + result->col1.vec128 = spu_madd( Z1, X1, spu_mul( tmp, X0 ) ); + result->col2.vec128 = spu_nmsub( Z1, X0, spu_mul( tmp, X1 ) ); + vmathV3MakeFromScalar( &result->col3, 0.0f ); +} + +static inline void vmathT3MakeRotationAxis( VmathTransform3 *result, float radians, const VmathVector3 *unitVec ) +{ + VmathMatrix3 tmpM3_0; + VmathVector3 tmpV3_0; + vmathM3MakeRotationAxis( &tmpM3_0, radians, unitVec ); + vmathV3MakeFromScalar( &tmpV3_0, 0.0f ); + vmathT3MakeFromM3V3( result, &tmpM3_0, &tmpV3_0 ); +} + +static inline void vmathT3MakeRotationQ( VmathTransform3 *result, const VmathQuat *unitQuat ) +{ + VmathMatrix3 tmpM3_0; + VmathVector3 tmpV3_0; + vmathM3MakeFromQ( &tmpM3_0, unitQuat ); + vmathV3MakeFromScalar( &tmpV3_0, 0.0f ); + vmathT3MakeFromM3V3( result, &tmpM3_0, &tmpV3_0 ); +} + +static inline void vmathT3MakeScale( VmathTransform3 *result, const VmathVector3 *scaleVec ) +{ + vec_float4 zero = spu_splats(0.0f); + result->col0.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0xf000) ); + result->col1.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0x0f00) ); + result->col2.vec128 = spu_sel( zero, scaleVec->vec128, (vec_uint4)spu_maskb(0x00f0) ); + vmathV3MakeFromScalar( &result->col3, 0.0f ); +} + +static inline void vmathT3AppendScale( VmathTransform3 *result, const VmathTransform3 *tfrm, const VmathVector3 *scaleVec ) +{ + vmathV3ScalarMul( &result->col0, &tfrm->col0, vmathV3GetX( scaleVec ) ); + vmathV3ScalarMul( &result->col1, &tfrm->col1, vmathV3GetY( scaleVec ) ); + vmathV3ScalarMul( &result->col2, &tfrm->col2, vmathV3GetZ( scaleVec ) ); + vmathV3Copy( &result->col3, &tfrm->col3 ); +} + +static inline void vmathT3PrependScale( VmathTransform3 *result, const VmathVector3 *scaleVec, const VmathTransform3 *tfrm ) +{ + vmathV3MulPerElem( &result->col0, &tfrm->col0, scaleVec ); + vmathV3MulPerElem( &result->col1, &tfrm->col1, scaleVec ); + vmathV3MulPerElem( &result->col2, &tfrm->col2, scaleVec ); + vmathV3MulPerElem( &result->col3, &tfrm->col3, scaleVec ); +} + +static inline void vmathT3MakeTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ) +{ + vmathV3MakeXAxis( &result->col0 ); + vmathV3MakeYAxis( &result->col1 ); + vmathV3MakeZAxis( &result->col2 ); + vmathV3Copy( &result->col3, translateVec ); +} + +static inline void vmathT3Select( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1, unsigned int select1 ) +{ + vmathV3Select( &result->col0, &tfrm0->col0, &tfrm1->col0, select1 ); + vmathV3Select( &result->col1, &tfrm0->col1, &tfrm1->col1, select1 ); + vmathV3Select( &result->col2, &tfrm0->col2, &tfrm1->col2, select1 ); + vmathV3Select( &result->col3, &tfrm0->col3, &tfrm1->col3, select1 ); +} + +#ifdef _VECTORMATH_DEBUG + +static inline void vmathT3Print( const VmathTransform3 *tfrm ) +{ + VmathVector4 tmpV4_0, tmpV4_1, tmpV4_2; + vmathT3GetRow( &tmpV4_0, tfrm, 0 ); + vmathV4Print( &tmpV4_0 ); + vmathT3GetRow( &tmpV4_1, tfrm, 1 ); + vmathV4Print( &tmpV4_1 ); + vmathT3GetRow( &tmpV4_2, tfrm, 2 ); + vmathV4Print( &tmpV4_2 ); +} + +static inline void vmathT3Prints( const VmathTransform3 *tfrm, const char *name ) +{ + printf("%s:\n", name); + vmathT3Print( tfrm ); +} + +#endif + +static inline void vmathQMakeFromM3( VmathQuat *result, const VmathMatrix3 *tfrm ) +{ + vec_float4 res; + vec_float4 col0, col1, col2; + vec_float4 xx_yy, xx_yy_zz_xx, yy_zz_xx_yy, zz_xx_yy_zz, diagSum, diagDiff; + vec_float4 zy_xz_yx, yz_zx_xy, sum, diff; + vec_float4 radicand, invSqrt, scale; + vec_float4 res0, res1, res2, res3; + vec_float4 xx, yy, zz; + vec_uint4 select_x = (vec_uint4)spu_maskb( 0xf000 ); + vec_uint4 select_y = (vec_uint4)spu_maskb( 0x0f00 ); + vec_uint4 select_z = (vec_uint4)spu_maskb( 0x00f0 ); + vec_uint4 select_w = (vec_uint4)spu_maskb( 0x000f ); + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((unsigned int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((unsigned int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((unsigned int)0x08090a0b); + vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((unsigned int)0x0c0d0e0f); + + col0 = tfrm->col0.vec128; + col1 = tfrm->col1.vec128; + col2 = tfrm->col2.vec128; + + /* four cases: */ + /* trace > 0 */ + /* else */ + /* xx largest diagonal element */ + /* yy largest diagonal element */ + /* zz largest diagonal element */ + + /* compute quaternion for each case */ + + xx_yy = spu_sel( col0, col1, select_y ); + xx_yy_zz_xx = spu_shuffle( xx_yy, col2, _VECTORMATH_SHUF_XYCX ); + yy_zz_xx_yy = spu_shuffle( xx_yy, col2, _VECTORMATH_SHUF_YCXY ); + zz_xx_yy_zz = spu_shuffle( xx_yy, col2, _VECTORMATH_SHUF_CXYC ); + + diagSum = spu_add( spu_add( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); + diagDiff = spu_sub( spu_sub( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); + radicand = spu_add( spu_sel( diagDiff, diagSum, select_w ), spu_splats(1.0f) ); + invSqrt = rsqrtf4( radicand ); + + zy_xz_yx = spu_sel( col0, col1, select_z ); + zy_xz_yx = spu_shuffle( zy_xz_yx, col2, _VECTORMATH_SHUF_ZAY0 ); + yz_zx_xy = spu_sel( col0, col1, select_x ); + yz_zx_xy = spu_shuffle( yz_zx_xy, col2, _VECTORMATH_SHUF_BZX0 ); + + sum = spu_add( zy_xz_yx, yz_zx_xy ); + diff = spu_sub( zy_xz_yx, yz_zx_xy ); + + scale = spu_mul( invSqrt, spu_splats(0.5f) ); + res0 = spu_shuffle( sum, diff, _VECTORMATH_SHUF_0ZYA ); + res1 = spu_shuffle( sum, diff, _VECTORMATH_SHUF_Z0XB ); + res2 = spu_shuffle( sum, diff, _VECTORMATH_SHUF_YX0C ); + res3 = diff; + res0 = spu_sel( res0, radicand, select_x ); + res1 = spu_sel( res1, radicand, select_y ); + res2 = spu_sel( res2, radicand, select_z ); + res3 = spu_sel( res3, radicand, select_w ); + res0 = spu_mul( res0, spu_shuffle( scale, scale, shuffle_xxxx ) ); + res1 = spu_mul( res1, spu_shuffle( scale, scale, shuffle_yyyy ) ); + res2 = spu_mul( res2, spu_shuffle( scale, scale, shuffle_zzzz ) ); + res3 = spu_mul( res3, spu_shuffle( scale, scale, shuffle_wwww ) ); + + /* determine case and select answer */ + + xx = spu_shuffle( col0, col0, shuffle_xxxx ); + yy = spu_shuffle( col1, col1, shuffle_yyyy ); + zz = spu_shuffle( col2, col2, shuffle_zzzz ); + res = spu_sel( res0, res1, spu_cmpgt( yy, xx ) ); + res = spu_sel( res, res2, spu_and( spu_cmpgt( zz, xx ), spu_cmpgt( zz, yy ) ) ); + res = spu_sel( res, res3, spu_cmpgt( spu_shuffle( diagSum, diagSum, shuffle_xxxx ), spu_splats(0.0f) ) ); + result->vec128 = res; +} + +static inline void vmathV3Outer( VmathMatrix3 *result, const VmathVector3 *tfrm0, const VmathVector3 *tfrm1 ) +{ + vmathV3ScalarMul( &result->col0, tfrm0, vmathV3GetX( tfrm1 ) ); + vmathV3ScalarMul( &result->col1, tfrm0, vmathV3GetY( tfrm1 ) ); + vmathV3ScalarMul( &result->col2, tfrm0, vmathV3GetZ( tfrm1 ) ); +} + +static inline void vmathV4Outer( VmathMatrix4 *result, const VmathVector4 *tfrm0, const VmathVector4 *tfrm1 ) +{ + vmathV4ScalarMul( &result->col0, tfrm0, vmathV4GetX( tfrm1 ) ); + vmathV4ScalarMul( &result->col1, tfrm0, vmathV4GetY( tfrm1 ) ); + vmathV4ScalarMul( &result->col2, tfrm0, vmathV4GetZ( tfrm1 ) ); + vmathV4ScalarMul( &result->col3, tfrm0, vmathV4GetW( tfrm1 ) ); +} + +static inline void vmathV3RowMul( VmathVector3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ) +{ + vec_float4 tmp0, tmp1, mcol0, mcol1, mcol2, res; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + tmp0 = spu_shuffle( mat->col0.vec128, mat->col2.vec128, _VECTORMATH_SHUF_XAYB ); + tmp1 = spu_shuffle( mat->col0.vec128, mat->col2.vec128, _VECTORMATH_SHUF_ZCWD ); + xxxx = spu_shuffle( vec->vec128, vec->vec128, shuffle_xxxx ); + mcol0 = spu_shuffle( tmp0, mat->col1.vec128, _VECTORMATH_SHUF_XAYB ); + mcol1 = spu_shuffle( tmp0, mat->col1.vec128, _VECTORMATH_SHUF_ZBW0 ); + mcol2 = spu_shuffle( tmp1, mat->col1.vec128, _VECTORMATH_SHUF_XCY0 ); + yyyy = spu_shuffle( vec->vec128, vec->vec128, shuffle_yyyy ); + res = spu_mul( mcol0, xxxx ); + zzzz = spu_shuffle( vec->vec128, vec->vec128, shuffle_zzzz ); + res = spu_madd( mcol1, yyyy, res ); + res = spu_madd( mcol2, zzzz, res ); + result->vec128 = res; +} + +static inline void vmathV3CrossMatrix( VmathMatrix3 *result, const VmathVector3 *vec ) +{ + vec_float4 neg, res0, res1, res2; + neg = negatef4( vec->vec128 ); + res0 = spu_shuffle( vec->vec128, neg, _VECTORMATH_SHUF_0ZB0 ); + res1 = spu_shuffle( vec->vec128, neg, _VECTORMATH_SHUF_C0X0 ); + res2 = spu_shuffle( vec->vec128, neg, _VECTORMATH_SHUF_YA00 ); + result->col0.vec128 = res0; + result->col1.vec128 = res1; + result->col2.vec128 = res2; +} + +static inline void vmathV3CrossMatrixMul( VmathMatrix3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ) +{ + VmathVector3 tmpV3_0, tmpV3_1, tmpV3_2; + vmathV3Cross( &tmpV3_0, vec, &mat->col0 ); + vmathV3Cross( &tmpV3_1, vec, &mat->col1 ); + vmathV3Cross( &tmpV3_2, vec, &mat->col2 ); + vmathM3MakeFromCols( result, &tmpV3_0, &tmpV3_1, &tmpV3_2 ); +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/common/vectormath/spu/c/quat_aos.h b/common/vectormath/spu/c/quat_aos.h index 0f25d654..b20109b1 100644 --- a/common/vectormath/spu/c/quat_aos.h +++ b/common/vectormath/spu/c/quat_aos.h @@ -1,371 +1,371 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_QUAT_AOS_C_H -#define _VECTORMATH_QUAT_AOS_C_H -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/*----------------------------------------------------------------------------- - * Definitions - */ -#ifndef _VECTORMATH_INTERNAL_FUNCTIONS -#define _VECTORMATH_INTERNAL_FUNCTIONS - -#endif - -static inline void vmathQCopy( VmathQuat *result, const VmathQuat *quat ) -{ - result->vec128 = quat->vec128; -} - -static inline void vmathQMakeFromElems( VmathQuat *result, float _x, float _y, float _z, float _w ) -{ - result->vec128 = (vec_float4){ _x, _y, _z, _w }; -} - -static inline void vmathQMakeFromV3Scalar( VmathQuat *result, const VmathVector3 *xyz, float _w ) -{ - result->vec128 = spu_shuffle( xyz->vec128, spu_promote( _w, 0 ), _VECTORMATH_SHUF_XYZA ); -} - -static inline void vmathQMakeFromV4( VmathQuat *result, const VmathVector4 *vec ) -{ - result->vec128 = vec->vec128; -} - -static inline void vmathQMakeFromScalar( VmathQuat *result, float scalar ) -{ - result->vec128 = spu_splats( scalar ); -} - -static inline void vmathQMakeFrom128( VmathQuat *result, vec_float4 vf4 ) -{ - result->vec128 = vf4; -} - -static inline void vmathQMakeIdentity( VmathQuat *result ) -{ - result->vec128 = _VECTORMATH_UNIT_0001; -} - -static inline void vmathQLerp( VmathQuat *result, float t, const VmathQuat *quat0, const VmathQuat *quat1 ) -{ - VmathQuat tmpQ_0, tmpQ_1; - vmathQSub( &tmpQ_0, quat1, quat0 ); - vmathQScalarMul( &tmpQ_1, &tmpQ_0, t ); - vmathQAdd( result, quat0, &tmpQ_1 ); -} - -static inline void vmathQSlerp( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1 ) -{ - VmathQuat start; - vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; - vec_uint4 selectMask; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - cosAngle = _vmathVfDot4( unitQuat0->vec128, unitQuat1->vec128 ); - cosAngle = spu_shuffle( cosAngle, cosAngle, shuffle_xxxx ); - selectMask = (vec_uint4)spu_cmpgt( spu_splats(0.0f), cosAngle ); - cosAngle = spu_sel( cosAngle, negatef4( cosAngle ), selectMask ); - start.vec128 = spu_sel( unitQuat0->vec128, negatef4( unitQuat0->vec128 ), selectMask ); - selectMask = (vec_uint4)spu_cmpgt( spu_splats(_VECTORMATH_SLERP_TOL), cosAngle ); - angle = acosf4( cosAngle ); - tttt = spu_splats(t); - oneMinusT = spu_sub( spu_splats(1.0f), tttt ); - angles = spu_sel( spu_splats(1.0f), oneMinusT, (vec_uint4)spu_maskb(0x0f00) ); - angles = spu_sel( angles, tttt, (vec_uint4)spu_maskb(0x00f0) ); - angles = spu_mul( angles, angle ); - sines = sinf4( angles ); - scales = divf4( sines, spu_shuffle( sines, sines, shuffle_xxxx ) ); - scale0 = spu_sel( oneMinusT, spu_shuffle( scales, scales, shuffle_yyyy ), selectMask ); - scale1 = spu_sel( tttt, spu_shuffle( scales, scales, shuffle_zzzz ), selectMask ); - result->vec128 = spu_madd( start.vec128, scale0, spu_mul( unitQuat1->vec128, scale1 ) ); -} - -static inline void vmathQSquad( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1, const VmathQuat *unitQuat2, const VmathQuat *unitQuat3 ) -{ - VmathQuat tmp0, tmp1; - vmathQSlerp( &tmp0, t, unitQuat0, unitQuat3 ); - vmathQSlerp( &tmp1, t, unitQuat1, unitQuat2 ); - vmathQSlerp( result, ( ( 2.0f * t ) * ( 1.0f - t ) ), &tmp0, &tmp1 ); -} - -static inline vec_float4 vmathQGet128( const VmathQuat *quat ) -{ - return quat->vec128; -} - -static inline void vmathQSetXYZ( VmathQuat *result, const VmathVector3 *vec ) -{ - result->vec128 = spu_sel( vec->vec128, result->vec128, (vec_uint4)spu_maskb(0x000f) ); -} - -static inline void vmathQGetXYZ( VmathVector3 *result, const VmathQuat *quat ) -{ - result->vec128 = quat->vec128; -} - -static inline void vmathQSetX( VmathQuat *result, float _x ) -{ - result->vec128 = spu_insert( _x, result->vec128, 0 ); -} - -static inline float vmathQGetX( const VmathQuat *quat ) -{ - return spu_extract( quat->vec128, 0 ); -} - -static inline void vmathQSetY( VmathQuat *result, float _y ) -{ - result->vec128 = spu_insert( _y, result->vec128, 1 ); -} - -static inline float vmathQGetY( const VmathQuat *quat ) -{ - return spu_extract( quat->vec128, 1 ); -} - -static inline void vmathQSetZ( VmathQuat *result, float _z ) -{ - result->vec128 = spu_insert( _z, result->vec128, 2 ); -} - -static inline float vmathQGetZ( const VmathQuat *quat ) -{ - return spu_extract( quat->vec128, 2 ); -} - -static inline void vmathQSetW( VmathQuat *result, float _w ) -{ - result->vec128 = spu_insert( _w, result->vec128, 3 ); -} - -static inline float vmathQGetW( const VmathQuat *quat ) -{ - return spu_extract( quat->vec128, 3 ); -} - -static inline void vmathQSetElem( VmathQuat *result, int idx, float value ) -{ - result->vec128 = spu_insert( value, result->vec128, idx ); -} - -static inline float vmathQGetElem( const VmathQuat *quat, int idx ) -{ - return spu_extract( quat->vec128, idx ); -} - -static inline void vmathQAdd( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ) -{ - result->vec128 = spu_add( quat0->vec128, quat1->vec128 ); -} - -static inline void vmathQSub( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ) -{ - result->vec128 = spu_sub( quat0->vec128, quat1->vec128 ); -} - -static inline void vmathQScalarMul( VmathQuat *result, const VmathQuat *quat, float scalar ) -{ - result->vec128 = spu_mul( quat->vec128, spu_splats(scalar) ); -} - -static inline void vmathQScalarDiv( VmathQuat *result, const VmathQuat *quat, float scalar ) -{ - result->vec128 = divf4( quat->vec128, spu_splats(scalar) ); -} - -static inline void vmathQNeg( VmathQuat *result, const VmathQuat *quat ) -{ - result->vec128 = negatef4( quat->vec128 ); -} - -static inline float vmathQDot( const VmathQuat *quat0, const VmathQuat *quat1 ) -{ - return spu_extract( _vmathVfDot4( quat0->vec128, quat1->vec128 ), 0 ); -} - -static inline float vmathQNorm( const VmathQuat *quat ) -{ - return spu_extract( _vmathVfDot4( quat->vec128, quat->vec128 ), 0 ); -} - -static inline float vmathQLength( const VmathQuat *quat ) -{ - return sqrtf( vmathQNorm( quat ) ); -} - -static inline void vmathQNormalize( VmathQuat *result, const VmathQuat *quat ) -{ - vec_float4 dot = _vmathVfDot4( quat->vec128, quat->vec128 ); - result->vec128 = spu_mul( quat->vec128, rsqrtf4( dot ) ); -} - -static inline void vmathQMakeRotationArc( VmathQuat *result, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ) -{ - VmathVector3 crossVec, tmpV3_0; - vec_float4 cosAngle, cosAngleX2Plus2, recipCosHalfAngleX2, cosHalfAngleX2, res; - cosAngle = _vmathVfDot3( unitVec0->vec128, unitVec1->vec128 ); - cosAngle = spu_shuffle( cosAngle, cosAngle, (vec_uchar16)spu_splats(0x00010203) ); - cosAngleX2Plus2 = spu_madd( cosAngle, spu_splats(2.0f), spu_splats(2.0f) ); - recipCosHalfAngleX2 = rsqrtf4( cosAngleX2Plus2 ); - cosHalfAngleX2 = spu_mul( recipCosHalfAngleX2, cosAngleX2Plus2 ); - vmathV3Cross( &tmpV3_0, unitVec0, unitVec1 ); - crossVec = tmpV3_0; - res = spu_mul( crossVec.vec128, recipCosHalfAngleX2 ); - res = spu_sel( res, spu_mul( cosHalfAngleX2, spu_splats(0.5f) ), (vec_uint4)spu_maskb(0x000f) ); - result->vec128 = res; -} - -static inline void vmathQMakeRotationAxis( VmathQuat *result, float radians, const VmathVector3 *unitVec ) -{ - vec_float4 s, c, angle, res; - angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); - sincosf4( angle, &s, &c ); - res = spu_sel( spu_mul( unitVec->vec128, s ), c, (vec_uint4)spu_maskb(0x000f) ); - result->vec128 = res; -} - -static inline void vmathQMakeRotationX( VmathQuat *result, float radians ) -{ - vec_float4 s, c, angle, res; - angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); - sincosf4( angle, &s, &c ); - res = spu_sel( spu_splats(0.0f), s, (vec_uint4)spu_maskb(0xf000) ); - res = spu_sel( res, c, (vec_uint4)spu_maskb(0x000f) ); - result->vec128 = res; -} - -static inline void vmathQMakeRotationY( VmathQuat *result, float radians ) -{ - vec_float4 s, c, angle, res; - angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); - sincosf4( angle, &s, &c ); - res = spu_sel( spu_splats(0.0f), s, (vec_uint4)spu_maskb(0x0f00) ); - res = spu_sel( res, c, (vec_uint4)spu_maskb(0x000f) ); - result->vec128 = res; -} - -static inline void vmathQMakeRotationZ( VmathQuat *result, float radians ) -{ - vec_float4 s, c, angle, res; - angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); - sincosf4( angle, &s, &c ); - res = spu_sel( spu_splats(0.0f), s, (vec_uint4)spu_maskb(0x00f0) ); - res = spu_sel( res, c, (vec_uint4)spu_maskb(0x000f) ); - result->vec128 = res; -} - -static inline void vmathQMul( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ) -{ - vec_float4 ldata, rdata, qv, tmp0, tmp1, tmp2, tmp3; - vec_float4 product, l_wxyz, r_wxyz, xy, qw; - ldata = quat0->vec128; - rdata = quat1->vec128; - vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); - tmp0 = spu_shuffle( ldata, ldata, _VECTORMATH_SHUF_YZXW ); - tmp1 = spu_shuffle( rdata, rdata, _VECTORMATH_SHUF_ZXYW ); - tmp2 = spu_shuffle( ldata, ldata, _VECTORMATH_SHUF_ZXYW ); - tmp3 = spu_shuffle( rdata, rdata, _VECTORMATH_SHUF_YZXW ); - qv = spu_mul( spu_shuffle( ldata, ldata, shuffle_wwww ), rdata ); - qv = spu_madd( spu_shuffle( rdata, rdata, shuffle_wwww ), ldata, qv ); - qv = spu_madd( tmp0, tmp1, qv ); - qv = spu_nmsub( tmp2, tmp3, qv ); - product = spu_mul( ldata, rdata ); - l_wxyz = spu_rlqwbyte( ldata, 12 ); - r_wxyz = spu_rlqwbyte( rdata, 12 ); - qw = spu_nmsub( l_wxyz, r_wxyz, product ); - xy = spu_madd( l_wxyz, r_wxyz, product ); - qw = spu_sub( qw, spu_rlqwbyte( xy, 8 ) ); - result->vec128 = spu_sel( qv, qw, (vec_uint4)spu_maskb( 0x000f ) ); -} - -static inline void vmathQRotate( VmathVector3 *result, const VmathQuat *quat, const VmathVector3 *vec ) -{ - vec_float4 qdata, vdata, product, tmp0, tmp1, tmp2, tmp3, wwww, qv, qw, res; - qdata = quat->vec128; - vdata = vec->vec128; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); - tmp0 = spu_shuffle( qdata, qdata, _VECTORMATH_SHUF_YZXW ); - tmp1 = spu_shuffle( vdata, vdata, _VECTORMATH_SHUF_ZXYW ); - tmp2 = spu_shuffle( qdata, qdata, _VECTORMATH_SHUF_ZXYW ); - tmp3 = spu_shuffle( vdata, vdata, _VECTORMATH_SHUF_YZXW ); - wwww = spu_shuffle( qdata, qdata, shuffle_wwww ); - qv = spu_mul( wwww, vdata ); - qv = spu_madd( tmp0, tmp1, qv ); - qv = spu_nmsub( tmp2, tmp3, qv ); - product = spu_mul( qdata, vdata ); - qw = spu_madd( spu_rlqwbyte( qdata, 4 ), spu_rlqwbyte( vdata, 4 ), product ); - qw = spu_add( spu_rlqwbyte( product, 8 ), qw ); - tmp1 = spu_shuffle( qv, qv, _VECTORMATH_SHUF_ZXYW ); - tmp3 = spu_shuffle( qv, qv, _VECTORMATH_SHUF_YZXW ); - res = spu_mul( spu_shuffle( qw, qw, shuffle_xxxx ), qdata ); - res = spu_madd( wwww, qv, res ); - res = spu_madd( tmp0, tmp1, res ); - res = spu_nmsub( tmp2, tmp3, res ); - result->vec128 = res; -} - -static inline void vmathQConj( VmathQuat *result, const VmathQuat *quat ) -{ - result->vec128 = spu_xor( quat->vec128, ((vec_float4)(vec_int4){0x80000000,0x80000000,0x80000000,0}) ); -} - -static inline void vmathQSelect( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1, unsigned int select1 ) -{ - result->vec128 = spu_sel( quat0->vec128, quat1->vec128, spu_splats( (unsigned int)-(select1 > 0) ) ); -} - -#ifdef _VECTORMATH_DEBUG - -static inline void vmathQPrint( const VmathQuat *quat ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = quat->vec128; - printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -static inline void vmathQPrints( const VmathQuat *quat, const char *name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = quat->vec128; - printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -#endif - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_QUAT_AOS_C_H +#define _VECTORMATH_QUAT_AOS_C_H +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*----------------------------------------------------------------------------- + * Definitions + */ +#ifndef _VECTORMATH_INTERNAL_FUNCTIONS +#define _VECTORMATH_INTERNAL_FUNCTIONS + +#endif + +static inline void vmathQCopy( VmathQuat *result, const VmathQuat *quat ) +{ + result->vec128 = quat->vec128; +} + +static inline void vmathQMakeFromElems( VmathQuat *result, float _x, float _y, float _z, float _w ) +{ + result->vec128 = (vec_float4){ _x, _y, _z, _w }; +} + +static inline void vmathQMakeFromV3Scalar( VmathQuat *result, const VmathVector3 *xyz, float _w ) +{ + result->vec128 = spu_shuffle( xyz->vec128, spu_promote( _w, 0 ), _VECTORMATH_SHUF_XYZA ); +} + +static inline void vmathQMakeFromV4( VmathQuat *result, const VmathVector4 *vec ) +{ + result->vec128 = vec->vec128; +} + +static inline void vmathQMakeFromScalar( VmathQuat *result, float scalar ) +{ + result->vec128 = spu_splats( scalar ); +} + +static inline void vmathQMakeFrom128( VmathQuat *result, vec_float4 vf4 ) +{ + result->vec128 = vf4; +} + +static inline void vmathQMakeIdentity( VmathQuat *result ) +{ + result->vec128 = _VECTORMATH_UNIT_0001; +} + +static inline void vmathQLerp( VmathQuat *result, float t, const VmathQuat *quat0, const VmathQuat *quat1 ) +{ + VmathQuat tmpQ_0, tmpQ_1; + vmathQSub( &tmpQ_0, quat1, quat0 ); + vmathQScalarMul( &tmpQ_1, &tmpQ_0, t ); + vmathQAdd( result, quat0, &tmpQ_1 ); +} + +static inline void vmathQSlerp( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1 ) +{ + VmathQuat start; + vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; + vec_uint4 selectMask; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + cosAngle = _vmathVfDot4( unitQuat0->vec128, unitQuat1->vec128 ); + cosAngle = spu_shuffle( cosAngle, cosAngle, shuffle_xxxx ); + selectMask = (vec_uint4)spu_cmpgt( spu_splats(0.0f), cosAngle ); + cosAngle = spu_sel( cosAngle, negatef4( cosAngle ), selectMask ); + start.vec128 = spu_sel( unitQuat0->vec128, negatef4( unitQuat0->vec128 ), selectMask ); + selectMask = (vec_uint4)spu_cmpgt( spu_splats(_VECTORMATH_SLERP_TOL), cosAngle ); + angle = acosf4( cosAngle ); + tttt = spu_splats(t); + oneMinusT = spu_sub( spu_splats(1.0f), tttt ); + angles = spu_sel( spu_splats(1.0f), oneMinusT, (vec_uint4)spu_maskb(0x0f00) ); + angles = spu_sel( angles, tttt, (vec_uint4)spu_maskb(0x00f0) ); + angles = spu_mul( angles, angle ); + sines = sinf4( angles ); + scales = divf4( sines, spu_shuffle( sines, sines, shuffle_xxxx ) ); + scale0 = spu_sel( oneMinusT, spu_shuffle( scales, scales, shuffle_yyyy ), selectMask ); + scale1 = spu_sel( tttt, spu_shuffle( scales, scales, shuffle_zzzz ), selectMask ); + result->vec128 = spu_madd( start.vec128, scale0, spu_mul( unitQuat1->vec128, scale1 ) ); +} + +static inline void vmathQSquad( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1, const VmathQuat *unitQuat2, const VmathQuat *unitQuat3 ) +{ + VmathQuat tmp0, tmp1; + vmathQSlerp( &tmp0, t, unitQuat0, unitQuat3 ); + vmathQSlerp( &tmp1, t, unitQuat1, unitQuat2 ); + vmathQSlerp( result, ( ( 2.0f * t ) * ( 1.0f - t ) ), &tmp0, &tmp1 ); +} + +static inline vec_float4 vmathQGet128( const VmathQuat *quat ) +{ + return quat->vec128; +} + +static inline void vmathQSetXYZ( VmathQuat *result, const VmathVector3 *vec ) +{ + result->vec128 = spu_sel( vec->vec128, result->vec128, (vec_uint4)spu_maskb(0x000f) ); +} + +static inline void vmathQGetXYZ( VmathVector3 *result, const VmathQuat *quat ) +{ + result->vec128 = quat->vec128; +} + +static inline void vmathQSetX( VmathQuat *result, float _x ) +{ + result->vec128 = spu_insert( _x, result->vec128, 0 ); +} + +static inline float vmathQGetX( const VmathQuat *quat ) +{ + return spu_extract( quat->vec128, 0 ); +} + +static inline void vmathQSetY( VmathQuat *result, float _y ) +{ + result->vec128 = spu_insert( _y, result->vec128, 1 ); +} + +static inline float vmathQGetY( const VmathQuat *quat ) +{ + return spu_extract( quat->vec128, 1 ); +} + +static inline void vmathQSetZ( VmathQuat *result, float _z ) +{ + result->vec128 = spu_insert( _z, result->vec128, 2 ); +} + +static inline float vmathQGetZ( const VmathQuat *quat ) +{ + return spu_extract( quat->vec128, 2 ); +} + +static inline void vmathQSetW( VmathQuat *result, float _w ) +{ + result->vec128 = spu_insert( _w, result->vec128, 3 ); +} + +static inline float vmathQGetW( const VmathQuat *quat ) +{ + return spu_extract( quat->vec128, 3 ); +} + +static inline void vmathQSetElem( VmathQuat *result, int idx, float value ) +{ + result->vec128 = spu_insert( value, result->vec128, idx ); +} + +static inline float vmathQGetElem( const VmathQuat *quat, int idx ) +{ + return spu_extract( quat->vec128, idx ); +} + +static inline void vmathQAdd( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ) +{ + result->vec128 = spu_add( quat0->vec128, quat1->vec128 ); +} + +static inline void vmathQSub( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ) +{ + result->vec128 = spu_sub( quat0->vec128, quat1->vec128 ); +} + +static inline void vmathQScalarMul( VmathQuat *result, const VmathQuat *quat, float scalar ) +{ + result->vec128 = spu_mul( quat->vec128, spu_splats(scalar) ); +} + +static inline void vmathQScalarDiv( VmathQuat *result, const VmathQuat *quat, float scalar ) +{ + result->vec128 = divf4( quat->vec128, spu_splats(scalar) ); +} + +static inline void vmathQNeg( VmathQuat *result, const VmathQuat *quat ) +{ + result->vec128 = negatef4( quat->vec128 ); +} + +static inline float vmathQDot( const VmathQuat *quat0, const VmathQuat *quat1 ) +{ + return spu_extract( _vmathVfDot4( quat0->vec128, quat1->vec128 ), 0 ); +} + +static inline float vmathQNorm( const VmathQuat *quat ) +{ + return spu_extract( _vmathVfDot4( quat->vec128, quat->vec128 ), 0 ); +} + +static inline float vmathQLength( const VmathQuat *quat ) +{ + return sqrtf( vmathQNorm( quat ) ); +} + +static inline void vmathQNormalize( VmathQuat *result, const VmathQuat *quat ) +{ + vec_float4 dot = _vmathVfDot4( quat->vec128, quat->vec128 ); + result->vec128 = spu_mul( quat->vec128, rsqrtf4( dot ) ); +} + +static inline void vmathQMakeRotationArc( VmathQuat *result, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ) +{ + VmathVector3 crossVec, tmpV3_0; + vec_float4 cosAngle, cosAngleX2Plus2, recipCosHalfAngleX2, cosHalfAngleX2, res; + cosAngle = _vmathVfDot3( unitVec0->vec128, unitVec1->vec128 ); + cosAngle = spu_shuffle( cosAngle, cosAngle, (vec_uchar16)spu_splats(0x00010203) ); + cosAngleX2Plus2 = spu_madd( cosAngle, spu_splats(2.0f), spu_splats(2.0f) ); + recipCosHalfAngleX2 = rsqrtf4( cosAngleX2Plus2 ); + cosHalfAngleX2 = spu_mul( recipCosHalfAngleX2, cosAngleX2Plus2 ); + vmathV3Cross( &tmpV3_0, unitVec0, unitVec1 ); + crossVec = tmpV3_0; + res = spu_mul( crossVec.vec128, recipCosHalfAngleX2 ); + res = spu_sel( res, spu_mul( cosHalfAngleX2, spu_splats(0.5f) ), (vec_uint4)spu_maskb(0x000f) ); + result->vec128 = res; +} + +static inline void vmathQMakeRotationAxis( VmathQuat *result, float radians, const VmathVector3 *unitVec ) +{ + vec_float4 s, c, angle, res; + angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); + sincosf4( angle, &s, &c ); + res = spu_sel( spu_mul( unitVec->vec128, s ), c, (vec_uint4)spu_maskb(0x000f) ); + result->vec128 = res; +} + +static inline void vmathQMakeRotationX( VmathQuat *result, float radians ) +{ + vec_float4 s, c, angle, res; + angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); + sincosf4( angle, &s, &c ); + res = spu_sel( spu_splats(0.0f), s, (vec_uint4)spu_maskb(0xf000) ); + res = spu_sel( res, c, (vec_uint4)spu_maskb(0x000f) ); + result->vec128 = res; +} + +static inline void vmathQMakeRotationY( VmathQuat *result, float radians ) +{ + vec_float4 s, c, angle, res; + angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); + sincosf4( angle, &s, &c ); + res = spu_sel( spu_splats(0.0f), s, (vec_uint4)spu_maskb(0x0f00) ); + res = spu_sel( res, c, (vec_uint4)spu_maskb(0x000f) ); + result->vec128 = res; +} + +static inline void vmathQMakeRotationZ( VmathQuat *result, float radians ) +{ + vec_float4 s, c, angle, res; + angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); + sincosf4( angle, &s, &c ); + res = spu_sel( spu_splats(0.0f), s, (vec_uint4)spu_maskb(0x00f0) ); + res = spu_sel( res, c, (vec_uint4)spu_maskb(0x000f) ); + result->vec128 = res; +} + +static inline void vmathQMul( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ) +{ + vec_float4 ldata, rdata, qv, tmp0, tmp1, tmp2, tmp3; + vec_float4 product, l_wxyz, r_wxyz, xy, qw; + ldata = quat0->vec128; + rdata = quat1->vec128; + vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); + tmp0 = spu_shuffle( ldata, ldata, _VECTORMATH_SHUF_YZXW ); + tmp1 = spu_shuffle( rdata, rdata, _VECTORMATH_SHUF_ZXYW ); + tmp2 = spu_shuffle( ldata, ldata, _VECTORMATH_SHUF_ZXYW ); + tmp3 = spu_shuffle( rdata, rdata, _VECTORMATH_SHUF_YZXW ); + qv = spu_mul( spu_shuffle( ldata, ldata, shuffle_wwww ), rdata ); + qv = spu_madd( spu_shuffle( rdata, rdata, shuffle_wwww ), ldata, qv ); + qv = spu_madd( tmp0, tmp1, qv ); + qv = spu_nmsub( tmp2, tmp3, qv ); + product = spu_mul( ldata, rdata ); + l_wxyz = spu_rlqwbyte( ldata, 12 ); + r_wxyz = spu_rlqwbyte( rdata, 12 ); + qw = spu_nmsub( l_wxyz, r_wxyz, product ); + xy = spu_madd( l_wxyz, r_wxyz, product ); + qw = spu_sub( qw, spu_rlqwbyte( xy, 8 ) ); + result->vec128 = spu_sel( qv, qw, (vec_uint4)spu_maskb( 0x000f ) ); +} + +static inline void vmathQRotate( VmathVector3 *result, const VmathQuat *quat, const VmathVector3 *vec ) +{ + vec_float4 qdata, vdata, product, tmp0, tmp1, tmp2, tmp3, wwww, qv, qw, res; + qdata = quat->vec128; + vdata = vec->vec128; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); + tmp0 = spu_shuffle( qdata, qdata, _VECTORMATH_SHUF_YZXW ); + tmp1 = spu_shuffle( vdata, vdata, _VECTORMATH_SHUF_ZXYW ); + tmp2 = spu_shuffle( qdata, qdata, _VECTORMATH_SHUF_ZXYW ); + tmp3 = spu_shuffle( vdata, vdata, _VECTORMATH_SHUF_YZXW ); + wwww = spu_shuffle( qdata, qdata, shuffle_wwww ); + qv = spu_mul( wwww, vdata ); + qv = spu_madd( tmp0, tmp1, qv ); + qv = spu_nmsub( tmp2, tmp3, qv ); + product = spu_mul( qdata, vdata ); + qw = spu_madd( spu_rlqwbyte( qdata, 4 ), spu_rlqwbyte( vdata, 4 ), product ); + qw = spu_add( spu_rlqwbyte( product, 8 ), qw ); + tmp1 = spu_shuffle( qv, qv, _VECTORMATH_SHUF_ZXYW ); + tmp3 = spu_shuffle( qv, qv, _VECTORMATH_SHUF_YZXW ); + res = spu_mul( spu_shuffle( qw, qw, shuffle_xxxx ), qdata ); + res = spu_madd( wwww, qv, res ); + res = spu_madd( tmp0, tmp1, res ); + res = spu_nmsub( tmp2, tmp3, res ); + result->vec128 = res; +} + +static inline void vmathQConj( VmathQuat *result, const VmathQuat *quat ) +{ + result->vec128 = spu_xor( quat->vec128, ((vec_float4)(vec_uint4){0x80000000,0x80000000,0x80000000,0}) ); +} + +static inline void vmathQSelect( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1, unsigned int select1 ) +{ + result->vec128 = spu_sel( quat0->vec128, quat1->vec128, spu_splats( (unsigned int)-(select1 > 0) ) ); +} + +#ifdef _VECTORMATH_DEBUG + +static inline void vmathQPrint( const VmathQuat *quat ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = quat->vec128; + printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +static inline void vmathQPrints( const VmathQuat *quat, const char *name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = quat->vec128; + printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/common/vectormath/spu/c/vec_aos.h b/common/vectormath/spu/c/vec_aos.h index 715f27df..332e0db9 100644 --- a/common/vectormath/spu/c/vec_aos.h +++ b/common/vectormath/spu/c/vec_aos.h @@ -1,1029 +1,1029 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_VEC_AOS_C_H -#define _VECTORMATH_VEC_AOS_C_H -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/*----------------------------------------------------------------------------- - * Constants - * for shuffles, words are labeled [x,y,z,w] [a,b,c,d] - */ -#define _VECTORMATH_SHUF_X 0x00010203 -#define _VECTORMATH_SHUF_Y 0x04050607 -#define _VECTORMATH_SHUF_Z 0x08090a0b -#define _VECTORMATH_SHUF_W 0x0c0d0e0f -#define _VECTORMATH_SHUF_A 0x10111213 -#define _VECTORMATH_SHUF_B 0x14151617 -#define _VECTORMATH_SHUF_C 0x18191a1b -#define _VECTORMATH_SHUF_D 0x1c1d1e1f -#define _VECTORMATH_SHUF_0 0x80808080 -#define _VECTORMATH_SHUF_XYZA (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A } -#define _VECTORMATH_SHUF_ZXYW (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_W } -#define _VECTORMATH_SHUF_YZXW (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_W } -#define _VECTORMATH_SHUF_WABC (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_C } -#define _VECTORMATH_SHUF_ZWAB (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B } -#define _VECTORMATH_SHUF_XYZA (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A } -#define _VECTORMATH_SHUF_YZAB (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B } -#define _VECTORMATH_SHUF_ZABC (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_C } -#define _VECTORMATH_UNIT_1000 (vec_float4){ 1.0f, 0.0f, 0.0f, 0.0f } -#define _VECTORMATH_UNIT_0100 (vec_float4){ 0.0f, 1.0f, 0.0f, 0.0f } -#define _VECTORMATH_UNIT_0010 (vec_float4){ 0.0f, 0.0f, 1.0f, 0.0f } -#define _VECTORMATH_UNIT_0001 (vec_float4){ 0.0f, 0.0f, 0.0f, 1.0f } -#define _VECTORMATH_SLERP_TOL 0.999f - -/*----------------------------------------------------------------------------- - * Definitions - */ -#ifndef _VECTORMATH_INTERNAL_FUNCTIONS -#define _VECTORMATH_INTERNAL_FUNCTIONS - -static inline vec_float4 _vmathVfDot3( vec_float4 vec0, vec_float4 vec1 ) -{ - vec_float4 result; - result = spu_mul( vec0, vec1 ); - result = spu_madd( spu_rlqwbyte( vec0, 4 ), spu_rlqwbyte( vec1, 4 ), result ); - return spu_madd( spu_rlqwbyte( vec0, 8 ), spu_rlqwbyte( vec1, 8 ), result ); -} - -static inline vec_float4 _vmathVfDot4( vec_float4 vec0, vec_float4 vec1 ) -{ - vec_float4 result; - result = spu_mul( vec0, vec1 ); - result = spu_madd( spu_rlqwbyte( vec0, 4 ), spu_rlqwbyte( vec1, 4 ), result ); - return spu_add( spu_rlqwbyte( result, 8 ), result ); -} - -static inline vec_float4 _vmathVfCross( vec_float4 vec0, vec_float4 vec1 ) -{ - vec_float4 tmp0, tmp1, tmp2, tmp3, result; - tmp0 = spu_shuffle( vec0, vec0, _VECTORMATH_SHUF_YZXW ); - tmp1 = spu_shuffle( vec1, vec1, _VECTORMATH_SHUF_ZXYW ); - tmp2 = spu_shuffle( vec0, vec0, _VECTORMATH_SHUF_ZXYW ); - tmp3 = spu_shuffle( vec1, vec1, _VECTORMATH_SHUF_YZXW ); - result = spu_mul( tmp0, tmp1 ); - result = spu_nmsub( tmp2, tmp3, result ); - return result; -} - -static inline vec_uint4 _vmathVfToHalfFloatsUnpacked(vec_float4 v) -{ - vec_int4 bexp; - vec_uint4 mant, sign, hfloat; - vec_uint4 notZero, isInf; - const vec_uint4 hfloatInf = spu_splats(0x00007c00u); - const vec_uint4 mergeMant = spu_splats(0x000003ffu); - const vec_uint4 mergeSign = spu_splats(0x00008000u); - - sign = spu_rlmask((vec_uint4)v, -16); - mant = spu_rlmask((vec_uint4)v, -13); - bexp = spu_and(spu_rlmask((vec_int4)v, -23), 0xff); - - notZero = spu_cmpgt(bexp, 112); - isInf = spu_cmpgt(bexp, 142); - - bexp = spu_add(bexp, -112); - bexp = spu_sl(bexp, 10); - - hfloat = spu_sel((vec_uint4)bexp, mant, mergeMant); - hfloat = spu_sel(spu_splats(0u), hfloat, notZero); - hfloat = spu_sel(hfloat, hfloatInf, isInf); - hfloat = spu_sel(hfloat, sign, mergeSign); - - return hfloat; -} - -static inline vec_ushort8 _vmath2VfToHalfFloats(vec_float4 u, vec_float4 v) -{ - vec_uint4 hfloat_u, hfloat_v; - const vec_uchar16 pack = (vec_uchar16){2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31}; - hfloat_u = _vmathVfToHalfFloatsUnpacked(u); - hfloat_v = _vmathVfToHalfFloatsUnpacked(v); - return (vec_ushort8)spu_shuffle(hfloat_u, hfloat_v, pack); -} - -#endif - -static inline void vmathV3Copy( VmathVector3 *result, const VmathVector3 *vec ) -{ - result->vec128 = vec->vec128; -} - -static inline void vmathV3MakeFromElems( VmathVector3 *result, float _x, float _y, float _z ) -{ - result->vec128 = (vec_float4){ _x, _y, _z, 0.0f }; -} - -static inline void vmathV3MakeFromP3( VmathVector3 *result, const VmathPoint3 *pnt ) -{ - result->vec128 = pnt->vec128; -} - -static inline void vmathV3MakeFromScalar( VmathVector3 *result, float scalar ) -{ - result->vec128 = spu_splats( scalar ); -} - -static inline void vmathV3MakeFrom128( VmathVector3 *result, vec_float4 vf4 ) -{ - result->vec128 = vf4; -} - -static inline void vmathV3MakeXAxis( VmathVector3 *result ) -{ - result->vec128 = _VECTORMATH_UNIT_1000; -} - -static inline void vmathV3MakeYAxis( VmathVector3 *result ) -{ - result->vec128 = _VECTORMATH_UNIT_0100; -} - -static inline void vmathV3MakeZAxis( VmathVector3 *result ) -{ - result->vec128 = _VECTORMATH_UNIT_0010; -} - -static inline void vmathV3Lerp( VmathVector3 *result, float t, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - VmathVector3 tmpV3_0, tmpV3_1; - vmathV3Sub( &tmpV3_0, vec1, vec0 ); - vmathV3ScalarMul( &tmpV3_1, &tmpV3_0, t ); - vmathV3Add( result, vec0, &tmpV3_1 ); -} - -static inline void vmathV3Slerp( VmathVector3 *result, float t, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ) -{ - vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; - vec_uint4 selectMask; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - cosAngle = _vmathVfDot3( unitVec0->vec128, unitVec1->vec128 ); - cosAngle = spu_shuffle( cosAngle, cosAngle, shuffle_xxxx ); - selectMask = (vec_uint4)spu_cmpgt( spu_splats(_VECTORMATH_SLERP_TOL), cosAngle ); - angle = acosf4( cosAngle ); - tttt = spu_splats(t); - oneMinusT = spu_sub( spu_splats(1.0f), tttt ); - angles = spu_sel( spu_splats(1.0f), oneMinusT, (vec_uint4)spu_maskb(0x0f00) ); - angles = spu_sel( angles, tttt, (vec_uint4)spu_maskb(0x00f0) ); - angles = spu_mul( angles, angle ); - sines = sinf4( angles ); - scales = divf4( sines, spu_shuffle( sines, sines, shuffle_xxxx ) ); - scale0 = spu_sel( oneMinusT, spu_shuffle( scales, scales, shuffle_yyyy ), selectMask ); - scale1 = spu_sel( tttt, spu_shuffle( scales, scales, shuffle_zzzz ), selectMask ); - result->vec128 = spu_madd( unitVec0->vec128, scale0, spu_mul( unitVec1->vec128, scale1 ) ); -} - -static inline vec_float4 vmathV3Get128( const VmathVector3 *vec ) -{ - return vec->vec128; -} - -static inline void vmathV3StoreXYZ( const VmathVector3 *vec, vec_float4 *quad ) -{ - vec_float4 dstVec = *quad; - vec_uint4 mask = (vec_uint4)spu_maskb(0x000f); - dstVec = spu_sel(vec->vec128, dstVec, mask); - *quad = dstVec; -} - -static inline void vmathV3LoadXYZArray( VmathVector3 *vec0, VmathVector3 *vec1, VmathVector3 *vec2, VmathVector3 *vec3, const vec_float4 *threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; - xyzx = threeQuads[0]; - yzxy = threeQuads[1]; - zxyz = threeQuads[2]; - xyz1 = spu_shuffle( xyzx, yzxy, _VECTORMATH_SHUF_WABC ); - xyz2 = spu_shuffle( yzxy, zxyz, _VECTORMATH_SHUF_ZWAB ); - xyz3 = spu_rlqwbyte( zxyz, 4 ); - vec0->vec128 = xyzx; - vec1->vec128 = xyz1; - vec2->vec128 = xyz2; - vec3->vec128 = xyz3; -} - -static inline void vmathV3StoreXYZArray( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, vec_float4 *threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz; - xyzx = spu_shuffle( vec0->vec128, vec1->vec128, _VECTORMATH_SHUF_XYZA ); - yzxy = spu_shuffle( vec1->vec128, vec2->vec128, _VECTORMATH_SHUF_YZAB ); - zxyz = spu_shuffle( vec2->vec128, vec3->vec128, _VECTORMATH_SHUF_ZABC ); - threeQuads[0] = xyzx; - threeQuads[1] = yzxy; - threeQuads[2] = zxyz; -} - -static inline void vmathV3StoreHalfFloats( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, const VmathVector3 *vec4, const VmathVector3 *vec5, const VmathVector3 *vec6, const VmathVector3 *vec7, vec_ushort8 *threeQuads ) -{ - vec_float4 xyz0[3]; - vec_float4 xyz1[3]; - vmathV3StoreXYZArray( vec0, vec1, vec2, vec3, xyz0 ); - vmathV3StoreXYZArray( vec4, vec5, vec6, vec7, xyz1 ); - threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); - threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); - threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); -} - -static inline void vmathV3SetX( VmathVector3 *result, float _x ) -{ - result->vec128 = spu_insert( _x, result->vec128, 0 ); -} - -static inline float vmathV3GetX( const VmathVector3 *vec ) -{ - return spu_extract( vec->vec128, 0 ); -} - -static inline void vmathV3SetY( VmathVector3 *result, float _y ) -{ - result->vec128 = spu_insert( _y, result->vec128, 1 ); -} - -static inline float vmathV3GetY( const VmathVector3 *vec ) -{ - return spu_extract( vec->vec128, 1 ); -} - -static inline void vmathV3SetZ( VmathVector3 *result, float _z ) -{ - result->vec128 = spu_insert( _z, result->vec128, 2 ); -} - -static inline float vmathV3GetZ( const VmathVector3 *vec ) -{ - return spu_extract( vec->vec128, 2 ); -} - -static inline void vmathV3SetElem( VmathVector3 *result, int idx, float value ) -{ - result->vec128 = spu_insert( value, result->vec128, idx ); -} - -static inline float vmathV3GetElem( const VmathVector3 *vec, int idx ) -{ - return spu_extract( vec->vec128, idx ); -} - -static inline void vmathV3Add( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = spu_add( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV3Sub( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = spu_sub( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV3AddP3( VmathPoint3 *result, const VmathVector3 *vec, const VmathPoint3 *pnt1 ) -{ - result->vec128 = spu_add( vec->vec128, pnt1->vec128 ); -} - -static inline void vmathV3ScalarMul( VmathVector3 *result, const VmathVector3 *vec, float scalar ) -{ - result->vec128 = spu_mul( vec->vec128, spu_splats(scalar) ); -} - -static inline void vmathV3ScalarDiv( VmathVector3 *result, const VmathVector3 *vec, float scalar ) -{ - result->vec128 = divf4( vec->vec128, spu_splats(scalar) ); -} - -static inline void vmathV3Neg( VmathVector3 *result, const VmathVector3 *vec ) -{ - result->vec128 = negatef4( vec->vec128 ); -} - -static inline void vmathV3MulPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = spu_mul( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV3DivPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = divf4( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV3RecipPerElem( VmathVector3 *result, const VmathVector3 *vec ) -{ - result->vec128 = recipf4( vec->vec128 ); -} - -static inline void vmathV3SqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ) -{ - result->vec128 = sqrtf4( vec->vec128 ); -} - -static inline void vmathV3RsqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ) -{ - result->vec128 = rsqrtf4( vec->vec128 ); -} - -static inline void vmathV3AbsPerElem( VmathVector3 *result, const VmathVector3 *vec ) -{ - result->vec128 = fabsf4( vec->vec128 ); -} - -static inline void vmathV3CopySignPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = copysignf4( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV3MaxPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = fmaxf4( vec0->vec128, vec1->vec128 ); -} - -static inline float vmathV3MaxElem( const VmathVector3 *vec ) -{ - vec_float4 result; - result = fmaxf4( spu_promote( spu_extract( vec->vec128, 1 ), 0 ), vec->vec128 ); - result = fmaxf4( spu_promote( spu_extract( vec->vec128, 2 ), 0 ), result ); - return spu_extract( result, 0 ); -} - -static inline void vmathV3MinPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = fminf4( vec0->vec128, vec1->vec128 ); -} - -static inline float vmathV3MinElem( const VmathVector3 *vec ) -{ - vec_float4 result; - result = fminf4( spu_promote( spu_extract( vec->vec128, 1 ), 0 ), vec->vec128 ); - result = fminf4( spu_promote( spu_extract( vec->vec128, 2 ), 0 ), result ); - return spu_extract( result, 0 ); -} - -static inline float vmathV3Sum( const VmathVector3 *vec ) -{ - return - spu_extract( vec->vec128, 0 ) + - spu_extract( vec->vec128, 1 ) + - spu_extract( vec->vec128, 2 ); -} - -static inline float vmathV3Dot( const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - return spu_extract( _vmathVfDot3( vec0->vec128, vec1->vec128 ), 0 ); -} - -static inline float vmathV3LengthSqr( const VmathVector3 *vec ) -{ - return spu_extract( _vmathVfDot3( vec->vec128, vec->vec128 ), 0 ); -} - -static inline float vmathV3Length( const VmathVector3 *vec ) -{ - return sqrtf( vmathV3LengthSqr( vec ) ); -} - -static inline void vmathV3Normalize( VmathVector3 *result, const VmathVector3 *vec ) -{ - vec_float4 dot = _vmathVfDot3( vec->vec128, vec->vec128 ); - dot = spu_shuffle( dot, dot, (vec_uchar16)spu_splats(0x00010203) ); - result->vec128 = spu_mul( vec->vec128, rsqrtf4( dot ) ); -} - -static inline void vmathV3Cross( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) -{ - result->vec128 = _vmathVfCross( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV3Select( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1, unsigned int select1 ) -{ - result->vec128 = spu_sel( vec0->vec128, vec1->vec128, spu_splats( (unsigned int)-(select1 > 0) ) ); -} - -#ifdef _VECTORMATH_DEBUG - -static inline void vmathV3Print( const VmathVector3 *vec ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec->vec128; - printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -static inline void vmathV3Prints( const VmathVector3 *vec, const char *name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec->vec128; - printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -#endif - -static inline void vmathV4Copy( VmathVector4 *result, const VmathVector4 *vec ) -{ - result->vec128 = vec->vec128; -} - -static inline void vmathV4MakeFromElems( VmathVector4 *result, float _x, float _y, float _z, float _w ) -{ - result->vec128 = (vec_float4){ _x, _y, _z, _w }; -} - -static inline void vmathV4MakeFromV3Scalar( VmathVector4 *result, const VmathVector3 *xyz, float _w ) -{ - result->vec128 = spu_shuffle( xyz->vec128, spu_promote( _w, 0 ), _VECTORMATH_SHUF_XYZA ); -} - -static inline void vmathV4MakeFromV3( VmathVector4 *result, const VmathVector3 *vec ) -{ - result->vec128 = spu_sel( vec->vec128, spu_splats(0.0f), (vec_uint4)spu_maskb(0x000f) ); -} - -static inline void vmathV4MakeFromP3( VmathVector4 *result, const VmathPoint3 *pnt ) -{ - result->vec128 = spu_sel( pnt->vec128, spu_splats(1.0f), (vec_uint4)spu_maskb(0x000f) ); -} - -static inline void vmathV4MakeFromQ( VmathVector4 *result, const VmathQuat *quat ) -{ - result->vec128 = quat->vec128; -} - -static inline void vmathV4MakeFromScalar( VmathVector4 *result, float scalar ) -{ - result->vec128 = spu_splats( scalar ); -} - -static inline void vmathV4MakeFrom128( VmathVector4 *result, vec_float4 vf4 ) -{ - result->vec128 = vf4; -} - -static inline void vmathV4MakeXAxis( VmathVector4 *result ) -{ - result->vec128 = _VECTORMATH_UNIT_1000; -} - -static inline void vmathV4MakeYAxis( VmathVector4 *result ) -{ - result->vec128 = _VECTORMATH_UNIT_0100; -} - -static inline void vmathV4MakeZAxis( VmathVector4 *result ) -{ - result->vec128 = _VECTORMATH_UNIT_0010; -} - -static inline void vmathV4MakeWAxis( VmathVector4 *result ) -{ - result->vec128 = _VECTORMATH_UNIT_0001; -} - -static inline void vmathV4Lerp( VmathVector4 *result, float t, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - VmathVector4 tmpV4_0, tmpV4_1; - vmathV4Sub( &tmpV4_0, vec1, vec0 ); - vmathV4ScalarMul( &tmpV4_1, &tmpV4_0, t ); - vmathV4Add( result, vec0, &tmpV4_1 ); -} - -static inline void vmathV4Slerp( VmathVector4 *result, float t, const VmathVector4 *unitVec0, const VmathVector4 *unitVec1 ) -{ - vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; - vec_uint4 selectMask; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - cosAngle = _vmathVfDot4( unitVec0->vec128, unitVec1->vec128 ); - cosAngle = spu_shuffle( cosAngle, cosAngle, shuffle_xxxx ); - selectMask = (vec_uint4)spu_cmpgt( spu_splats(_VECTORMATH_SLERP_TOL), cosAngle ); - angle = acosf4( cosAngle ); - tttt = spu_splats(t); - oneMinusT = spu_sub( spu_splats(1.0f), tttt ); - angles = spu_sel( spu_splats(1.0f), oneMinusT, (vec_uint4)spu_maskb(0x0f00) ); - angles = spu_sel( angles, tttt, (vec_uint4)spu_maskb(0x00f0) ); - angles = spu_mul( angles, angle ); - sines = sinf4( angles ); - scales = divf4( sines, spu_shuffle( sines, sines, shuffle_xxxx ) ); - scale0 = spu_sel( oneMinusT, spu_shuffle( scales, scales, shuffle_yyyy ), selectMask ); - scale1 = spu_sel( tttt, spu_shuffle( scales, scales, shuffle_zzzz ), selectMask ); - result->vec128 = spu_madd( unitVec0->vec128, scale0, spu_mul( unitVec1->vec128, scale1 ) ); -} - -static inline vec_float4 vmathV4Get128( const VmathVector4 *vec ) -{ - return vec->vec128; -} - -static inline void vmathV4StoreHalfFloats( const VmathVector4 *vec0, const VmathVector4 *vec1, const VmathVector4 *vec2, const VmathVector4 *vec3, vec_ushort8 *twoQuads ) -{ - twoQuads[0] = _vmath2VfToHalfFloats(vec0->vec128, vec1->vec128); - twoQuads[1] = _vmath2VfToHalfFloats(vec2->vec128, vec3->vec128); -} - -static inline void vmathV4SetXYZ( VmathVector4 *result, const VmathVector3 *vec ) -{ - result->vec128 = spu_sel( vec->vec128, result->vec128, (vec_uint4)spu_maskb(0x000f) ); -} - -static inline void vmathV4GetXYZ( VmathVector3 *result, const VmathVector4 *vec ) -{ - result->vec128 = vec->vec128; -} - -static inline void vmathV4SetX( VmathVector4 *result, float _x ) -{ - result->vec128 = spu_insert( _x, result->vec128, 0 ); -} - -static inline float vmathV4GetX( const VmathVector4 *vec ) -{ - return spu_extract( vec->vec128, 0 ); -} - -static inline void vmathV4SetY( VmathVector4 *result, float _y ) -{ - result->vec128 = spu_insert( _y, result->vec128, 1 ); -} - -static inline float vmathV4GetY( const VmathVector4 *vec ) -{ - return spu_extract( vec->vec128, 1 ); -} - -static inline void vmathV4SetZ( VmathVector4 *result, float _z ) -{ - result->vec128 = spu_insert( _z, result->vec128, 2 ); -} - -static inline float vmathV4GetZ( const VmathVector4 *vec ) -{ - return spu_extract( vec->vec128, 2 ); -} - -static inline void vmathV4SetW( VmathVector4 *result, float _w ) -{ - result->vec128 = spu_insert( _w, result->vec128, 3 ); -} - -static inline float vmathV4GetW( const VmathVector4 *vec ) -{ - return spu_extract( vec->vec128, 3 ); -} - -static inline void vmathV4SetElem( VmathVector4 *result, int idx, float value ) -{ - result->vec128 = spu_insert( value, result->vec128, idx ); -} - -static inline float vmathV4GetElem( const VmathVector4 *vec, int idx ) -{ - return spu_extract( vec->vec128, idx ); -} - -static inline void vmathV4Add( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - result->vec128 = spu_add( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV4Sub( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - result->vec128 = spu_sub( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV4ScalarMul( VmathVector4 *result, const VmathVector4 *vec, float scalar ) -{ - result->vec128 = spu_mul( vec->vec128, spu_splats(scalar) ); -} - -static inline void vmathV4ScalarDiv( VmathVector4 *result, const VmathVector4 *vec, float scalar ) -{ - result->vec128 = divf4( vec->vec128, spu_splats(scalar) ); -} - -static inline void vmathV4Neg( VmathVector4 *result, const VmathVector4 *vec ) -{ - result->vec128 = negatef4( vec->vec128 ); -} - -static inline void vmathV4MulPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - result->vec128 = spu_mul( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV4DivPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - result->vec128 = divf4( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV4RecipPerElem( VmathVector4 *result, const VmathVector4 *vec ) -{ - result->vec128 = recipf4( vec->vec128 ); -} - -static inline void vmathV4SqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ) -{ - result->vec128 = sqrtf4( vec->vec128 ); -} - -static inline void vmathV4RsqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ) -{ - result->vec128 = rsqrtf4( vec->vec128 ); -} - -static inline void vmathV4AbsPerElem( VmathVector4 *result, const VmathVector4 *vec ) -{ - result->vec128 = fabsf4( vec->vec128 ); -} - -static inline void vmathV4CopySignPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - result->vec128 = copysignf4( vec0->vec128, vec1->vec128 ); -} - -static inline void vmathV4MaxPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - result->vec128 = fmaxf4( vec0->vec128, vec1->vec128 ); -} - -static inline float vmathV4MaxElem( const VmathVector4 *vec ) -{ - vec_float4 result; - result = fmaxf4( spu_promote( spu_extract( vec->vec128, 1 ), 0 ), vec->vec128 ); - result = fmaxf4( spu_promote( spu_extract( vec->vec128, 2 ), 0 ), result ); - result = fmaxf4( spu_promote( spu_extract( vec->vec128, 3 ), 0 ), result ); - return spu_extract( result, 0 ); -} - -static inline void vmathV4MinPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - result->vec128 = fminf4( vec0->vec128, vec1->vec128 ); -} - -static inline float vmathV4MinElem( const VmathVector4 *vec ) -{ - vec_float4 result; - result = fminf4( spu_promote( spu_extract( vec->vec128, 1 ), 0 ), vec->vec128 ); - result = fminf4( spu_promote( spu_extract( vec->vec128, 2 ), 0 ), result ); - result = fminf4( spu_promote( spu_extract( vec->vec128, 3 ), 0 ), result ); - return spu_extract( result, 0 ); -} - -static inline float vmathV4Sum( const VmathVector4 *vec ) -{ - return - spu_extract( vec->vec128, 0 ) + - spu_extract( vec->vec128, 1 ) + - spu_extract( vec->vec128, 2 ) + - spu_extract( vec->vec128, 3 ); -} - -static inline float vmathV4Dot( const VmathVector4 *vec0, const VmathVector4 *vec1 ) -{ - return spu_extract( _vmathVfDot4( vec0->vec128, vec1->vec128 ), 0 ); -} - -static inline float vmathV4LengthSqr( const VmathVector4 *vec ) -{ - return spu_extract( _vmathVfDot4( vec->vec128, vec->vec128 ), 0 ); -} - -static inline float vmathV4Length( const VmathVector4 *vec ) -{ - return sqrtf( vmathV4LengthSqr( vec ) ); -} - -static inline void vmathV4Normalize( VmathVector4 *result, const VmathVector4 *vec ) -{ - vec_float4 dot = _vmathVfDot4( vec->vec128, vec->vec128 ); - result->vec128 = spu_mul( vec->vec128, rsqrtf4( dot ) ); -} - -static inline void vmathV4Select( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1, unsigned int select1 ) -{ - result->vec128 = spu_sel( vec0->vec128, vec1->vec128, spu_splats( (unsigned int)-(select1 > 0) ) ); -} - -#ifdef _VECTORMATH_DEBUG - -static inline void vmathV4Print( const VmathVector4 *vec ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec->vec128; - printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -static inline void vmathV4Prints( const VmathVector4 *vec, const char *name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec->vec128; - printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -#endif - -static inline void vmathP3Copy( VmathPoint3 *result, const VmathPoint3 *pnt ) -{ - result->vec128 = pnt->vec128; -} - -static inline void vmathP3MakeFromElems( VmathPoint3 *result, float _x, float _y, float _z ) -{ - result->vec128 = (vec_float4){ _x, _y, _z, 0.0f }; -} - -static inline void vmathP3MakeFromV3( VmathPoint3 *result, const VmathVector3 *vec ) -{ - result->vec128 = vec->vec128; -} - -static inline void vmathP3MakeFromScalar( VmathPoint3 *result, float scalar ) -{ - result->vec128 = spu_splats( scalar ); -} - -static inline void vmathP3MakeFrom128( VmathPoint3 *result, vec_float4 vf4 ) -{ - result->vec128 = vf4; -} - -static inline void vmathP3Lerp( VmathPoint3 *result, float t, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - VmathVector3 tmpV3_0, tmpV3_1; - vmathP3Sub( &tmpV3_0, pnt1, pnt0 ); - vmathV3ScalarMul( &tmpV3_1, &tmpV3_0, t ); - vmathP3AddV3( result, pnt0, &tmpV3_1 ); -} - -static inline vec_float4 vmathP3Get128( const VmathPoint3 *pnt ) -{ - return pnt->vec128; -} - -static inline void vmathP3StoreXYZ( const VmathPoint3 *pnt, vec_float4 *quad ) -{ - vec_float4 dstVec = *quad; - vec_uint4 mask = (vec_uint4)spu_maskb(0x000f); - dstVec = spu_sel(pnt->vec128, dstVec, mask); - *quad = dstVec; -} - -static inline void vmathP3LoadXYZArray( VmathPoint3 *pnt0, VmathPoint3 *pnt1, VmathPoint3 *pnt2, VmathPoint3 *pnt3, const vec_float4 *threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; - xyzx = threeQuads[0]; - yzxy = threeQuads[1]; - zxyz = threeQuads[2]; - xyz1 = spu_shuffle( xyzx, yzxy, _VECTORMATH_SHUF_WABC ); - xyz2 = spu_shuffle( yzxy, zxyz, _VECTORMATH_SHUF_ZWAB ); - xyz3 = spu_rlqwbyte( zxyz, 4 ); - pnt0->vec128 = xyzx; - pnt1->vec128 = xyz1; - pnt2->vec128 = xyz2; - pnt3->vec128 = xyz3; -} - -static inline void vmathP3StoreXYZArray( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, vec_float4 *threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz; - xyzx = spu_shuffle( pnt0->vec128, pnt1->vec128, _VECTORMATH_SHUF_XYZA ); - yzxy = spu_shuffle( pnt1->vec128, pnt2->vec128, _VECTORMATH_SHUF_YZAB ); - zxyz = spu_shuffle( pnt2->vec128, pnt3->vec128, _VECTORMATH_SHUF_ZABC ); - threeQuads[0] = xyzx; - threeQuads[1] = yzxy; - threeQuads[2] = zxyz; -} - -static inline void vmathP3StoreHalfFloats( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, const VmathPoint3 *pnt4, const VmathPoint3 *pnt5, const VmathPoint3 *pnt6, const VmathPoint3 *pnt7, vec_ushort8 *threeQuads ) -{ - vec_float4 xyz0[3]; - vec_float4 xyz1[3]; - vmathP3StoreXYZArray( pnt0, pnt1, pnt2, pnt3, xyz0 ); - vmathP3StoreXYZArray( pnt4, pnt5, pnt6, pnt7, xyz1 ); - threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); - threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); - threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); -} - -static inline void vmathP3SetX( VmathPoint3 *result, float _x ) -{ - result->vec128 = spu_insert( _x, result->vec128, 0 ); -} - -static inline float vmathP3GetX( const VmathPoint3 *pnt ) -{ - return spu_extract( pnt->vec128, 0 ); -} - -static inline void vmathP3SetY( VmathPoint3 *result, float _y ) -{ - result->vec128 = spu_insert( _y, result->vec128, 1 ); -} - -static inline float vmathP3GetY( const VmathPoint3 *pnt ) -{ - return spu_extract( pnt->vec128, 1 ); -} - -static inline void vmathP3SetZ( VmathPoint3 *result, float _z ) -{ - result->vec128 = spu_insert( _z, result->vec128, 2 ); -} - -static inline float vmathP3GetZ( const VmathPoint3 *pnt ) -{ - return spu_extract( pnt->vec128, 2 ); -} - -static inline void vmathP3SetElem( VmathPoint3 *result, int idx, float value ) -{ - result->vec128 = spu_insert( value, result->vec128, idx ); -} - -static inline float vmathP3GetElem( const VmathPoint3 *pnt, int idx ) -{ - return spu_extract( pnt->vec128, idx ); -} - -static inline void vmathP3Sub( VmathVector3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - result->vec128 = spu_sub( pnt0->vec128, pnt1->vec128 ); -} - -static inline void vmathP3AddV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec1 ) -{ - result->vec128 = spu_add( pnt->vec128, vec1->vec128 ); -} - -static inline void vmathP3SubV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec1 ) -{ - result->vec128 = spu_sub( pnt->vec128, vec1->vec128 ); -} - -static inline void vmathP3MulPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - result->vec128 = spu_mul( pnt0->vec128, pnt1->vec128 ); -} - -static inline void vmathP3DivPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - result->vec128 = divf4( pnt0->vec128, pnt1->vec128 ); -} - -static inline void vmathP3RecipPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) -{ - result->vec128 = recipf4( pnt->vec128 ); -} - -static inline void vmathP3SqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) -{ - result->vec128 = sqrtf4( pnt->vec128 ); -} - -static inline void vmathP3RsqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) -{ - result->vec128 = rsqrtf4( pnt->vec128 ); -} - -static inline void vmathP3AbsPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) -{ - result->vec128 = fabsf4( pnt->vec128 ); -} - -static inline void vmathP3CopySignPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - result->vec128 = copysignf4( pnt0->vec128, pnt1->vec128 ); -} - -static inline void vmathP3MaxPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - result->vec128 = fmaxf4( pnt0->vec128, pnt1->vec128 ); -} - -static inline float vmathP3MaxElem( const VmathPoint3 *pnt ) -{ - vec_float4 result; - result = fmaxf4( spu_promote( spu_extract( pnt->vec128, 1 ), 0 ), pnt->vec128 ); - result = fmaxf4( spu_promote( spu_extract( pnt->vec128, 2 ), 0 ), result ); - return spu_extract( result, 0 ); -} - -static inline void vmathP3MinPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - result->vec128 = fminf4( pnt0->vec128, pnt1->vec128 ); -} - -static inline float vmathP3MinElem( const VmathPoint3 *pnt ) -{ - vec_float4 result; - result = fminf4( spu_promote( spu_extract( pnt->vec128, 1 ), 0 ), pnt->vec128 ); - result = fminf4( spu_promote( spu_extract( pnt->vec128, 2 ), 0 ), result ); - return spu_extract( result, 0 ); -} - -static inline float vmathP3Sum( const VmathPoint3 *pnt ) -{ - return - spu_extract( pnt->vec128, 0 ) + - spu_extract( pnt->vec128, 1 ) + - spu_extract( pnt->vec128, 2 ); -} - -static inline void vmathP3Scale( VmathPoint3 *result, const VmathPoint3 *pnt, float scaleVal ) -{ - VmathPoint3 tmpP3_0; - vmathP3MakeFromScalar( &tmpP3_0, scaleVal ); - vmathP3MulPerElem( result, pnt, &tmpP3_0 ); -} - -static inline void vmathP3NonUniformScale( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *scaleVec ) -{ - VmathPoint3 tmpP3_0; - vmathP3MakeFromV3( &tmpP3_0, scaleVec ); - vmathP3MulPerElem( result, pnt, &tmpP3_0 ); -} - -static inline float vmathP3Projection( const VmathPoint3 *pnt, const VmathVector3 *unitVec ) -{ - return spu_extract( _vmathVfDot3( pnt->vec128, unitVec->vec128 ), 0 ); -} - -static inline float vmathP3DistSqrFromOrigin( const VmathPoint3 *pnt ) -{ - VmathVector3 tmpV3_0; - vmathV3MakeFromP3( &tmpV3_0, pnt ); - return vmathV3LengthSqr( &tmpV3_0 ); -} - -static inline float vmathP3DistFromOrigin( const VmathPoint3 *pnt ) -{ - VmathVector3 tmpV3_0; - vmathV3MakeFromP3( &tmpV3_0, pnt ); - return vmathV3Length( &tmpV3_0 ); -} - -static inline float vmathP3DistSqr( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - VmathVector3 tmpV3_0; - vmathP3Sub( &tmpV3_0, pnt1, pnt0 ); - return vmathV3LengthSqr( &tmpV3_0 ); -} - -static inline float vmathP3Dist( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) -{ - VmathVector3 tmpV3_0; - vmathP3Sub( &tmpV3_0, pnt1, pnt0 ); - return vmathV3Length( &tmpV3_0 ); -} - -static inline void vmathP3Select( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, unsigned int select1 ) -{ - result->vec128 = spu_sel( pnt0->vec128, pnt1->vec128, spu_splats( (unsigned int)-(select1 > 0) ) ); -} - -#ifdef _VECTORMATH_DEBUG - -static inline void vmathP3Print( const VmathPoint3 *pnt ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = pnt->vec128; - printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -static inline void vmathP3Prints( const VmathPoint3 *pnt, const char *name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = pnt->vec128; - printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -#endif - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_VEC_AOS_C_H +#define _VECTORMATH_VEC_AOS_C_H +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*----------------------------------------------------------------------------- + * Constants + * for shuffles, words are labeled [x,y,z,w] [a,b,c,d] + */ +#define _VECTORMATH_SHUF_X 0x00010203 +#define _VECTORMATH_SHUF_Y 0x04050607 +#define _VECTORMATH_SHUF_Z 0x08090a0b +#define _VECTORMATH_SHUF_W 0x0c0d0e0f +#define _VECTORMATH_SHUF_A 0x10111213 +#define _VECTORMATH_SHUF_B 0x14151617 +#define _VECTORMATH_SHUF_C 0x18191a1b +#define _VECTORMATH_SHUF_D 0x1c1d1e1f +#define _VECTORMATH_SHUF_0 0x80808080 +#define _VECTORMATH_SHUF_XYZA (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A } +#define _VECTORMATH_SHUF_ZXYW (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_W } +#define _VECTORMATH_SHUF_YZXW (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_W } +#define _VECTORMATH_SHUF_WABC (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_C } +#define _VECTORMATH_SHUF_ZWAB (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B } +#define _VECTORMATH_SHUF_XYZA (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A } +#define _VECTORMATH_SHUF_YZAB (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B } +#define _VECTORMATH_SHUF_ZABC (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_C } +#define _VECTORMATH_UNIT_1000 (vec_float4){ 1.0f, 0.0f, 0.0f, 0.0f } +#define _VECTORMATH_UNIT_0100 (vec_float4){ 0.0f, 1.0f, 0.0f, 0.0f } +#define _VECTORMATH_UNIT_0010 (vec_float4){ 0.0f, 0.0f, 1.0f, 0.0f } +#define _VECTORMATH_UNIT_0001 (vec_float4){ 0.0f, 0.0f, 0.0f, 1.0f } +#define _VECTORMATH_SLERP_TOL 0.999f + +/*----------------------------------------------------------------------------- + * Definitions + */ +#ifndef _VECTORMATH_INTERNAL_FUNCTIONS +#define _VECTORMATH_INTERNAL_FUNCTIONS + +static inline vec_float4 _vmathVfDot3( vec_float4 vec0, vec_float4 vec1 ) +{ + vec_float4 result; + result = spu_mul( vec0, vec1 ); + result = spu_madd( spu_rlqwbyte( vec0, 4 ), spu_rlqwbyte( vec1, 4 ), result ); + return spu_madd( spu_rlqwbyte( vec0, 8 ), spu_rlqwbyte( vec1, 8 ), result ); +} + +static inline vec_float4 _vmathVfDot4( vec_float4 vec0, vec_float4 vec1 ) +{ + vec_float4 result; + result = spu_mul( vec0, vec1 ); + result = spu_madd( spu_rlqwbyte( vec0, 4 ), spu_rlqwbyte( vec1, 4 ), result ); + return spu_add( spu_rlqwbyte( result, 8 ), result ); +} + +static inline vec_float4 _vmathVfCross( vec_float4 vec0, vec_float4 vec1 ) +{ + vec_float4 tmp0, tmp1, tmp2, tmp3, result; + tmp0 = spu_shuffle( vec0, vec0, _VECTORMATH_SHUF_YZXW ); + tmp1 = spu_shuffle( vec1, vec1, _VECTORMATH_SHUF_ZXYW ); + tmp2 = spu_shuffle( vec0, vec0, _VECTORMATH_SHUF_ZXYW ); + tmp3 = spu_shuffle( vec1, vec1, _VECTORMATH_SHUF_YZXW ); + result = spu_mul( tmp0, tmp1 ); + result = spu_nmsub( tmp2, tmp3, result ); + return result; +} + +static inline vec_uint4 _vmathVfToHalfFloatsUnpacked(vec_float4 v) +{ + vec_int4 bexp; + vec_uint4 mant, sign, hfloat; + vec_uint4 notZero, isInf; + const vec_uint4 hfloatInf = spu_splats(0x00007c00u); + const vec_uint4 mergeMant = spu_splats(0x000003ffu); + const vec_uint4 mergeSign = spu_splats(0x00008000u); + + sign = spu_rlmask((vec_uint4)v, -16); + mant = spu_rlmask((vec_uint4)v, -13); + bexp = spu_and(spu_rlmask((vec_int4)v, -23), 0xff); + + notZero = spu_cmpgt(bexp, 112); + isInf = spu_cmpgt(bexp, 142); + + bexp = spu_add(bexp, -112); + bexp = spu_sl(bexp, 10); + + hfloat = spu_sel((vec_uint4)bexp, mant, mergeMant); + hfloat = spu_sel(spu_splats(0u), hfloat, notZero); + hfloat = spu_sel(hfloat, hfloatInf, isInf); + hfloat = spu_sel(hfloat, sign, mergeSign); + + return hfloat; +} + +static inline vec_ushort8 _vmath2VfToHalfFloats(vec_float4 u, vec_float4 v) +{ + vec_uint4 hfloat_u, hfloat_v; + const vec_uchar16 pack = (vec_uchar16){2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31}; + hfloat_u = _vmathVfToHalfFloatsUnpacked(u); + hfloat_v = _vmathVfToHalfFloatsUnpacked(v); + return (vec_ushort8)spu_shuffle(hfloat_u, hfloat_v, pack); +} + +#endif + +static inline void vmathV3Copy( VmathVector3 *result, const VmathVector3 *vec ) +{ + result->vec128 = vec->vec128; +} + +static inline void vmathV3MakeFromElems( VmathVector3 *result, float _x, float _y, float _z ) +{ + result->vec128 = (vec_float4){ _x, _y, _z, 0.0f }; +} + +static inline void vmathV3MakeFromP3( VmathVector3 *result, const VmathPoint3 *pnt ) +{ + result->vec128 = pnt->vec128; +} + +static inline void vmathV3MakeFromScalar( VmathVector3 *result, float scalar ) +{ + result->vec128 = spu_splats( scalar ); +} + +static inline void vmathV3MakeFrom128( VmathVector3 *result, vec_float4 vf4 ) +{ + result->vec128 = vf4; +} + +static inline void vmathV3MakeXAxis( VmathVector3 *result ) +{ + result->vec128 = _VECTORMATH_UNIT_1000; +} + +static inline void vmathV3MakeYAxis( VmathVector3 *result ) +{ + result->vec128 = _VECTORMATH_UNIT_0100; +} + +static inline void vmathV3MakeZAxis( VmathVector3 *result ) +{ + result->vec128 = _VECTORMATH_UNIT_0010; +} + +static inline void vmathV3Lerp( VmathVector3 *result, float t, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + VmathVector3 tmpV3_0, tmpV3_1; + vmathV3Sub( &tmpV3_0, vec1, vec0 ); + vmathV3ScalarMul( &tmpV3_1, &tmpV3_0, t ); + vmathV3Add( result, vec0, &tmpV3_1 ); +} + +static inline void vmathV3Slerp( VmathVector3 *result, float t, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ) +{ + vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; + vec_uint4 selectMask; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + cosAngle = _vmathVfDot3( unitVec0->vec128, unitVec1->vec128 ); + cosAngle = spu_shuffle( cosAngle, cosAngle, shuffle_xxxx ); + selectMask = (vec_uint4)spu_cmpgt( spu_splats(_VECTORMATH_SLERP_TOL), cosAngle ); + angle = acosf4( cosAngle ); + tttt = spu_splats(t); + oneMinusT = spu_sub( spu_splats(1.0f), tttt ); + angles = spu_sel( spu_splats(1.0f), oneMinusT, (vec_uint4)spu_maskb(0x0f00) ); + angles = spu_sel( angles, tttt, (vec_uint4)spu_maskb(0x00f0) ); + angles = spu_mul( angles, angle ); + sines = sinf4( angles ); + scales = divf4( sines, spu_shuffle( sines, sines, shuffle_xxxx ) ); + scale0 = spu_sel( oneMinusT, spu_shuffle( scales, scales, shuffle_yyyy ), selectMask ); + scale1 = spu_sel( tttt, spu_shuffle( scales, scales, shuffle_zzzz ), selectMask ); + result->vec128 = spu_madd( unitVec0->vec128, scale0, spu_mul( unitVec1->vec128, scale1 ) ); +} + +static inline vec_float4 vmathV3Get128( const VmathVector3 *vec ) +{ + return vec->vec128; +} + +static inline void vmathV3StoreXYZ( const VmathVector3 *vec, vec_float4 *quad ) +{ + vec_float4 dstVec = *quad; + vec_uint4 mask = (vec_uint4)spu_maskb(0x000f); + dstVec = spu_sel(vec->vec128, dstVec, mask); + *quad = dstVec; +} + +static inline void vmathV3LoadXYZArray( VmathVector3 *vec0, VmathVector3 *vec1, VmathVector3 *vec2, VmathVector3 *vec3, const vec_float4 *threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; + xyzx = threeQuads[0]; + yzxy = threeQuads[1]; + zxyz = threeQuads[2]; + xyz1 = spu_shuffle( xyzx, yzxy, _VECTORMATH_SHUF_WABC ); + xyz2 = spu_shuffle( yzxy, zxyz, _VECTORMATH_SHUF_ZWAB ); + xyz3 = spu_rlqwbyte( zxyz, 4 ); + vec0->vec128 = xyzx; + vec1->vec128 = xyz1; + vec2->vec128 = xyz2; + vec3->vec128 = xyz3; +} + +static inline void vmathV3StoreXYZArray( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, vec_float4 *threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz; + xyzx = spu_shuffle( vec0->vec128, vec1->vec128, _VECTORMATH_SHUF_XYZA ); + yzxy = spu_shuffle( vec1->vec128, vec2->vec128, _VECTORMATH_SHUF_YZAB ); + zxyz = spu_shuffle( vec2->vec128, vec3->vec128, _VECTORMATH_SHUF_ZABC ); + threeQuads[0] = xyzx; + threeQuads[1] = yzxy; + threeQuads[2] = zxyz; +} + +static inline void vmathV3StoreHalfFloats( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, const VmathVector3 *vec4, const VmathVector3 *vec5, const VmathVector3 *vec6, const VmathVector3 *vec7, vec_ushort8 *threeQuads ) +{ + vec_float4 xyz0[3]; + vec_float4 xyz1[3]; + vmathV3StoreXYZArray( vec0, vec1, vec2, vec3, xyz0 ); + vmathV3StoreXYZArray( vec4, vec5, vec6, vec7, xyz1 ); + threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); + threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); + threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); +} + +static inline void vmathV3SetX( VmathVector3 *result, float _x ) +{ + result->vec128 = spu_insert( _x, result->vec128, 0 ); +} + +static inline float vmathV3GetX( const VmathVector3 *vec ) +{ + return spu_extract( vec->vec128, 0 ); +} + +static inline void vmathV3SetY( VmathVector3 *result, float _y ) +{ + result->vec128 = spu_insert( _y, result->vec128, 1 ); +} + +static inline float vmathV3GetY( const VmathVector3 *vec ) +{ + return spu_extract( vec->vec128, 1 ); +} + +static inline void vmathV3SetZ( VmathVector3 *result, float _z ) +{ + result->vec128 = spu_insert( _z, result->vec128, 2 ); +} + +static inline float vmathV3GetZ( const VmathVector3 *vec ) +{ + return spu_extract( vec->vec128, 2 ); +} + +static inline void vmathV3SetElem( VmathVector3 *result, int idx, float value ) +{ + result->vec128 = spu_insert( value, result->vec128, idx ); +} + +static inline float vmathV3GetElem( const VmathVector3 *vec, int idx ) +{ + return spu_extract( vec->vec128, idx ); +} + +static inline void vmathV3Add( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = spu_add( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV3Sub( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = spu_sub( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV3AddP3( VmathPoint3 *result, const VmathVector3 *vec, const VmathPoint3 *pnt1 ) +{ + result->vec128 = spu_add( vec->vec128, pnt1->vec128 ); +} + +static inline void vmathV3ScalarMul( VmathVector3 *result, const VmathVector3 *vec, float scalar ) +{ + result->vec128 = spu_mul( vec->vec128, spu_splats(scalar) ); +} + +static inline void vmathV3ScalarDiv( VmathVector3 *result, const VmathVector3 *vec, float scalar ) +{ + result->vec128 = divf4( vec->vec128, spu_splats(scalar) ); +} + +static inline void vmathV3Neg( VmathVector3 *result, const VmathVector3 *vec ) +{ + result->vec128 = negatef4( vec->vec128 ); +} + +static inline void vmathV3MulPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = spu_mul( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV3DivPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = divf4( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV3RecipPerElem( VmathVector3 *result, const VmathVector3 *vec ) +{ + result->vec128 = recipf4( vec->vec128 ); +} + +static inline void vmathV3SqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ) +{ + result->vec128 = sqrtf4( vec->vec128 ); +} + +static inline void vmathV3RsqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ) +{ + result->vec128 = rsqrtf4( vec->vec128 ); +} + +static inline void vmathV3AbsPerElem( VmathVector3 *result, const VmathVector3 *vec ) +{ + result->vec128 = fabsf4( vec->vec128 ); +} + +static inline void vmathV3CopySignPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = copysignf4( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV3MaxPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = fmaxf4( vec0->vec128, vec1->vec128 ); +} + +static inline float vmathV3MaxElem( const VmathVector3 *vec ) +{ + vec_float4 result; + result = fmaxf4( spu_promote( spu_extract( vec->vec128, 1 ), 0 ), vec->vec128 ); + result = fmaxf4( spu_promote( spu_extract( vec->vec128, 2 ), 0 ), result ); + return spu_extract( result, 0 ); +} + +static inline void vmathV3MinPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = fminf4( vec0->vec128, vec1->vec128 ); +} + +static inline float vmathV3MinElem( const VmathVector3 *vec ) +{ + vec_float4 result; + result = fminf4( spu_promote( spu_extract( vec->vec128, 1 ), 0 ), vec->vec128 ); + result = fminf4( spu_promote( spu_extract( vec->vec128, 2 ), 0 ), result ); + return spu_extract( result, 0 ); +} + +static inline float vmathV3Sum( const VmathVector3 *vec ) +{ + return + spu_extract( vec->vec128, 0 ) + + spu_extract( vec->vec128, 1 ) + + spu_extract( vec->vec128, 2 ); +} + +static inline float vmathV3Dot( const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + return spu_extract( _vmathVfDot3( vec0->vec128, vec1->vec128 ), 0 ); +} + +static inline float vmathV3LengthSqr( const VmathVector3 *vec ) +{ + return spu_extract( _vmathVfDot3( vec->vec128, vec->vec128 ), 0 ); +} + +static inline float vmathV3Length( const VmathVector3 *vec ) +{ + return sqrtf( vmathV3LengthSqr( vec ) ); +} + +static inline void vmathV3Normalize( VmathVector3 *result, const VmathVector3 *vec ) +{ + vec_float4 dot = _vmathVfDot3( vec->vec128, vec->vec128 ); + dot = spu_shuffle( dot, dot, (vec_uchar16)spu_splats(0x00010203) ); + result->vec128 = spu_mul( vec->vec128, rsqrtf4( dot ) ); +} + +static inline void vmathV3Cross( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ) +{ + result->vec128 = _vmathVfCross( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV3Select( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1, unsigned int select1 ) +{ + result->vec128 = spu_sel( vec0->vec128, vec1->vec128, spu_splats( (unsigned int)-(select1 > 0) ) ); +} + +#ifdef _VECTORMATH_DEBUG + +static inline void vmathV3Print( const VmathVector3 *vec ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec->vec128; + printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +static inline void vmathV3Prints( const VmathVector3 *vec, const char *name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec->vec128; + printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +#endif + +static inline void vmathV4Copy( VmathVector4 *result, const VmathVector4 *vec ) +{ + result->vec128 = vec->vec128; +} + +static inline void vmathV4MakeFromElems( VmathVector4 *result, float _x, float _y, float _z, float _w ) +{ + result->vec128 = (vec_float4){ _x, _y, _z, _w }; +} + +static inline void vmathV4MakeFromV3Scalar( VmathVector4 *result, const VmathVector3 *xyz, float _w ) +{ + result->vec128 = spu_shuffle( xyz->vec128, spu_promote( _w, 0 ), _VECTORMATH_SHUF_XYZA ); +} + +static inline void vmathV4MakeFromV3( VmathVector4 *result, const VmathVector3 *vec ) +{ + result->vec128 = spu_sel( vec->vec128, spu_splats(0.0f), (vec_uint4)spu_maskb(0x000f) ); +} + +static inline void vmathV4MakeFromP3( VmathVector4 *result, const VmathPoint3 *pnt ) +{ + result->vec128 = spu_sel( pnt->vec128, spu_splats(1.0f), (vec_uint4)spu_maskb(0x000f) ); +} + +static inline void vmathV4MakeFromQ( VmathVector4 *result, const VmathQuat *quat ) +{ + result->vec128 = quat->vec128; +} + +static inline void vmathV4MakeFromScalar( VmathVector4 *result, float scalar ) +{ + result->vec128 = spu_splats( scalar ); +} + +static inline void vmathV4MakeFrom128( VmathVector4 *result, vec_float4 vf4 ) +{ + result->vec128 = vf4; +} + +static inline void vmathV4MakeXAxis( VmathVector4 *result ) +{ + result->vec128 = _VECTORMATH_UNIT_1000; +} + +static inline void vmathV4MakeYAxis( VmathVector4 *result ) +{ + result->vec128 = _VECTORMATH_UNIT_0100; +} + +static inline void vmathV4MakeZAxis( VmathVector4 *result ) +{ + result->vec128 = _VECTORMATH_UNIT_0010; +} + +static inline void vmathV4MakeWAxis( VmathVector4 *result ) +{ + result->vec128 = _VECTORMATH_UNIT_0001; +} + +static inline void vmathV4Lerp( VmathVector4 *result, float t, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + VmathVector4 tmpV4_0, tmpV4_1; + vmathV4Sub( &tmpV4_0, vec1, vec0 ); + vmathV4ScalarMul( &tmpV4_1, &tmpV4_0, t ); + vmathV4Add( result, vec0, &tmpV4_1 ); +} + +static inline void vmathV4Slerp( VmathVector4 *result, float t, const VmathVector4 *unitVec0, const VmathVector4 *unitVec1 ) +{ + vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; + vec_uint4 selectMask; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + cosAngle = _vmathVfDot4( unitVec0->vec128, unitVec1->vec128 ); + cosAngle = spu_shuffle( cosAngle, cosAngle, shuffle_xxxx ); + selectMask = (vec_uint4)spu_cmpgt( spu_splats(_VECTORMATH_SLERP_TOL), cosAngle ); + angle = acosf4( cosAngle ); + tttt = spu_splats(t); + oneMinusT = spu_sub( spu_splats(1.0f), tttt ); + angles = spu_sel( spu_splats(1.0f), oneMinusT, (vec_uint4)spu_maskb(0x0f00) ); + angles = spu_sel( angles, tttt, (vec_uint4)spu_maskb(0x00f0) ); + angles = spu_mul( angles, angle ); + sines = sinf4( angles ); + scales = divf4( sines, spu_shuffle( sines, sines, shuffle_xxxx ) ); + scale0 = spu_sel( oneMinusT, spu_shuffle( scales, scales, shuffle_yyyy ), selectMask ); + scale1 = spu_sel( tttt, spu_shuffle( scales, scales, shuffle_zzzz ), selectMask ); + result->vec128 = spu_madd( unitVec0->vec128, scale0, spu_mul( unitVec1->vec128, scale1 ) ); +} + +static inline vec_float4 vmathV4Get128( const VmathVector4 *vec ) +{ + return vec->vec128; +} + +static inline void vmathV4StoreHalfFloats( const VmathVector4 *vec0, const VmathVector4 *vec1, const VmathVector4 *vec2, const VmathVector4 *vec3, vec_ushort8 *twoQuads ) +{ + twoQuads[0] = _vmath2VfToHalfFloats(vec0->vec128, vec1->vec128); + twoQuads[1] = _vmath2VfToHalfFloats(vec2->vec128, vec3->vec128); +} + +static inline void vmathV4SetXYZ( VmathVector4 *result, const VmathVector3 *vec ) +{ + result->vec128 = spu_sel( vec->vec128, result->vec128, (vec_uint4)spu_maskb(0x000f) ); +} + +static inline void vmathV4GetXYZ( VmathVector3 *result, const VmathVector4 *vec ) +{ + result->vec128 = vec->vec128; +} + +static inline void vmathV4SetX( VmathVector4 *result, float _x ) +{ + result->vec128 = spu_insert( _x, result->vec128, 0 ); +} + +static inline float vmathV4GetX( const VmathVector4 *vec ) +{ + return spu_extract( vec->vec128, 0 ); +} + +static inline void vmathV4SetY( VmathVector4 *result, float _y ) +{ + result->vec128 = spu_insert( _y, result->vec128, 1 ); +} + +static inline float vmathV4GetY( const VmathVector4 *vec ) +{ + return spu_extract( vec->vec128, 1 ); +} + +static inline void vmathV4SetZ( VmathVector4 *result, float _z ) +{ + result->vec128 = spu_insert( _z, result->vec128, 2 ); +} + +static inline float vmathV4GetZ( const VmathVector4 *vec ) +{ + return spu_extract( vec->vec128, 2 ); +} + +static inline void vmathV4SetW( VmathVector4 *result, float _w ) +{ + result->vec128 = spu_insert( _w, result->vec128, 3 ); +} + +static inline float vmathV4GetW( const VmathVector4 *vec ) +{ + return spu_extract( vec->vec128, 3 ); +} + +static inline void vmathV4SetElem( VmathVector4 *result, int idx, float value ) +{ + result->vec128 = spu_insert( value, result->vec128, idx ); +} + +static inline float vmathV4GetElem( const VmathVector4 *vec, int idx ) +{ + return spu_extract( vec->vec128, idx ); +} + +static inline void vmathV4Add( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + result->vec128 = spu_add( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV4Sub( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + result->vec128 = spu_sub( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV4ScalarMul( VmathVector4 *result, const VmathVector4 *vec, float scalar ) +{ + result->vec128 = spu_mul( vec->vec128, spu_splats(scalar) ); +} + +static inline void vmathV4ScalarDiv( VmathVector4 *result, const VmathVector4 *vec, float scalar ) +{ + result->vec128 = divf4( vec->vec128, spu_splats(scalar) ); +} + +static inline void vmathV4Neg( VmathVector4 *result, const VmathVector4 *vec ) +{ + result->vec128 = negatef4( vec->vec128 ); +} + +static inline void vmathV4MulPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + result->vec128 = spu_mul( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV4DivPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + result->vec128 = divf4( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV4RecipPerElem( VmathVector4 *result, const VmathVector4 *vec ) +{ + result->vec128 = recipf4( vec->vec128 ); +} + +static inline void vmathV4SqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ) +{ + result->vec128 = sqrtf4( vec->vec128 ); +} + +static inline void vmathV4RsqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ) +{ + result->vec128 = rsqrtf4( vec->vec128 ); +} + +static inline void vmathV4AbsPerElem( VmathVector4 *result, const VmathVector4 *vec ) +{ + result->vec128 = fabsf4( vec->vec128 ); +} + +static inline void vmathV4CopySignPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + result->vec128 = copysignf4( vec0->vec128, vec1->vec128 ); +} + +static inline void vmathV4MaxPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + result->vec128 = fmaxf4( vec0->vec128, vec1->vec128 ); +} + +static inline float vmathV4MaxElem( const VmathVector4 *vec ) +{ + vec_float4 result; + result = fmaxf4( spu_promote( spu_extract( vec->vec128, 1 ), 0 ), vec->vec128 ); + result = fmaxf4( spu_promote( spu_extract( vec->vec128, 2 ), 0 ), result ); + result = fmaxf4( spu_promote( spu_extract( vec->vec128, 3 ), 0 ), result ); + return spu_extract( result, 0 ); +} + +static inline void vmathV4MinPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + result->vec128 = fminf4( vec0->vec128, vec1->vec128 ); +} + +static inline float vmathV4MinElem( const VmathVector4 *vec ) +{ + vec_float4 result; + result = fminf4( spu_promote( spu_extract( vec->vec128, 1 ), 0 ), vec->vec128 ); + result = fminf4( spu_promote( spu_extract( vec->vec128, 2 ), 0 ), result ); + result = fminf4( spu_promote( spu_extract( vec->vec128, 3 ), 0 ), result ); + return spu_extract( result, 0 ); +} + +static inline float vmathV4Sum( const VmathVector4 *vec ) +{ + return + spu_extract( vec->vec128, 0 ) + + spu_extract( vec->vec128, 1 ) + + spu_extract( vec->vec128, 2 ) + + spu_extract( vec->vec128, 3 ); +} + +static inline float vmathV4Dot( const VmathVector4 *vec0, const VmathVector4 *vec1 ) +{ + return spu_extract( _vmathVfDot4( vec0->vec128, vec1->vec128 ), 0 ); +} + +static inline float vmathV4LengthSqr( const VmathVector4 *vec ) +{ + return spu_extract( _vmathVfDot4( vec->vec128, vec->vec128 ), 0 ); +} + +static inline float vmathV4Length( const VmathVector4 *vec ) +{ + return sqrtf( vmathV4LengthSqr( vec ) ); +} + +static inline void vmathV4Normalize( VmathVector4 *result, const VmathVector4 *vec ) +{ + vec_float4 dot = _vmathVfDot4( vec->vec128, vec->vec128 ); + result->vec128 = spu_mul( vec->vec128, rsqrtf4( dot ) ); +} + +static inline void vmathV4Select( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1, unsigned int select1 ) +{ + result->vec128 = spu_sel( vec0->vec128, vec1->vec128, spu_splats( (unsigned int)-(select1 > 0) ) ); +} + +#ifdef _VECTORMATH_DEBUG + +static inline void vmathV4Print( const VmathVector4 *vec ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec->vec128; + printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +static inline void vmathV4Prints( const VmathVector4 *vec, const char *name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec->vec128; + printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +#endif + +static inline void vmathP3Copy( VmathPoint3 *result, const VmathPoint3 *pnt ) +{ + result->vec128 = pnt->vec128; +} + +static inline void vmathP3MakeFromElems( VmathPoint3 *result, float _x, float _y, float _z ) +{ + result->vec128 = (vec_float4){ _x, _y, _z, 0.0f }; +} + +static inline void vmathP3MakeFromV3( VmathPoint3 *result, const VmathVector3 *vec ) +{ + result->vec128 = vec->vec128; +} + +static inline void vmathP3MakeFromScalar( VmathPoint3 *result, float scalar ) +{ + result->vec128 = spu_splats( scalar ); +} + +static inline void vmathP3MakeFrom128( VmathPoint3 *result, vec_float4 vf4 ) +{ + result->vec128 = vf4; +} + +static inline void vmathP3Lerp( VmathPoint3 *result, float t, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + VmathVector3 tmpV3_0, tmpV3_1; + vmathP3Sub( &tmpV3_0, pnt1, pnt0 ); + vmathV3ScalarMul( &tmpV3_1, &tmpV3_0, t ); + vmathP3AddV3( result, pnt0, &tmpV3_1 ); +} + +static inline vec_float4 vmathP3Get128( const VmathPoint3 *pnt ) +{ + return pnt->vec128; +} + +static inline void vmathP3StoreXYZ( const VmathPoint3 *pnt, vec_float4 *quad ) +{ + vec_float4 dstVec = *quad; + vec_uint4 mask = (vec_uint4)spu_maskb(0x000f); + dstVec = spu_sel(pnt->vec128, dstVec, mask); + *quad = dstVec; +} + +static inline void vmathP3LoadXYZArray( VmathPoint3 *pnt0, VmathPoint3 *pnt1, VmathPoint3 *pnt2, VmathPoint3 *pnt3, const vec_float4 *threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; + xyzx = threeQuads[0]; + yzxy = threeQuads[1]; + zxyz = threeQuads[2]; + xyz1 = spu_shuffle( xyzx, yzxy, _VECTORMATH_SHUF_WABC ); + xyz2 = spu_shuffle( yzxy, zxyz, _VECTORMATH_SHUF_ZWAB ); + xyz3 = spu_rlqwbyte( zxyz, 4 ); + pnt0->vec128 = xyzx; + pnt1->vec128 = xyz1; + pnt2->vec128 = xyz2; + pnt3->vec128 = xyz3; +} + +static inline void vmathP3StoreXYZArray( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, vec_float4 *threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz; + xyzx = spu_shuffle( pnt0->vec128, pnt1->vec128, _VECTORMATH_SHUF_XYZA ); + yzxy = spu_shuffle( pnt1->vec128, pnt2->vec128, _VECTORMATH_SHUF_YZAB ); + zxyz = spu_shuffle( pnt2->vec128, pnt3->vec128, _VECTORMATH_SHUF_ZABC ); + threeQuads[0] = xyzx; + threeQuads[1] = yzxy; + threeQuads[2] = zxyz; +} + +static inline void vmathP3StoreHalfFloats( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, const VmathPoint3 *pnt4, const VmathPoint3 *pnt5, const VmathPoint3 *pnt6, const VmathPoint3 *pnt7, vec_ushort8 *threeQuads ) +{ + vec_float4 xyz0[3]; + vec_float4 xyz1[3]; + vmathP3StoreXYZArray( pnt0, pnt1, pnt2, pnt3, xyz0 ); + vmathP3StoreXYZArray( pnt4, pnt5, pnt6, pnt7, xyz1 ); + threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); + threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); + threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); +} + +static inline void vmathP3SetX( VmathPoint3 *result, float _x ) +{ + result->vec128 = spu_insert( _x, result->vec128, 0 ); +} + +static inline float vmathP3GetX( const VmathPoint3 *pnt ) +{ + return spu_extract( pnt->vec128, 0 ); +} + +static inline void vmathP3SetY( VmathPoint3 *result, float _y ) +{ + result->vec128 = spu_insert( _y, result->vec128, 1 ); +} + +static inline float vmathP3GetY( const VmathPoint3 *pnt ) +{ + return spu_extract( pnt->vec128, 1 ); +} + +static inline void vmathP3SetZ( VmathPoint3 *result, float _z ) +{ + result->vec128 = spu_insert( _z, result->vec128, 2 ); +} + +static inline float vmathP3GetZ( const VmathPoint3 *pnt ) +{ + return spu_extract( pnt->vec128, 2 ); +} + +static inline void vmathP3SetElem( VmathPoint3 *result, int idx, float value ) +{ + result->vec128 = spu_insert( value, result->vec128, idx ); +} + +static inline float vmathP3GetElem( const VmathPoint3 *pnt, int idx ) +{ + return spu_extract( pnt->vec128, idx ); +} + +static inline void vmathP3Sub( VmathVector3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + result->vec128 = spu_sub( pnt0->vec128, pnt1->vec128 ); +} + +static inline void vmathP3AddV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec1 ) +{ + result->vec128 = spu_add( pnt->vec128, vec1->vec128 ); +} + +static inline void vmathP3SubV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec1 ) +{ + result->vec128 = spu_sub( pnt->vec128, vec1->vec128 ); +} + +static inline void vmathP3MulPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + result->vec128 = spu_mul( pnt0->vec128, pnt1->vec128 ); +} + +static inline void vmathP3DivPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + result->vec128 = divf4( pnt0->vec128, pnt1->vec128 ); +} + +static inline void vmathP3RecipPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) +{ + result->vec128 = recipf4( pnt->vec128 ); +} + +static inline void vmathP3SqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) +{ + result->vec128 = sqrtf4( pnt->vec128 ); +} + +static inline void vmathP3RsqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) +{ + result->vec128 = rsqrtf4( pnt->vec128 ); +} + +static inline void vmathP3AbsPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ) +{ + result->vec128 = fabsf4( pnt->vec128 ); +} + +static inline void vmathP3CopySignPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + result->vec128 = copysignf4( pnt0->vec128, pnt1->vec128 ); +} + +static inline void vmathP3MaxPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + result->vec128 = fmaxf4( pnt0->vec128, pnt1->vec128 ); +} + +static inline float vmathP3MaxElem( const VmathPoint3 *pnt ) +{ + vec_float4 result; + result = fmaxf4( spu_promote( spu_extract( pnt->vec128, 1 ), 0 ), pnt->vec128 ); + result = fmaxf4( spu_promote( spu_extract( pnt->vec128, 2 ), 0 ), result ); + return spu_extract( result, 0 ); +} + +static inline void vmathP3MinPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + result->vec128 = fminf4( pnt0->vec128, pnt1->vec128 ); +} + +static inline float vmathP3MinElem( const VmathPoint3 *pnt ) +{ + vec_float4 result; + result = fminf4( spu_promote( spu_extract( pnt->vec128, 1 ), 0 ), pnt->vec128 ); + result = fminf4( spu_promote( spu_extract( pnt->vec128, 2 ), 0 ), result ); + return spu_extract( result, 0 ); +} + +static inline float vmathP3Sum( const VmathPoint3 *pnt ) +{ + return + spu_extract( pnt->vec128, 0 ) + + spu_extract( pnt->vec128, 1 ) + + spu_extract( pnt->vec128, 2 ); +} + +static inline void vmathP3Scale( VmathPoint3 *result, const VmathPoint3 *pnt, float scaleVal ) +{ + VmathPoint3 tmpP3_0; + vmathP3MakeFromScalar( &tmpP3_0, scaleVal ); + vmathP3MulPerElem( result, pnt, &tmpP3_0 ); +} + +static inline void vmathP3NonUniformScale( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *scaleVec ) +{ + VmathPoint3 tmpP3_0; + vmathP3MakeFromV3( &tmpP3_0, scaleVec ); + vmathP3MulPerElem( result, pnt, &tmpP3_0 ); +} + +static inline float vmathP3Projection( const VmathPoint3 *pnt, const VmathVector3 *unitVec ) +{ + return spu_extract( _vmathVfDot3( pnt->vec128, unitVec->vec128 ), 0 ); +} + +static inline float vmathP3DistSqrFromOrigin( const VmathPoint3 *pnt ) +{ + VmathVector3 tmpV3_0; + vmathV3MakeFromP3( &tmpV3_0, pnt ); + return vmathV3LengthSqr( &tmpV3_0 ); +} + +static inline float vmathP3DistFromOrigin( const VmathPoint3 *pnt ) +{ + VmathVector3 tmpV3_0; + vmathV3MakeFromP3( &tmpV3_0, pnt ); + return vmathV3Length( &tmpV3_0 ); +} + +static inline float vmathP3DistSqr( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + VmathVector3 tmpV3_0; + vmathP3Sub( &tmpV3_0, pnt1, pnt0 ); + return vmathV3LengthSqr( &tmpV3_0 ); +} + +static inline float vmathP3Dist( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ) +{ + VmathVector3 tmpV3_0; + vmathP3Sub( &tmpV3_0, pnt1, pnt0 ); + return vmathV3Length( &tmpV3_0 ); +} + +static inline void vmathP3Select( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, unsigned int select1 ) +{ + result->vec128 = spu_sel( pnt0->vec128, pnt1->vec128, spu_splats( (unsigned int)-(select1 > 0) ) ); +} + +#ifdef _VECTORMATH_DEBUG + +static inline void vmathP3Print( const VmathPoint3 *pnt ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = pnt->vec128; + printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +static inline void vmathP3Prints( const VmathPoint3 *pnt, const char *name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = pnt->vec128; + printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/common/vectormath/spu/c/vectormath_aos.h b/common/vectormath/spu/c/vectormath_aos.h index 3bd4e0fe..5fa9950e 100644 --- a/common/vectormath/spu/c/vectormath_aos.h +++ b/common/vectormath/spu/c/vectormath_aos.h @@ -1,1951 +1,1951 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_AOS_C_SPU_H -#define _VECTORMATH_AOS_C_SPU_H - -#include -#include -#include - -#ifdef _VECTORMATH_DEBUG -#endif - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef _VECTORMATH_AOS_C_TYPES_H -#define _VECTORMATH_AOS_C_TYPES_H - -/* A 3-D vector in array-of-structures format - */ -typedef struct _VmathVector3 -{ - vec_float4 vec128; -} VmathVector3; - -/* A 4-D vector in array-of-structures format - */ -typedef struct _VmathVector4 -{ - vec_float4 vec128; -} VmathVector4; - -/* A 3-D point in array-of-structures format - */ -typedef struct _VmathPoint3 -{ - vec_float4 vec128; -} VmathPoint3; - -/* A quaternion in array-of-structures format - */ -typedef struct _VmathQuat -{ - vec_float4 vec128; -} VmathQuat; - -/* A 3x3 matrix in array-of-structures format - */ -typedef struct _VmathMatrix3 -{ - VmathVector3 col0; - VmathVector3 col1; - VmathVector3 col2; -} VmathMatrix3; - -/* A 4x4 matrix in array-of-structures format - */ -typedef struct _VmathMatrix4 -{ - VmathVector4 col0; - VmathVector4 col1; - VmathVector4 col2; - VmathVector4 col3; -} VmathMatrix4; - -/* A 3x4 transformation matrix in array-of-structures format - */ -typedef struct _VmathTransform3 -{ - VmathVector3 col0; - VmathVector3 col1; - VmathVector3 col2; - VmathVector3 col3; -} VmathTransform3; - -#endif - -/* - * Copy a 3-D vector - */ -static inline void vmathV3Copy( VmathVector3 *result, const VmathVector3 *vec ); - -/* - * Construct a 3-D vector from x, y, and z elements - */ -static inline void vmathV3MakeFromElems( VmathVector3 *result, float x, float y, float z ); - -/* - * Copy elements from a 3-D point into a 3-D vector - */ -static inline void vmathV3MakeFromP3( VmathVector3 *result, const VmathPoint3 *pnt ); - -/* - * Set all elements of a 3-D vector to the same scalar value - */ -static inline void vmathV3MakeFromScalar( VmathVector3 *result, float scalar ); - -/* - * Set vector float data in a 3-D vector - */ -static inline void vmathV3MakeFrom128( VmathVector3 *result, vec_float4 vf4 ); - -/* - * Get vector float data from a 3-D vector - */ -static inline vec_float4 vmathV3Get128( const VmathVector3 *vec ); - -/* - * Set the x element of a 3-D vector - */ -static inline void vmathV3SetX( VmathVector3 *result, float x ); - -/* - * Set the y element of a 3-D vector - */ -static inline void vmathV3SetY( VmathVector3 *result, float y ); - -/* - * Set the z element of a 3-D vector - */ -static inline void vmathV3SetZ( VmathVector3 *result, float z ); - -/* - * Get the x element of a 3-D vector - */ -static inline float vmathV3GetX( const VmathVector3 *vec ); - -/* - * Get the y element of a 3-D vector - */ -static inline float vmathV3GetY( const VmathVector3 *vec ); - -/* - * Get the z element of a 3-D vector - */ -static inline float vmathV3GetZ( const VmathVector3 *vec ); - -/* - * Set an x, y, or z element of a 3-D vector by index - */ -static inline void vmathV3SetElem( VmathVector3 *result, int idx, float value ); - -/* - * Get an x, y, or z element of a 3-D vector by index - */ -static inline float vmathV3GetElem( const VmathVector3 *vec, int idx ); - -/* - * Add two 3-D vectors - */ -static inline void vmathV3Add( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Subtract a 3-D vector from another 3-D vector - */ -static inline void vmathV3Sub( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Add a 3-D vector to a 3-D point - */ -static inline void vmathV3AddP3( VmathPoint3 *result, const VmathVector3 *vec, const VmathPoint3 *pnt ); - -/* - * Multiply a 3-D vector by a scalar - */ -static inline void vmathV3ScalarMul( VmathVector3 *result, const VmathVector3 *vec, float scalar ); - -/* - * Divide a 3-D vector by a scalar - */ -static inline void vmathV3ScalarDiv( VmathVector3 *result, const VmathVector3 *vec, float scalar ); - -/* - * Negate all elements of a 3-D vector - */ -static inline void vmathV3Neg( VmathVector3 *result, const VmathVector3 *vec ); - -/* - * Construct x axis - */ -static inline void vmathV3MakeXAxis( VmathVector3 *result ); - -/* - * Construct y axis - */ -static inline void vmathV3MakeYAxis( VmathVector3 *result ); - -/* - * Construct z axis - */ -static inline void vmathV3MakeZAxis( VmathVector3 *result ); - -/* - * Multiply two 3-D vectors per element - */ -static inline void vmathV3MulPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Divide two 3-D vectors per element - * NOTE: - * Floating-point behavior matches standard library function divf4. - */ -static inline void vmathV3DivPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Compute the reciprocal of a 3-D vector per element - * NOTE: - * Floating-point behavior matches standard library function recipf4. - */ -static inline void vmathV3RecipPerElem( VmathVector3 *result, const VmathVector3 *vec ); - -/* - * Compute the square root of a 3-D vector per element - * NOTE: - * Floating-point behavior matches standard library function sqrtf4. - */ -static inline void vmathV3SqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ); - -/* - * Compute the reciprocal square root of a 3-D vector per element - * NOTE: - * Floating-point behavior matches standard library function rsqrtf4. - */ -static inline void vmathV3RsqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ); - -/* - * Compute the absolute value of a 3-D vector per element - */ -static inline void vmathV3AbsPerElem( VmathVector3 *result, const VmathVector3 *vec ); - -/* - * Copy sign from one 3-D vector to another, per element - */ -static inline void vmathV3CopySignPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Maximum of two 3-D vectors per element - */ -static inline void vmathV3MaxPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Minimum of two 3-D vectors per element - */ -static inline void vmathV3MinPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Maximum element of a 3-D vector - */ -static inline float vmathV3MaxElem( const VmathVector3 *vec ); - -/* - * Minimum element of a 3-D vector - */ -static inline float vmathV3MinElem( const VmathVector3 *vec ); - -/* - * Compute the sum of all elements of a 3-D vector - */ -static inline float vmathV3Sum( const VmathVector3 *vec ); - -/* - * Compute the dot product of two 3-D vectors - */ -static inline float vmathV3Dot( const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Compute the square of the length of a 3-D vector - */ -static inline float vmathV3LengthSqr( const VmathVector3 *vec ); - -/* - * Compute the length of a 3-D vector - */ -static inline float vmathV3Length( const VmathVector3 *vec ); - -/* - * Normalize a 3-D vector - * NOTE: - * The result is unpredictable when all elements of vec are at or near zero. - */ -static inline void vmathV3Normalize( VmathVector3 *result, const VmathVector3 *vec ); - -/* - * Compute cross product of two 3-D vectors - */ -static inline void vmathV3Cross( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Outer product of two 3-D vectors - */ -static inline void vmathV3Outer( VmathMatrix3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Pre-multiply a row vector by a 3x3 matrix - * NOTE: - * Slower than column post-multiply. - */ -static inline void vmathV3RowMul( VmathVector3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ); - -/* - * Cross-product matrix of a 3-D vector - */ -static inline void vmathV3CrossMatrix( VmathMatrix3 *result, const VmathVector3 *vec ); - -/* - * Create cross-product matrix and multiply - * NOTE: - * Faster than separately creating a cross-product matrix and multiplying. - */ -static inline void vmathV3CrossMatrixMul( VmathMatrix3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ); - -/* - * Linear interpolation between two 3-D vectors - * NOTE: - * Does not clamp t between 0 and 1. - */ -static inline void vmathV3Lerp( VmathVector3 *result, float t, const VmathVector3 *vec0, const VmathVector3 *vec1 ); - -/* - * Spherical linear interpolation between two 3-D vectors - * NOTE: - * The result is unpredictable if the vectors point in opposite directions. - * Does not clamp t between 0 and 1. - */ -static inline void vmathV3Slerp( VmathVector3 *result, float t, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ); - -/* - * Conditionally select between two 3-D vectors - * NOTE: - * This function uses a conditional select instruction to avoid a branch. - */ -static inline void vmathV3Select( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1, unsigned int select1 ); - -/* - * Store x, y, and z elements of a 3-D vector in the first three words of a quadword. - * The value of the fourth word (the word with the highest address) remains unchanged - */ -static inline void vmathV3StoreXYZ( const VmathVector3 *vec, vec_float4 *quad ); - -/* - * Load four three-float 3-D vectors, stored in three quadwords - */ -static inline void vmathV3LoadXYZArray( VmathVector3 *vec0, VmathVector3 *vec1, VmathVector3 *vec2, VmathVector3 *vec3, const vec_float4 *threeQuads ); - -/* - * Store four 3-D vectors in three quadwords - */ -static inline void vmathV3StoreXYZArray( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, vec_float4 *threeQuads ); - -/* - * Store eight 3-D vectors as half-floats - */ -static inline void vmathV3StoreHalfFloats( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, const VmathVector3 *vec4, const VmathVector3 *vec5, const VmathVector3 *vec6, const VmathVector3 *vec7, vec_ushort8 *threeQuads ); - -#ifdef _VECTORMATH_DEBUG - -/* - * Print a 3-D vector - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathV3Print( const VmathVector3 *vec ); - -/* - * Print a 3-D vector and an associated string identifier - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathV3Prints( const VmathVector3 *vec, const char *name ); - -#endif - -/* - * Copy a 4-D vector - */ -static inline void vmathV4Copy( VmathVector4 *result, const VmathVector4 *vec ); - -/* - * Construct a 4-D vector from x, y, z, and w elements - */ -static inline void vmathV4MakeFromElems( VmathVector4 *result, float x, float y, float z, float w ); - -/* - * Construct a 4-D vector from a 3-D vector and a scalar - */ -static inline void vmathV4MakeFromV3Scalar( VmathVector4 *result, const VmathVector3 *xyz, float w ); - -/* - * Copy x, y, and z from a 3-D vector into a 4-D vector, and set w to 0 - */ -static inline void vmathV4MakeFromV3( VmathVector4 *result, const VmathVector3 *vec ); - -/* - * Copy x, y, and z from a 3-D point into a 4-D vector, and set w to 1 - */ -static inline void vmathV4MakeFromP3( VmathVector4 *result, const VmathPoint3 *pnt ); - -/* - * Copy elements from a quaternion into a 4-D vector - */ -static inline void vmathV4MakeFromQ( VmathVector4 *result, const VmathQuat *quat ); - -/* - * Set all elements of a 4-D vector to the same scalar value - */ -static inline void vmathV4MakeFromScalar( VmathVector4 *result, float scalar ); - -/* - * Set vector float data in a 4-D vector - */ -static inline void vmathV4MakeFrom128( VmathVector4 *result, vec_float4 vf4 ); - -/* - * Get vector float data from a 4-D vector - */ -static inline vec_float4 vmathV4Get128( const VmathVector4 *vec ); - -/* - * Set the x, y, and z elements of a 4-D vector - * NOTE: - * This function does not change the w element. - */ -static inline void vmathV4SetXYZ( VmathVector4 *result, const VmathVector3 *vec ); - -/* - * Get the x, y, and z elements of a 4-D vector - */ -static inline void vmathV4GetXYZ( VmathVector3 *result, const VmathVector4 *vec ); - -/* - * Set the x element of a 4-D vector - */ -static inline void vmathV4SetX( VmathVector4 *result, float x ); - -/* - * Set the y element of a 4-D vector - */ -static inline void vmathV4SetY( VmathVector4 *result, float y ); - -/* - * Set the z element of a 4-D vector - */ -static inline void vmathV4SetZ( VmathVector4 *result, float z ); - -/* - * Set the w element of a 4-D vector - */ -static inline void vmathV4SetW( VmathVector4 *result, float w ); - -/* - * Get the x element of a 4-D vector - */ -static inline float vmathV4GetX( const VmathVector4 *vec ); - -/* - * Get the y element of a 4-D vector - */ -static inline float vmathV4GetY( const VmathVector4 *vec ); - -/* - * Get the z element of a 4-D vector - */ -static inline float vmathV4GetZ( const VmathVector4 *vec ); - -/* - * Get the w element of a 4-D vector - */ -static inline float vmathV4GetW( const VmathVector4 *vec ); - -/* - * Set an x, y, z, or w element of a 4-D vector by index - */ -static inline void vmathV4SetElem( VmathVector4 *result, int idx, float value ); - -/* - * Get an x, y, z, or w element of a 4-D vector by index - */ -static inline float vmathV4GetElem( const VmathVector4 *vec, int idx ); - -/* - * Add two 4-D vectors - */ -static inline void vmathV4Add( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Subtract a 4-D vector from another 4-D vector - */ -static inline void vmathV4Sub( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Multiply a 4-D vector by a scalar - */ -static inline void vmathV4ScalarMul( VmathVector4 *result, const VmathVector4 *vec, float scalar ); - -/* - * Divide a 4-D vector by a scalar - */ -static inline void vmathV4ScalarDiv( VmathVector4 *result, const VmathVector4 *vec, float scalar ); - -/* - * Negate all elements of a 4-D vector - */ -static inline void vmathV4Neg( VmathVector4 *result, const VmathVector4 *vec ); - -/* - * Construct x axis - */ -static inline void vmathV4MakeXAxis( VmathVector4 *result ); - -/* - * Construct y axis - */ -static inline void vmathV4MakeYAxis( VmathVector4 *result ); - -/* - * Construct z axis - */ -static inline void vmathV4MakeZAxis( VmathVector4 *result ); - -/* - * Construct w axis - */ -static inline void vmathV4MakeWAxis( VmathVector4 *result ); - -/* - * Multiply two 4-D vectors per element - */ -static inline void vmathV4MulPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Divide two 4-D vectors per element - * NOTE: - * Floating-point behavior matches standard library function divf4. - */ -static inline void vmathV4DivPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Compute the reciprocal of a 4-D vector per element - * NOTE: - * Floating-point behavior matches standard library function recipf4. - */ -static inline void vmathV4RecipPerElem( VmathVector4 *result, const VmathVector4 *vec ); - -/* - * Compute the square root of a 4-D vector per element - * NOTE: - * Floating-point behavior matches standard library function sqrtf4. - */ -static inline void vmathV4SqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ); - -/* - * Compute the reciprocal square root of a 4-D vector per element - * NOTE: - * Floating-point behavior matches standard library function rsqrtf4. - */ -static inline void vmathV4RsqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ); - -/* - * Compute the absolute value of a 4-D vector per element - */ -static inline void vmathV4AbsPerElem( VmathVector4 *result, const VmathVector4 *vec ); - -/* - * Copy sign from one 4-D vector to another, per element - */ -static inline void vmathV4CopySignPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Maximum of two 4-D vectors per element - */ -static inline void vmathV4MaxPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Minimum of two 4-D vectors per element - */ -static inline void vmathV4MinPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Maximum element of a 4-D vector - */ -static inline float vmathV4MaxElem( const VmathVector4 *vec ); - -/* - * Minimum element of a 4-D vector - */ -static inline float vmathV4MinElem( const VmathVector4 *vec ); - -/* - * Compute the sum of all elements of a 4-D vector - */ -static inline float vmathV4Sum( const VmathVector4 *vec ); - -/* - * Compute the dot product of two 4-D vectors - */ -static inline float vmathV4Dot( const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Compute the square of the length of a 4-D vector - */ -static inline float vmathV4LengthSqr( const VmathVector4 *vec ); - -/* - * Compute the length of a 4-D vector - */ -static inline float vmathV4Length( const VmathVector4 *vec ); - -/* - * Normalize a 4-D vector - * NOTE: - * The result is unpredictable when all elements of vec are at or near zero. - */ -static inline void vmathV4Normalize( VmathVector4 *result, const VmathVector4 *vec ); - -/* - * Outer product of two 4-D vectors - */ -static inline void vmathV4Outer( VmathMatrix4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Linear interpolation between two 4-D vectors - * NOTE: - * Does not clamp t between 0 and 1. - */ -static inline void vmathV4Lerp( VmathVector4 *result, float t, const VmathVector4 *vec0, const VmathVector4 *vec1 ); - -/* - * Spherical linear interpolation between two 4-D vectors - * NOTE: - * The result is unpredictable if the vectors point in opposite directions. - * Does not clamp t between 0 and 1. - */ -static inline void vmathV4Slerp( VmathVector4 *result, float t, const VmathVector4 *unitVec0, const VmathVector4 *unitVec1 ); - -/* - * Conditionally select between two 4-D vectors - * NOTE: - * This function uses a conditional select instruction to avoid a branch. - */ -static inline void vmathV4Select( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1, unsigned int select1 ); - -/* - * Store four 4-D vectors as half-floats - */ -static inline void vmathV4StoreHalfFloats( const VmathVector4 *vec0, const VmathVector4 *vec1, const VmathVector4 *vec2, const VmathVector4 *vec3, vec_ushort8 *twoQuads ); - -#ifdef _VECTORMATH_DEBUG - -/* - * Print a 4-D vector - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathV4Print( const VmathVector4 *vec ); - -/* - * Print a 4-D vector and an associated string identifier - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathV4Prints( const VmathVector4 *vec, const char *name ); - -#endif - -/* - * Copy a 3-D point - */ -static inline void vmathP3Copy( VmathPoint3 *result, const VmathPoint3 *pnt ); - -/* - * Construct a 3-D point from x, y, and z elements - */ -static inline void vmathP3MakeFromElems( VmathPoint3 *result, float x, float y, float z ); - -/* - * Copy elements from a 3-D vector into a 3-D point - */ -static inline void vmathP3MakeFromV3( VmathPoint3 *result, const VmathVector3 *vec ); - -/* - * Set all elements of a 3-D point to the same scalar value - */ -static inline void vmathP3MakeFromScalar( VmathPoint3 *result, float scalar ); - -/* - * Set vector float data in a 3-D point - */ -static inline void vmathP3MakeFrom128( VmathPoint3 *result, vec_float4 vf4 ); - -/* - * Get vector float data from a 3-D point - */ -static inline vec_float4 vmathP3Get128( const VmathPoint3 *pnt ); - -/* - * Set the x element of a 3-D point - */ -static inline void vmathP3SetX( VmathPoint3 *result, float x ); - -/* - * Set the y element of a 3-D point - */ -static inline void vmathP3SetY( VmathPoint3 *result, float y ); - -/* - * Set the z element of a 3-D point - */ -static inline void vmathP3SetZ( VmathPoint3 *result, float z ); - -/* - * Get the x element of a 3-D point - */ -static inline float vmathP3GetX( const VmathPoint3 *pnt ); - -/* - * Get the y element of a 3-D point - */ -static inline float vmathP3GetY( const VmathPoint3 *pnt ); - -/* - * Get the z element of a 3-D point - */ -static inline float vmathP3GetZ( const VmathPoint3 *pnt ); - -/* - * Set an x, y, or z element of a 3-D point by index - */ -static inline void vmathP3SetElem( VmathPoint3 *result, int idx, float value ); - -/* - * Get an x, y, or z element of a 3-D point by index - */ -static inline float vmathP3GetElem( const VmathPoint3 *pnt, int idx ); - -/* - * Subtract a 3-D point from another 3-D point - */ -static inline void vmathP3Sub( VmathVector3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Add a 3-D point to a 3-D vector - */ -static inline void vmathP3AddV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec ); - -/* - * Subtract a 3-D vector from a 3-D point - */ -static inline void vmathP3SubV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec ); - -/* - * Multiply two 3-D points per element - */ -static inline void vmathP3MulPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Divide two 3-D points per element - * NOTE: - * Floating-point behavior matches standard library function divf4. - */ -static inline void vmathP3DivPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Compute the reciprocal of a 3-D point per element - * NOTE: - * Floating-point behavior matches standard library function recipf4. - */ -static inline void vmathP3RecipPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); - -/* - * Compute the square root of a 3-D point per element - * NOTE: - * Floating-point behavior matches standard library function sqrtf4. - */ -static inline void vmathP3SqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); - -/* - * Compute the reciprocal square root of a 3-D point per element - * NOTE: - * Floating-point behavior matches standard library function rsqrtf4. - */ -static inline void vmathP3RsqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); - -/* - * Compute the absolute value of a 3-D point per element - */ -static inline void vmathP3AbsPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); - -/* - * Copy sign from one 3-D point to another, per element - */ -static inline void vmathP3CopySignPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Maximum of two 3-D points per element - */ -static inline void vmathP3MaxPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Minimum of two 3-D points per element - */ -static inline void vmathP3MinPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Maximum element of a 3-D point - */ -static inline float vmathP3MaxElem( const VmathPoint3 *pnt ); - -/* - * Minimum element of a 3-D point - */ -static inline float vmathP3MinElem( const VmathPoint3 *pnt ); - -/* - * Compute the sum of all elements of a 3-D point - */ -static inline float vmathP3Sum( const VmathPoint3 *pnt ); - -/* - * Apply uniform scale to a 3-D point - */ -static inline void vmathP3Scale( VmathPoint3 *result, const VmathPoint3 *pnt, float scaleVal ); - -/* - * Apply non-uniform scale to a 3-D point - */ -static inline void vmathP3NonUniformScale( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *scaleVec ); - -/* - * Scalar projection of a 3-D point on a unit-length 3-D vector - */ -static inline float vmathP3Projection( const VmathPoint3 *pnt, const VmathVector3 *unitVec ); - -/* - * Compute the square of the distance of a 3-D point from the coordinate-system origin - */ -static inline float vmathP3DistSqrFromOrigin( const VmathPoint3 *pnt ); - -/* - * Compute the distance of a 3-D point from the coordinate-system origin - */ -static inline float vmathP3DistFromOrigin( const VmathPoint3 *pnt ); - -/* - * Compute the square of the distance between two 3-D points - */ -static inline float vmathP3DistSqr( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Compute the distance between two 3-D points - */ -static inline float vmathP3Dist( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Linear interpolation between two 3-D points - * NOTE: - * Does not clamp t between 0 and 1. - */ -static inline void vmathP3Lerp( VmathPoint3 *result, float t, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); - -/* - * Conditionally select between two 3-D points - * NOTE: - * This function uses a conditional select instruction to avoid a branch. - */ -static inline void vmathP3Select( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, unsigned int select1 ); - -/* - * Store x, y, and z elements of a 3-D point in the first three words of a quadword. - * The value of the fourth word (the word with the highest address) remains unchanged - */ -static inline void vmathP3StoreXYZ( const VmathPoint3 *pnt, vec_float4 *quad ); - -/* - * Load four three-float 3-D points, stored in three quadwords - */ -static inline void vmathP3LoadXYZArray( VmathPoint3 *pnt0, VmathPoint3 *pnt1, VmathPoint3 *pnt2, VmathPoint3 *pnt3, const vec_float4 *threeQuads ); - -/* - * Store four 3-D points in three quadwords - */ -static inline void vmathP3StoreXYZArray( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, vec_float4 *threeQuads ); - -/* - * Store eight 3-D points as half-floats - */ -static inline void vmathP3StoreHalfFloats( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, const VmathPoint3 *pnt4, const VmathPoint3 *pnt5, const VmathPoint3 *pnt6, const VmathPoint3 *pnt7, vec_ushort8 *threeQuads ); - -#ifdef _VECTORMATH_DEBUG - -/* - * Print a 3-D point - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathP3Print( const VmathPoint3 *pnt ); - -/* - * Print a 3-D point and an associated string identifier - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathP3Prints( const VmathPoint3 *pnt, const char *name ); - -#endif - -/* - * Copy a quaternion - */ -static inline void vmathQCopy( VmathQuat *result, const VmathQuat *quat ); - -/* - * Construct a quaternion from x, y, z, and w elements - */ -static inline void vmathQMakeFromElems( VmathQuat *result, float x, float y, float z, float w ); - -/* - * Construct a quaternion from a 3-D vector and a scalar - */ -static inline void vmathQMakeFromV3Scalar( VmathQuat *result, const VmathVector3 *xyz, float w ); - -/* - * Copy elements from a 4-D vector into a quaternion - */ -static inline void vmathQMakeFromV4( VmathQuat *result, const VmathVector4 *vec ); - -/* - * Convert a rotation matrix to a unit-length quaternion - */ -static inline void vmathQMakeFromM3( VmathQuat *result, const VmathMatrix3 *rotMat ); - -/* - * Set all elements of a quaternion to the same scalar value - */ -static inline void vmathQMakeFromScalar( VmathQuat *result, float scalar ); - -/* - * Set vector float data in a quaternion - */ -static inline void vmathQMakeFrom128( VmathQuat *result, vec_float4 vf4 ); - -/* - * Get vector float data from a quaternion - */ -static inline vec_float4 vmathQGet128( const VmathQuat *quat ); - -/* - * Set the x, y, and z elements of a quaternion - * NOTE: - * This function does not change the w element. - */ -static inline void vmathQSetXYZ( VmathQuat *result, const VmathVector3 *vec ); - -/* - * Get the x, y, and z elements of a quaternion - */ -static inline void vmathQGetXYZ( VmathVector3 *result, const VmathQuat *quat ); - -/* - * Set the x element of a quaternion - */ -static inline void vmathQSetX( VmathQuat *result, float x ); - -/* - * Set the y element of a quaternion - */ -static inline void vmathQSetY( VmathQuat *result, float y ); - -/* - * Set the z element of a quaternion - */ -static inline void vmathQSetZ( VmathQuat *result, float z ); - -/* - * Set the w element of a quaternion - */ -static inline void vmathQSetW( VmathQuat *result, float w ); - -/* - * Get the x element of a quaternion - */ -static inline float vmathQGetX( const VmathQuat *quat ); - -/* - * Get the y element of a quaternion - */ -static inline float vmathQGetY( const VmathQuat *quat ); - -/* - * Get the z element of a quaternion - */ -static inline float vmathQGetZ( const VmathQuat *quat ); - -/* - * Get the w element of a quaternion - */ -static inline float vmathQGetW( const VmathQuat *quat ); - -/* - * Set an x, y, z, or w element of a quaternion by index - */ -static inline void vmathQSetElem( VmathQuat *result, int idx, float value ); - -/* - * Get an x, y, z, or w element of a quaternion by index - */ -static inline float vmathQGetElem( const VmathQuat *quat, int idx ); - -/* - * Add two quaternions - */ -static inline void vmathQAdd( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ); - -/* - * Subtract a quaternion from another quaternion - */ -static inline void vmathQSub( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ); - -/* - * Multiply two quaternions - */ -static inline void vmathQMul( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ); - -/* - * Multiply a quaternion by a scalar - */ -static inline void vmathQScalarMul( VmathQuat *result, const VmathQuat *quat, float scalar ); - -/* - * Divide a quaternion by a scalar - */ -static inline void vmathQScalarDiv( VmathQuat *result, const VmathQuat *quat, float scalar ); - -/* - * Negate all elements of a quaternion - */ -static inline void vmathQNeg( VmathQuat *result, const VmathQuat *quat ); - -/* - * Construct an identity quaternion - */ -static inline void vmathQMakeIdentity( VmathQuat *result ); - -/* - * Construct a quaternion to rotate between two unit-length 3-D vectors - * NOTE: - * The result is unpredictable if unitVec0 and unitVec1 point in opposite directions. - */ -static inline void vmathQMakeRotationArc( VmathQuat *result, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ); - -/* - * Construct a quaternion to rotate around a unit-length 3-D vector - */ -static inline void vmathQMakeRotationAxis( VmathQuat *result, float radians, const VmathVector3 *unitVec ); - -/* - * Construct a quaternion to rotate around the x axis - */ -static inline void vmathQMakeRotationX( VmathQuat *result, float radians ); - -/* - * Construct a quaternion to rotate around the y axis - */ -static inline void vmathQMakeRotationY( VmathQuat *result, float radians ); - -/* - * Construct a quaternion to rotate around the z axis - */ -static inline void vmathQMakeRotationZ( VmathQuat *result, float radians ); - -/* - * Compute the conjugate of a quaternion - */ -static inline void vmathQConj( VmathQuat *result, const VmathQuat *quat ); - -/* - * Use a unit-length quaternion to rotate a 3-D vector - */ -static inline void vmathQRotate( VmathVector3 *result, const VmathQuat *unitQuat, const VmathVector3 *vec ); - -/* - * Compute the dot product of two quaternions - */ -static inline float vmathQDot( const VmathQuat *quat0, const VmathQuat *quat1 ); - -/* - * Compute the norm of a quaternion - */ -static inline float vmathQNorm( const VmathQuat *quat ); - -/* - * Compute the length of a quaternion - */ -static inline float vmathQLength( const VmathQuat *quat ); - -/* - * Normalize a quaternion - * NOTE: - * The result is unpredictable when all elements of quat are at or near zero. - */ -static inline void vmathQNormalize( VmathQuat *result, const VmathQuat *quat ); - -/* - * Linear interpolation between two quaternions - * NOTE: - * Does not clamp t between 0 and 1. - */ -static inline void vmathQLerp( VmathQuat *result, float t, const VmathQuat *quat0, const VmathQuat *quat1 ); - -/* - * Spherical linear interpolation between two quaternions - * NOTE: - * Interpolates along the shortest path between orientations. - * Does not clamp t between 0 and 1. - */ -static inline void vmathQSlerp( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1 ); - -/* - * Spherical quadrangle interpolation - */ -static inline void vmathQSquad( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1, const VmathQuat *unitQuat2, const VmathQuat *unitQuat3 ); - -/* - * Conditionally select between two quaternions - * NOTE: - * This function uses a conditional select instruction to avoid a branch. - */ -static inline void vmathQSelect( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1, unsigned int select1 ); - -#ifdef _VECTORMATH_DEBUG - -/* - * Print a quaternion - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathQPrint( const VmathQuat *quat ); - -/* - * Print a quaternion and an associated string identifier - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathQPrints( const VmathQuat *quat, const char *name ); - -#endif - -/* - * Copy a 3x3 matrix - */ -static inline void vmathM3Copy( VmathMatrix3 *result, const VmathMatrix3 *mat ); - -/* - * Construct a 3x3 matrix containing the specified columns - */ -static inline void vmathM3MakeFromCols( VmathMatrix3 *result, const VmathVector3 *col0, const VmathVector3 *col1, const VmathVector3 *col2 ); - -/* - * Construct a 3x3 rotation matrix from a unit-length quaternion - */ -static inline void vmathM3MakeFromQ( VmathMatrix3 *result, const VmathQuat *unitQuat ); - -/* - * Set all elements of a 3x3 matrix to the same scalar value - */ -static inline void vmathM3MakeFromScalar( VmathMatrix3 *result, float scalar ); - -/* - * Set column 0 of a 3x3 matrix - */ -static inline void vmathM3SetCol0( VmathMatrix3 *result, const VmathVector3 *col0 ); - -/* - * Set column 1 of a 3x3 matrix - */ -static inline void vmathM3SetCol1( VmathMatrix3 *result, const VmathVector3 *col1 ); - -/* - * Set column 2 of a 3x3 matrix - */ -static inline void vmathM3SetCol2( VmathMatrix3 *result, const VmathVector3 *col2 ); - -/* - * Get column 0 of a 3x3 matrix - */ -static inline void vmathM3GetCol0( VmathVector3 *result, const VmathMatrix3 *mat ); - -/* - * Get column 1 of a 3x3 matrix - */ -static inline void vmathM3GetCol1( VmathVector3 *result, const VmathMatrix3 *mat ); - -/* - * Get column 2 of a 3x3 matrix - */ -static inline void vmathM3GetCol2( VmathVector3 *result, const VmathMatrix3 *mat ); - -/* - * Set the column of a 3x3 matrix referred to by the specified index - */ -static inline void vmathM3SetCol( VmathMatrix3 *result, int col, const VmathVector3 *vec ); - -/* - * Set the row of a 3x3 matrix referred to by the specified index - */ -static inline void vmathM3SetRow( VmathMatrix3 *result, int row, const VmathVector3 *vec ); - -/* - * Get the column of a 3x3 matrix referred to by the specified index - */ -static inline void vmathM3GetCol( VmathVector3 *result, const VmathMatrix3 *mat, int col ); - -/* - * Get the row of a 3x3 matrix referred to by the specified index - */ -static inline void vmathM3GetRow( VmathVector3 *result, const VmathMatrix3 *mat, int row ); - -/* - * Set the element of a 3x3 matrix referred to by column and row indices - */ -static inline void vmathM3SetElem( VmathMatrix3 *result, int col, int row, float val ); - -/* - * Get the element of a 3x3 matrix referred to by column and row indices - */ -static inline float vmathM3GetElem( const VmathMatrix3 *mat, int col, int row ); - -/* - * Add two 3x3 matrices - */ -static inline void vmathM3Add( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); - -/* - * Subtract a 3x3 matrix from another 3x3 matrix - */ -static inline void vmathM3Sub( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); - -/* - * Negate all elements of a 3x3 matrix - */ -static inline void vmathM3Neg( VmathMatrix3 *result, const VmathMatrix3 *mat ); - -/* - * Multiply a 3x3 matrix by a scalar - */ -static inline void vmathM3ScalarMul( VmathMatrix3 *result, const VmathMatrix3 *mat, float scalar ); - -/* - * Multiply a 3x3 matrix by a 3-D vector - */ -static inline void vmathM3MulV3( VmathVector3 *result, const VmathMatrix3 *mat, const VmathVector3 *vec ); - -/* - * Multiply two 3x3 matrices - */ -static inline void vmathM3Mul( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); - -/* - * Construct an identity 3x3 matrix - */ -static inline void vmathM3MakeIdentity( VmathMatrix3 *result ); - -/* - * Construct a 3x3 matrix to rotate around the x axis - */ -static inline void vmathM3MakeRotationX( VmathMatrix3 *result, float radians ); - -/* - * Construct a 3x3 matrix to rotate around the y axis - */ -static inline void vmathM3MakeRotationY( VmathMatrix3 *result, float radians ); - -/* - * Construct a 3x3 matrix to rotate around the z axis - */ -static inline void vmathM3MakeRotationZ( VmathMatrix3 *result, float radians ); - -/* - * Construct a 3x3 matrix to rotate around the x, y, and z axes - */ -static inline void vmathM3MakeRotationZYX( VmathMatrix3 *result, const VmathVector3 *radiansXYZ ); - -/* - * Construct a 3x3 matrix to rotate around a unit-length 3-D vector - */ -static inline void vmathM3MakeRotationAxis( VmathMatrix3 *result, float radians, const VmathVector3 *unitVec ); - -/* - * Construct a rotation matrix from a unit-length quaternion - */ -static inline void vmathM3MakeRotationQ( VmathMatrix3 *result, const VmathQuat *unitQuat ); - -/* - * Construct a 3x3 matrix to perform scaling - */ -static inline void vmathM3MakeScale( VmathMatrix3 *result, const VmathVector3 *scaleVec ); - -/* - * Append (post-multiply) a scale transformation to a 3x3 matrix - * NOTE: - * Faster than creating and multiplying a scale transformation matrix. - */ -static inline void vmathM3AppendScale( VmathMatrix3 *result, const VmathMatrix3 *mat, const VmathVector3 *scaleVec ); - -/* - * Prepend (pre-multiply) a scale transformation to a 3x3 matrix - * NOTE: - * Faster than creating and multiplying a scale transformation matrix. - */ -static inline void vmathM3PrependScale( VmathMatrix3 *result, const VmathVector3 *scaleVec, const VmathMatrix3 *mat ); - -/* - * Multiply two 3x3 matrices per element - */ -static inline void vmathM3MulPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); - -/* - * Compute the absolute value of a 3x3 matrix per element - */ -static inline void vmathM3AbsPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat ); - -/* - * Transpose of a 3x3 matrix - */ -static inline void vmathM3Transpose( VmathMatrix3 *result, const VmathMatrix3 *mat ); - -/* - * Compute the inverse of a 3x3 matrix - * NOTE: - * Result is unpredictable when the determinant of mat is equal to or near 0. - */ -static inline void vmathM3Inverse( VmathMatrix3 *result, const VmathMatrix3 *mat ); - -/* - * Determinant of a 3x3 matrix - */ -static inline float vmathM3Determinant( const VmathMatrix3 *mat ); - -/* - * Conditionally select between two 3x3 matrices - * NOTE: - * This function uses a conditional select instruction to avoid a branch. - */ -static inline void vmathM3Select( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1, unsigned int select1 ); - -#ifdef _VECTORMATH_DEBUG - -/* - * Print a 3x3 matrix - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathM3Print( const VmathMatrix3 *mat ); - -/* - * Print a 3x3 matrix and an associated string identifier - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathM3Prints( const VmathMatrix3 *mat, const char *name ); - -#endif - -/* - * Copy a 4x4 matrix - */ -static inline void vmathM4Copy( VmathMatrix4 *result, const VmathMatrix4 *mat ); - -/* - * Construct a 4x4 matrix containing the specified columns - */ -static inline void vmathM4MakeFromCols( VmathMatrix4 *result, const VmathVector4 *col0, const VmathVector4 *col1, const VmathVector4 *col2, const VmathVector4 *col3 ); - -/* - * Construct a 4x4 matrix from a 3x4 transformation matrix - */ -static inline void vmathM4MakeFromT3( VmathMatrix4 *result, const VmathTransform3 *mat ); - -/* - * Construct a 4x4 matrix from a 3x3 matrix and a 3-D vector - */ -static inline void vmathM4MakeFromM3V3( VmathMatrix4 *result, const VmathMatrix3 *mat, const VmathVector3 *translateVec ); - -/* - * Construct a 4x4 matrix from a unit-length quaternion and a 3-D vector - */ -static inline void vmathM4MakeFromQV3( VmathMatrix4 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ); - -/* - * Set all elements of a 4x4 matrix to the same scalar value - */ -static inline void vmathM4MakeFromScalar( VmathMatrix4 *result, float scalar ); - -/* - * Set the upper-left 3x3 submatrix - * NOTE: - * This function does not change the bottom row elements. - */ -static inline void vmathM4SetUpper3x3( VmathMatrix4 *result, const VmathMatrix3 *mat3 ); - -/* - * Get the upper-left 3x3 submatrix of a 4x4 matrix - */ -static inline void vmathM4GetUpper3x3( VmathMatrix3 *result, const VmathMatrix4 *mat ); - -/* - * Set translation component - * NOTE: - * This function does not change the bottom row elements. - */ -static inline void vmathM4SetTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ); - -/* - * Get the translation component of a 4x4 matrix - */ -static inline void vmathM4GetTranslation( VmathVector3 *result, const VmathMatrix4 *mat ); - -/* - * Set column 0 of a 4x4 matrix - */ -static inline void vmathM4SetCol0( VmathMatrix4 *result, const VmathVector4 *col0 ); - -/* - * Set column 1 of a 4x4 matrix - */ -static inline void vmathM4SetCol1( VmathMatrix4 *result, const VmathVector4 *col1 ); - -/* - * Set column 2 of a 4x4 matrix - */ -static inline void vmathM4SetCol2( VmathMatrix4 *result, const VmathVector4 *col2 ); - -/* - * Set column 3 of a 4x4 matrix - */ -static inline void vmathM4SetCol3( VmathMatrix4 *result, const VmathVector4 *col3 ); - -/* - * Get column 0 of a 4x4 matrix - */ -static inline void vmathM4GetCol0( VmathVector4 *result, const VmathMatrix4 *mat ); - -/* - * Get column 1 of a 4x4 matrix - */ -static inline void vmathM4GetCol1( VmathVector4 *result, const VmathMatrix4 *mat ); - -/* - * Get column 2 of a 4x4 matrix - */ -static inline void vmathM4GetCol2( VmathVector4 *result, const VmathMatrix4 *mat ); - -/* - * Get column 3 of a 4x4 matrix - */ -static inline void vmathM4GetCol3( VmathVector4 *result, const VmathMatrix4 *mat ); - -/* - * Set the column of a 4x4 matrix referred to by the specified index - */ -static inline void vmathM4SetCol( VmathMatrix4 *result, int col, const VmathVector4 *vec ); - -/* - * Set the row of a 4x4 matrix referred to by the specified index - */ -static inline void vmathM4SetRow( VmathMatrix4 *result, int row, const VmathVector4 *vec ); - -/* - * Get the column of a 4x4 matrix referred to by the specified index - */ -static inline void vmathM4GetCol( VmathVector4 *result, const VmathMatrix4 *mat, int col ); - -/* - * Get the row of a 4x4 matrix referred to by the specified index - */ -static inline void vmathM4GetRow( VmathVector4 *result, const VmathMatrix4 *mat, int row ); - -/* - * Set the element of a 4x4 matrix referred to by column and row indices - */ -static inline void vmathM4SetElem( VmathMatrix4 *result, int col, int row, float val ); - -/* - * Get the element of a 4x4 matrix referred to by column and row indices - */ -static inline float vmathM4GetElem( const VmathMatrix4 *mat, int col, int row ); - -/* - * Add two 4x4 matrices - */ -static inline void vmathM4Add( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); - -/* - * Subtract a 4x4 matrix from another 4x4 matrix - */ -static inline void vmathM4Sub( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); - -/* - * Negate all elements of a 4x4 matrix - */ -static inline void vmathM4Neg( VmathMatrix4 *result, const VmathMatrix4 *mat ); - -/* - * Multiply a 4x4 matrix by a scalar - */ -static inline void vmathM4ScalarMul( VmathMatrix4 *result, const VmathMatrix4 *mat, float scalar ); - -/* - * Multiply a 4x4 matrix by a 4-D vector - */ -static inline void vmathM4MulV4( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector4 *vec ); - -/* - * Multiply a 4x4 matrix by a 3-D vector - */ -static inline void vmathM4MulV3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector3 *vec ); - -/* - * Multiply a 4x4 matrix by a 3-D point - */ -static inline void vmathM4MulP3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathPoint3 *pnt ); - -/* - * Multiply two 4x4 matrices - */ -static inline void vmathM4Mul( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); - -/* - * Multiply a 4x4 matrix by a 3x4 transformation matrix - */ -static inline void vmathM4MulT3( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathTransform3 *tfrm ); - -/* - * Construct an identity 4x4 matrix - */ -static inline void vmathM4MakeIdentity( VmathMatrix4 *result ); - -/* - * Construct a 4x4 matrix to rotate around the x axis - */ -static inline void vmathM4MakeRotationX( VmathMatrix4 *result, float radians ); - -/* - * Construct a 4x4 matrix to rotate around the y axis - */ -static inline void vmathM4MakeRotationY( VmathMatrix4 *result, float radians ); - -/* - * Construct a 4x4 matrix to rotate around the z axis - */ -static inline void vmathM4MakeRotationZ( VmathMatrix4 *result, float radians ); - -/* - * Construct a 4x4 matrix to rotate around the x, y, and z axes - */ -static inline void vmathM4MakeRotationZYX( VmathMatrix4 *result, const VmathVector3 *radiansXYZ ); - -/* - * Construct a 4x4 matrix to rotate around a unit-length 3-D vector - */ -static inline void vmathM4MakeRotationAxis( VmathMatrix4 *result, float radians, const VmathVector3 *unitVec ); - -/* - * Construct a rotation matrix from a unit-length quaternion - */ -static inline void vmathM4MakeRotationQ( VmathMatrix4 *result, const VmathQuat *unitQuat ); - -/* - * Construct a 4x4 matrix to perform scaling - */ -static inline void vmathM4MakeScale( VmathMatrix4 *result, const VmathVector3 *scaleVec ); - -/* - * Construct a 4x4 matrix to perform translation - */ -static inline void vmathM4MakeTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ); - -/* - * Construct viewing matrix based on eye position, position looked at, and up direction - */ -static inline void vmathM4MakeLookAt( VmathMatrix4 *result, const VmathPoint3 *eyePos, const VmathPoint3 *lookAtPos, const VmathVector3 *upVec ); - -/* - * Construct a perspective projection matrix - */ -static inline void vmathM4MakePerspective( VmathMatrix4 *result, float fovyRadians, float aspect, float zNear, float zFar ); - -/* - * Construct a perspective projection matrix based on frustum - */ -static inline void vmathM4MakeFrustum( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ); - -/* - * Construct an orthographic projection matrix - */ -static inline void vmathM4MakeOrthographic( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ); - -/* - * Append (post-multiply) a scale transformation to a 4x4 matrix - * NOTE: - * Faster than creating and multiplying a scale transformation matrix. - */ -static inline void vmathM4AppendScale( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathVector3 *scaleVec ); - -/* - * Prepend (pre-multiply) a scale transformation to a 4x4 matrix - * NOTE: - * Faster than creating and multiplying a scale transformation matrix. - */ -static inline void vmathM4PrependScale( VmathMatrix4 *result, const VmathVector3 *scaleVec, const VmathMatrix4 *mat ); - -/* - * Multiply two 4x4 matrices per element - */ -static inline void vmathM4MulPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); - -/* - * Compute the absolute value of a 4x4 matrix per element - */ -static inline void vmathM4AbsPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat ); - -/* - * Transpose of a 4x4 matrix - */ -static inline void vmathM4Transpose( VmathMatrix4 *result, const VmathMatrix4 *mat ); - -/* - * Compute the inverse of a 4x4 matrix - * NOTE: - * Result is unpredictable when the determinant of mat is equal to or near 0. - */ -static inline void vmathM4Inverse( VmathMatrix4 *result, const VmathMatrix4 *mat ); - -/* - * Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix - * NOTE: - * This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. The result is unpredictable when the determinant of mat is equal to or near 0. - */ -static inline void vmathM4AffineInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ); - -/* - * Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix with an orthogonal upper-left 3x3 submatrix - * NOTE: - * This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. - */ -static inline void vmathM4OrthoInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ); - -/* - * Determinant of a 4x4 matrix - */ -static inline float vmathM4Determinant( const VmathMatrix4 *mat ); - -/* - * Conditionally select between two 4x4 matrices - * NOTE: - * This function uses a conditional select instruction to avoid a branch. - */ -static inline void vmathM4Select( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1, unsigned int select1 ); - -#ifdef _VECTORMATH_DEBUG - -/* - * Print a 4x4 matrix - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathM4Print( const VmathMatrix4 *mat ); - -/* - * Print a 4x4 matrix and an associated string identifier - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathM4Prints( const VmathMatrix4 *mat, const char *name ); - -#endif - -/* - * Copy a 3x4 transformation matrix - */ -static inline void vmathT3Copy( VmathTransform3 *result, const VmathTransform3 *tfrm ); - -/* - * Construct a 3x4 transformation matrix containing the specified columns - */ -static inline void vmathT3MakeFromCols( VmathTransform3 *result, const VmathVector3 *col0, const VmathVector3 *col1, const VmathVector3 *col2, const VmathVector3 *col3 ); - -/* - * Construct a 3x4 transformation matrix from a 3x3 matrix and a 3-D vector - */ -static inline void vmathT3MakeFromM3V3( VmathTransform3 *result, const VmathMatrix3 *tfrm, const VmathVector3 *translateVec ); - -/* - * Construct a 3x4 transformation matrix from a unit-length quaternion and a 3-D vector - */ -static inline void vmathT3MakeFromQV3( VmathTransform3 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ); - -/* - * Set all elements of a 3x4 transformation matrix to the same scalar value - */ -static inline void vmathT3MakeFromScalar( VmathTransform3 *result, float scalar ); - -/* - * Set the upper-left 3x3 submatrix - */ -static inline void vmathT3SetUpper3x3( VmathTransform3 *result, const VmathMatrix3 *mat3 ); - -/* - * Get the upper-left 3x3 submatrix of a 3x4 transformation matrix - */ -static inline void vmathT3GetUpper3x3( VmathMatrix3 *result, const VmathTransform3 *tfrm ); - -/* - * Set translation component - */ -static inline void vmathT3SetTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ); - -/* - * Get the translation component of a 3x4 transformation matrix - */ -static inline void vmathT3GetTranslation( VmathVector3 *result, const VmathTransform3 *tfrm ); - -/* - * Set column 0 of a 3x4 transformation matrix - */ -static inline void vmathT3SetCol0( VmathTransform3 *result, const VmathVector3 *col0 ); - -/* - * Set column 1 of a 3x4 transformation matrix - */ -static inline void vmathT3SetCol1( VmathTransform3 *result, const VmathVector3 *col1 ); - -/* - * Set column 2 of a 3x4 transformation matrix - */ -static inline void vmathT3SetCol2( VmathTransform3 *result, const VmathVector3 *col2 ); - -/* - * Set column 3 of a 3x4 transformation matrix - */ -static inline void vmathT3SetCol3( VmathTransform3 *result, const VmathVector3 *col3 ); - -/* - * Get column 0 of a 3x4 transformation matrix - */ -static inline void vmathT3GetCol0( VmathVector3 *result, const VmathTransform3 *tfrm ); - -/* - * Get column 1 of a 3x4 transformation matrix - */ -static inline void vmathT3GetCol1( VmathVector3 *result, const VmathTransform3 *tfrm ); - -/* - * Get column 2 of a 3x4 transformation matrix - */ -static inline void vmathT3GetCol2( VmathVector3 *result, const VmathTransform3 *tfrm ); - -/* - * Get column 3 of a 3x4 transformation matrix - */ -static inline void vmathT3GetCol3( VmathVector3 *result, const VmathTransform3 *tfrm ); - -/* - * Set the column of a 3x4 transformation matrix referred to by the specified index - */ -static inline void vmathT3SetCol( VmathTransform3 *result, int col, const VmathVector3 *vec ); - -/* - * Set the row of a 3x4 transformation matrix referred to by the specified index - */ -static inline void vmathT3SetRow( VmathTransform3 *result, int row, const VmathVector4 *vec ); - -/* - * Get the column of a 3x4 transformation matrix referred to by the specified index - */ -static inline void vmathT3GetCol( VmathVector3 *result, const VmathTransform3 *tfrm, int col ); - -/* - * Get the row of a 3x4 transformation matrix referred to by the specified index - */ -static inline void vmathT3GetRow( VmathVector4 *result, const VmathTransform3 *tfrm, int row ); - -/* - * Set the element of a 3x4 transformation matrix referred to by column and row indices - */ -static inline void vmathT3SetElem( VmathTransform3 *result, int col, int row, float val ); - -/* - * Get the element of a 3x4 transformation matrix referred to by column and row indices - */ -static inline float vmathT3GetElem( const VmathTransform3 *tfrm, int col, int row ); - -/* - * Multiply a 3x4 transformation matrix by a 3-D vector - */ -static inline void vmathT3MulV3( VmathVector3 *result, const VmathTransform3 *tfrm, const VmathVector3 *vec ); - -/* - * Multiply a 3x4 transformation matrix by a 3-D point - */ -static inline void vmathT3MulP3( VmathPoint3 *result, const VmathTransform3 *tfrm, const VmathPoint3 *pnt ); - -/* - * Multiply two 3x4 transformation matrices - */ -static inline void vmathT3Mul( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ); - -/* - * Construct an identity 3x4 transformation matrix - */ -static inline void vmathT3MakeIdentity( VmathTransform3 *result ); - -/* - * Construct a 3x4 transformation matrix to rotate around the x axis - */ -static inline void vmathT3MakeRotationX( VmathTransform3 *result, float radians ); - -/* - * Construct a 3x4 transformation matrix to rotate around the y axis - */ -static inline void vmathT3MakeRotationY( VmathTransform3 *result, float radians ); - -/* - * Construct a 3x4 transformation matrix to rotate around the z axis - */ -static inline void vmathT3MakeRotationZ( VmathTransform3 *result, float radians ); - -/* - * Construct a 3x4 transformation matrix to rotate around the x, y, and z axes - */ -static inline void vmathT3MakeRotationZYX( VmathTransform3 *result, const VmathVector3 *radiansXYZ ); - -/* - * Construct a 3x4 transformation matrix to rotate around a unit-length 3-D vector - */ -static inline void vmathT3MakeRotationAxis( VmathTransform3 *result, float radians, const VmathVector3 *unitVec ); - -/* - * Construct a rotation matrix from a unit-length quaternion - */ -static inline void vmathT3MakeRotationQ( VmathTransform3 *result, const VmathQuat *unitQuat ); - -/* - * Construct a 3x4 transformation matrix to perform scaling - */ -static inline void vmathT3MakeScale( VmathTransform3 *result, const VmathVector3 *scaleVec ); - -/* - * Construct a 3x4 transformation matrix to perform translation - */ -static inline void vmathT3MakeTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ); - -/* - * Append (post-multiply) a scale transformation to a 3x4 transformation matrix - * NOTE: - * Faster than creating and multiplying a scale transformation matrix. - */ -static inline void vmathT3AppendScale( VmathTransform3 *result, const VmathTransform3 *tfrm, const VmathVector3 *scaleVec ); - -/* - * Prepend (pre-multiply) a scale transformation to a 3x4 transformation matrix - * NOTE: - * Faster than creating and multiplying a scale transformation matrix. - */ -static inline void vmathT3PrependScale( VmathTransform3 *result, const VmathVector3 *scaleVec, const VmathTransform3 *tfrm ); - -/* - * Multiply two 3x4 transformation matrices per element - */ -static inline void vmathT3MulPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ); - -/* - * Compute the absolute value of a 3x4 transformation matrix per element - */ -static inline void vmathT3AbsPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm ); - -/* - * Inverse of a 3x4 transformation matrix - * NOTE: - * Result is unpredictable when the determinant of the left 3x3 submatrix is equal to or near 0. - */ -static inline void vmathT3Inverse( VmathTransform3 *result, const VmathTransform3 *tfrm ); - -/* - * Compute the inverse of a 3x4 transformation matrix, expected to have an orthogonal upper-left 3x3 submatrix - * NOTE: - * This can be used to achieve better performance than a general inverse when the specified 3x4 transformation matrix meets the given restrictions. - */ -static inline void vmathT3OrthoInverse( VmathTransform3 *result, const VmathTransform3 *tfrm ); - -/* - * Conditionally select between two 3x4 transformation matrices - * NOTE: - * This function uses a conditional select instruction to avoid a branch. - */ -static inline void vmathT3Select( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1, unsigned int select1 ); - -#ifdef _VECTORMATH_DEBUG - -/* - * Print a 3x4 transformation matrix - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathT3Print( const VmathTransform3 *tfrm ); - -/* - * Print a 3x4 transformation matrix and an associated string identifier - * NOTE: - * Function is only defined when _VECTORMATH_DEBUG is defined. - */ -static inline void vmathT3Prints( const VmathTransform3 *tfrm, const char *name ); - -#endif - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#include "vec_aos.h" -#include "quat_aos.h" -#include "mat_aos.h" - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_AOS_C_SPU_H +#define _VECTORMATH_AOS_C_SPU_H + +#include +#include +#include + +#ifdef _VECTORMATH_DEBUG +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef _VECTORMATH_AOS_C_TYPES_H +#define _VECTORMATH_AOS_C_TYPES_H + +/* A 3-D vector in array-of-structures format + */ +typedef struct _VmathVector3 +{ + vec_float4 vec128; +} VmathVector3; + +/* A 4-D vector in array-of-structures format + */ +typedef struct _VmathVector4 +{ + vec_float4 vec128; +} VmathVector4; + +/* A 3-D point in array-of-structures format + */ +typedef struct _VmathPoint3 +{ + vec_float4 vec128; +} VmathPoint3; + +/* A quaternion in array-of-structures format + */ +typedef struct _VmathQuat +{ + vec_float4 vec128; +} VmathQuat; + +/* A 3x3 matrix in array-of-structures format + */ +typedef struct _VmathMatrix3 +{ + VmathVector3 col0; + VmathVector3 col1; + VmathVector3 col2; +} VmathMatrix3; + +/* A 4x4 matrix in array-of-structures format + */ +typedef struct _VmathMatrix4 +{ + VmathVector4 col0; + VmathVector4 col1; + VmathVector4 col2; + VmathVector4 col3; +} VmathMatrix4; + +/* A 3x4 transformation matrix in array-of-structures format + */ +typedef struct _VmathTransform3 +{ + VmathVector3 col0; + VmathVector3 col1; + VmathVector3 col2; + VmathVector3 col3; +} VmathTransform3; + +#endif + +/* + * Copy a 3-D vector + */ +static inline void vmathV3Copy( VmathVector3 *result, const VmathVector3 *vec ); + +/* + * Construct a 3-D vector from x, y, and z elements + */ +static inline void vmathV3MakeFromElems( VmathVector3 *result, float x, float y, float z ); + +/* + * Copy elements from a 3-D point into a 3-D vector + */ +static inline void vmathV3MakeFromP3( VmathVector3 *result, const VmathPoint3 *pnt ); + +/* + * Set all elements of a 3-D vector to the same scalar value + */ +static inline void vmathV3MakeFromScalar( VmathVector3 *result, float scalar ); + +/* + * Set vector float data in a 3-D vector + */ +static inline void vmathV3MakeFrom128( VmathVector3 *result, vec_float4 vf4 ); + +/* + * Get vector float data from a 3-D vector + */ +static inline vec_float4 vmathV3Get128( const VmathVector3 *vec ); + +/* + * Set the x element of a 3-D vector + */ +static inline void vmathV3SetX( VmathVector3 *result, float x ); + +/* + * Set the y element of a 3-D vector + */ +static inline void vmathV3SetY( VmathVector3 *result, float y ); + +/* + * Set the z element of a 3-D vector + */ +static inline void vmathV3SetZ( VmathVector3 *result, float z ); + +/* + * Get the x element of a 3-D vector + */ +static inline float vmathV3GetX( const VmathVector3 *vec ); + +/* + * Get the y element of a 3-D vector + */ +static inline float vmathV3GetY( const VmathVector3 *vec ); + +/* + * Get the z element of a 3-D vector + */ +static inline float vmathV3GetZ( const VmathVector3 *vec ); + +/* + * Set an x, y, or z element of a 3-D vector by index + */ +static inline void vmathV3SetElem( VmathVector3 *result, int idx, float value ); + +/* + * Get an x, y, or z element of a 3-D vector by index + */ +static inline float vmathV3GetElem( const VmathVector3 *vec, int idx ); + +/* + * Add two 3-D vectors + */ +static inline void vmathV3Add( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Subtract a 3-D vector from another 3-D vector + */ +static inline void vmathV3Sub( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Add a 3-D vector to a 3-D point + */ +static inline void vmathV3AddP3( VmathPoint3 *result, const VmathVector3 *vec, const VmathPoint3 *pnt ); + +/* + * Multiply a 3-D vector by a scalar + */ +static inline void vmathV3ScalarMul( VmathVector3 *result, const VmathVector3 *vec, float scalar ); + +/* + * Divide a 3-D vector by a scalar + */ +static inline void vmathV3ScalarDiv( VmathVector3 *result, const VmathVector3 *vec, float scalar ); + +/* + * Negate all elements of a 3-D vector + */ +static inline void vmathV3Neg( VmathVector3 *result, const VmathVector3 *vec ); + +/* + * Construct x axis + */ +static inline void vmathV3MakeXAxis( VmathVector3 *result ); + +/* + * Construct y axis + */ +static inline void vmathV3MakeYAxis( VmathVector3 *result ); + +/* + * Construct z axis + */ +static inline void vmathV3MakeZAxis( VmathVector3 *result ); + +/* + * Multiply two 3-D vectors per element + */ +static inline void vmathV3MulPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Divide two 3-D vectors per element + * NOTE: + * Floating-point behavior matches standard library function divf4. + */ +static inline void vmathV3DivPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Compute the reciprocal of a 3-D vector per element + * NOTE: + * Floating-point behavior matches standard library function recipf4. + */ +static inline void vmathV3RecipPerElem( VmathVector3 *result, const VmathVector3 *vec ); + +/* + * Compute the square root of a 3-D vector per element + * NOTE: + * Floating-point behavior matches standard library function sqrtf4. + */ +static inline void vmathV3SqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ); + +/* + * Compute the reciprocal square root of a 3-D vector per element + * NOTE: + * Floating-point behavior matches standard library function rsqrtf4. + */ +static inline void vmathV3RsqrtPerElem( VmathVector3 *result, const VmathVector3 *vec ); + +/* + * Compute the absolute value of a 3-D vector per element + */ +static inline void vmathV3AbsPerElem( VmathVector3 *result, const VmathVector3 *vec ); + +/* + * Copy sign from one 3-D vector to another, per element + */ +static inline void vmathV3CopySignPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Maximum of two 3-D vectors per element + */ +static inline void vmathV3MaxPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Minimum of two 3-D vectors per element + */ +static inline void vmathV3MinPerElem( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Maximum element of a 3-D vector + */ +static inline float vmathV3MaxElem( const VmathVector3 *vec ); + +/* + * Minimum element of a 3-D vector + */ +static inline float vmathV3MinElem( const VmathVector3 *vec ); + +/* + * Compute the sum of all elements of a 3-D vector + */ +static inline float vmathV3Sum( const VmathVector3 *vec ); + +/* + * Compute the dot product of two 3-D vectors + */ +static inline float vmathV3Dot( const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Compute the square of the length of a 3-D vector + */ +static inline float vmathV3LengthSqr( const VmathVector3 *vec ); + +/* + * Compute the length of a 3-D vector + */ +static inline float vmathV3Length( const VmathVector3 *vec ); + +/* + * Normalize a 3-D vector + * NOTE: + * The result is unpredictable when all elements of vec are at or near zero. + */ +static inline void vmathV3Normalize( VmathVector3 *result, const VmathVector3 *vec ); + +/* + * Compute cross product of two 3-D vectors + */ +static inline void vmathV3Cross( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Outer product of two 3-D vectors + */ +static inline void vmathV3Outer( VmathMatrix3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Pre-multiply a row vector by a 3x3 matrix + * NOTE: + * Slower than column post-multiply. + */ +static inline void vmathV3RowMul( VmathVector3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ); + +/* + * Cross-product matrix of a 3-D vector + */ +static inline void vmathV3CrossMatrix( VmathMatrix3 *result, const VmathVector3 *vec ); + +/* + * Create cross-product matrix and multiply + * NOTE: + * Faster than separately creating a cross-product matrix and multiplying. + */ +static inline void vmathV3CrossMatrixMul( VmathMatrix3 *result, const VmathVector3 *vec, const VmathMatrix3 *mat ); + +/* + * Linear interpolation between two 3-D vectors + * NOTE: + * Does not clamp t between 0 and 1. + */ +static inline void vmathV3Lerp( VmathVector3 *result, float t, const VmathVector3 *vec0, const VmathVector3 *vec1 ); + +/* + * Spherical linear interpolation between two 3-D vectors + * NOTE: + * The result is unpredictable if the vectors point in opposite directions. + * Does not clamp t between 0 and 1. + */ +static inline void vmathV3Slerp( VmathVector3 *result, float t, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ); + +/* + * Conditionally select between two 3-D vectors + * NOTE: + * This function uses a conditional select instruction to avoid a branch. + */ +static inline void vmathV3Select( VmathVector3 *result, const VmathVector3 *vec0, const VmathVector3 *vec1, unsigned int select1 ); + +/* + * Store x, y, and z elements of a 3-D vector in the first three words of a quadword. + * The value of the fourth word (the word with the highest address) remains unchanged + */ +static inline void vmathV3StoreXYZ( const VmathVector3 *vec, vec_float4 *quad ); + +/* + * Load four three-float 3-D vectors, stored in three quadwords + */ +static inline void vmathV3LoadXYZArray( VmathVector3 *vec0, VmathVector3 *vec1, VmathVector3 *vec2, VmathVector3 *vec3, const vec_float4 *threeQuads ); + +/* + * Store four 3-D vectors in three quadwords + */ +static inline void vmathV3StoreXYZArray( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, vec_float4 *threeQuads ); + +/* + * Store eight 3-D vectors as half-floats + */ +static inline void vmathV3StoreHalfFloats( const VmathVector3 *vec0, const VmathVector3 *vec1, const VmathVector3 *vec2, const VmathVector3 *vec3, const VmathVector3 *vec4, const VmathVector3 *vec5, const VmathVector3 *vec6, const VmathVector3 *vec7, vec_ushort8 *threeQuads ); + +#ifdef _VECTORMATH_DEBUG + +/* + * Print a 3-D vector + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathV3Print( const VmathVector3 *vec ); + +/* + * Print a 3-D vector and an associated string identifier + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathV3Prints( const VmathVector3 *vec, const char *name ); + +#endif + +/* + * Copy a 4-D vector + */ +static inline void vmathV4Copy( VmathVector4 *result, const VmathVector4 *vec ); + +/* + * Construct a 4-D vector from x, y, z, and w elements + */ +static inline void vmathV4MakeFromElems( VmathVector4 *result, float x, float y, float z, float w ); + +/* + * Construct a 4-D vector from a 3-D vector and a scalar + */ +static inline void vmathV4MakeFromV3Scalar( VmathVector4 *result, const VmathVector3 *xyz, float w ); + +/* + * Copy x, y, and z from a 3-D vector into a 4-D vector, and set w to 0 + */ +static inline void vmathV4MakeFromV3( VmathVector4 *result, const VmathVector3 *vec ); + +/* + * Copy x, y, and z from a 3-D point into a 4-D vector, and set w to 1 + */ +static inline void vmathV4MakeFromP3( VmathVector4 *result, const VmathPoint3 *pnt ); + +/* + * Copy elements from a quaternion into a 4-D vector + */ +static inline void vmathV4MakeFromQ( VmathVector4 *result, const VmathQuat *quat ); + +/* + * Set all elements of a 4-D vector to the same scalar value + */ +static inline void vmathV4MakeFromScalar( VmathVector4 *result, float scalar ); + +/* + * Set vector float data in a 4-D vector + */ +static inline void vmathV4MakeFrom128( VmathVector4 *result, vec_float4 vf4 ); + +/* + * Get vector float data from a 4-D vector + */ +static inline vec_float4 vmathV4Get128( const VmathVector4 *vec ); + +/* + * Set the x, y, and z elements of a 4-D vector + * NOTE: + * This function does not change the w element. + */ +static inline void vmathV4SetXYZ( VmathVector4 *result, const VmathVector3 *vec ); + +/* + * Get the x, y, and z elements of a 4-D vector + */ +static inline void vmathV4GetXYZ( VmathVector3 *result, const VmathVector4 *vec ); + +/* + * Set the x element of a 4-D vector + */ +static inline void vmathV4SetX( VmathVector4 *result, float x ); + +/* + * Set the y element of a 4-D vector + */ +static inline void vmathV4SetY( VmathVector4 *result, float y ); + +/* + * Set the z element of a 4-D vector + */ +static inline void vmathV4SetZ( VmathVector4 *result, float z ); + +/* + * Set the w element of a 4-D vector + */ +static inline void vmathV4SetW( VmathVector4 *result, float w ); + +/* + * Get the x element of a 4-D vector + */ +static inline float vmathV4GetX( const VmathVector4 *vec ); + +/* + * Get the y element of a 4-D vector + */ +static inline float vmathV4GetY( const VmathVector4 *vec ); + +/* + * Get the z element of a 4-D vector + */ +static inline float vmathV4GetZ( const VmathVector4 *vec ); + +/* + * Get the w element of a 4-D vector + */ +static inline float vmathV4GetW( const VmathVector4 *vec ); + +/* + * Set an x, y, z, or w element of a 4-D vector by index + */ +static inline void vmathV4SetElem( VmathVector4 *result, int idx, float value ); + +/* + * Get an x, y, z, or w element of a 4-D vector by index + */ +static inline float vmathV4GetElem( const VmathVector4 *vec, int idx ); + +/* + * Add two 4-D vectors + */ +static inline void vmathV4Add( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Subtract a 4-D vector from another 4-D vector + */ +static inline void vmathV4Sub( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Multiply a 4-D vector by a scalar + */ +static inline void vmathV4ScalarMul( VmathVector4 *result, const VmathVector4 *vec, float scalar ); + +/* + * Divide a 4-D vector by a scalar + */ +static inline void vmathV4ScalarDiv( VmathVector4 *result, const VmathVector4 *vec, float scalar ); + +/* + * Negate all elements of a 4-D vector + */ +static inline void vmathV4Neg( VmathVector4 *result, const VmathVector4 *vec ); + +/* + * Construct x axis + */ +static inline void vmathV4MakeXAxis( VmathVector4 *result ); + +/* + * Construct y axis + */ +static inline void vmathV4MakeYAxis( VmathVector4 *result ); + +/* + * Construct z axis + */ +static inline void vmathV4MakeZAxis( VmathVector4 *result ); + +/* + * Construct w axis + */ +static inline void vmathV4MakeWAxis( VmathVector4 *result ); + +/* + * Multiply two 4-D vectors per element + */ +static inline void vmathV4MulPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Divide two 4-D vectors per element + * NOTE: + * Floating-point behavior matches standard library function divf4. + */ +static inline void vmathV4DivPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Compute the reciprocal of a 4-D vector per element + * NOTE: + * Floating-point behavior matches standard library function recipf4. + */ +static inline void vmathV4RecipPerElem( VmathVector4 *result, const VmathVector4 *vec ); + +/* + * Compute the square root of a 4-D vector per element + * NOTE: + * Floating-point behavior matches standard library function sqrtf4. + */ +static inline void vmathV4SqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ); + +/* + * Compute the reciprocal square root of a 4-D vector per element + * NOTE: + * Floating-point behavior matches standard library function rsqrtf4. + */ +static inline void vmathV4RsqrtPerElem( VmathVector4 *result, const VmathVector4 *vec ); + +/* + * Compute the absolute value of a 4-D vector per element + */ +static inline void vmathV4AbsPerElem( VmathVector4 *result, const VmathVector4 *vec ); + +/* + * Copy sign from one 4-D vector to another, per element + */ +static inline void vmathV4CopySignPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Maximum of two 4-D vectors per element + */ +static inline void vmathV4MaxPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Minimum of two 4-D vectors per element + */ +static inline void vmathV4MinPerElem( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Maximum element of a 4-D vector + */ +static inline float vmathV4MaxElem( const VmathVector4 *vec ); + +/* + * Minimum element of a 4-D vector + */ +static inline float vmathV4MinElem( const VmathVector4 *vec ); + +/* + * Compute the sum of all elements of a 4-D vector + */ +static inline float vmathV4Sum( const VmathVector4 *vec ); + +/* + * Compute the dot product of two 4-D vectors + */ +static inline float vmathV4Dot( const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Compute the square of the length of a 4-D vector + */ +static inline float vmathV4LengthSqr( const VmathVector4 *vec ); + +/* + * Compute the length of a 4-D vector + */ +static inline float vmathV4Length( const VmathVector4 *vec ); + +/* + * Normalize a 4-D vector + * NOTE: + * The result is unpredictable when all elements of vec are at or near zero. + */ +static inline void vmathV4Normalize( VmathVector4 *result, const VmathVector4 *vec ); + +/* + * Outer product of two 4-D vectors + */ +static inline void vmathV4Outer( VmathMatrix4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Linear interpolation between two 4-D vectors + * NOTE: + * Does not clamp t between 0 and 1. + */ +static inline void vmathV4Lerp( VmathVector4 *result, float t, const VmathVector4 *vec0, const VmathVector4 *vec1 ); + +/* + * Spherical linear interpolation between two 4-D vectors + * NOTE: + * The result is unpredictable if the vectors point in opposite directions. + * Does not clamp t between 0 and 1. + */ +static inline void vmathV4Slerp( VmathVector4 *result, float t, const VmathVector4 *unitVec0, const VmathVector4 *unitVec1 ); + +/* + * Conditionally select between two 4-D vectors + * NOTE: + * This function uses a conditional select instruction to avoid a branch. + */ +static inline void vmathV4Select( VmathVector4 *result, const VmathVector4 *vec0, const VmathVector4 *vec1, unsigned int select1 ); + +/* + * Store four 4-D vectors as half-floats + */ +static inline void vmathV4StoreHalfFloats( const VmathVector4 *vec0, const VmathVector4 *vec1, const VmathVector4 *vec2, const VmathVector4 *vec3, vec_ushort8 *twoQuads ); + +#ifdef _VECTORMATH_DEBUG + +/* + * Print a 4-D vector + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathV4Print( const VmathVector4 *vec ); + +/* + * Print a 4-D vector and an associated string identifier + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathV4Prints( const VmathVector4 *vec, const char *name ); + +#endif + +/* + * Copy a 3-D point + */ +static inline void vmathP3Copy( VmathPoint3 *result, const VmathPoint3 *pnt ); + +/* + * Construct a 3-D point from x, y, and z elements + */ +static inline void vmathP3MakeFromElems( VmathPoint3 *result, float x, float y, float z ); + +/* + * Copy elements from a 3-D vector into a 3-D point + */ +static inline void vmathP3MakeFromV3( VmathPoint3 *result, const VmathVector3 *vec ); + +/* + * Set all elements of a 3-D point to the same scalar value + */ +static inline void vmathP3MakeFromScalar( VmathPoint3 *result, float scalar ); + +/* + * Set vector float data in a 3-D point + */ +static inline void vmathP3MakeFrom128( VmathPoint3 *result, vec_float4 vf4 ); + +/* + * Get vector float data from a 3-D point + */ +static inline vec_float4 vmathP3Get128( const VmathPoint3 *pnt ); + +/* + * Set the x element of a 3-D point + */ +static inline void vmathP3SetX( VmathPoint3 *result, float x ); + +/* + * Set the y element of a 3-D point + */ +static inline void vmathP3SetY( VmathPoint3 *result, float y ); + +/* + * Set the z element of a 3-D point + */ +static inline void vmathP3SetZ( VmathPoint3 *result, float z ); + +/* + * Get the x element of a 3-D point + */ +static inline float vmathP3GetX( const VmathPoint3 *pnt ); + +/* + * Get the y element of a 3-D point + */ +static inline float vmathP3GetY( const VmathPoint3 *pnt ); + +/* + * Get the z element of a 3-D point + */ +static inline float vmathP3GetZ( const VmathPoint3 *pnt ); + +/* + * Set an x, y, or z element of a 3-D point by index + */ +static inline void vmathP3SetElem( VmathPoint3 *result, int idx, float value ); + +/* + * Get an x, y, or z element of a 3-D point by index + */ +static inline float vmathP3GetElem( const VmathPoint3 *pnt, int idx ); + +/* + * Subtract a 3-D point from another 3-D point + */ +static inline void vmathP3Sub( VmathVector3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Add a 3-D point to a 3-D vector + */ +static inline void vmathP3AddV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec ); + +/* + * Subtract a 3-D vector from a 3-D point + */ +static inline void vmathP3SubV3( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *vec ); + +/* + * Multiply two 3-D points per element + */ +static inline void vmathP3MulPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Divide two 3-D points per element + * NOTE: + * Floating-point behavior matches standard library function divf4. + */ +static inline void vmathP3DivPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Compute the reciprocal of a 3-D point per element + * NOTE: + * Floating-point behavior matches standard library function recipf4. + */ +static inline void vmathP3RecipPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); + +/* + * Compute the square root of a 3-D point per element + * NOTE: + * Floating-point behavior matches standard library function sqrtf4. + */ +static inline void vmathP3SqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); + +/* + * Compute the reciprocal square root of a 3-D point per element + * NOTE: + * Floating-point behavior matches standard library function rsqrtf4. + */ +static inline void vmathP3RsqrtPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); + +/* + * Compute the absolute value of a 3-D point per element + */ +static inline void vmathP3AbsPerElem( VmathPoint3 *result, const VmathPoint3 *pnt ); + +/* + * Copy sign from one 3-D point to another, per element + */ +static inline void vmathP3CopySignPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Maximum of two 3-D points per element + */ +static inline void vmathP3MaxPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Minimum of two 3-D points per element + */ +static inline void vmathP3MinPerElem( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Maximum element of a 3-D point + */ +static inline float vmathP3MaxElem( const VmathPoint3 *pnt ); + +/* + * Minimum element of a 3-D point + */ +static inline float vmathP3MinElem( const VmathPoint3 *pnt ); + +/* + * Compute the sum of all elements of a 3-D point + */ +static inline float vmathP3Sum( const VmathPoint3 *pnt ); + +/* + * Apply uniform scale to a 3-D point + */ +static inline void vmathP3Scale( VmathPoint3 *result, const VmathPoint3 *pnt, float scaleVal ); + +/* + * Apply non-uniform scale to a 3-D point + */ +static inline void vmathP3NonUniformScale( VmathPoint3 *result, const VmathPoint3 *pnt, const VmathVector3 *scaleVec ); + +/* + * Scalar projection of a 3-D point on a unit-length 3-D vector + */ +static inline float vmathP3Projection( const VmathPoint3 *pnt, const VmathVector3 *unitVec ); + +/* + * Compute the square of the distance of a 3-D point from the coordinate-system origin + */ +static inline float vmathP3DistSqrFromOrigin( const VmathPoint3 *pnt ); + +/* + * Compute the distance of a 3-D point from the coordinate-system origin + */ +static inline float vmathP3DistFromOrigin( const VmathPoint3 *pnt ); + +/* + * Compute the square of the distance between two 3-D points + */ +static inline float vmathP3DistSqr( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Compute the distance between two 3-D points + */ +static inline float vmathP3Dist( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Linear interpolation between two 3-D points + * NOTE: + * Does not clamp t between 0 and 1. + */ +static inline void vmathP3Lerp( VmathPoint3 *result, float t, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1 ); + +/* + * Conditionally select between two 3-D points + * NOTE: + * This function uses a conditional select instruction to avoid a branch. + */ +static inline void vmathP3Select( VmathPoint3 *result, const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, unsigned int select1 ); + +/* + * Store x, y, and z elements of a 3-D point in the first three words of a quadword. + * The value of the fourth word (the word with the highest address) remains unchanged + */ +static inline void vmathP3StoreXYZ( const VmathPoint3 *pnt, vec_float4 *quad ); + +/* + * Load four three-float 3-D points, stored in three quadwords + */ +static inline void vmathP3LoadXYZArray( VmathPoint3 *pnt0, VmathPoint3 *pnt1, VmathPoint3 *pnt2, VmathPoint3 *pnt3, const vec_float4 *threeQuads ); + +/* + * Store four 3-D points in three quadwords + */ +static inline void vmathP3StoreXYZArray( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, vec_float4 *threeQuads ); + +/* + * Store eight 3-D points as half-floats + */ +static inline void vmathP3StoreHalfFloats( const VmathPoint3 *pnt0, const VmathPoint3 *pnt1, const VmathPoint3 *pnt2, const VmathPoint3 *pnt3, const VmathPoint3 *pnt4, const VmathPoint3 *pnt5, const VmathPoint3 *pnt6, const VmathPoint3 *pnt7, vec_ushort8 *threeQuads ); + +#ifdef _VECTORMATH_DEBUG + +/* + * Print a 3-D point + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathP3Print( const VmathPoint3 *pnt ); + +/* + * Print a 3-D point and an associated string identifier + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathP3Prints( const VmathPoint3 *pnt, const char *name ); + +#endif + +/* + * Copy a quaternion + */ +static inline void vmathQCopy( VmathQuat *result, const VmathQuat *quat ); + +/* + * Construct a quaternion from x, y, z, and w elements + */ +static inline void vmathQMakeFromElems( VmathQuat *result, float x, float y, float z, float w ); + +/* + * Construct a quaternion from a 3-D vector and a scalar + */ +static inline void vmathQMakeFromV3Scalar( VmathQuat *result, const VmathVector3 *xyz, float w ); + +/* + * Copy elements from a 4-D vector into a quaternion + */ +static inline void vmathQMakeFromV4( VmathQuat *result, const VmathVector4 *vec ); + +/* + * Convert a rotation matrix to a unit-length quaternion + */ +static inline void vmathQMakeFromM3( VmathQuat *result, const VmathMatrix3 *rotMat ); + +/* + * Set all elements of a quaternion to the same scalar value + */ +static inline void vmathQMakeFromScalar( VmathQuat *result, float scalar ); + +/* + * Set vector float data in a quaternion + */ +static inline void vmathQMakeFrom128( VmathQuat *result, vec_float4 vf4 ); + +/* + * Get vector float data from a quaternion + */ +static inline vec_float4 vmathQGet128( const VmathQuat *quat ); + +/* + * Set the x, y, and z elements of a quaternion + * NOTE: + * This function does not change the w element. + */ +static inline void vmathQSetXYZ( VmathQuat *result, const VmathVector3 *vec ); + +/* + * Get the x, y, and z elements of a quaternion + */ +static inline void vmathQGetXYZ( VmathVector3 *result, const VmathQuat *quat ); + +/* + * Set the x element of a quaternion + */ +static inline void vmathQSetX( VmathQuat *result, float x ); + +/* + * Set the y element of a quaternion + */ +static inline void vmathQSetY( VmathQuat *result, float y ); + +/* + * Set the z element of a quaternion + */ +static inline void vmathQSetZ( VmathQuat *result, float z ); + +/* + * Set the w element of a quaternion + */ +static inline void vmathQSetW( VmathQuat *result, float w ); + +/* + * Get the x element of a quaternion + */ +static inline float vmathQGetX( const VmathQuat *quat ); + +/* + * Get the y element of a quaternion + */ +static inline float vmathQGetY( const VmathQuat *quat ); + +/* + * Get the z element of a quaternion + */ +static inline float vmathQGetZ( const VmathQuat *quat ); + +/* + * Get the w element of a quaternion + */ +static inline float vmathQGetW( const VmathQuat *quat ); + +/* + * Set an x, y, z, or w element of a quaternion by index + */ +static inline void vmathQSetElem( VmathQuat *result, int idx, float value ); + +/* + * Get an x, y, z, or w element of a quaternion by index + */ +static inline float vmathQGetElem( const VmathQuat *quat, int idx ); + +/* + * Add two quaternions + */ +static inline void vmathQAdd( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ); + +/* + * Subtract a quaternion from another quaternion + */ +static inline void vmathQSub( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ); + +/* + * Multiply two quaternions + */ +static inline void vmathQMul( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1 ); + +/* + * Multiply a quaternion by a scalar + */ +static inline void vmathQScalarMul( VmathQuat *result, const VmathQuat *quat, float scalar ); + +/* + * Divide a quaternion by a scalar + */ +static inline void vmathQScalarDiv( VmathQuat *result, const VmathQuat *quat, float scalar ); + +/* + * Negate all elements of a quaternion + */ +static inline void vmathQNeg( VmathQuat *result, const VmathQuat *quat ); + +/* + * Construct an identity quaternion + */ +static inline void vmathQMakeIdentity( VmathQuat *result ); + +/* + * Construct a quaternion to rotate between two unit-length 3-D vectors + * NOTE: + * The result is unpredictable if unitVec0 and unitVec1 point in opposite directions. + */ +static inline void vmathQMakeRotationArc( VmathQuat *result, const VmathVector3 *unitVec0, const VmathVector3 *unitVec1 ); + +/* + * Construct a quaternion to rotate around a unit-length 3-D vector + */ +static inline void vmathQMakeRotationAxis( VmathQuat *result, float radians, const VmathVector3 *unitVec ); + +/* + * Construct a quaternion to rotate around the x axis + */ +static inline void vmathQMakeRotationX( VmathQuat *result, float radians ); + +/* + * Construct a quaternion to rotate around the y axis + */ +static inline void vmathQMakeRotationY( VmathQuat *result, float radians ); + +/* + * Construct a quaternion to rotate around the z axis + */ +static inline void vmathQMakeRotationZ( VmathQuat *result, float radians ); + +/* + * Compute the conjugate of a quaternion + */ +static inline void vmathQConj( VmathQuat *result, const VmathQuat *quat ); + +/* + * Use a unit-length quaternion to rotate a 3-D vector + */ +static inline void vmathQRotate( VmathVector3 *result, const VmathQuat *unitQuat, const VmathVector3 *vec ); + +/* + * Compute the dot product of two quaternions + */ +static inline float vmathQDot( const VmathQuat *quat0, const VmathQuat *quat1 ); + +/* + * Compute the norm of a quaternion + */ +static inline float vmathQNorm( const VmathQuat *quat ); + +/* + * Compute the length of a quaternion + */ +static inline float vmathQLength( const VmathQuat *quat ); + +/* + * Normalize a quaternion + * NOTE: + * The result is unpredictable when all elements of quat are at or near zero. + */ +static inline void vmathQNormalize( VmathQuat *result, const VmathQuat *quat ); + +/* + * Linear interpolation between two quaternions + * NOTE: + * Does not clamp t between 0 and 1. + */ +static inline void vmathQLerp( VmathQuat *result, float t, const VmathQuat *quat0, const VmathQuat *quat1 ); + +/* + * Spherical linear interpolation between two quaternions + * NOTE: + * Interpolates along the shortest path between orientations. + * Does not clamp t between 0 and 1. + */ +static inline void vmathQSlerp( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1 ); + +/* + * Spherical quadrangle interpolation + */ +static inline void vmathQSquad( VmathQuat *result, float t, const VmathQuat *unitQuat0, const VmathQuat *unitQuat1, const VmathQuat *unitQuat2, const VmathQuat *unitQuat3 ); + +/* + * Conditionally select between two quaternions + * NOTE: + * This function uses a conditional select instruction to avoid a branch. + */ +static inline void vmathQSelect( VmathQuat *result, const VmathQuat *quat0, const VmathQuat *quat1, unsigned int select1 ); + +#ifdef _VECTORMATH_DEBUG + +/* + * Print a quaternion + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathQPrint( const VmathQuat *quat ); + +/* + * Print a quaternion and an associated string identifier + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathQPrints( const VmathQuat *quat, const char *name ); + +#endif + +/* + * Copy a 3x3 matrix + */ +static inline void vmathM3Copy( VmathMatrix3 *result, const VmathMatrix3 *mat ); + +/* + * Construct a 3x3 matrix containing the specified columns + */ +static inline void vmathM3MakeFromCols( VmathMatrix3 *result, const VmathVector3 *col0, const VmathVector3 *col1, const VmathVector3 *col2 ); + +/* + * Construct a 3x3 rotation matrix from a unit-length quaternion + */ +static inline void vmathM3MakeFromQ( VmathMatrix3 *result, const VmathQuat *unitQuat ); + +/* + * Set all elements of a 3x3 matrix to the same scalar value + */ +static inline void vmathM3MakeFromScalar( VmathMatrix3 *result, float scalar ); + +/* + * Set column 0 of a 3x3 matrix + */ +static inline void vmathM3SetCol0( VmathMatrix3 *result, const VmathVector3 *col0 ); + +/* + * Set column 1 of a 3x3 matrix + */ +static inline void vmathM3SetCol1( VmathMatrix3 *result, const VmathVector3 *col1 ); + +/* + * Set column 2 of a 3x3 matrix + */ +static inline void vmathM3SetCol2( VmathMatrix3 *result, const VmathVector3 *col2 ); + +/* + * Get column 0 of a 3x3 matrix + */ +static inline void vmathM3GetCol0( VmathVector3 *result, const VmathMatrix3 *mat ); + +/* + * Get column 1 of a 3x3 matrix + */ +static inline void vmathM3GetCol1( VmathVector3 *result, const VmathMatrix3 *mat ); + +/* + * Get column 2 of a 3x3 matrix + */ +static inline void vmathM3GetCol2( VmathVector3 *result, const VmathMatrix3 *mat ); + +/* + * Set the column of a 3x3 matrix referred to by the specified index + */ +static inline void vmathM3SetCol( VmathMatrix3 *result, int col, const VmathVector3 *vec ); + +/* + * Set the row of a 3x3 matrix referred to by the specified index + */ +static inline void vmathM3SetRow( VmathMatrix3 *result, int row, const VmathVector3 *vec ); + +/* + * Get the column of a 3x3 matrix referred to by the specified index + */ +static inline void vmathM3GetCol( VmathVector3 *result, const VmathMatrix3 *mat, int col ); + +/* + * Get the row of a 3x3 matrix referred to by the specified index + */ +static inline void vmathM3GetRow( VmathVector3 *result, const VmathMatrix3 *mat, int row ); + +/* + * Set the element of a 3x3 matrix referred to by column and row indices + */ +static inline void vmathM3SetElem( VmathMatrix3 *result, int col, int row, float val ); + +/* + * Get the element of a 3x3 matrix referred to by column and row indices + */ +static inline float vmathM3GetElem( const VmathMatrix3 *mat, int col, int row ); + +/* + * Add two 3x3 matrices + */ +static inline void vmathM3Add( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); + +/* + * Subtract a 3x3 matrix from another 3x3 matrix + */ +static inline void vmathM3Sub( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); + +/* + * Negate all elements of a 3x3 matrix + */ +static inline void vmathM3Neg( VmathMatrix3 *result, const VmathMatrix3 *mat ); + +/* + * Multiply a 3x3 matrix by a scalar + */ +static inline void vmathM3ScalarMul( VmathMatrix3 *result, const VmathMatrix3 *mat, float scalar ); + +/* + * Multiply a 3x3 matrix by a 3-D vector + */ +static inline void vmathM3MulV3( VmathVector3 *result, const VmathMatrix3 *mat, const VmathVector3 *vec ); + +/* + * Multiply two 3x3 matrices + */ +static inline void vmathM3Mul( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); + +/* + * Construct an identity 3x3 matrix + */ +static inline void vmathM3MakeIdentity( VmathMatrix3 *result ); + +/* + * Construct a 3x3 matrix to rotate around the x axis + */ +static inline void vmathM3MakeRotationX( VmathMatrix3 *result, float radians ); + +/* + * Construct a 3x3 matrix to rotate around the y axis + */ +static inline void vmathM3MakeRotationY( VmathMatrix3 *result, float radians ); + +/* + * Construct a 3x3 matrix to rotate around the z axis + */ +static inline void vmathM3MakeRotationZ( VmathMatrix3 *result, float radians ); + +/* + * Construct a 3x3 matrix to rotate around the x, y, and z axes + */ +static inline void vmathM3MakeRotationZYX( VmathMatrix3 *result, const VmathVector3 *radiansXYZ ); + +/* + * Construct a 3x3 matrix to rotate around a unit-length 3-D vector + */ +static inline void vmathM3MakeRotationAxis( VmathMatrix3 *result, float radians, const VmathVector3 *unitVec ); + +/* + * Construct a rotation matrix from a unit-length quaternion + */ +static inline void vmathM3MakeRotationQ( VmathMatrix3 *result, const VmathQuat *unitQuat ); + +/* + * Construct a 3x3 matrix to perform scaling + */ +static inline void vmathM3MakeScale( VmathMatrix3 *result, const VmathVector3 *scaleVec ); + +/* + * Append (post-multiply) a scale transformation to a 3x3 matrix + * NOTE: + * Faster than creating and multiplying a scale transformation matrix. + */ +static inline void vmathM3AppendScale( VmathMatrix3 *result, const VmathMatrix3 *mat, const VmathVector3 *scaleVec ); + +/* + * Prepend (pre-multiply) a scale transformation to a 3x3 matrix + * NOTE: + * Faster than creating and multiplying a scale transformation matrix. + */ +static inline void vmathM3PrependScale( VmathMatrix3 *result, const VmathVector3 *scaleVec, const VmathMatrix3 *mat ); + +/* + * Multiply two 3x3 matrices per element + */ +static inline void vmathM3MulPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1 ); + +/* + * Compute the absolute value of a 3x3 matrix per element + */ +static inline void vmathM3AbsPerElem( VmathMatrix3 *result, const VmathMatrix3 *mat ); + +/* + * Transpose of a 3x3 matrix + */ +static inline void vmathM3Transpose( VmathMatrix3 *result, const VmathMatrix3 *mat ); + +/* + * Compute the inverse of a 3x3 matrix + * NOTE: + * Result is unpredictable when the determinant of mat is equal to or near 0. + */ +static inline void vmathM3Inverse( VmathMatrix3 *result, const VmathMatrix3 *mat ); + +/* + * Determinant of a 3x3 matrix + */ +static inline float vmathM3Determinant( const VmathMatrix3 *mat ); + +/* + * Conditionally select between two 3x3 matrices + * NOTE: + * This function uses a conditional select instruction to avoid a branch. + */ +static inline void vmathM3Select( VmathMatrix3 *result, const VmathMatrix3 *mat0, const VmathMatrix3 *mat1, unsigned int select1 ); + +#ifdef _VECTORMATH_DEBUG + +/* + * Print a 3x3 matrix + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathM3Print( const VmathMatrix3 *mat ); + +/* + * Print a 3x3 matrix and an associated string identifier + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathM3Prints( const VmathMatrix3 *mat, const char *name ); + +#endif + +/* + * Copy a 4x4 matrix + */ +static inline void vmathM4Copy( VmathMatrix4 *result, const VmathMatrix4 *mat ); + +/* + * Construct a 4x4 matrix containing the specified columns + */ +static inline void vmathM4MakeFromCols( VmathMatrix4 *result, const VmathVector4 *col0, const VmathVector4 *col1, const VmathVector4 *col2, const VmathVector4 *col3 ); + +/* + * Construct a 4x4 matrix from a 3x4 transformation matrix + */ +static inline void vmathM4MakeFromT3( VmathMatrix4 *result, const VmathTransform3 *mat ); + +/* + * Construct a 4x4 matrix from a 3x3 matrix and a 3-D vector + */ +static inline void vmathM4MakeFromM3V3( VmathMatrix4 *result, const VmathMatrix3 *mat, const VmathVector3 *translateVec ); + +/* + * Construct a 4x4 matrix from a unit-length quaternion and a 3-D vector + */ +static inline void vmathM4MakeFromQV3( VmathMatrix4 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ); + +/* + * Set all elements of a 4x4 matrix to the same scalar value + */ +static inline void vmathM4MakeFromScalar( VmathMatrix4 *result, float scalar ); + +/* + * Set the upper-left 3x3 submatrix + * NOTE: + * This function does not change the bottom row elements. + */ +static inline void vmathM4SetUpper3x3( VmathMatrix4 *result, const VmathMatrix3 *mat3 ); + +/* + * Get the upper-left 3x3 submatrix of a 4x4 matrix + */ +static inline void vmathM4GetUpper3x3( VmathMatrix3 *result, const VmathMatrix4 *mat ); + +/* + * Set translation component + * NOTE: + * This function does not change the bottom row elements. + */ +static inline void vmathM4SetTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ); + +/* + * Get the translation component of a 4x4 matrix + */ +static inline void vmathM4GetTranslation( VmathVector3 *result, const VmathMatrix4 *mat ); + +/* + * Set column 0 of a 4x4 matrix + */ +static inline void vmathM4SetCol0( VmathMatrix4 *result, const VmathVector4 *col0 ); + +/* + * Set column 1 of a 4x4 matrix + */ +static inline void vmathM4SetCol1( VmathMatrix4 *result, const VmathVector4 *col1 ); + +/* + * Set column 2 of a 4x4 matrix + */ +static inline void vmathM4SetCol2( VmathMatrix4 *result, const VmathVector4 *col2 ); + +/* + * Set column 3 of a 4x4 matrix + */ +static inline void vmathM4SetCol3( VmathMatrix4 *result, const VmathVector4 *col3 ); + +/* + * Get column 0 of a 4x4 matrix + */ +static inline void vmathM4GetCol0( VmathVector4 *result, const VmathMatrix4 *mat ); + +/* + * Get column 1 of a 4x4 matrix + */ +static inline void vmathM4GetCol1( VmathVector4 *result, const VmathMatrix4 *mat ); + +/* + * Get column 2 of a 4x4 matrix + */ +static inline void vmathM4GetCol2( VmathVector4 *result, const VmathMatrix4 *mat ); + +/* + * Get column 3 of a 4x4 matrix + */ +static inline void vmathM4GetCol3( VmathVector4 *result, const VmathMatrix4 *mat ); + +/* + * Set the column of a 4x4 matrix referred to by the specified index + */ +static inline void vmathM4SetCol( VmathMatrix4 *result, int col, const VmathVector4 *vec ); + +/* + * Set the row of a 4x4 matrix referred to by the specified index + */ +static inline void vmathM4SetRow( VmathMatrix4 *result, int row, const VmathVector4 *vec ); + +/* + * Get the column of a 4x4 matrix referred to by the specified index + */ +static inline void vmathM4GetCol( VmathVector4 *result, const VmathMatrix4 *mat, int col ); + +/* + * Get the row of a 4x4 matrix referred to by the specified index + */ +static inline void vmathM4GetRow( VmathVector4 *result, const VmathMatrix4 *mat, int row ); + +/* + * Set the element of a 4x4 matrix referred to by column and row indices + */ +static inline void vmathM4SetElem( VmathMatrix4 *result, int col, int row, float val ); + +/* + * Get the element of a 4x4 matrix referred to by column and row indices + */ +static inline float vmathM4GetElem( const VmathMatrix4 *mat, int col, int row ); + +/* + * Add two 4x4 matrices + */ +static inline void vmathM4Add( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); + +/* + * Subtract a 4x4 matrix from another 4x4 matrix + */ +static inline void vmathM4Sub( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); + +/* + * Negate all elements of a 4x4 matrix + */ +static inline void vmathM4Neg( VmathMatrix4 *result, const VmathMatrix4 *mat ); + +/* + * Multiply a 4x4 matrix by a scalar + */ +static inline void vmathM4ScalarMul( VmathMatrix4 *result, const VmathMatrix4 *mat, float scalar ); + +/* + * Multiply a 4x4 matrix by a 4-D vector + */ +static inline void vmathM4MulV4( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector4 *vec ); + +/* + * Multiply a 4x4 matrix by a 3-D vector + */ +static inline void vmathM4MulV3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathVector3 *vec ); + +/* + * Multiply a 4x4 matrix by a 3-D point + */ +static inline void vmathM4MulP3( VmathVector4 *result, const VmathMatrix4 *mat, const VmathPoint3 *pnt ); + +/* + * Multiply two 4x4 matrices + */ +static inline void vmathM4Mul( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); + +/* + * Multiply a 4x4 matrix by a 3x4 transformation matrix + */ +static inline void vmathM4MulT3( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathTransform3 *tfrm ); + +/* + * Construct an identity 4x4 matrix + */ +static inline void vmathM4MakeIdentity( VmathMatrix4 *result ); + +/* + * Construct a 4x4 matrix to rotate around the x axis + */ +static inline void vmathM4MakeRotationX( VmathMatrix4 *result, float radians ); + +/* + * Construct a 4x4 matrix to rotate around the y axis + */ +static inline void vmathM4MakeRotationY( VmathMatrix4 *result, float radians ); + +/* + * Construct a 4x4 matrix to rotate around the z axis + */ +static inline void vmathM4MakeRotationZ( VmathMatrix4 *result, float radians ); + +/* + * Construct a 4x4 matrix to rotate around the x, y, and z axes + */ +static inline void vmathM4MakeRotationZYX( VmathMatrix4 *result, const VmathVector3 *radiansXYZ ); + +/* + * Construct a 4x4 matrix to rotate around a unit-length 3-D vector + */ +static inline void vmathM4MakeRotationAxis( VmathMatrix4 *result, float radians, const VmathVector3 *unitVec ); + +/* + * Construct a rotation matrix from a unit-length quaternion + */ +static inline void vmathM4MakeRotationQ( VmathMatrix4 *result, const VmathQuat *unitQuat ); + +/* + * Construct a 4x4 matrix to perform scaling + */ +static inline void vmathM4MakeScale( VmathMatrix4 *result, const VmathVector3 *scaleVec ); + +/* + * Construct a 4x4 matrix to perform translation + */ +static inline void vmathM4MakeTranslation( VmathMatrix4 *result, const VmathVector3 *translateVec ); + +/* + * Construct viewing matrix based on eye position, position looked at, and up direction + */ +static inline void vmathM4MakeLookAt( VmathMatrix4 *result, const VmathPoint3 *eyePos, const VmathPoint3 *lookAtPos, const VmathVector3 *upVec ); + +/* + * Construct a perspective projection matrix + */ +static inline void vmathM4MakePerspective( VmathMatrix4 *result, float fovyRadians, float aspect, float zNear, float zFar ); + +/* + * Construct a perspective projection matrix based on frustum + */ +static inline void vmathM4MakeFrustum( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ); + +/* + * Construct an orthographic projection matrix + */ +static inline void vmathM4MakeOrthographic( VmathMatrix4 *result, float left, float right, float bottom, float top, float zNear, float zFar ); + +/* + * Append (post-multiply) a scale transformation to a 4x4 matrix + * NOTE: + * Faster than creating and multiplying a scale transformation matrix. + */ +static inline void vmathM4AppendScale( VmathMatrix4 *result, const VmathMatrix4 *mat, const VmathVector3 *scaleVec ); + +/* + * Prepend (pre-multiply) a scale transformation to a 4x4 matrix + * NOTE: + * Faster than creating and multiplying a scale transformation matrix. + */ +static inline void vmathM4PrependScale( VmathMatrix4 *result, const VmathVector3 *scaleVec, const VmathMatrix4 *mat ); + +/* + * Multiply two 4x4 matrices per element + */ +static inline void vmathM4MulPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1 ); + +/* + * Compute the absolute value of a 4x4 matrix per element + */ +static inline void vmathM4AbsPerElem( VmathMatrix4 *result, const VmathMatrix4 *mat ); + +/* + * Transpose of a 4x4 matrix + */ +static inline void vmathM4Transpose( VmathMatrix4 *result, const VmathMatrix4 *mat ); + +/* + * Compute the inverse of a 4x4 matrix + * NOTE: + * Result is unpredictable when the determinant of mat is equal to or near 0. + */ +static inline void vmathM4Inverse( VmathMatrix4 *result, const VmathMatrix4 *mat ); + +/* + * Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix + * NOTE: + * This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. The result is unpredictable when the determinant of mat is equal to or near 0. + */ +static inline void vmathM4AffineInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ); + +/* + * Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix with an orthogonal upper-left 3x3 submatrix + * NOTE: + * This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. + */ +static inline void vmathM4OrthoInverse( VmathMatrix4 *result, const VmathMatrix4 *mat ); + +/* + * Determinant of a 4x4 matrix + */ +static inline float vmathM4Determinant( const VmathMatrix4 *mat ); + +/* + * Conditionally select between two 4x4 matrices + * NOTE: + * This function uses a conditional select instruction to avoid a branch. + */ +static inline void vmathM4Select( VmathMatrix4 *result, const VmathMatrix4 *mat0, const VmathMatrix4 *mat1, unsigned int select1 ); + +#ifdef _VECTORMATH_DEBUG + +/* + * Print a 4x4 matrix + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathM4Print( const VmathMatrix4 *mat ); + +/* + * Print a 4x4 matrix and an associated string identifier + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathM4Prints( const VmathMatrix4 *mat, const char *name ); + +#endif + +/* + * Copy a 3x4 transformation matrix + */ +static inline void vmathT3Copy( VmathTransform3 *result, const VmathTransform3 *tfrm ); + +/* + * Construct a 3x4 transformation matrix containing the specified columns + */ +static inline void vmathT3MakeFromCols( VmathTransform3 *result, const VmathVector3 *col0, const VmathVector3 *col1, const VmathVector3 *col2, const VmathVector3 *col3 ); + +/* + * Construct a 3x4 transformation matrix from a 3x3 matrix and a 3-D vector + */ +static inline void vmathT3MakeFromM3V3( VmathTransform3 *result, const VmathMatrix3 *tfrm, const VmathVector3 *translateVec ); + +/* + * Construct a 3x4 transformation matrix from a unit-length quaternion and a 3-D vector + */ +static inline void vmathT3MakeFromQV3( VmathTransform3 *result, const VmathQuat *unitQuat, const VmathVector3 *translateVec ); + +/* + * Set all elements of a 3x4 transformation matrix to the same scalar value + */ +static inline void vmathT3MakeFromScalar( VmathTransform3 *result, float scalar ); + +/* + * Set the upper-left 3x3 submatrix + */ +static inline void vmathT3SetUpper3x3( VmathTransform3 *result, const VmathMatrix3 *mat3 ); + +/* + * Get the upper-left 3x3 submatrix of a 3x4 transformation matrix + */ +static inline void vmathT3GetUpper3x3( VmathMatrix3 *result, const VmathTransform3 *tfrm ); + +/* + * Set translation component + */ +static inline void vmathT3SetTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ); + +/* + * Get the translation component of a 3x4 transformation matrix + */ +static inline void vmathT3GetTranslation( VmathVector3 *result, const VmathTransform3 *tfrm ); + +/* + * Set column 0 of a 3x4 transformation matrix + */ +static inline void vmathT3SetCol0( VmathTransform3 *result, const VmathVector3 *col0 ); + +/* + * Set column 1 of a 3x4 transformation matrix + */ +static inline void vmathT3SetCol1( VmathTransform3 *result, const VmathVector3 *col1 ); + +/* + * Set column 2 of a 3x4 transformation matrix + */ +static inline void vmathT3SetCol2( VmathTransform3 *result, const VmathVector3 *col2 ); + +/* + * Set column 3 of a 3x4 transformation matrix + */ +static inline void vmathT3SetCol3( VmathTransform3 *result, const VmathVector3 *col3 ); + +/* + * Get column 0 of a 3x4 transformation matrix + */ +static inline void vmathT3GetCol0( VmathVector3 *result, const VmathTransform3 *tfrm ); + +/* + * Get column 1 of a 3x4 transformation matrix + */ +static inline void vmathT3GetCol1( VmathVector3 *result, const VmathTransform3 *tfrm ); + +/* + * Get column 2 of a 3x4 transformation matrix + */ +static inline void vmathT3GetCol2( VmathVector3 *result, const VmathTransform3 *tfrm ); + +/* + * Get column 3 of a 3x4 transformation matrix + */ +static inline void vmathT3GetCol3( VmathVector3 *result, const VmathTransform3 *tfrm ); + +/* + * Set the column of a 3x4 transformation matrix referred to by the specified index + */ +static inline void vmathT3SetCol( VmathTransform3 *result, int col, const VmathVector3 *vec ); + +/* + * Set the row of a 3x4 transformation matrix referred to by the specified index + */ +static inline void vmathT3SetRow( VmathTransform3 *result, int row, const VmathVector4 *vec ); + +/* + * Get the column of a 3x4 transformation matrix referred to by the specified index + */ +static inline void vmathT3GetCol( VmathVector3 *result, const VmathTransform3 *tfrm, int col ); + +/* + * Get the row of a 3x4 transformation matrix referred to by the specified index + */ +static inline void vmathT3GetRow( VmathVector4 *result, const VmathTransform3 *tfrm, int row ); + +/* + * Set the element of a 3x4 transformation matrix referred to by column and row indices + */ +static inline void vmathT3SetElem( VmathTransform3 *result, int col, int row, float val ); + +/* + * Get the element of a 3x4 transformation matrix referred to by column and row indices + */ +static inline float vmathT3GetElem( const VmathTransform3 *tfrm, int col, int row ); + +/* + * Multiply a 3x4 transformation matrix by a 3-D vector + */ +static inline void vmathT3MulV3( VmathVector3 *result, const VmathTransform3 *tfrm, const VmathVector3 *vec ); + +/* + * Multiply a 3x4 transformation matrix by a 3-D point + */ +static inline void vmathT3MulP3( VmathPoint3 *result, const VmathTransform3 *tfrm, const VmathPoint3 *pnt ); + +/* + * Multiply two 3x4 transformation matrices + */ +static inline void vmathT3Mul( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ); + +/* + * Construct an identity 3x4 transformation matrix + */ +static inline void vmathT3MakeIdentity( VmathTransform3 *result ); + +/* + * Construct a 3x4 transformation matrix to rotate around the x axis + */ +static inline void vmathT3MakeRotationX( VmathTransform3 *result, float radians ); + +/* + * Construct a 3x4 transformation matrix to rotate around the y axis + */ +static inline void vmathT3MakeRotationY( VmathTransform3 *result, float radians ); + +/* + * Construct a 3x4 transformation matrix to rotate around the z axis + */ +static inline void vmathT3MakeRotationZ( VmathTransform3 *result, float radians ); + +/* + * Construct a 3x4 transformation matrix to rotate around the x, y, and z axes + */ +static inline void vmathT3MakeRotationZYX( VmathTransform3 *result, const VmathVector3 *radiansXYZ ); + +/* + * Construct a 3x4 transformation matrix to rotate around a unit-length 3-D vector + */ +static inline void vmathT3MakeRotationAxis( VmathTransform3 *result, float radians, const VmathVector3 *unitVec ); + +/* + * Construct a rotation matrix from a unit-length quaternion + */ +static inline void vmathT3MakeRotationQ( VmathTransform3 *result, const VmathQuat *unitQuat ); + +/* + * Construct a 3x4 transformation matrix to perform scaling + */ +static inline void vmathT3MakeScale( VmathTransform3 *result, const VmathVector3 *scaleVec ); + +/* + * Construct a 3x4 transformation matrix to perform translation + */ +static inline void vmathT3MakeTranslation( VmathTransform3 *result, const VmathVector3 *translateVec ); + +/* + * Append (post-multiply) a scale transformation to a 3x4 transformation matrix + * NOTE: + * Faster than creating and multiplying a scale transformation matrix. + */ +static inline void vmathT3AppendScale( VmathTransform3 *result, const VmathTransform3 *tfrm, const VmathVector3 *scaleVec ); + +/* + * Prepend (pre-multiply) a scale transformation to a 3x4 transformation matrix + * NOTE: + * Faster than creating and multiplying a scale transformation matrix. + */ +static inline void vmathT3PrependScale( VmathTransform3 *result, const VmathVector3 *scaleVec, const VmathTransform3 *tfrm ); + +/* + * Multiply two 3x4 transformation matrices per element + */ +static inline void vmathT3MulPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1 ); + +/* + * Compute the absolute value of a 3x4 transformation matrix per element + */ +static inline void vmathT3AbsPerElem( VmathTransform3 *result, const VmathTransform3 *tfrm ); + +/* + * Inverse of a 3x4 transformation matrix + * NOTE: + * Result is unpredictable when the determinant of the left 3x3 submatrix is equal to or near 0. + */ +static inline void vmathT3Inverse( VmathTransform3 *result, const VmathTransform3 *tfrm ); + +/* + * Compute the inverse of a 3x4 transformation matrix, expected to have an orthogonal upper-left 3x3 submatrix + * NOTE: + * This can be used to achieve better performance than a general inverse when the specified 3x4 transformation matrix meets the given restrictions. + */ +static inline void vmathT3OrthoInverse( VmathTransform3 *result, const VmathTransform3 *tfrm ); + +/* + * Conditionally select between two 3x4 transformation matrices + * NOTE: + * This function uses a conditional select instruction to avoid a branch. + */ +static inline void vmathT3Select( VmathTransform3 *result, const VmathTransform3 *tfrm0, const VmathTransform3 *tfrm1, unsigned int select1 ); + +#ifdef _VECTORMATH_DEBUG + +/* + * Print a 3x4 transformation matrix + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathT3Print( const VmathTransform3 *tfrm ); + +/* + * Print a 3x4 transformation matrix and an associated string identifier + * NOTE: + * Function is only defined when _VECTORMATH_DEBUG is defined. + */ +static inline void vmathT3Prints( const VmathTransform3 *tfrm, const char *name ); + +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#include "vec_aos.h" +#include "quat_aos.h" +#include "mat_aos.h" + +#endif diff --git a/common/vectormath/spu/cpp/boolInVec.h b/common/vectormath/spu/cpp/boolInVec.h index 93a3ad29..ac535843 100644 --- a/common/vectormath/spu/cpp/boolInVec.h +++ b/common/vectormath/spu/cpp/boolInVec.h @@ -1,246 +1,246 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _BOOLINVEC_H -#define _BOOLINVEC_H - -#include - -namespace Vectormath { - -class floatInVec; - -//-------------------------------------------------------------------------------------------------- -// boolInVec class -// - -class boolInVec -{ - private: - vec_uint4 mData; - - inline boolInVec(vec_uint4 vec); - public: - inline boolInVec() {} - - // matches standard type conversions - // - inline boolInVec(floatInVec vec); - - // explicit cast from bool - // - explicit inline boolInVec(bool scalar); - -#ifdef _VECTORMATH_NO_SCALAR_CAST - // explicit cast to bool - // - inline bool getAsBool() const; -#else - // implicit cast to bool - // - inline operator bool() const; -#endif - - // get vector data - // bool value is in the 0 word slot of vector as 0 (false) or -1 (true) - // - inline vec_uint4 get128() const; - - // operators - // - inline const boolInVec operator ! () const; - inline boolInVec& operator = (boolInVec vec); - inline boolInVec& operator &= (boolInVec vec); - inline boolInVec& operator ^= (boolInVec vec); - inline boolInVec& operator |= (boolInVec vec); - - // friend functions - // - friend inline const boolInVec operator == (boolInVec vec0, boolInVec vec1); - friend inline const boolInVec operator != (boolInVec vec0, boolInVec vec1); - friend inline const boolInVec operator < (floatInVec vec0, floatInVec vec1); - friend inline const boolInVec operator <= (floatInVec vec0, floatInVec vec1); - friend inline const boolInVec operator > (floatInVec vec0, floatInVec vec1); - friend inline const boolInVec operator >= (floatInVec vec0, floatInVec vec1); - friend inline const boolInVec operator == (floatInVec vec0, floatInVec vec1); - friend inline const boolInVec operator != (floatInVec vec0, floatInVec vec1); - friend inline const boolInVec operator & (boolInVec vec0, boolInVec vec1); - friend inline const boolInVec operator ^ (boolInVec vec0, boolInVec vec1); - friend inline const boolInVec operator | (boolInVec vec0, boolInVec vec1); - friend inline const boolInVec select(boolInVec vec0, boolInVec vec1, boolInVec select_vec1); -}; - -//-------------------------------------------------------------------------------------------------- -// boolInVec functions -// - -// operators -// -inline const boolInVec operator == (boolInVec vec0, boolInVec vec1); -inline const boolInVec operator != (boolInVec vec0, boolInVec vec1); -inline const boolInVec operator & (boolInVec vec0, boolInVec vec1); -inline const boolInVec operator ^ (boolInVec vec0, boolInVec vec1); -inline const boolInVec operator | (boolInVec vec0, boolInVec vec1); - -// select between vec0 and vec1 using boolInVec. -// false selects vec0, true selects vec1 -// -inline const boolInVec select(boolInVec vec0, boolInVec vec1, boolInVec select_vec1); - -} // namespace Vectormath - -//-------------------------------------------------------------------------------------------------- -// boolInVec implementation -// - -#include "floatInVec.h" - -namespace Vectormath { - -inline -boolInVec::boolInVec(vec_uint4 vec) -{ - mData = vec; -} - -inline -boolInVec::boolInVec(floatInVec vec) -{ - *this = (vec != floatInVec(0.0f)); -} - -inline -boolInVec::boolInVec(bool scalar) -{ - mData = spu_promote((unsigned int)-scalar, 0); -} - -#ifdef _VECTORMATH_NO_SCALAR_CAST -inline -bool -boolInVec::getAsBool() const -#else -inline -boolInVec::operator bool() const -#endif -{ - return (bool)spu_extract(mData, 0); -} - -inline -vec_uint4 -boolInVec::get128() const -{ - return mData; -} - -inline -const boolInVec -boolInVec::operator ! () const -{ - return boolInVec(spu_nor(mData, mData)); -} - -inline -boolInVec& -boolInVec::operator = (boolInVec vec) -{ - mData = vec.mData; - return *this; -} - -inline -boolInVec& -boolInVec::operator &= (boolInVec vec) -{ - *this = *this & vec; - return *this; -} - -inline -boolInVec& -boolInVec::operator ^= (boolInVec vec) -{ - *this = *this ^ vec; - return *this; -} - -inline -boolInVec& -boolInVec::operator |= (boolInVec vec) -{ - *this = *this | vec; - return *this; -} - -inline -const boolInVec -operator == (boolInVec vec0, boolInVec vec1) -{ - return boolInVec(spu_cmpeq(vec0.get128(), vec1.get128())); -} - -inline -const boolInVec -operator != (boolInVec vec0, boolInVec vec1) -{ - return !(vec0 == vec1); -} - -inline -const boolInVec -operator & (boolInVec vec0, boolInVec vec1) -{ - return boolInVec(spu_and(vec0.get128(), vec1.get128())); -} - -inline -const boolInVec -operator | (boolInVec vec0, boolInVec vec1) -{ - return boolInVec(spu_or(vec0.get128(), vec1.get128())); -} - -inline -const boolInVec -operator ^ (boolInVec vec0, boolInVec vec1) -{ - return boolInVec(spu_xor(vec0.get128(), vec1.get128())); -} - -inline -const boolInVec -select(boolInVec vec0, boolInVec vec1, boolInVec select_vec1) -{ - return boolInVec(spu_sel(vec0.get128(), vec1.get128(), select_vec1.get128())); -} - -} // namespace Vectormath - -#endif // boolInVec_h +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _BOOLINVEC_H +#define _BOOLINVEC_H + +#include + +namespace Vectormath { + +class floatInVec; + +//-------------------------------------------------------------------------------------------------- +// boolInVec class +// + +class boolInVec +{ + private: + vec_uint4 mData; + + inline boolInVec(vec_uint4 vec); + public: + inline boolInVec() {} + + // matches standard type conversions + // + inline boolInVec(floatInVec vec); + + // explicit cast from bool + // + explicit inline boolInVec(bool scalar); + +#ifdef _VECTORMATH_NO_SCALAR_CAST + // explicit cast to bool + // + inline bool getAsBool() const; +#else + // implicit cast to bool + // + inline operator bool() const; +#endif + + // get vector data + // bool value is in the 0 word slot of vector as 0 (false) or -1 (true) + // + inline vec_uint4 get128() const; + + // operators + // + inline const boolInVec operator ! () const; + inline boolInVec& operator = (boolInVec vec); + inline boolInVec& operator &= (boolInVec vec); + inline boolInVec& operator ^= (boolInVec vec); + inline boolInVec& operator |= (boolInVec vec); + + // friend functions + // + friend inline const boolInVec operator == (boolInVec vec0, boolInVec vec1); + friend inline const boolInVec operator != (boolInVec vec0, boolInVec vec1); + friend inline const boolInVec operator < (floatInVec vec0, floatInVec vec1); + friend inline const boolInVec operator <= (floatInVec vec0, floatInVec vec1); + friend inline const boolInVec operator > (floatInVec vec0, floatInVec vec1); + friend inline const boolInVec operator >= (floatInVec vec0, floatInVec vec1); + friend inline const boolInVec operator == (floatInVec vec0, floatInVec vec1); + friend inline const boolInVec operator != (floatInVec vec0, floatInVec vec1); + friend inline const boolInVec operator & (boolInVec vec0, boolInVec vec1); + friend inline const boolInVec operator ^ (boolInVec vec0, boolInVec vec1); + friend inline const boolInVec operator | (boolInVec vec0, boolInVec vec1); + friend inline const boolInVec select(boolInVec vec0, boolInVec vec1, boolInVec select_vec1); +}; + +//-------------------------------------------------------------------------------------------------- +// boolInVec functions +// + +// operators +// +inline const boolInVec operator == (boolInVec vec0, boolInVec vec1); +inline const boolInVec operator != (boolInVec vec0, boolInVec vec1); +inline const boolInVec operator & (boolInVec vec0, boolInVec vec1); +inline const boolInVec operator ^ (boolInVec vec0, boolInVec vec1); +inline const boolInVec operator | (boolInVec vec0, boolInVec vec1); + +// select between vec0 and vec1 using boolInVec. +// false selects vec0, true selects vec1 +// +inline const boolInVec select(boolInVec vec0, boolInVec vec1, boolInVec select_vec1); + +} // namespace Vectormath + +//-------------------------------------------------------------------------------------------------- +// boolInVec implementation +// + +#include "floatInVec.h" + +namespace Vectormath { + +inline +boolInVec::boolInVec(vec_uint4 vec) +{ + mData = vec; +} + +inline +boolInVec::boolInVec(floatInVec vec) +{ + *this = (vec != floatInVec(0.0f)); +} + +inline +boolInVec::boolInVec(bool scalar) +{ + mData = spu_promote((unsigned int)-scalar, 0); +} + +#ifdef _VECTORMATH_NO_SCALAR_CAST +inline +bool +boolInVec::getAsBool() const +#else +inline +boolInVec::operator bool() const +#endif +{ + return (bool)spu_extract(mData, 0); +} + +inline +vec_uint4 +boolInVec::get128() const +{ + return mData; +} + +inline +const boolInVec +boolInVec::operator ! () const +{ + return boolInVec(spu_nor(mData, mData)); +} + +inline +boolInVec& +boolInVec::operator = (boolInVec vec) +{ + mData = vec.mData; + return *this; +} + +inline +boolInVec& +boolInVec::operator &= (boolInVec vec) +{ + *this = *this & vec; + return *this; +} + +inline +boolInVec& +boolInVec::operator ^= (boolInVec vec) +{ + *this = *this ^ vec; + return *this; +} + +inline +boolInVec& +boolInVec::operator |= (boolInVec vec) +{ + *this = *this | vec; + return *this; +} + +inline +const boolInVec +operator == (boolInVec vec0, boolInVec vec1) +{ + return boolInVec(spu_cmpeq(vec0.get128(), vec1.get128())); +} + +inline +const boolInVec +operator != (boolInVec vec0, boolInVec vec1) +{ + return !(vec0 == vec1); +} + +inline +const boolInVec +operator & (boolInVec vec0, boolInVec vec1) +{ + return boolInVec(spu_and(vec0.get128(), vec1.get128())); +} + +inline +const boolInVec +operator | (boolInVec vec0, boolInVec vec1) +{ + return boolInVec(spu_or(vec0.get128(), vec1.get128())); +} + +inline +const boolInVec +operator ^ (boolInVec vec0, boolInVec vec1) +{ + return boolInVec(spu_xor(vec0.get128(), vec1.get128())); +} + +inline +const boolInVec +select(boolInVec vec0, boolInVec vec1, boolInVec select_vec1) +{ + return boolInVec(spu_sel(vec0.get128(), vec1.get128(), select_vec1.get128())); +} + +} // namespace Vectormath + +#endif // boolInVec_h diff --git a/common/vectormath/spu/cpp/floatInVec.h b/common/vectormath/spu/cpp/floatInVec.h index 7521c0c4..638f22f9 100644 --- a/common/vectormath/spu/cpp/floatInVec.h +++ b/common/vectormath/spu/cpp/floatInVec.h @@ -1,339 +1,339 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _FLOATINVEC_H -#define _FLOATINVEC_H - -#include -#include -#include -#undef bool - -namespace Vectormath { - -class boolInVec; - -//-------------------------------------------------------------------------------------------------- -// floatInVec class -// - -class floatInVec -{ - private: - vec_float4 mData; - - inline floatInVec(vec_float4 vec); - public: - inline floatInVec() {} - - // matches standard type conversions - // - inline floatInVec(boolInVec vec); - - // construct from a slot of vec_float4 - // - inline floatInVec(vec_float4 vec, int slot); - - // explicit cast from float - // - explicit inline floatInVec(float scalar); - -#ifdef _VECTORMATH_NO_SCALAR_CAST - // explicit cast to float - // - inline float getAsFloat() const; -#else - // implicit cast to float - // - inline operator float() const; -#endif - - // get vector data - // float value is in 0 word slot of vector - // - inline vec_float4 get128() const; - - // operators - // - inline const floatInVec operator ++ (int); - inline const floatInVec operator -- (int); - inline floatInVec& operator ++ (); - inline floatInVec& operator -- (); - inline const floatInVec operator - () const; - inline floatInVec& operator = (floatInVec vec); - inline floatInVec& operator *= (floatInVec vec); - inline floatInVec& operator /= (floatInVec vec); - inline floatInVec& operator += (floatInVec vec); - inline floatInVec& operator -= (floatInVec vec); - - // friend functions - // - friend inline const floatInVec operator * (floatInVec vec0, floatInVec vec1); - friend inline const floatInVec operator / (floatInVec vec0, floatInVec vec1); - friend inline const floatInVec operator + (floatInVec vec0, floatInVec vec1); - friend inline const floatInVec operator - (floatInVec vec0, floatInVec vec1); - friend inline const floatInVec select(floatInVec vec0, floatInVec vec1, boolInVec select_vec1); -}; - -//-------------------------------------------------------------------------------------------------- -// floatInVec functions -// - -// operators -// -inline const floatInVec operator * (floatInVec vec0, floatInVec vec1); -inline const floatInVec operator / (floatInVec vec0, floatInVec vec1); -inline const floatInVec operator + (floatInVec vec0, floatInVec vec1); -inline const floatInVec operator - (floatInVec vec0, floatInVec vec1); -inline const boolInVec operator < (floatInVec vec0, floatInVec vec1); -inline const boolInVec operator <= (floatInVec vec0, floatInVec vec1); -inline const boolInVec operator > (floatInVec vec0, floatInVec vec1); -inline const boolInVec operator >= (floatInVec vec0, floatInVec vec1); -inline const boolInVec operator == (floatInVec vec0, floatInVec vec1); -inline const boolInVec operator != (floatInVec vec0, floatInVec vec1); - -// select between vec0 and vec1 using boolInVec. -// false selects vec0, true selects vec1 -// -inline const floatInVec select(floatInVec vec0, floatInVec vec1, boolInVec select_vec1); - -} // namespace Vectormath - -//-------------------------------------------------------------------------------------------------- -// floatInVec implementation -// - -#include "boolInVec.h" - -namespace Vectormath { - -inline -floatInVec::floatInVec(vec_float4 vec) -{ - mData = vec; -} - -inline -floatInVec::floatInVec(boolInVec vec) -{ - mData = spu_sel(spu_splats(0.0f), spu_splats(1.0f), vec.get128()); -} - -inline -floatInVec::floatInVec(vec_float4 vec, int slot) -{ - mData = spu_promote(spu_extract(vec, slot), 0); -} - -inline -floatInVec::floatInVec(float scalar) -{ - mData = spu_promote(scalar, 0); -} - -#ifdef _VECTORMATH_NO_SCALAR_CAST -inline -float -floatInVec::getAsFloat() const -#else -inline -floatInVec::operator float() const -#endif -{ - return spu_extract(mData,0); -} - -inline -vec_float4 -floatInVec::get128() const -{ - return mData; -} - -inline -const floatInVec -floatInVec::operator ++ (int) -{ - vec_float4 olddata = mData; - operator ++(); - return floatInVec(olddata); -} - -inline -const floatInVec -floatInVec::operator -- (int) -{ - vec_float4 olddata = mData; - operator --(); - return floatInVec(olddata); -} - -inline -floatInVec& -floatInVec::operator ++ () -{ - *this += floatInVec(1.0f); - return *this; -} - -inline -floatInVec& -floatInVec::operator -- () -{ - *this -= floatInVec(1.0f); - return *this; -} - -inline -const floatInVec -floatInVec::operator - () const -{ - return floatInVec((vec_float4)spu_xor((vec_uint4)mData, spu_splats(0x80000000))); -} - -inline -floatInVec& -floatInVec::operator = (floatInVec vec) -{ - mData = vec.mData; - return *this; -} - -inline -floatInVec& -floatInVec::operator *= (floatInVec vec) -{ - *this = *this * vec; - return *this; -} - -inline -floatInVec& -floatInVec::operator /= (floatInVec vec) -{ - *this = *this / vec; - return *this; -} - -inline -floatInVec& -floatInVec::operator += (floatInVec vec) -{ - *this = *this + vec; - return *this; -} - -inline -floatInVec& -floatInVec::operator -= (floatInVec vec) -{ - *this = *this - vec; - return *this; -} - -inline -const floatInVec -operator * (floatInVec vec0, floatInVec vec1) -{ - return floatInVec(spu_mul(vec0.get128(), vec1.get128())); -} - -inline -const floatInVec -operator / (floatInVec num, floatInVec den) -{ - return floatInVec(divf4(num.get128(), den.get128())); -} - -inline -const floatInVec -operator + (floatInVec vec0, floatInVec vec1) -{ - return floatInVec(spu_add(vec0.get128(), vec1.get128())); -} - -inline -const floatInVec -operator - (floatInVec vec0, floatInVec vec1) -{ - return floatInVec(spu_sub(vec0.get128(), vec1.get128())); -} - -inline -const boolInVec -operator < (floatInVec vec0, floatInVec vec1) -{ - return boolInVec(spu_cmpgt(vec1.get128(), vec0.get128())); -} - -inline -const boolInVec -operator <= (floatInVec vec0, floatInVec vec1) -{ - return !(vec0 > vec1); -} - -inline -const boolInVec -operator > (floatInVec vec0, floatInVec vec1) -{ - return boolInVec(spu_cmpgt(vec0.get128(), vec1.get128())); -} - -inline -const boolInVec -operator >= (floatInVec vec0, floatInVec vec1) -{ - return !(vec0 < vec1); -} - -inline -const boolInVec -operator == (floatInVec vec0, floatInVec vec1) -{ - return boolInVec(spu_cmpeq(vec0.get128(), vec1.get128())); -} - -inline -const boolInVec -operator != (floatInVec vec0, floatInVec vec1) -{ - return !(vec0 == vec1); -} - -inline -const floatInVec -select(floatInVec vec0, floatInVec vec1, boolInVec select_vec1) -{ - return floatInVec(spu_sel(vec0.get128(), vec1.get128(), select_vec1.get128())); -} - -} // namespace Vectormath - -#endif // floatInVec_h +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _FLOATINVEC_H +#define _FLOATINVEC_H + +#include +#include +#include +#undef bool + +namespace Vectormath { + +class boolInVec; + +//-------------------------------------------------------------------------------------------------- +// floatInVec class +// + +class floatInVec +{ + private: + vec_float4 mData; + + inline floatInVec(vec_float4 vec); + public: + inline floatInVec() {} + + // matches standard type conversions + // + inline floatInVec(boolInVec vec); + + // construct from a slot of vec_float4 + // + inline floatInVec(vec_float4 vec, int slot); + + // explicit cast from float + // + explicit inline floatInVec(float scalar); + +#ifdef _VECTORMATH_NO_SCALAR_CAST + // explicit cast to float + // + inline float getAsFloat() const; +#else + // implicit cast to float + // + inline operator float() const; +#endif + + // get vector data + // float value is in 0 word slot of vector + // + inline vec_float4 get128() const; + + // operators + // + inline const floatInVec operator ++ (int); + inline const floatInVec operator -- (int); + inline floatInVec& operator ++ (); + inline floatInVec& operator -- (); + inline const floatInVec operator - () const; + inline floatInVec& operator = (floatInVec vec); + inline floatInVec& operator *= (floatInVec vec); + inline floatInVec& operator /= (floatInVec vec); + inline floatInVec& operator += (floatInVec vec); + inline floatInVec& operator -= (floatInVec vec); + + // friend functions + // + friend inline const floatInVec operator * (floatInVec vec0, floatInVec vec1); + friend inline const floatInVec operator / (floatInVec vec0, floatInVec vec1); + friend inline const floatInVec operator + (floatInVec vec0, floatInVec vec1); + friend inline const floatInVec operator - (floatInVec vec0, floatInVec vec1); + friend inline const floatInVec select(floatInVec vec0, floatInVec vec1, boolInVec select_vec1); +}; + +//-------------------------------------------------------------------------------------------------- +// floatInVec functions +// + +// operators +// +inline const floatInVec operator * (floatInVec vec0, floatInVec vec1); +inline const floatInVec operator / (floatInVec vec0, floatInVec vec1); +inline const floatInVec operator + (floatInVec vec0, floatInVec vec1); +inline const floatInVec operator - (floatInVec vec0, floatInVec vec1); +inline const boolInVec operator < (floatInVec vec0, floatInVec vec1); +inline const boolInVec operator <= (floatInVec vec0, floatInVec vec1); +inline const boolInVec operator > (floatInVec vec0, floatInVec vec1); +inline const boolInVec operator >= (floatInVec vec0, floatInVec vec1); +inline const boolInVec operator == (floatInVec vec0, floatInVec vec1); +inline const boolInVec operator != (floatInVec vec0, floatInVec vec1); + +// select between vec0 and vec1 using boolInVec. +// false selects vec0, true selects vec1 +// +inline const floatInVec select(floatInVec vec0, floatInVec vec1, boolInVec select_vec1); + +} // namespace Vectormath + +//-------------------------------------------------------------------------------------------------- +// floatInVec implementation +// + +#include "boolInVec.h" + +namespace Vectormath { + +inline +floatInVec::floatInVec(vec_float4 vec) +{ + mData = vec; +} + +inline +floatInVec::floatInVec(boolInVec vec) +{ + mData = spu_sel(spu_splats(0.0f), spu_splats(1.0f), vec.get128()); +} + +inline +floatInVec::floatInVec(vec_float4 vec, int slot) +{ + mData = spu_promote(spu_extract(vec, slot), 0); +} + +inline +floatInVec::floatInVec(float scalar) +{ + mData = spu_promote(scalar, 0); +} + +#ifdef _VECTORMATH_NO_SCALAR_CAST +inline +float +floatInVec::getAsFloat() const +#else +inline +floatInVec::operator float() const +#endif +{ + return spu_extract(mData,0); +} + +inline +vec_float4 +floatInVec::get128() const +{ + return mData; +} + +inline +const floatInVec +floatInVec::operator ++ (int) +{ + vec_float4 olddata = mData; + operator ++(); + return floatInVec(olddata); +} + +inline +const floatInVec +floatInVec::operator -- (int) +{ + vec_float4 olddata = mData; + operator --(); + return floatInVec(olddata); +} + +inline +floatInVec& +floatInVec::operator ++ () +{ + *this += floatInVec(1.0f); + return *this; +} + +inline +floatInVec& +floatInVec::operator -- () +{ + *this -= floatInVec(1.0f); + return *this; +} + +inline +const floatInVec +floatInVec::operator - () const +{ + return floatInVec((vec_float4)spu_xor((vec_uint4)mData, spu_splats(0x80000000))); +} + +inline +floatInVec& +floatInVec::operator = (floatInVec vec) +{ + mData = vec.mData; + return *this; +} + +inline +floatInVec& +floatInVec::operator *= (floatInVec vec) +{ + *this = *this * vec; + return *this; +} + +inline +floatInVec& +floatInVec::operator /= (floatInVec vec) +{ + *this = *this / vec; + return *this; +} + +inline +floatInVec& +floatInVec::operator += (floatInVec vec) +{ + *this = *this + vec; + return *this; +} + +inline +floatInVec& +floatInVec::operator -= (floatInVec vec) +{ + *this = *this - vec; + return *this; +} + +inline +const floatInVec +operator * (floatInVec vec0, floatInVec vec1) +{ + return floatInVec(spu_mul(vec0.get128(), vec1.get128())); +} + +inline +const floatInVec +operator / (floatInVec num, floatInVec den) +{ + return floatInVec(divf4(num.get128(), den.get128())); +} + +inline +const floatInVec +operator + (floatInVec vec0, floatInVec vec1) +{ + return floatInVec(spu_add(vec0.get128(), vec1.get128())); +} + +inline +const floatInVec +operator - (floatInVec vec0, floatInVec vec1) +{ + return floatInVec(spu_sub(vec0.get128(), vec1.get128())); +} + +inline +const boolInVec +operator < (floatInVec vec0, floatInVec vec1) +{ + return boolInVec(spu_cmpgt(vec1.get128(), vec0.get128())); +} + +inline +const boolInVec +operator <= (floatInVec vec0, floatInVec vec1) +{ + return !(vec0 > vec1); +} + +inline +const boolInVec +operator > (floatInVec vec0, floatInVec vec1) +{ + return boolInVec(spu_cmpgt(vec0.get128(), vec1.get128())); +} + +inline +const boolInVec +operator >= (floatInVec vec0, floatInVec vec1) +{ + return !(vec0 < vec1); +} + +inline +const boolInVec +operator == (floatInVec vec0, floatInVec vec1) +{ + return boolInVec(spu_cmpeq(vec0.get128(), vec1.get128())); +} + +inline +const boolInVec +operator != (floatInVec vec0, floatInVec vec1) +{ + return !(vec0 == vec1); +} + +inline +const floatInVec +select(floatInVec vec0, floatInVec vec1, boolInVec select_vec1) +{ + return floatInVec(spu_sel(vec0.get128(), vec1.get128(), select_vec1.get128())); +} + +} // namespace Vectormath + +#endif // floatInVec_h diff --git a/common/vectormath/spu/cpp/mat_aos.h b/common/vectormath/spu/cpp/mat_aos.h index a2fd611e..d4f955c2 100644 --- a/common/vectormath/spu/cpp/mat_aos.h +++ b/common/vectormath/spu/cpp/mat_aos.h @@ -1,2027 +1,2027 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_MAT_AOS_CPP_H -#define _VECTORMATH_MAT_AOS_CPP_H - -namespace Vectormath { -namespace Aos { - -//----------------------------------------------------------------------------- -// Constants -// for shuffles, words are labeled [x,y,z,w] [a,b,c,d] - -#define _VECTORMATH_SHUF_XAYB ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_B }) -#define _VECTORMATH_SHUF_ZCWD ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_D }) -#define _VECTORMATH_SHUF_ZBW0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_XCY0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_XYAB ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B }) -#define _VECTORMATH_SHUF_ZWCD ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_D }) -#define _VECTORMATH_SHUF_0ZB0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_C0X0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_YA00 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_XAZC ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_C }) -#define _VECTORMATH_SHUF_YXWZ ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_Z }) -#define _VECTORMATH_SHUF_YBWD ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_D }) -#define _VECTORMATH_SHUF_XYCX ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_X }) -#define _VECTORMATH_SHUF_YCXY ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y }) -#define _VECTORMATH_SHUF_CXYC ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_C }) -#define _VECTORMATH_SHUF_ZAY0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_BZX0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_0ZYA ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_A }) -#define _VECTORMATH_SHUF_Z0XB ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_B }) -#define _VECTORMATH_SHUF_YX0C ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_C }) -#define _VECTORMATH_SHUF_CZD0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_D, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_SHUF_BBY0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_0 }) -#define _VECTORMATH_PI_OVER_2 1.570796327f - -//----------------------------------------------------------------------------- -// Definitions - -inline Matrix3::Matrix3( const Matrix3 & mat ) -{ - mCol0 = mat.mCol0; - mCol1 = mat.mCol1; - mCol2 = mat.mCol2; -} - -inline Matrix3::Matrix3( float scalar ) -{ - mCol0 = Vector3( scalar ); - mCol1 = Vector3( scalar ); - mCol2 = Vector3( scalar ); -} - -inline Matrix3::Matrix3( Quat unitQuat ) -{ - vec_float4 xyzw_2, wwww, yzxw, zxyw, yzxw_2, zxyw_2; - vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; - vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); - vec_uint4 select_x = (vec_uint4)spu_maskb(0xf000); - vec_uint4 select_z = (vec_uint4)spu_maskb(0x00f0); - xyzw_2 = spu_add( unitQuat.get128(), unitQuat.get128() ); - wwww = spu_shuffle( unitQuat.get128(), unitQuat.get128(), shuffle_wwww ); - yzxw = spu_shuffle( unitQuat.get128(), unitQuat.get128(), _VECTORMATH_SHUF_YZXW ); - zxyw = spu_shuffle( unitQuat.get128(), unitQuat.get128(), _VECTORMATH_SHUF_ZXYW ); - yzxw_2 = spu_shuffle( xyzw_2, xyzw_2, _VECTORMATH_SHUF_YZXW ); - zxyw_2 = spu_shuffle( xyzw_2, xyzw_2, _VECTORMATH_SHUF_ZXYW ); - tmp0 = spu_mul( yzxw_2, wwww ); - tmp1 = spu_nmsub( yzxw, yzxw_2, spu_splats(1.0f) ); - tmp2 = spu_mul( yzxw, xyzw_2 ); - tmp0 = spu_madd( zxyw, xyzw_2, tmp0 ); - tmp1 = spu_nmsub( zxyw, zxyw_2, tmp1 ); - tmp2 = spu_nmsub( zxyw_2, wwww, tmp2 ); - tmp3 = spu_sel( tmp0, tmp1, select_x ); - tmp4 = spu_sel( tmp1, tmp2, select_x ); - tmp5 = spu_sel( tmp2, tmp0, select_x ); - mCol0 = Vector3( spu_sel( tmp3, tmp2, select_z ) ); - mCol1 = Vector3( spu_sel( tmp4, tmp0, select_z ) ); - mCol2 = Vector3( spu_sel( tmp5, tmp1, select_z ) ); -} - -inline Matrix3::Matrix3( Vector3 _col0, Vector3 _col1, Vector3 _col2 ) -{ - mCol0 = _col0; - mCol1 = _col1; - mCol2 = _col2; -} - -inline Matrix3 & Matrix3::setCol0( Vector3 _col0 ) -{ - mCol0 = _col0; - return *this; -} - -inline Matrix3 & Matrix3::setCol1( Vector3 _col1 ) -{ - mCol1 = _col1; - return *this; -} - -inline Matrix3 & Matrix3::setCol2( Vector3 _col2 ) -{ - mCol2 = _col2; - return *this; -} - -inline Matrix3 & Matrix3::setCol( int col, Vector3 vec ) -{ - *(&mCol0 + col) = vec; - return *this; -} - -inline Matrix3 & Matrix3::setRow( int row, Vector3 vec ) -{ - mCol0.setElem( row, vec.getElem( 0 ) ); - mCol1.setElem( row, vec.getElem( 1 ) ); - mCol2.setElem( row, vec.getElem( 2 ) ); - return *this; -} - -inline Matrix3 & Matrix3::setElem( int col, int row, float val ) -{ - (*this)[col].setElem(row, val); - return *this; -} - -inline float Matrix3::getElem( int col, int row ) const -{ - return this->getCol( col ).getElem( row ); -} - -inline const Vector3 Matrix3::getCol0( ) const -{ - return mCol0; -} - -inline const Vector3 Matrix3::getCol1( ) const -{ - return mCol1; -} - -inline const Vector3 Matrix3::getCol2( ) const -{ - return mCol2; -} - -inline const Vector3 Matrix3::getCol( int col ) const -{ - return *(&mCol0 + col); -} - -inline const Vector3 Matrix3::getRow( int row ) const -{ - return Vector3( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ) ); -} - -inline Vector3 & Matrix3::operator []( int col ) -{ - return *(&mCol0 + col); -} - -inline const Vector3 Matrix3::operator []( int col ) const -{ - return *(&mCol0 + col); -} - -inline Matrix3 & Matrix3::operator =( const Matrix3 & mat ) -{ - mCol0 = mat.mCol0; - mCol1 = mat.mCol1; - mCol2 = mat.mCol2; - return *this; -} - -inline const Matrix3 transpose( const Matrix3 & mat ) -{ - vec_float4 tmp0, tmp1, res0, res1, res2; - tmp0 = spu_shuffle( mat.getCol0().get128(), mat.getCol2().get128(), _VECTORMATH_SHUF_XAYB ); - tmp1 = spu_shuffle( mat.getCol0().get128(), mat.getCol2().get128(), _VECTORMATH_SHUF_ZCWD ); - res0 = spu_shuffle( tmp0, mat.getCol1().get128(), _VECTORMATH_SHUF_XAYB ); - res1 = spu_shuffle( tmp0, mat.getCol1().get128(), _VECTORMATH_SHUF_ZBW0 ); - res2 = spu_shuffle( tmp1, mat.getCol1().get128(), _VECTORMATH_SHUF_XCY0 ); - return Matrix3( - Vector3( res0 ), - Vector3( res1 ), - Vector3( res2 ) - ); -} - -inline const Matrix3 inverse( const Matrix3 & mat ) -{ - vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet, inv0, inv1, inv2; - tmp2 = _vmathVfCross( mat.getCol0().get128(), mat.getCol1().get128() ); - tmp0 = _vmathVfCross( mat.getCol1().get128(), mat.getCol2().get128() ); - tmp1 = _vmathVfCross( mat.getCol2().get128(), mat.getCol0().get128() ); - dot = _vmathVfDot3( tmp2, mat.getCol2().get128() ); - dot = spu_shuffle( dot, dot, (vec_uchar16)spu_splats(0x00010203) ); - invdet = recipf4( dot ); - tmp3 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_XAYB ); - tmp4 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_ZCWD ); - inv0 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_XAYB ); - inv1 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_ZBW0 ); - inv2 = spu_shuffle( tmp4, tmp1, _VECTORMATH_SHUF_XCY0 ); - inv0 = spu_mul( inv0, invdet ); - inv1 = spu_mul( inv1, invdet ); - inv2 = spu_mul( inv2, invdet ); - return Matrix3( - Vector3( inv0 ), - Vector3( inv1 ), - Vector3( inv2 ) - ); -} - -inline float determinant( const Matrix3 & mat ) -{ - return dot( mat.getCol2(), cross( mat.getCol0(), mat.getCol1() ) ); -} - -inline const Matrix3 Matrix3::operator +( const Matrix3 & mat ) const -{ - return Matrix3( - ( mCol0 + mat.mCol0 ), - ( mCol1 + mat.mCol1 ), - ( mCol2 + mat.mCol2 ) - ); -} - -inline const Matrix3 Matrix3::operator -( const Matrix3 & mat ) const -{ - return Matrix3( - ( mCol0 - mat.mCol0 ), - ( mCol1 - mat.mCol1 ), - ( mCol2 - mat.mCol2 ) - ); -} - -inline Matrix3 & Matrix3::operator +=( const Matrix3 & mat ) -{ - *this = *this + mat; - return *this; -} - -inline Matrix3 & Matrix3::operator -=( const Matrix3 & mat ) -{ - *this = *this - mat; - return *this; -} - -inline const Matrix3 Matrix3::operator -( ) const -{ - return Matrix3( - ( -mCol0 ), - ( -mCol1 ), - ( -mCol2 ) - ); -} - -inline const Matrix3 absPerElem( const Matrix3 & mat ) -{ - return Matrix3( - absPerElem( mat.getCol0() ), - absPerElem( mat.getCol1() ), - absPerElem( mat.getCol2() ) - ); -} - -inline const Matrix3 Matrix3::operator *( float scalar ) const -{ - return Matrix3( - ( mCol0 * scalar ), - ( mCol1 * scalar ), - ( mCol2 * scalar ) - ); -} - -inline Matrix3 & Matrix3::operator *=( float scalar ) -{ - *this = *this * scalar; - return *this; -} - -inline const Matrix3 operator *( float scalar, const Matrix3 & mat ) -{ - return mat * scalar; -} - -inline const Vector3 Matrix3::operator *( Vector3 vec ) const -{ - vec_float4 res; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - xxxx = spu_shuffle( vec.get128(), vec.get128(), shuffle_xxxx ); - yyyy = spu_shuffle( vec.get128(), vec.get128(), shuffle_yyyy ); - zzzz = spu_shuffle( vec.get128(), vec.get128(), shuffle_zzzz ); - res = spu_mul( mCol0.get128(), xxxx ); - res = spu_madd( mCol1.get128(), yyyy, res ); - res = spu_madd( mCol2.get128(), zzzz, res ); - return Vector3( res ); -} - -inline const Matrix3 Matrix3::operator *( const Matrix3 & mat ) const -{ - return Matrix3( - ( *this * mat.mCol0 ), - ( *this * mat.mCol1 ), - ( *this * mat.mCol2 ) - ); -} - -inline Matrix3 & Matrix3::operator *=( const Matrix3 & mat ) -{ - *this = *this * mat; - return *this; -} - -inline const Matrix3 mulPerElem( const Matrix3 & mat0, const Matrix3 & mat1 ) -{ - return Matrix3( - mulPerElem( mat0.getCol0(), mat1.getCol0() ), - mulPerElem( mat0.getCol1(), mat1.getCol1() ), - mulPerElem( mat0.getCol2(), mat1.getCol2() ) - ); -} - -inline const Matrix3 Matrix3::identity( ) -{ - return Matrix3( - Vector3::xAxis( ), - Vector3::yAxis( ), - Vector3::zAxis( ) - ); -} - -inline const Matrix3 Matrix3::rotationX( float radians ) -{ - vec_float4 s, c, res1, res2; - vec_uint4 select_y, select_z; - vec_float4 zero; - select_y = (vec_uint4)spu_maskb(0x0f00); - select_z = (vec_uint4)spu_maskb(0x00f0); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res1 = spu_sel( zero, c, select_y ); - res1 = spu_sel( res1, s, select_z ); - res2 = spu_sel( zero, negatef4(s), select_y ); - res2 = spu_sel( res2, c, select_z ); - return Matrix3( - Vector3::xAxis( ), - Vector3( res1 ), - Vector3( res2 ) - ); -} - -inline const Matrix3 Matrix3::rotationY( float radians ) -{ - vec_float4 s, c, res0, res2; - vec_uint4 select_x, select_z; - vec_float4 zero; - select_x = (vec_uint4)spu_maskb(0xf000); - select_z = (vec_uint4)spu_maskb(0x00f0); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res0 = spu_sel( zero, c, select_x ); - res0 = spu_sel( res0, negatef4(s), select_z ); - res2 = spu_sel( zero, s, select_x ); - res2 = spu_sel( res2, c, select_z ); - return Matrix3( - Vector3( res0 ), - Vector3::yAxis( ), - Vector3( res2 ) - ); -} - -inline const Matrix3 Matrix3::rotationZ( float radians ) -{ - vec_float4 s, c, res0, res1; - vec_uint4 select_x, select_y; - vec_float4 zero; - select_x = (vec_uint4)spu_maskb(0xf000); - select_y = (vec_uint4)spu_maskb(0x0f00); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res0 = spu_sel( zero, c, select_x ); - res0 = spu_sel( res0, s, select_y ); - res1 = spu_sel( zero, negatef4(s), select_x ); - res1 = spu_sel( res1, c, select_y ); - return Matrix3( - Vector3( res0 ), - Vector3( res1 ), - Vector3::zAxis( ) - ); -} - -inline const Matrix3 Matrix3::rotationZYX( Vector3 radiansXYZ ) -{ - vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - angles = radiansXYZ.get128(); - angles = spu_insert( 0.0f, angles, 3 ); - sincosf4( angles, &s, &c ); - negS = negatef4( s ); - Z0 = spu_shuffle( s, c, _VECTORMATH_SHUF_CZD0 ); - Z1 = spu_shuffle( c, negS, _VECTORMATH_SHUF_CZD0 ); - Y0 = spu_shuffle( negS, c, _VECTORMATH_SHUF_BBY0 ); - Y1 = spu_shuffle( c, s, _VECTORMATH_SHUF_BBY0 ); - X0 = spu_shuffle( s, s, shuffle_xxxx ); - X1 = spu_shuffle( c, c, shuffle_xxxx ); - tmp = spu_mul( Z0, Y1 ); - return Matrix3( - Vector3( spu_mul( Z0, Y0 ) ), - Vector3( spu_madd( Z1, X1, spu_mul( tmp, X0 ) ) ), - Vector3( spu_nmsub( Z1, X0, spu_mul( tmp, X1 ) ) ) - ); -} - -inline const Matrix3 Matrix3::rotation( float radians, Vector3 unitVec ) -{ - vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - axis = unitVec.get128(); - sincosf4( spu_splats( radians ), &s, &c ); - xxxx = spu_shuffle( axis, axis, shuffle_xxxx ); - yyyy = spu_shuffle( axis, axis, shuffle_yyyy ); - zzzz = spu_shuffle( axis, axis, shuffle_zzzz ); - oneMinusC = spu_sub( spu_splats(1.0f), c ); - axisS = spu_mul( axis, s ); - negAxisS = negatef4( axisS ); - tmp0 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_0ZB0 ); - tmp1 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_C0X0 ); - tmp2 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_YA00 ); - tmp0 = spu_sel( tmp0, c, (vec_uint4)spu_maskb(0xf000) ); - tmp1 = spu_sel( tmp1, c, (vec_uint4)spu_maskb(0x0f00) ); - tmp2 = spu_sel( tmp2, c, (vec_uint4)spu_maskb(0x00f0) ); - return Matrix3( - Vector3( spu_madd( spu_mul( axis, xxxx ), oneMinusC, tmp0 ) ), - Vector3( spu_madd( spu_mul( axis, yyyy ), oneMinusC, tmp1 ) ), - Vector3( spu_madd( spu_mul( axis, zzzz ), oneMinusC, tmp2 ) ) - ); -} - -inline const Matrix3 Matrix3::rotation( Quat unitQuat ) -{ - return Matrix3( unitQuat ); -} - -inline const Matrix3 Matrix3::scale( Vector3 scaleVec ) -{ - vec_float4 zero = spu_splats(0.0f); - return Matrix3( - Vector3( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0xf000) ) ), - Vector3( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0x0f00) ) ), - Vector3( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0x00f0) ) ) - ); -} - -inline const Matrix3 appendScale( const Matrix3 & mat, Vector3 scaleVec ) -{ - return Matrix3( - ( mat.getCol0() * scaleVec.getX( ) ), - ( mat.getCol1() * scaleVec.getY( ) ), - ( mat.getCol2() * scaleVec.getZ( ) ) - ); -} - -inline const Matrix3 prependScale( Vector3 scaleVec, const Matrix3 & mat ) -{ - return Matrix3( - mulPerElem( mat.getCol0(), scaleVec ), - mulPerElem( mat.getCol1(), scaleVec ), - mulPerElem( mat.getCol2(), scaleVec ) - ); -} - -inline const Matrix3 select( const Matrix3 & mat0, const Matrix3 & mat1, bool select1 ) -{ - return Matrix3( - select( mat0.getCol0(), mat1.getCol0(), select1 ), - select( mat0.getCol1(), mat1.getCol1(), select1 ), - select( mat0.getCol2(), mat1.getCol2(), select1 ) - ); -} - -#ifdef _VECTORMATH_DEBUG - -inline void print( const Matrix3 & mat ) -{ - print( mat.getRow( 0 ) ); - print( mat.getRow( 1 ) ); - print( mat.getRow( 2 ) ); -} - -inline void print( const Matrix3 & mat, const char * name ) -{ - printf("%s:\n", name); - print( mat ); -} - -#endif - -inline Matrix4::Matrix4( const Matrix4 & mat ) -{ - mCol0 = mat.mCol0; - mCol1 = mat.mCol1; - mCol2 = mat.mCol2; - mCol3 = mat.mCol3; -} - -inline Matrix4::Matrix4( float scalar ) -{ - mCol0 = Vector4( scalar ); - mCol1 = Vector4( scalar ); - mCol2 = Vector4( scalar ); - mCol3 = Vector4( scalar ); -} - -inline Matrix4::Matrix4( const Transform3 & mat ) -{ - mCol0 = Vector4( mat.getCol0(), 0.0f ); - mCol1 = Vector4( mat.getCol1(), 0.0f ); - mCol2 = Vector4( mat.getCol2(), 0.0f ); - mCol3 = Vector4( mat.getCol3(), 1.0f ); -} - -inline Matrix4::Matrix4( Vector4 _col0, Vector4 _col1, Vector4 _col2, Vector4 _col3 ) -{ - mCol0 = _col0; - mCol1 = _col1; - mCol2 = _col2; - mCol3 = _col3; -} - -inline Matrix4::Matrix4( const Matrix3 & mat, Vector3 translateVec ) -{ - mCol0 = Vector4( mat.getCol0(), 0.0f ); - mCol1 = Vector4( mat.getCol1(), 0.0f ); - mCol2 = Vector4( mat.getCol2(), 0.0f ); - mCol3 = Vector4( translateVec, 1.0f ); -} - -inline Matrix4::Matrix4( Quat unitQuat, Vector3 translateVec ) -{ - Matrix3 mat; - mat = Matrix3( unitQuat ); - mCol0 = Vector4( mat.getCol0(), 0.0f ); - mCol1 = Vector4( mat.getCol1(), 0.0f ); - mCol2 = Vector4( mat.getCol2(), 0.0f ); - mCol3 = Vector4( translateVec, 1.0f ); -} - -inline Matrix4 & Matrix4::setCol0( Vector4 _col0 ) -{ - mCol0 = _col0; - return *this; -} - -inline Matrix4 & Matrix4::setCol1( Vector4 _col1 ) -{ - mCol1 = _col1; - return *this; -} - -inline Matrix4 & Matrix4::setCol2( Vector4 _col2 ) -{ - mCol2 = _col2; - return *this; -} - -inline Matrix4 & Matrix4::setCol3( Vector4 _col3 ) -{ - mCol3 = _col3; - return *this; -} - -inline Matrix4 & Matrix4::setCol( int col, Vector4 vec ) -{ - *(&mCol0 + col) = vec; - return *this; -} - -inline Matrix4 & Matrix4::setRow( int row, Vector4 vec ) -{ - mCol0.setElem( row, vec.getElem( 0 ) ); - mCol1.setElem( row, vec.getElem( 1 ) ); - mCol2.setElem( row, vec.getElem( 2 ) ); - mCol3.setElem( row, vec.getElem( 3 ) ); - return *this; -} - -inline Matrix4 & Matrix4::setElem( int col, int row, float val ) -{ - (*this)[col].setElem(row, val); - return *this; -} - -inline float Matrix4::getElem( int col, int row ) const -{ - return this->getCol( col ).getElem( row ); -} - -inline const Vector4 Matrix4::getCol0( ) const -{ - return mCol0; -} - -inline const Vector4 Matrix4::getCol1( ) const -{ - return mCol1; -} - -inline const Vector4 Matrix4::getCol2( ) const -{ - return mCol2; -} - -inline const Vector4 Matrix4::getCol3( ) const -{ - return mCol3; -} - -inline const Vector4 Matrix4::getCol( int col ) const -{ - return *(&mCol0 + col); -} - -inline const Vector4 Matrix4::getRow( int row ) const -{ - return Vector4( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ), mCol3.getElem( row ) ); -} - -inline Vector4 & Matrix4::operator []( int col ) -{ - return *(&mCol0 + col); -} - -inline const Vector4 Matrix4::operator []( int col ) const -{ - return *(&mCol0 + col); -} - -inline Matrix4 & Matrix4::operator =( const Matrix4 & mat ) -{ - mCol0 = mat.mCol0; - mCol1 = mat.mCol1; - mCol2 = mat.mCol2; - mCol3 = mat.mCol3; - return *this; -} - -inline const Matrix4 transpose( const Matrix4 & mat ) -{ - vec_float4 tmp0, tmp1, tmp2, tmp3, res0, res1, res2, res3; - tmp0 = spu_shuffle( mat.getCol0().get128(), mat.getCol2().get128(), _VECTORMATH_SHUF_XAYB ); - tmp1 = spu_shuffle( mat.getCol1().get128(), mat.getCol3().get128(), _VECTORMATH_SHUF_XAYB ); - tmp2 = spu_shuffle( mat.getCol0().get128(), mat.getCol2().get128(), _VECTORMATH_SHUF_ZCWD ); - tmp3 = spu_shuffle( mat.getCol1().get128(), mat.getCol3().get128(), _VECTORMATH_SHUF_ZCWD ); - res0 = spu_shuffle( tmp0, tmp1, _VECTORMATH_SHUF_XAYB ); - res1 = spu_shuffle( tmp0, tmp1, _VECTORMATH_SHUF_ZCWD ); - res2 = spu_shuffle( tmp2, tmp3, _VECTORMATH_SHUF_XAYB ); - res3 = spu_shuffle( tmp2, tmp3, _VECTORMATH_SHUF_ZCWD ); - return Matrix4( - Vector4( res0 ), - Vector4( res1 ), - Vector4( res2 ), - Vector4( res3 ) - ); -} - -inline const Matrix4 inverse( const Matrix4 & mat ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vec_float4 in0, in1, in2, in3; - vec_float4 tmp0, tmp1, tmp2, tmp3; - vec_float4 cof0, cof1, cof2, cof3; - vec_float4 t0, t1, t2, t3; - vec_float4 t01, t02, t03, t12, t23; - vec_float4 t1r, t2r; - vec_float4 t01r, t02r, t03r, t12r, t23r; - vec_float4 t1r3, t1r3r; - vec_float4 det, det1, det2, det3, invdet; - in0 = mat.getCol0().get128(); - in1 = mat.getCol1().get128(); - in2 = mat.getCol2().get128(); - in3 = mat.getCol3().get128(); - /* Perform transform of the input matrix of the form: - * A B C D - * E F G H - * I J K L - * M N O P - * - * The pseudo transpose of the input matrix is trans: - * A E I M - * J N B F - * C G K O - * L P D H - */ - tmp0 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_XAZC); /* A E C G */ - tmp1 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_XAZC); /* I M K O */ - tmp2 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_YBWD); /* B F D H */ - tmp3 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_YBWD); /* J N L P */ - t0 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_XYAB); /* A E I M */ - t1 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_XYAB); /* J N B F */ - t2 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_ZWCD); /* C G K O */ - t3 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_ZWCD); /* L P D H */ - /* Generate a cofactor matrix. The computed cofactors reside in - * cof0, cof1, cof2, cof3. - */ - t23 = spu_mul(t2, t3); /* CL GP KD OH */ - t23 = spu_shuffle(t23, t23, _VECTORMATH_SHUF_YXWZ); /* GP CL OH KD */ - cof0 = spu_mul(t1, t23); /* JGP NCL BOH FKD */ - cof1 = spu_mul(t0, t23); /* AGP ECL IOH MKD */ - t23r = spu_rlqwbyte(t23, 8); /* OH KD GP CL */ - cof0 = spu_msub(t1, t23r, cof0); /* JOH NKD BGP FCL - cof0 */ - cof1 = spu_msub(t0, t23r, cof1); /* AOH EKD IGP MCL - cof1 */ - cof1 = spu_rlqwbyte(cof1, 8); /* IGP MCL AOH EKD - IOH MKD AGP ECL */ - - t12 = spu_mul(t1, t2); /* JC NG BK FO */ - t12 = spu_shuffle(t12, t12, _VECTORMATH_SHUF_YXWZ); /* NG JC FO BK */ - cof0 = spu_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ - cof3 = spu_mul(t0, t12); /* ANG EJC IFO MBK */ - t12r = spu_rlqwbyte(t12, 8); /* FO BK NG JC */ - cof0 = spu_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ - cof3 = spu_msub(t0, t12r, cof3); /* AFO EBK ING MJC - cof3 */ - cof3 = spu_rlqwbyte(cof3, 8); /* ING MJC AFO EBK - IFO MBK ANG EJC */ - t1r = spu_rlqwbyte(t1, 8); /* B F J N */ - t2r = spu_rlqwbyte(t2, 8); /* K O C G */ - t1r3 = spu_mul(t1r, t3); /* BL FP JD NH */ - t1r3 = spu_shuffle(t1r3, t1r3, _VECTORMATH_SHUF_YXWZ); /* FP BL NH JD */ - cof0 = spu_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ - cof2 = spu_mul(t0, t1r3); /* AFP EBL INH MJD */ - t1r3r = spu_rlqwbyte(t1r3, 8); /* NH JD FP BL */ - cof0 = spu_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ - cof2 = spu_msub(t0, t1r3r, cof2); /* ANH EJD IFP MBL - cof2 */ - cof2 = spu_rlqwbyte(cof2, 8); /* IFP MBL ANH EJD - INH MJD AFP EBL */ - t01 = spu_mul(t0, t1); /* AJ EN IB MF */ - t01 = spu_shuffle(t01, t01, _VECTORMATH_SHUF_YXWZ); /* EN AJ MF IB */ - cof2 = spu_madd(t3, t01, cof2); /* LEN PAJ DMF HIB + cof2 */ - cof3 = spu_msub(t2r, t01, cof3); /* KEN OAJ CMF GIB - cof3 */ - t01r = spu_rlqwbyte(t01, 8); /* MF IB EN AJ */ - cof2 = spu_msub(t3, t01r, cof2); /* LMF PIB DEN HAJ - cof2 */ - cof3 = spu_nmsub(t2r, t01r, cof3); /* cof3 - KMF OIB CEN GAJ */ - t03 = spu_mul(t0, t3); /* AL EP ID MH */ - t03 = spu_shuffle(t03, t03, _VECTORMATH_SHUF_YXWZ); /* EP AL MH ID */ - cof1 = spu_nmsub(t2r, t03, cof1); /* cof1 - KEP OAL CMH GID */ - cof2 = spu_madd(t1, t03, cof2); /* JEP NAL BMH FID + cof2 */ - t03r = spu_rlqwbyte(t03, 8); /* MH ID EP AL */ - cof1 = spu_madd(t2r, t03r, cof1); /* KMH OID CEP GAL + cof1 */ - cof2 = spu_nmsub(t1, t03r, cof2); /* cof2 - JMH NID BEP FAL */ - t02 = spu_mul(t0, t2r); /* AK EO IC MG */ - t02 = spu_shuffle(t02, t02, _VECTORMATH_SHUF_YXWZ); /* E0 AK MG IC */ - cof1 = spu_madd(t3, t02, cof1); /* LEO PAK DMG HIC + cof1 */ - cof3 = spu_nmsub(t1, t02, cof3); /* cof3 - JEO NAK BMG FIC */ - t02r = spu_rlqwbyte(t02, 8); /* MG IC EO AK */ - cof1 = spu_nmsub(t3, t02r, cof1); /* cof1 - LMG PIC DEO HAK */ - cof3 = spu_madd(t1, t02r, cof3); /* JMG NIC BEO FAK + cof3 */ - /* Compute the determinant of the matrix - * - * det = sum_across(t0 * cof0); - * - * We perform a sum across the entire vector so that - * we don't have to splat the result when multiplying the - * cofactors by the inverse of the determinant. - */ - det = spu_mul(t0, cof0); - det1 = spu_rlqwbyte(det, 4); - det2 = spu_rlqwbyte(det, 8); - det3 = spu_rlqwbyte(det, 12); - det = spu_add(det, det1); - det2 = spu_add(det2, det3); - det = spu_add(det, det2); - /* Compute the reciprocal of the determinant. - */ - invdet = recipf4(det); - /* Multiply the cofactors by the reciprocal of the determinant. - */ - return Matrix4( - Vector4( spu_mul(cof0, invdet) ), - Vector4( spu_mul(cof1, invdet) ), - Vector4( spu_mul(cof2, invdet) ), - Vector4( spu_mul(cof3, invdet) ) - ); -} - -inline const Matrix4 affineInverse( const Matrix4 & mat ) -{ - Transform3 affineMat; - affineMat.setCol0( mat.getCol0().getXYZ( ) ); - affineMat.setCol1( mat.getCol1().getXYZ( ) ); - affineMat.setCol2( mat.getCol2().getXYZ( ) ); - affineMat.setCol3( mat.getCol3().getXYZ( ) ); - return Matrix4( inverse( affineMat ) ); -} - -inline const Matrix4 orthoInverse( const Matrix4 & mat ) -{ - Transform3 affineMat; - affineMat.setCol0( mat.getCol0().getXYZ( ) ); - affineMat.setCol1( mat.getCol1().getXYZ( ) ); - affineMat.setCol2( mat.getCol2().getXYZ( ) ); - affineMat.setCol3( mat.getCol3().getXYZ( ) ); - return Matrix4( orthoInverse( affineMat ) ); -} - -inline float determinant( const Matrix4 & mat ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vec_float4 in0, in1, in2, in3; - vec_float4 tmp0, tmp1, tmp2, tmp3; - vec_float4 cof0; - vec_float4 t0, t1, t2, t3; - vec_float4 t12, t23; - vec_float4 t1r, t2r; - vec_float4 t12r, t23r; - vec_float4 t1r3, t1r3r; - in0 = mat.getCol0().get128(); - in1 = mat.getCol1().get128(); - in2 = mat.getCol2().get128(); - in3 = mat.getCol3().get128(); - /* Perform transform of the input matrix of the form: - * A B C D - * E F G H - * I J K L - * M N O P - * - * The pseudo transpose of the input matrix is trans: - * A E I M - * J N B F - * C G K O - * L P D H - */ - tmp0 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_XAZC); /* A E C G */ - tmp1 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_XAZC); /* I M K O */ - tmp2 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_YBWD); /* B F D H */ - tmp3 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_YBWD); /* J N L P */ - t0 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_XYAB); /* A E I M */ - t1 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_XYAB); /* J N B F */ - t2 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_ZWCD); /* C G K O */ - t3 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_ZWCD); /* L P D H */ - /* Generate a cofactor matrix. The computed cofactors reside in - * cof0, cof1, cof2, cof3. - */ - t23 = spu_mul(t2, t3); /* CL GP KD OH */ - t23 = spu_shuffle(t23, t23, _VECTORMATH_SHUF_YXWZ); /* GP CL OH KD */ - cof0 = spu_mul(t1, t23); /* JGP NCL BOH FKD */ - t23r = spu_rlqwbyte(t23, 8); /* OH KD GP CL */ - cof0 = spu_msub(t1, t23r, cof0); /* JOH NKD BGP FCL - cof0 */ - - t12 = spu_mul(t1, t2); /* JC NG BK FO */ - t12 = spu_shuffle(t12, t12, _VECTORMATH_SHUF_YXWZ); /* NG JC FO BK */ - cof0 = spu_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ - t12r = spu_rlqwbyte(t12, 8); /* FO BK NG JC */ - cof0 = spu_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ - t1r = spu_rlqwbyte(t1, 8); /* B F J N */ - t2r = spu_rlqwbyte(t2, 8); /* K O C G */ - t1r3 = spu_mul(t1r, t3); /* BL FP JD NH */ - t1r3 = spu_shuffle(t1r3, t1r3, _VECTORMATH_SHUF_YXWZ); /* FP BL NH JD */ - cof0 = spu_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ - t1r3r = spu_rlqwbyte(t1r3, 8); /* NH JD FP BL */ - cof0 = spu_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ - return spu_extract( _vmathVfDot4(t0,cof0), 0 ); -} - -inline const Matrix4 Matrix4::operator +( const Matrix4 & mat ) const -{ - return Matrix4( - ( mCol0 + mat.mCol0 ), - ( mCol1 + mat.mCol1 ), - ( mCol2 + mat.mCol2 ), - ( mCol3 + mat.mCol3 ) - ); -} - -inline const Matrix4 Matrix4::operator -( const Matrix4 & mat ) const -{ - return Matrix4( - ( mCol0 - mat.mCol0 ), - ( mCol1 - mat.mCol1 ), - ( mCol2 - mat.mCol2 ), - ( mCol3 - mat.mCol3 ) - ); -} - -inline Matrix4 & Matrix4::operator +=( const Matrix4 & mat ) -{ - *this = *this + mat; - return *this; -} - -inline Matrix4 & Matrix4::operator -=( const Matrix4 & mat ) -{ - *this = *this - mat; - return *this; -} - -inline const Matrix4 Matrix4::operator -( ) const -{ - return Matrix4( - ( -mCol0 ), - ( -mCol1 ), - ( -mCol2 ), - ( -mCol3 ) - ); -} - -inline const Matrix4 absPerElem( const Matrix4 & mat ) -{ - return Matrix4( - absPerElem( mat.getCol0() ), - absPerElem( mat.getCol1() ), - absPerElem( mat.getCol2() ), - absPerElem( mat.getCol3() ) - ); -} - -inline const Matrix4 Matrix4::operator *( float scalar ) const -{ - return Matrix4( - ( mCol0 * scalar ), - ( mCol1 * scalar ), - ( mCol2 * scalar ), - ( mCol3 * scalar ) - ); -} - -inline Matrix4 & Matrix4::operator *=( float scalar ) -{ - *this = *this * scalar; - return *this; -} - -inline const Matrix4 operator *( float scalar, const Matrix4 & mat ) -{ - return mat * scalar; -} - -inline const Vector4 Matrix4::operator *( Vector4 vec ) const -{ - vec_float4 tmp0, tmp1, res; - vec_float4 xxxx, yyyy, zzzz, wwww; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); - xxxx = spu_shuffle( vec.get128(), vec.get128(), shuffle_xxxx ); - yyyy = spu_shuffle( vec.get128(), vec.get128(), shuffle_yyyy ); - zzzz = spu_shuffle( vec.get128(), vec.get128(), shuffle_zzzz ); - wwww = spu_shuffle( vec.get128(), vec.get128(), shuffle_wwww ); - tmp0 = spu_mul( mCol0.get128(), xxxx ); - tmp1 = spu_mul( mCol1.get128(), yyyy ); - tmp0 = spu_madd( mCol2.get128(), zzzz, tmp0 ); - tmp1 = spu_madd( mCol3.get128(), wwww, tmp1 ); - res = spu_add( tmp0, tmp1 ); - return Vector4( res ); -} - -inline const Vector4 Matrix4::operator *( Vector3 vec ) const -{ - vec_float4 res; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - xxxx = spu_shuffle( vec.get128(), vec.get128(), shuffle_xxxx ); - yyyy = spu_shuffle( vec.get128(), vec.get128(), shuffle_yyyy ); - zzzz = spu_shuffle( vec.get128(), vec.get128(), shuffle_zzzz ); - res = spu_mul( mCol0.get128(), xxxx ); - res = spu_madd( mCol1.get128(), yyyy, res ); - res = spu_madd( mCol2.get128(), zzzz, res ); - return Vector4( res ); -} - -inline const Vector4 Matrix4::operator *( Point3 pnt ) const -{ - vec_float4 tmp0, tmp1, res; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - xxxx = spu_shuffle( pnt.get128(), pnt.get128(), shuffle_xxxx ); - yyyy = spu_shuffle( pnt.get128(), pnt.get128(), shuffle_yyyy ); - zzzz = spu_shuffle( pnt.get128(), pnt.get128(), shuffle_zzzz ); - tmp0 = spu_mul( mCol0.get128(), xxxx ); - tmp1 = spu_mul( mCol1.get128(), yyyy ); - tmp0 = spu_madd( mCol2.get128(), zzzz, tmp0 ); - tmp1 = spu_add( mCol3.get128(), tmp1 ); - res = spu_add( tmp0, tmp1 ); - return Vector4( res ); -} - -inline const Matrix4 Matrix4::operator *( const Matrix4 & mat ) const -{ - return Matrix4( - ( *this * mat.mCol0 ), - ( *this * mat.mCol1 ), - ( *this * mat.mCol2 ), - ( *this * mat.mCol3 ) - ); -} - -inline Matrix4 & Matrix4::operator *=( const Matrix4 & mat ) -{ - *this = *this * mat; - return *this; -} - -inline const Matrix4 Matrix4::operator *( const Transform3 & tfrm ) const -{ - return Matrix4( - ( *this * tfrm.getCol0() ), - ( *this * tfrm.getCol1() ), - ( *this * tfrm.getCol2() ), - ( *this * Point3( tfrm.getCol3() ) ) - ); -} - -inline Matrix4 & Matrix4::operator *=( const Transform3 & tfrm ) -{ - *this = *this * tfrm; - return *this; -} - -inline const Matrix4 mulPerElem( const Matrix4 & mat0, const Matrix4 & mat1 ) -{ - return Matrix4( - mulPerElem( mat0.getCol0(), mat1.getCol0() ), - mulPerElem( mat0.getCol1(), mat1.getCol1() ), - mulPerElem( mat0.getCol2(), mat1.getCol2() ), - mulPerElem( mat0.getCol3(), mat1.getCol3() ) - ); -} - -inline const Matrix4 Matrix4::identity( ) -{ - return Matrix4( - Vector4::xAxis( ), - Vector4::yAxis( ), - Vector4::zAxis( ), - Vector4::wAxis( ) - ); -} - -inline Matrix4 & Matrix4::setUpper3x3( const Matrix3 & mat3 ) -{ - mCol0.setXYZ( mat3.getCol0() ); - mCol1.setXYZ( mat3.getCol1() ); - mCol2.setXYZ( mat3.getCol2() ); - return *this; -} - -inline const Matrix3 Matrix4::getUpper3x3( ) const -{ - return Matrix3( - mCol0.getXYZ( ), - mCol1.getXYZ( ), - mCol2.getXYZ( ) - ); -} - -inline Matrix4 & Matrix4::setTranslation( Vector3 translateVec ) -{ - mCol3.setXYZ( translateVec ); - return *this; -} - -inline const Vector3 Matrix4::getTranslation( ) const -{ - return mCol3.getXYZ( ); -} - -inline const Matrix4 Matrix4::rotationX( float radians ) -{ - vec_float4 s, c, res1, res2; - vec_uint4 select_y, select_z; - vec_float4 zero; - select_y = (vec_uint4)spu_maskb(0x0f00); - select_z = (vec_uint4)spu_maskb(0x00f0); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res1 = spu_sel( zero, c, select_y ); - res1 = spu_sel( res1, s, select_z ); - res2 = spu_sel( zero, negatef4(s), select_y ); - res2 = spu_sel( res2, c, select_z ); - return Matrix4( - Vector4::xAxis( ), - Vector4( res1 ), - Vector4( res2 ), - Vector4::wAxis( ) - ); -} - -inline const Matrix4 Matrix4::rotationY( float radians ) -{ - vec_float4 s, c, res0, res2; - vec_uint4 select_x, select_z; - vec_float4 zero; - select_x = (vec_uint4)spu_maskb(0xf000); - select_z = (vec_uint4)spu_maskb(0x00f0); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res0 = spu_sel( zero, c, select_x ); - res0 = spu_sel( res0, negatef4(s), select_z ); - res2 = spu_sel( zero, s, select_x ); - res2 = spu_sel( res2, c, select_z ); - return Matrix4( - Vector4( res0 ), - Vector4::yAxis( ), - Vector4( res2 ), - Vector4::wAxis( ) - ); -} - -inline const Matrix4 Matrix4::rotationZ( float radians ) -{ - vec_float4 s, c, res0, res1; - vec_uint4 select_x, select_y; - vec_float4 zero; - select_x = (vec_uint4)spu_maskb(0xf000); - select_y = (vec_uint4)spu_maskb(0x0f00); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res0 = spu_sel( zero, c, select_x ); - res0 = spu_sel( res0, s, select_y ); - res1 = spu_sel( zero, negatef4(s), select_x ); - res1 = spu_sel( res1, c, select_y ); - return Matrix4( - Vector4( res0 ), - Vector4( res1 ), - Vector4::zAxis( ), - Vector4::wAxis( ) - ); -} - -inline const Matrix4 Matrix4::rotationZYX( Vector3 radiansXYZ ) -{ - vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - angles = radiansXYZ.get128(); - angles = spu_insert( 0.0f, angles, 3 ); - sincosf4( angles, &s, &c ); - negS = negatef4( s ); - Z0 = spu_shuffle( s, c, _VECTORMATH_SHUF_CZD0 ); - Z1 = spu_shuffle( c, negS, _VECTORMATH_SHUF_CZD0 ); - Y0 = spu_shuffle( negS, c, _VECTORMATH_SHUF_BBY0 ); - Y1 = spu_shuffle( c, s, _VECTORMATH_SHUF_BBY0 ); - X0 = spu_shuffle( s, s, shuffle_xxxx ); - X1 = spu_shuffle( c, c, shuffle_xxxx ); - tmp = spu_mul( Z0, Y1 ); - return Matrix4( - Vector4( spu_mul( Z0, Y0 ) ), - Vector4( spu_madd( Z1, X1, spu_mul( tmp, X0 ) ) ), - Vector4( spu_nmsub( Z1, X0, spu_mul( tmp, X1 ) ) ), - Vector4::wAxis( ) - ); -} - -inline const Matrix4 Matrix4::rotation( float radians, Vector3 unitVec ) -{ - vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2, zeroW; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - axis = unitVec.get128(); - sincosf4( spu_splats( radians ), &s, &c ); - xxxx = spu_shuffle( axis, axis, shuffle_xxxx ); - yyyy = spu_shuffle( axis, axis, shuffle_yyyy ); - zzzz = spu_shuffle( axis, axis, shuffle_zzzz ); - oneMinusC = spu_sub( spu_splats(1.0f), c ); - axisS = spu_mul( axis, s ); - negAxisS = negatef4( axisS ); - tmp0 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_0ZB0 ); - tmp1 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_C0X0 ); - tmp2 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_YA00 ); - tmp0 = spu_sel( tmp0, c, (vec_uint4)spu_maskb(0xf000) ); - tmp1 = spu_sel( tmp1, c, (vec_uint4)spu_maskb(0x0f00) ); - tmp2 = spu_sel( tmp2, c, (vec_uint4)spu_maskb(0x00f0) ); - zeroW = (vec_float4)spu_maskb(0x000f); - axis = spu_andc( axis, zeroW ); - return Matrix4( - Vector4( spu_madd( spu_mul( axis, xxxx ), oneMinusC, tmp0 ) ), - Vector4( spu_madd( spu_mul( axis, yyyy ), oneMinusC, tmp1 ) ), - Vector4( spu_madd( spu_mul( axis, zzzz ), oneMinusC, tmp2 ) ), - Vector4::wAxis( ) - ); -} - -inline const Matrix4 Matrix4::rotation( Quat unitQuat ) -{ - return Matrix4( Transform3::rotation( unitQuat ) ); -} - -inline const Matrix4 Matrix4::scale( Vector3 scaleVec ) -{ - vec_float4 zero = spu_splats(0.0f); - return Matrix4( - Vector4( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0xf000) ) ), - Vector4( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0x0f00) ) ), - Vector4( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0x00f0) ) ), - Vector4::wAxis( ) - ); -} - -inline const Matrix4 appendScale( const Matrix4 & mat, Vector3 scaleVec ) -{ - return Matrix4( - ( mat.getCol0() * scaleVec.getX( ) ), - ( mat.getCol1() * scaleVec.getY( ) ), - ( mat.getCol2() * scaleVec.getZ( ) ), - mat.getCol3() - ); -} - -inline const Matrix4 prependScale( Vector3 scaleVec, const Matrix4 & mat ) -{ - Vector4 scale4; - scale4 = Vector4( scaleVec, 1.0f ); - return Matrix4( - mulPerElem( mat.getCol0(), scale4 ), - mulPerElem( mat.getCol1(), scale4 ), - mulPerElem( mat.getCol2(), scale4 ), - mulPerElem( mat.getCol3(), scale4 ) - ); -} - -inline const Matrix4 Matrix4::translation( Vector3 translateVec ) -{ - return Matrix4( - Vector4::xAxis( ), - Vector4::yAxis( ), - Vector4::zAxis( ), - Vector4( translateVec, 1.0f ) - ); -} - -inline const Matrix4 Matrix4::lookAt( Point3 eyePos, Point3 lookAtPos, Vector3 upVec ) -{ - Matrix4 m4EyeFrame; - Vector3 v3X, v3Y, v3Z; - v3Y = normalize( upVec ); - v3Z = normalize( ( eyePos - lookAtPos ) ); - v3X = normalize( cross( v3Y, v3Z ) ); - v3Y = cross( v3Z, v3X ); - m4EyeFrame = Matrix4( Vector4( v3X ), Vector4( v3Y ), Vector4( v3Z ), Vector4( eyePos ) ); - return orthoInverse( m4EyeFrame ); -} - -inline const Matrix4 Matrix4::perspective( float fovyRadians, float aspect, float zNear, float zFar ) -{ - float f, rangeInv; - vec_float4 zero, col0, col1, col2, col3; - f = tanf( _VECTORMATH_PI_OVER_2 - fovyRadians * 0.5f ); - rangeInv = 1.0f / ( zNear - zFar ); - zero = spu_splats(0.0f); - col0 = zero; - col1 = zero; - col2 = zero; - col3 = zero; - col0 = spu_insert( f / aspect, col0, 0 ); - col1 = spu_insert( f, col1, 1 ); - col2 = spu_insert( ( zNear + zFar ) * rangeInv, col2, 2 ); - col2 = spu_insert( -1.0f, col2, 3 ); - col3 = spu_insert( zNear * zFar * rangeInv * 2.0f, col3, 2 ); - return Matrix4( - Vector4( col0 ), - Vector4( col1 ), - Vector4( col2 ), - Vector4( col3 ) - ); -} - -inline const Matrix4 Matrix4::frustum( float left, float right, float bottom, float top, float zNear, float zFar ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vec_float4 lbf, rtn; - vec_float4 diff, sum, inv_diff; - vec_float4 diagonal, column, near2; - vec_float4 zero = spu_splats(0.0f); - lbf = spu_shuffle( spu_promote(left,0), spu_promote(zFar,0), _VECTORMATH_SHUF_XAYB ); - rtn = spu_shuffle( spu_promote(right,0), spu_promote(zNear,0), _VECTORMATH_SHUF_XAYB ); - lbf = spu_shuffle( lbf, spu_promote(bottom,0), _VECTORMATH_SHUF_XAYB ); - rtn = spu_shuffle( rtn, spu_promote(top,0), _VECTORMATH_SHUF_XAYB ); - diff = spu_sub( rtn, lbf ); - sum = spu_add( rtn, lbf ); - inv_diff = recipf4( diff ); - near2 = spu_splats( zNear ); - near2 = spu_add( near2, near2 ); - diagonal = spu_mul( near2, inv_diff ); - column = spu_mul( sum, inv_diff ); - return Matrix4( - Vector4( spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0xf000) ) ), - Vector4( spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0x0f00) ) ), - Vector4( spu_sel( column, spu_splats(-1.0f), (vec_uint4)spu_maskb(0x000f) ) ), - Vector4( spu_sel( zero, spu_mul( diagonal, spu_splats(zFar) ), (vec_uint4)spu_maskb(0x00f0) ) ) - ); -} - -inline const Matrix4 Matrix4::orthographic( float left, float right, float bottom, float top, float zNear, float zFar ) -{ - /* function implementation based on code from STIDC SDK: */ - /* -------------------------------------------------------------- */ - /* PLEASE DO NOT MODIFY THIS SECTION */ - /* This prolog section is automatically generated. */ - /* */ - /* (C)Copyright */ - /* Sony Computer Entertainment, Inc., */ - /* Toshiba Corporation, */ - /* International Business Machines Corporation, */ - /* 2001,2002. */ - /* S/T/I Confidential Information */ - /* -------------------------------------------------------------- */ - vec_float4 lbf, rtn; - vec_float4 diff, sum, inv_diff, neg_inv_diff; - vec_float4 diagonal, column; - vec_float4 zero = spu_splats(0.0f); - lbf = spu_shuffle( spu_promote(left,0), spu_promote(zFar,0), _VECTORMATH_SHUF_XAYB ); - rtn = spu_shuffle( spu_promote(right,0), spu_promote(zNear,0), _VECTORMATH_SHUF_XAYB ); - lbf = spu_shuffle( lbf, spu_promote(bottom,0), _VECTORMATH_SHUF_XAYB ); - rtn = spu_shuffle( rtn, spu_promote(top,0), _VECTORMATH_SHUF_XAYB ); - diff = spu_sub( rtn, lbf ); - sum = spu_add( rtn, lbf ); - inv_diff = recipf4( diff ); - neg_inv_diff = negatef4( inv_diff ); - diagonal = spu_add( inv_diff, inv_diff ); - column = spu_mul( sum, spu_sel( neg_inv_diff, inv_diff, (vec_uint4)spu_maskb(0x00f0) ) ); - return Matrix4( - Vector4( spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0xf000) ) ), - Vector4( spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0x0f00) ) ), - Vector4( spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0x00f0) ) ), - Vector4( spu_sel( column, spu_splats(1.0f), (vec_uint4)spu_maskb(0x000f) ) ) - ); -} - -inline const Matrix4 select( const Matrix4 & mat0, const Matrix4 & mat1, bool select1 ) -{ - return Matrix4( - select( mat0.getCol0(), mat1.getCol0(), select1 ), - select( mat0.getCol1(), mat1.getCol1(), select1 ), - select( mat0.getCol2(), mat1.getCol2(), select1 ), - select( mat0.getCol3(), mat1.getCol3(), select1 ) - ); -} - -#ifdef _VECTORMATH_DEBUG - -inline void print( const Matrix4 & mat ) -{ - print( mat.getRow( 0 ) ); - print( mat.getRow( 1 ) ); - print( mat.getRow( 2 ) ); - print( mat.getRow( 3 ) ); -} - -inline void print( const Matrix4 & mat, const char * name ) -{ - printf("%s:\n", name); - print( mat ); -} - -#endif - -inline Transform3::Transform3( const Transform3 & tfrm ) -{ - mCol0 = tfrm.mCol0; - mCol1 = tfrm.mCol1; - mCol2 = tfrm.mCol2; - mCol3 = tfrm.mCol3; -} - -inline Transform3::Transform3( float scalar ) -{ - mCol0 = Vector3( scalar ); - mCol1 = Vector3( scalar ); - mCol2 = Vector3( scalar ); - mCol3 = Vector3( scalar ); -} - -inline Transform3::Transform3( Vector3 _col0, Vector3 _col1, Vector3 _col2, Vector3 _col3 ) -{ - mCol0 = _col0; - mCol1 = _col1; - mCol2 = _col2; - mCol3 = _col3; -} - -inline Transform3::Transform3( const Matrix3 & tfrm, Vector3 translateVec ) -{ - this->setUpper3x3( tfrm ); - this->setTranslation( translateVec ); -} - -inline Transform3::Transform3( Quat unitQuat, Vector3 translateVec ) -{ - this->setUpper3x3( Matrix3( unitQuat ) ); - this->setTranslation( translateVec ); -} - -inline Transform3 & Transform3::setCol0( Vector3 _col0 ) -{ - mCol0 = _col0; - return *this; -} - -inline Transform3 & Transform3::setCol1( Vector3 _col1 ) -{ - mCol1 = _col1; - return *this; -} - -inline Transform3 & Transform3::setCol2( Vector3 _col2 ) -{ - mCol2 = _col2; - return *this; -} - -inline Transform3 & Transform3::setCol3( Vector3 _col3 ) -{ - mCol3 = _col3; - return *this; -} - -inline Transform3 & Transform3::setCol( int col, Vector3 vec ) -{ - *(&mCol0 + col) = vec; - return *this; -} - -inline Transform3 & Transform3::setRow( int row, Vector4 vec ) -{ - mCol0.setElem( row, vec.getElem( 0 ) ); - mCol1.setElem( row, vec.getElem( 1 ) ); - mCol2.setElem( row, vec.getElem( 2 ) ); - mCol3.setElem( row, vec.getElem( 3 ) ); - return *this; -} - -inline Transform3 & Transform3::setElem( int col, int row, float val ) -{ - (*this)[col].setElem(row, val); - return *this; -} - -inline float Transform3::getElem( int col, int row ) const -{ - return this->getCol( col ).getElem( row ); -} - -inline const Vector3 Transform3::getCol0( ) const -{ - return mCol0; -} - -inline const Vector3 Transform3::getCol1( ) const -{ - return mCol1; -} - -inline const Vector3 Transform3::getCol2( ) const -{ - return mCol2; -} - -inline const Vector3 Transform3::getCol3( ) const -{ - return mCol3; -} - -inline const Vector3 Transform3::getCol( int col ) const -{ - return *(&mCol0 + col); -} - -inline const Vector4 Transform3::getRow( int row ) const -{ - return Vector4( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ), mCol3.getElem( row ) ); -} - -inline Vector3 & Transform3::operator []( int col ) -{ - return *(&mCol0 + col); -} - -inline const Vector3 Transform3::operator []( int col ) const -{ - return *(&mCol0 + col); -} - -inline Transform3 & Transform3::operator =( const Transform3 & tfrm ) -{ - mCol0 = tfrm.mCol0; - mCol1 = tfrm.mCol1; - mCol2 = tfrm.mCol2; - mCol3 = tfrm.mCol3; - return *this; -} - -inline const Transform3 inverse( const Transform3 & tfrm ) -{ - vec_float4 inv0, inv1, inv2, inv3; - vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - tmp2 = _vmathVfCross( tfrm.getCol0().get128(), tfrm.getCol1().get128() ); - tmp0 = _vmathVfCross( tfrm.getCol1().get128(), tfrm.getCol2().get128() ); - tmp1 = _vmathVfCross( tfrm.getCol2().get128(), tfrm.getCol0().get128() ); - inv3 = negatef4( tfrm.getCol3().get128() ); - dot = _vmathVfDot3( tmp2, tfrm.getCol2().get128() ); - dot = spu_shuffle( dot, dot, shuffle_xxxx ); - invdet = recipf4( dot ); - tmp3 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_XAYB ); - tmp4 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_ZCWD ); - inv0 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_XAYB ); - xxxx = spu_shuffle( inv3, inv3, shuffle_xxxx ); - inv1 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_ZBW0 ); - inv2 = spu_shuffle( tmp4, tmp1, _VECTORMATH_SHUF_XCY0 ); - yyyy = spu_shuffle( inv3, inv3, shuffle_yyyy ); - zzzz = spu_shuffle( inv3, inv3, shuffle_zzzz ); - inv3 = spu_mul( inv0, xxxx ); - inv3 = spu_madd( inv1, yyyy, inv3 ); - inv3 = spu_madd( inv2, zzzz, inv3 ); - inv0 = spu_mul( inv0, invdet ); - inv1 = spu_mul( inv1, invdet ); - inv2 = spu_mul( inv2, invdet ); - inv3 = spu_mul( inv3, invdet ); - return Transform3( - Vector3( inv0 ), - Vector3( inv1 ), - Vector3( inv2 ), - Vector3( inv3 ) - ); -} - -inline const Transform3 orthoInverse( const Transform3 & tfrm ) -{ - vec_float4 inv0, inv1, inv2, inv3; - vec_float4 tmp0, tmp1; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - tmp0 = spu_shuffle( tfrm.getCol0().get128(), tfrm.getCol2().get128(), _VECTORMATH_SHUF_XAYB ); - tmp1 = spu_shuffle( tfrm.getCol0().get128(), tfrm.getCol2().get128(), _VECTORMATH_SHUF_ZCWD ); - inv3 = negatef4( tfrm.getCol3().get128() ); - inv0 = spu_shuffle( tmp0, tfrm.getCol1().get128(), _VECTORMATH_SHUF_XAYB ); - xxxx = spu_shuffle( inv3, inv3, shuffle_xxxx ); - inv1 = spu_shuffle( tmp0, tfrm.getCol1().get128(), _VECTORMATH_SHUF_ZBW0 ); - inv2 = spu_shuffle( tmp1, tfrm.getCol1().get128(), _VECTORMATH_SHUF_XCY0 ); - yyyy = spu_shuffle( inv3, inv3, shuffle_yyyy ); - zzzz = spu_shuffle( inv3, inv3, shuffle_zzzz ); - inv3 = spu_mul( inv0, xxxx ); - inv3 = spu_madd( inv1, yyyy, inv3 ); - inv3 = spu_madd( inv2, zzzz, inv3 ); - return Transform3( - Vector3( inv0 ), - Vector3( inv1 ), - Vector3( inv2 ), - Vector3( inv3 ) - ); -} - -inline const Transform3 absPerElem( const Transform3 & tfrm ) -{ - return Transform3( - absPerElem( tfrm.getCol0() ), - absPerElem( tfrm.getCol1() ), - absPerElem( tfrm.getCol2() ), - absPerElem( tfrm.getCol3() ) - ); -} - -inline const Vector3 Transform3::operator *( Vector3 vec ) const -{ - vec_float4 res; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - xxxx = spu_shuffle( vec.get128(), vec.get128(), shuffle_xxxx ); - yyyy = spu_shuffle( vec.get128(), vec.get128(), shuffle_yyyy ); - zzzz = spu_shuffle( vec.get128(), vec.get128(), shuffle_zzzz ); - res = spu_mul( mCol0.get128(), xxxx ); - res = spu_madd( mCol1.get128(), yyyy, res ); - res = spu_madd( mCol2.get128(), zzzz, res ); - return Vector3( res ); -} - -inline const Point3 Transform3::operator *( Point3 pnt ) const -{ - vec_float4 tmp0, tmp1, res; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - xxxx = spu_shuffle( pnt.get128(), pnt.get128(), shuffle_xxxx ); - yyyy = spu_shuffle( pnt.get128(), pnt.get128(), shuffle_yyyy ); - zzzz = spu_shuffle( pnt.get128(), pnt.get128(), shuffle_zzzz ); - tmp0 = spu_mul( mCol0.get128(), xxxx ); - tmp1 = spu_mul( mCol1.get128(), yyyy ); - tmp0 = spu_madd( mCol2.get128(), zzzz, tmp0 ); - tmp1 = spu_add( mCol3.get128(), tmp1 ); - res = spu_add( tmp0, tmp1 ); - return Point3( res ); -} - -inline const Transform3 Transform3::operator *( const Transform3 & tfrm ) const -{ - return Transform3( - ( *this * tfrm.mCol0 ), - ( *this * tfrm.mCol1 ), - ( *this * tfrm.mCol2 ), - Vector3( ( *this * Point3( tfrm.mCol3 ) ) ) - ); -} - -inline Transform3 & Transform3::operator *=( const Transform3 & tfrm ) -{ - *this = *this * tfrm; - return *this; -} - -inline const Transform3 mulPerElem( const Transform3 & tfrm0, const Transform3 & tfrm1 ) -{ - return Transform3( - mulPerElem( tfrm0.getCol0(), tfrm1.getCol0() ), - mulPerElem( tfrm0.getCol1(), tfrm1.getCol1() ), - mulPerElem( tfrm0.getCol2(), tfrm1.getCol2() ), - mulPerElem( tfrm0.getCol3(), tfrm1.getCol3() ) - ); -} - -inline const Transform3 Transform3::identity( ) -{ - return Transform3( - Vector3::xAxis( ), - Vector3::yAxis( ), - Vector3::zAxis( ), - Vector3( 0.0f ) - ); -} - -inline Transform3 & Transform3::setUpper3x3( const Matrix3 & tfrm ) -{ - mCol0 = tfrm.getCol0(); - mCol1 = tfrm.getCol1(); - mCol2 = tfrm.getCol2(); - return *this; -} - -inline const Matrix3 Transform3::getUpper3x3( ) const -{ - return Matrix3( mCol0, mCol1, mCol2 ); -} - -inline Transform3 & Transform3::setTranslation( Vector3 translateVec ) -{ - mCol3 = translateVec; - return *this; -} - -inline const Vector3 Transform3::getTranslation( ) const -{ - return mCol3; -} - -inline const Transform3 Transform3::rotationX( float radians ) -{ - vec_float4 s, c, res1, res2; - vec_uint4 select_y, select_z; - vec_float4 zero; - select_y = (vec_uint4)spu_maskb(0x0f00); - select_z = (vec_uint4)spu_maskb(0x00f0); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res1 = spu_sel( zero, c, select_y ); - res1 = spu_sel( res1, s, select_z ); - res2 = spu_sel( zero, negatef4(s), select_y ); - res2 = spu_sel( res2, c, select_z ); - return Transform3( - Vector3::xAxis( ), - Vector3( res1 ), - Vector3( res2 ), - Vector3( 0.0f ) - ); -} - -inline const Transform3 Transform3::rotationY( float radians ) -{ - vec_float4 s, c, res0, res2; - vec_uint4 select_x, select_z; - vec_float4 zero; - select_x = (vec_uint4)spu_maskb(0xf000); - select_z = (vec_uint4)spu_maskb(0x00f0); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res0 = spu_sel( zero, c, select_x ); - res0 = spu_sel( res0, negatef4(s), select_z ); - res2 = spu_sel( zero, s, select_x ); - res2 = spu_sel( res2, c, select_z ); - return Transform3( - Vector3( res0 ), - Vector3::yAxis( ), - Vector3( res2 ), - Vector3( 0.0f ) - ); -} - -inline const Transform3 Transform3::rotationZ( float radians ) -{ - vec_float4 s, c, res0, res1; - vec_uint4 select_x, select_y; - vec_float4 zero; - select_x = (vec_uint4)spu_maskb(0xf000); - select_y = (vec_uint4)spu_maskb(0x0f00); - zero = spu_splats(0.0f); - sincosf4( spu_splats(radians), &s, &c ); - res0 = spu_sel( zero, c, select_x ); - res0 = spu_sel( res0, s, select_y ); - res1 = spu_sel( zero, negatef4(s), select_x ); - res1 = spu_sel( res1, c, select_y ); - return Transform3( - Vector3( res0 ), - Vector3( res1 ), - Vector3::zAxis( ), - Vector3( 0.0f ) - ); -} - -inline const Transform3 Transform3::rotationZYX( Vector3 radiansXYZ ) -{ - vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - angles = radiansXYZ.get128(); - angles = spu_insert( 0.0f, angles, 3 ); - sincosf4( angles, &s, &c ); - negS = negatef4( s ); - Z0 = spu_shuffle( s, c, _VECTORMATH_SHUF_CZD0 ); - Z1 = spu_shuffle( c, negS, _VECTORMATH_SHUF_CZD0 ); - Y0 = spu_shuffle( negS, c, _VECTORMATH_SHUF_BBY0 ); - Y1 = spu_shuffle( c, s, _VECTORMATH_SHUF_BBY0 ); - X0 = spu_shuffle( s, s, shuffle_xxxx ); - X1 = spu_shuffle( c, c, shuffle_xxxx ); - tmp = spu_mul( Z0, Y1 ); - return Transform3( - Vector3( spu_mul( Z0, Y0 ) ), - Vector3( spu_madd( Z1, X1, spu_mul( tmp, X0 ) ) ), - Vector3( spu_nmsub( Z1, X0, spu_mul( tmp, X1 ) ) ), - Vector3( 0.0f ) - ); -} - -inline const Transform3 Transform3::rotation( float radians, Vector3 unitVec ) -{ - return Transform3( Matrix3::rotation( radians, unitVec ), Vector3( 0.0f ) ); -} - -inline const Transform3 Transform3::rotation( Quat unitQuat ) -{ - return Transform3( Matrix3( unitQuat ), Vector3( 0.0f ) ); -} - -inline const Transform3 Transform3::scale( Vector3 scaleVec ) -{ - vec_float4 zero = spu_splats(0.0f); - return Transform3( - Vector3( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0xf000) ) ), - Vector3( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0x0f00) ) ), - Vector3( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0x00f0) ) ), - Vector3( 0.0f ) - ); -} - -inline const Transform3 appendScale( const Transform3 & tfrm, Vector3 scaleVec ) -{ - return Transform3( - ( tfrm.getCol0() * scaleVec.getX( ) ), - ( tfrm.getCol1() * scaleVec.getY( ) ), - ( tfrm.getCol2() * scaleVec.getZ( ) ), - tfrm.getCol3() - ); -} - -inline const Transform3 prependScale( Vector3 scaleVec, const Transform3 & tfrm ) -{ - return Transform3( - mulPerElem( tfrm.getCol0(), scaleVec ), - mulPerElem( tfrm.getCol1(), scaleVec ), - mulPerElem( tfrm.getCol2(), scaleVec ), - mulPerElem( tfrm.getCol3(), scaleVec ) - ); -} - -inline const Transform3 Transform3::translation( Vector3 translateVec ) -{ - return Transform3( - Vector3::xAxis( ), - Vector3::yAxis( ), - Vector3::zAxis( ), - translateVec - ); -} - -inline const Transform3 select( const Transform3 & tfrm0, const Transform3 & tfrm1, bool select1 ) -{ - return Transform3( - select( tfrm0.getCol0(), tfrm1.getCol0(), select1 ), - select( tfrm0.getCol1(), tfrm1.getCol1(), select1 ), - select( tfrm0.getCol2(), tfrm1.getCol2(), select1 ), - select( tfrm0.getCol3(), tfrm1.getCol3(), select1 ) - ); -} - -#ifdef _VECTORMATH_DEBUG - -inline void print( const Transform3 & tfrm ) -{ - print( tfrm.getRow( 0 ) ); - print( tfrm.getRow( 1 ) ); - print( tfrm.getRow( 2 ) ); -} - -inline void print( const Transform3 & tfrm, const char * name ) -{ - printf("%s:\n", name); - print( tfrm ); -} - -#endif - -inline Quat::Quat( const Matrix3 & tfrm ) -{ - vec_float4 res; - vec_float4 col0, col1, col2; - vec_float4 xx_yy, xx_yy_zz_xx, yy_zz_xx_yy, zz_xx_yy_zz, diagSum, diagDiff; - vec_float4 zy_xz_yx, yz_zx_xy, sum, diff; - vec_float4 radicand, invSqrt, scale; - vec_float4 res0, res1, res2, res3; - vec_float4 xx, yy, zz; - vec_uint4 select_x = (vec_uint4)spu_maskb( 0xf000 ); - vec_uint4 select_y = (vec_uint4)spu_maskb( 0x0f00 ); - vec_uint4 select_z = (vec_uint4)spu_maskb( 0x00f0 ); - vec_uint4 select_w = (vec_uint4)spu_maskb( 0x000f ); - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((unsigned int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((unsigned int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((unsigned int)0x08090a0b); - vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((unsigned int)0x0c0d0e0f); - - col0 = tfrm.getCol0().get128(); - col1 = tfrm.getCol1().get128(); - col2 = tfrm.getCol2().get128(); - - /* four cases: */ - /* trace > 0 */ - /* else */ - /* xx largest diagonal element */ - /* yy largest diagonal element */ - /* zz largest diagonal element */ - - /* compute quaternion for each case */ - - xx_yy = spu_sel( col0, col1, select_y ); - xx_yy_zz_xx = spu_shuffle( xx_yy, col2, _VECTORMATH_SHUF_XYCX ); - yy_zz_xx_yy = spu_shuffle( xx_yy, col2, _VECTORMATH_SHUF_YCXY ); - zz_xx_yy_zz = spu_shuffle( xx_yy, col2, _VECTORMATH_SHUF_CXYC ); - - diagSum = spu_add( spu_add( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); - diagDiff = spu_sub( spu_sub( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); - radicand = spu_add( spu_sel( diagDiff, diagSum, select_w ), spu_splats(1.0f) ); - invSqrt = rsqrtf4( radicand ); - - zy_xz_yx = spu_sel( col0, col1, select_z ); - zy_xz_yx = spu_shuffle( zy_xz_yx, col2, _VECTORMATH_SHUF_ZAY0 ); - yz_zx_xy = spu_sel( col0, col1, select_x ); - yz_zx_xy = spu_shuffle( yz_zx_xy, col2, _VECTORMATH_SHUF_BZX0 ); - - sum = spu_add( zy_xz_yx, yz_zx_xy ); - diff = spu_sub( zy_xz_yx, yz_zx_xy ); - - scale = spu_mul( invSqrt, spu_splats(0.5f) ); - res0 = spu_shuffle( sum, diff, _VECTORMATH_SHUF_0ZYA ); - res1 = spu_shuffle( sum, diff, _VECTORMATH_SHUF_Z0XB ); - res2 = spu_shuffle( sum, diff, _VECTORMATH_SHUF_YX0C ); - res3 = diff; - res0 = spu_sel( res0, radicand, select_x ); - res1 = spu_sel( res1, radicand, select_y ); - res2 = spu_sel( res2, radicand, select_z ); - res3 = spu_sel( res3, radicand, select_w ); - res0 = spu_mul( res0, spu_shuffle( scale, scale, shuffle_xxxx ) ); - res1 = spu_mul( res1, spu_shuffle( scale, scale, shuffle_yyyy ) ); - res2 = spu_mul( res2, spu_shuffle( scale, scale, shuffle_zzzz ) ); - res3 = spu_mul( res3, spu_shuffle( scale, scale, shuffle_wwww ) ); - - /* determine case and select answer */ - - xx = spu_shuffle( col0, col0, shuffle_xxxx ); - yy = spu_shuffle( col1, col1, shuffle_yyyy ); - zz = spu_shuffle( col2, col2, shuffle_zzzz ); - res = spu_sel( res0, res1, spu_cmpgt( yy, xx ) ); - res = spu_sel( res, res2, spu_and( spu_cmpgt( zz, xx ), spu_cmpgt( zz, yy ) ) ); - res = spu_sel( res, res3, spu_cmpgt( spu_shuffle( diagSum, diagSum, shuffle_xxxx ), spu_splats(0.0f) ) ); - mVec128 = res; -} - -inline const Matrix3 outer( Vector3 tfrm0, Vector3 tfrm1 ) -{ - return Matrix3( - ( tfrm0 * tfrm1.getX( ) ), - ( tfrm0 * tfrm1.getY( ) ), - ( tfrm0 * tfrm1.getZ( ) ) - ); -} - -inline const Matrix4 outer( Vector4 tfrm0, Vector4 tfrm1 ) -{ - return Matrix4( - ( tfrm0 * tfrm1.getX( ) ), - ( tfrm0 * tfrm1.getY( ) ), - ( tfrm0 * tfrm1.getZ( ) ), - ( tfrm0 * tfrm1.getW( ) ) - ); -} - -inline const Vector3 rowMul( Vector3 vec, const Matrix3 & mat ) -{ - vec_float4 tmp0, tmp1, mcol0, mcol1, mcol2, res; - vec_float4 xxxx, yyyy, zzzz; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - tmp0 = spu_shuffle( mat.getCol0().get128(), mat.getCol2().get128(), _VECTORMATH_SHUF_XAYB ); - tmp1 = spu_shuffle( mat.getCol0().get128(), mat.getCol2().get128(), _VECTORMATH_SHUF_ZCWD ); - xxxx = spu_shuffle( vec.get128(), vec.get128(), shuffle_xxxx ); - mcol0 = spu_shuffle( tmp0, mat.getCol1().get128(), _VECTORMATH_SHUF_XAYB ); - mcol1 = spu_shuffle( tmp0, mat.getCol1().get128(), _VECTORMATH_SHUF_ZBW0 ); - mcol2 = spu_shuffle( tmp1, mat.getCol1().get128(), _VECTORMATH_SHUF_XCY0 ); - yyyy = spu_shuffle( vec.get128(), vec.get128(), shuffle_yyyy ); - res = spu_mul( mcol0, xxxx ); - zzzz = spu_shuffle( vec.get128(), vec.get128(), shuffle_zzzz ); - res = spu_madd( mcol1, yyyy, res ); - res = spu_madd( mcol2, zzzz, res ); - return Vector3( res ); -} - -inline const Matrix3 crossMatrix( Vector3 vec ) -{ - vec_float4 neg, res0, res1, res2; - neg = negatef4( vec.get128() ); - res0 = spu_shuffle( vec.get128(), neg, _VECTORMATH_SHUF_0ZB0 ); - res1 = spu_shuffle( vec.get128(), neg, _VECTORMATH_SHUF_C0X0 ); - res2 = spu_shuffle( vec.get128(), neg, _VECTORMATH_SHUF_YA00 ); - return Matrix3( - Vector3( res0 ), - Vector3( res1 ), - Vector3( res2 ) - ); -} - -inline const Matrix3 crossMatrixMul( Vector3 vec, const Matrix3 & mat ) -{ - return Matrix3( cross( vec, mat.getCol0() ), cross( vec, mat.getCol1() ), cross( vec, mat.getCol2() ) ); -} - -} // namespace Aos -} // namespace Vectormath - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_MAT_AOS_CPP_H +#define _VECTORMATH_MAT_AOS_CPP_H + +namespace Vectormath { +namespace Aos { + +//----------------------------------------------------------------------------- +// Constants +// for shuffles, words are labeled [x,y,z,w] [a,b,c,d] + +#define _VECTORMATH_SHUF_XAYB ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_B }) +#define _VECTORMATH_SHUF_ZCWD ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_D }) +#define _VECTORMATH_SHUF_ZBW0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_XCY0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_XYAB ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B }) +#define _VECTORMATH_SHUF_ZWCD ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_D }) +#define _VECTORMATH_SHUF_0ZB0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_C0X0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_YA00 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_XAZC ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_C }) +#define _VECTORMATH_SHUF_YXWZ ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_Z }) +#define _VECTORMATH_SHUF_YBWD ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_D }) +#define _VECTORMATH_SHUF_XYCX ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_X }) +#define _VECTORMATH_SHUF_YCXY ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y }) +#define _VECTORMATH_SHUF_CXYC ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_C }) +#define _VECTORMATH_SHUF_ZAY0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_BZX0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_0ZYA ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_A }) +#define _VECTORMATH_SHUF_Z0XB ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_B }) +#define _VECTORMATH_SHUF_YX0C ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_0, _VECTORMATH_SHUF_C }) +#define _VECTORMATH_SHUF_CZD0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_C, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_D, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_SHUF_BBY0 ((vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_0 }) +#define _VECTORMATH_PI_OVER_2 1.570796327f + +//----------------------------------------------------------------------------- +// Definitions + +inline Matrix3::Matrix3( const Matrix3 & mat ) +{ + mCol0 = mat.mCol0; + mCol1 = mat.mCol1; + mCol2 = mat.mCol2; +} + +inline Matrix3::Matrix3( float scalar ) +{ + mCol0 = Vector3( scalar ); + mCol1 = Vector3( scalar ); + mCol2 = Vector3( scalar ); +} + +inline Matrix3::Matrix3( Quat unitQuat ) +{ + vec_float4 xyzw_2, wwww, yzxw, zxyw, yzxw_2, zxyw_2; + vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); + vec_uint4 select_x = (vec_uint4)spu_maskb(0xf000); + vec_uint4 select_z = (vec_uint4)spu_maskb(0x00f0); + xyzw_2 = spu_add( unitQuat.get128(), unitQuat.get128() ); + wwww = spu_shuffle( unitQuat.get128(), unitQuat.get128(), shuffle_wwww ); + yzxw = spu_shuffle( unitQuat.get128(), unitQuat.get128(), _VECTORMATH_SHUF_YZXW ); + zxyw = spu_shuffle( unitQuat.get128(), unitQuat.get128(), _VECTORMATH_SHUF_ZXYW ); + yzxw_2 = spu_shuffle( xyzw_2, xyzw_2, _VECTORMATH_SHUF_YZXW ); + zxyw_2 = spu_shuffle( xyzw_2, xyzw_2, _VECTORMATH_SHUF_ZXYW ); + tmp0 = spu_mul( yzxw_2, wwww ); + tmp1 = spu_nmsub( yzxw, yzxw_2, spu_splats(1.0f) ); + tmp2 = spu_mul( yzxw, xyzw_2 ); + tmp0 = spu_madd( zxyw, xyzw_2, tmp0 ); + tmp1 = spu_nmsub( zxyw, zxyw_2, tmp1 ); + tmp2 = spu_nmsub( zxyw_2, wwww, tmp2 ); + tmp3 = spu_sel( tmp0, tmp1, select_x ); + tmp4 = spu_sel( tmp1, tmp2, select_x ); + tmp5 = spu_sel( tmp2, tmp0, select_x ); + mCol0 = Vector3( spu_sel( tmp3, tmp2, select_z ) ); + mCol1 = Vector3( spu_sel( tmp4, tmp0, select_z ) ); + mCol2 = Vector3( spu_sel( tmp5, tmp1, select_z ) ); +} + +inline Matrix3::Matrix3( Vector3 _col0, Vector3 _col1, Vector3 _col2 ) +{ + mCol0 = _col0; + mCol1 = _col1; + mCol2 = _col2; +} + +inline Matrix3 & Matrix3::setCol0( Vector3 _col0 ) +{ + mCol0 = _col0; + return *this; +} + +inline Matrix3 & Matrix3::setCol1( Vector3 _col1 ) +{ + mCol1 = _col1; + return *this; +} + +inline Matrix3 & Matrix3::setCol2( Vector3 _col2 ) +{ + mCol2 = _col2; + return *this; +} + +inline Matrix3 & Matrix3::setCol( int col, Vector3 vec ) +{ + *(&mCol0 + col) = vec; + return *this; +} + +inline Matrix3 & Matrix3::setRow( int row, Vector3 vec ) +{ + mCol0.setElem( row, vec.getElem( 0 ) ); + mCol1.setElem( row, vec.getElem( 1 ) ); + mCol2.setElem( row, vec.getElem( 2 ) ); + return *this; +} + +inline Matrix3 & Matrix3::setElem( int col, int row, float val ) +{ + (*this)[col].setElem(row, val); + return *this; +} + +inline float Matrix3::getElem( int col, int row ) const +{ + return this->getCol( col ).getElem( row ); +} + +inline const Vector3 Matrix3::getCol0( ) const +{ + return mCol0; +} + +inline const Vector3 Matrix3::getCol1( ) const +{ + return mCol1; +} + +inline const Vector3 Matrix3::getCol2( ) const +{ + return mCol2; +} + +inline const Vector3 Matrix3::getCol( int col ) const +{ + return *(&mCol0 + col); +} + +inline const Vector3 Matrix3::getRow( int row ) const +{ + return Vector3( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ) ); +} + +inline Vector3 & Matrix3::operator []( int col ) +{ + return *(&mCol0 + col); +} + +inline const Vector3 Matrix3::operator []( int col ) const +{ + return *(&mCol0 + col); +} + +inline Matrix3 & Matrix3::operator =( const Matrix3 & mat ) +{ + mCol0 = mat.mCol0; + mCol1 = mat.mCol1; + mCol2 = mat.mCol2; + return *this; +} + +inline const Matrix3 transpose( const Matrix3 & mat ) +{ + vec_float4 tmp0, tmp1, res0, res1, res2; + tmp0 = spu_shuffle( mat.getCol0().get128(), mat.getCol2().get128(), _VECTORMATH_SHUF_XAYB ); + tmp1 = spu_shuffle( mat.getCol0().get128(), mat.getCol2().get128(), _VECTORMATH_SHUF_ZCWD ); + res0 = spu_shuffle( tmp0, mat.getCol1().get128(), _VECTORMATH_SHUF_XAYB ); + res1 = spu_shuffle( tmp0, mat.getCol1().get128(), _VECTORMATH_SHUF_ZBW0 ); + res2 = spu_shuffle( tmp1, mat.getCol1().get128(), _VECTORMATH_SHUF_XCY0 ); + return Matrix3( + Vector3( res0 ), + Vector3( res1 ), + Vector3( res2 ) + ); +} + +inline const Matrix3 inverse( const Matrix3 & mat ) +{ + vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet, inv0, inv1, inv2; + tmp2 = _vmathVfCross( mat.getCol0().get128(), mat.getCol1().get128() ); + tmp0 = _vmathVfCross( mat.getCol1().get128(), mat.getCol2().get128() ); + tmp1 = _vmathVfCross( mat.getCol2().get128(), mat.getCol0().get128() ); + dot = _vmathVfDot3( tmp2, mat.getCol2().get128() ); + dot = spu_shuffle( dot, dot, (vec_uchar16)spu_splats(0x00010203) ); + invdet = recipf4( dot ); + tmp3 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_XAYB ); + tmp4 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_ZCWD ); + inv0 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_XAYB ); + inv1 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_ZBW0 ); + inv2 = spu_shuffle( tmp4, tmp1, _VECTORMATH_SHUF_XCY0 ); + inv0 = spu_mul( inv0, invdet ); + inv1 = spu_mul( inv1, invdet ); + inv2 = spu_mul( inv2, invdet ); + return Matrix3( + Vector3( inv0 ), + Vector3( inv1 ), + Vector3( inv2 ) + ); +} + +inline float determinant( const Matrix3 & mat ) +{ + return dot( mat.getCol2(), cross( mat.getCol0(), mat.getCol1() ) ); +} + +inline const Matrix3 Matrix3::operator +( const Matrix3 & mat ) const +{ + return Matrix3( + ( mCol0 + mat.mCol0 ), + ( mCol1 + mat.mCol1 ), + ( mCol2 + mat.mCol2 ) + ); +} + +inline const Matrix3 Matrix3::operator -( const Matrix3 & mat ) const +{ + return Matrix3( + ( mCol0 - mat.mCol0 ), + ( mCol1 - mat.mCol1 ), + ( mCol2 - mat.mCol2 ) + ); +} + +inline Matrix3 & Matrix3::operator +=( const Matrix3 & mat ) +{ + *this = *this + mat; + return *this; +} + +inline Matrix3 & Matrix3::operator -=( const Matrix3 & mat ) +{ + *this = *this - mat; + return *this; +} + +inline const Matrix3 Matrix3::operator -( ) const +{ + return Matrix3( + ( -mCol0 ), + ( -mCol1 ), + ( -mCol2 ) + ); +} + +inline const Matrix3 absPerElem( const Matrix3 & mat ) +{ + return Matrix3( + absPerElem( mat.getCol0() ), + absPerElem( mat.getCol1() ), + absPerElem( mat.getCol2() ) + ); +} + +inline const Matrix3 Matrix3::operator *( float scalar ) const +{ + return Matrix3( + ( mCol0 * scalar ), + ( mCol1 * scalar ), + ( mCol2 * scalar ) + ); +} + +inline Matrix3 & Matrix3::operator *=( float scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Matrix3 operator *( float scalar, const Matrix3 & mat ) +{ + return mat * scalar; +} + +inline const Vector3 Matrix3::operator *( Vector3 vec ) const +{ + vec_float4 res; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + xxxx = spu_shuffle( vec.get128(), vec.get128(), shuffle_xxxx ); + yyyy = spu_shuffle( vec.get128(), vec.get128(), shuffle_yyyy ); + zzzz = spu_shuffle( vec.get128(), vec.get128(), shuffle_zzzz ); + res = spu_mul( mCol0.get128(), xxxx ); + res = spu_madd( mCol1.get128(), yyyy, res ); + res = spu_madd( mCol2.get128(), zzzz, res ); + return Vector3( res ); +} + +inline const Matrix3 Matrix3::operator *( const Matrix3 & mat ) const +{ + return Matrix3( + ( *this * mat.mCol0 ), + ( *this * mat.mCol1 ), + ( *this * mat.mCol2 ) + ); +} + +inline Matrix3 & Matrix3::operator *=( const Matrix3 & mat ) +{ + *this = *this * mat; + return *this; +} + +inline const Matrix3 mulPerElem( const Matrix3 & mat0, const Matrix3 & mat1 ) +{ + return Matrix3( + mulPerElem( mat0.getCol0(), mat1.getCol0() ), + mulPerElem( mat0.getCol1(), mat1.getCol1() ), + mulPerElem( mat0.getCol2(), mat1.getCol2() ) + ); +} + +inline const Matrix3 Matrix3::identity( ) +{ + return Matrix3( + Vector3::xAxis( ), + Vector3::yAxis( ), + Vector3::zAxis( ) + ); +} + +inline const Matrix3 Matrix3::rotationX( float radians ) +{ + vec_float4 s, c, res1, res2; + vec_uint4 select_y, select_z; + vec_float4 zero; + select_y = (vec_uint4)spu_maskb(0x0f00); + select_z = (vec_uint4)spu_maskb(0x00f0); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res1 = spu_sel( zero, c, select_y ); + res1 = spu_sel( res1, s, select_z ); + res2 = spu_sel( zero, negatef4(s), select_y ); + res2 = spu_sel( res2, c, select_z ); + return Matrix3( + Vector3::xAxis( ), + Vector3( res1 ), + Vector3( res2 ) + ); +} + +inline const Matrix3 Matrix3::rotationY( float radians ) +{ + vec_float4 s, c, res0, res2; + vec_uint4 select_x, select_z; + vec_float4 zero; + select_x = (vec_uint4)spu_maskb(0xf000); + select_z = (vec_uint4)spu_maskb(0x00f0); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res0 = spu_sel( zero, c, select_x ); + res0 = spu_sel( res0, negatef4(s), select_z ); + res2 = spu_sel( zero, s, select_x ); + res2 = spu_sel( res2, c, select_z ); + return Matrix3( + Vector3( res0 ), + Vector3::yAxis( ), + Vector3( res2 ) + ); +} + +inline const Matrix3 Matrix3::rotationZ( float radians ) +{ + vec_float4 s, c, res0, res1; + vec_uint4 select_x, select_y; + vec_float4 zero; + select_x = (vec_uint4)spu_maskb(0xf000); + select_y = (vec_uint4)spu_maskb(0x0f00); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res0 = spu_sel( zero, c, select_x ); + res0 = spu_sel( res0, s, select_y ); + res1 = spu_sel( zero, negatef4(s), select_x ); + res1 = spu_sel( res1, c, select_y ); + return Matrix3( + Vector3( res0 ), + Vector3( res1 ), + Vector3::zAxis( ) + ); +} + +inline const Matrix3 Matrix3::rotationZYX( Vector3 radiansXYZ ) +{ + vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + angles = radiansXYZ.get128(); + angles = spu_insert( 0.0f, angles, 3 ); + sincosf4( angles, &s, &c ); + negS = negatef4( s ); + Z0 = spu_shuffle( s, c, _VECTORMATH_SHUF_CZD0 ); + Z1 = spu_shuffle( c, negS, _VECTORMATH_SHUF_CZD0 ); + Y0 = spu_shuffle( negS, c, _VECTORMATH_SHUF_BBY0 ); + Y1 = spu_shuffle( c, s, _VECTORMATH_SHUF_BBY0 ); + X0 = spu_shuffle( s, s, shuffle_xxxx ); + X1 = spu_shuffle( c, c, shuffle_xxxx ); + tmp = spu_mul( Z0, Y1 ); + return Matrix3( + Vector3( spu_mul( Z0, Y0 ) ), + Vector3( spu_madd( Z1, X1, spu_mul( tmp, X0 ) ) ), + Vector3( spu_nmsub( Z1, X0, spu_mul( tmp, X1 ) ) ) + ); +} + +inline const Matrix3 Matrix3::rotation( float radians, Vector3 unitVec ) +{ + vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + axis = unitVec.get128(); + sincosf4( spu_splats( radians ), &s, &c ); + xxxx = spu_shuffle( axis, axis, shuffle_xxxx ); + yyyy = spu_shuffle( axis, axis, shuffle_yyyy ); + zzzz = spu_shuffle( axis, axis, shuffle_zzzz ); + oneMinusC = spu_sub( spu_splats(1.0f), c ); + axisS = spu_mul( axis, s ); + negAxisS = negatef4( axisS ); + tmp0 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_0ZB0 ); + tmp1 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_C0X0 ); + tmp2 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_YA00 ); + tmp0 = spu_sel( tmp0, c, (vec_uint4)spu_maskb(0xf000) ); + tmp1 = spu_sel( tmp1, c, (vec_uint4)spu_maskb(0x0f00) ); + tmp2 = spu_sel( tmp2, c, (vec_uint4)spu_maskb(0x00f0) ); + return Matrix3( + Vector3( spu_madd( spu_mul( axis, xxxx ), oneMinusC, tmp0 ) ), + Vector3( spu_madd( spu_mul( axis, yyyy ), oneMinusC, tmp1 ) ), + Vector3( spu_madd( spu_mul( axis, zzzz ), oneMinusC, tmp2 ) ) + ); +} + +inline const Matrix3 Matrix3::rotation( Quat unitQuat ) +{ + return Matrix3( unitQuat ); +} + +inline const Matrix3 Matrix3::scale( Vector3 scaleVec ) +{ + vec_float4 zero = spu_splats(0.0f); + return Matrix3( + Vector3( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0xf000) ) ), + Vector3( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0x0f00) ) ), + Vector3( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0x00f0) ) ) + ); +} + +inline const Matrix3 appendScale( const Matrix3 & mat, Vector3 scaleVec ) +{ + return Matrix3( + ( mat.getCol0() * scaleVec.getX( ) ), + ( mat.getCol1() * scaleVec.getY( ) ), + ( mat.getCol2() * scaleVec.getZ( ) ) + ); +} + +inline const Matrix3 prependScale( Vector3 scaleVec, const Matrix3 & mat ) +{ + return Matrix3( + mulPerElem( mat.getCol0(), scaleVec ), + mulPerElem( mat.getCol1(), scaleVec ), + mulPerElem( mat.getCol2(), scaleVec ) + ); +} + +inline const Matrix3 select( const Matrix3 & mat0, const Matrix3 & mat1, bool select1 ) +{ + return Matrix3( + select( mat0.getCol0(), mat1.getCol0(), select1 ), + select( mat0.getCol1(), mat1.getCol1(), select1 ), + select( mat0.getCol2(), mat1.getCol2(), select1 ) + ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( const Matrix3 & mat ) +{ + print( mat.getRow( 0 ) ); + print( mat.getRow( 1 ) ); + print( mat.getRow( 2 ) ); +} + +inline void print( const Matrix3 & mat, const char * name ) +{ + printf("%s:\n", name); + print( mat ); +} + +#endif + +inline Matrix4::Matrix4( const Matrix4 & mat ) +{ + mCol0 = mat.mCol0; + mCol1 = mat.mCol1; + mCol2 = mat.mCol2; + mCol3 = mat.mCol3; +} + +inline Matrix4::Matrix4( float scalar ) +{ + mCol0 = Vector4( scalar ); + mCol1 = Vector4( scalar ); + mCol2 = Vector4( scalar ); + mCol3 = Vector4( scalar ); +} + +inline Matrix4::Matrix4( const Transform3 & mat ) +{ + mCol0 = Vector4( mat.getCol0(), 0.0f ); + mCol1 = Vector4( mat.getCol1(), 0.0f ); + mCol2 = Vector4( mat.getCol2(), 0.0f ); + mCol3 = Vector4( mat.getCol3(), 1.0f ); +} + +inline Matrix4::Matrix4( Vector4 _col0, Vector4 _col1, Vector4 _col2, Vector4 _col3 ) +{ + mCol0 = _col0; + mCol1 = _col1; + mCol2 = _col2; + mCol3 = _col3; +} + +inline Matrix4::Matrix4( const Matrix3 & mat, Vector3 translateVec ) +{ + mCol0 = Vector4( mat.getCol0(), 0.0f ); + mCol1 = Vector4( mat.getCol1(), 0.0f ); + mCol2 = Vector4( mat.getCol2(), 0.0f ); + mCol3 = Vector4( translateVec, 1.0f ); +} + +inline Matrix4::Matrix4( Quat unitQuat, Vector3 translateVec ) +{ + Matrix3 mat; + mat = Matrix3( unitQuat ); + mCol0 = Vector4( mat.getCol0(), 0.0f ); + mCol1 = Vector4( mat.getCol1(), 0.0f ); + mCol2 = Vector4( mat.getCol2(), 0.0f ); + mCol3 = Vector4( translateVec, 1.0f ); +} + +inline Matrix4 & Matrix4::setCol0( Vector4 _col0 ) +{ + mCol0 = _col0; + return *this; +} + +inline Matrix4 & Matrix4::setCol1( Vector4 _col1 ) +{ + mCol1 = _col1; + return *this; +} + +inline Matrix4 & Matrix4::setCol2( Vector4 _col2 ) +{ + mCol2 = _col2; + return *this; +} + +inline Matrix4 & Matrix4::setCol3( Vector4 _col3 ) +{ + mCol3 = _col3; + return *this; +} + +inline Matrix4 & Matrix4::setCol( int col, Vector4 vec ) +{ + *(&mCol0 + col) = vec; + return *this; +} + +inline Matrix4 & Matrix4::setRow( int row, Vector4 vec ) +{ + mCol0.setElem( row, vec.getElem( 0 ) ); + mCol1.setElem( row, vec.getElem( 1 ) ); + mCol2.setElem( row, vec.getElem( 2 ) ); + mCol3.setElem( row, vec.getElem( 3 ) ); + return *this; +} + +inline Matrix4 & Matrix4::setElem( int col, int row, float val ) +{ + (*this)[col].setElem(row, val); + return *this; +} + +inline float Matrix4::getElem( int col, int row ) const +{ + return this->getCol( col ).getElem( row ); +} + +inline const Vector4 Matrix4::getCol0( ) const +{ + return mCol0; +} + +inline const Vector4 Matrix4::getCol1( ) const +{ + return mCol1; +} + +inline const Vector4 Matrix4::getCol2( ) const +{ + return mCol2; +} + +inline const Vector4 Matrix4::getCol3( ) const +{ + return mCol3; +} + +inline const Vector4 Matrix4::getCol( int col ) const +{ + return *(&mCol0 + col); +} + +inline const Vector4 Matrix4::getRow( int row ) const +{ + return Vector4( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ), mCol3.getElem( row ) ); +} + +inline Vector4 & Matrix4::operator []( int col ) +{ + return *(&mCol0 + col); +} + +inline const Vector4 Matrix4::operator []( int col ) const +{ + return *(&mCol0 + col); +} + +inline Matrix4 & Matrix4::operator =( const Matrix4 & mat ) +{ + mCol0 = mat.mCol0; + mCol1 = mat.mCol1; + mCol2 = mat.mCol2; + mCol3 = mat.mCol3; + return *this; +} + +inline const Matrix4 transpose( const Matrix4 & mat ) +{ + vec_float4 tmp0, tmp1, tmp2, tmp3, res0, res1, res2, res3; + tmp0 = spu_shuffle( mat.getCol0().get128(), mat.getCol2().get128(), _VECTORMATH_SHUF_XAYB ); + tmp1 = spu_shuffle( mat.getCol1().get128(), mat.getCol3().get128(), _VECTORMATH_SHUF_XAYB ); + tmp2 = spu_shuffle( mat.getCol0().get128(), mat.getCol2().get128(), _VECTORMATH_SHUF_ZCWD ); + tmp3 = spu_shuffle( mat.getCol1().get128(), mat.getCol3().get128(), _VECTORMATH_SHUF_ZCWD ); + res0 = spu_shuffle( tmp0, tmp1, _VECTORMATH_SHUF_XAYB ); + res1 = spu_shuffle( tmp0, tmp1, _VECTORMATH_SHUF_ZCWD ); + res2 = spu_shuffle( tmp2, tmp3, _VECTORMATH_SHUF_XAYB ); + res3 = spu_shuffle( tmp2, tmp3, _VECTORMATH_SHUF_ZCWD ); + return Matrix4( + Vector4( res0 ), + Vector4( res1 ), + Vector4( res2 ), + Vector4( res3 ) + ); +} + +inline const Matrix4 inverse( const Matrix4 & mat ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vec_float4 in0, in1, in2, in3; + vec_float4 tmp0, tmp1, tmp2, tmp3; + vec_float4 cof0, cof1, cof2, cof3; + vec_float4 t0, t1, t2, t3; + vec_float4 t01, t02, t03, t12, t23; + vec_float4 t1r, t2r; + vec_float4 t01r, t02r, t03r, t12r, t23r; + vec_float4 t1r3, t1r3r; + vec_float4 det, det1, det2, det3, invdet; + in0 = mat.getCol0().get128(); + in1 = mat.getCol1().get128(); + in2 = mat.getCol2().get128(); + in3 = mat.getCol3().get128(); + /* Perform transform of the input matrix of the form: + * A B C D + * E F G H + * I J K L + * M N O P + * + * The pseudo transpose of the input matrix is trans: + * A E I M + * J N B F + * C G K O + * L P D H + */ + tmp0 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_XAZC); /* A E C G */ + tmp1 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_XAZC); /* I M K O */ + tmp2 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_YBWD); /* B F D H */ + tmp3 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_YBWD); /* J N L P */ + t0 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_XYAB); /* A E I M */ + t1 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_XYAB); /* J N B F */ + t2 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_ZWCD); /* C G K O */ + t3 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_ZWCD); /* L P D H */ + /* Generate a cofactor matrix. The computed cofactors reside in + * cof0, cof1, cof2, cof3. + */ + t23 = spu_mul(t2, t3); /* CL GP KD OH */ + t23 = spu_shuffle(t23, t23, _VECTORMATH_SHUF_YXWZ); /* GP CL OH KD */ + cof0 = spu_mul(t1, t23); /* JGP NCL BOH FKD */ + cof1 = spu_mul(t0, t23); /* AGP ECL IOH MKD */ + t23r = spu_rlqwbyte(t23, 8); /* OH KD GP CL */ + cof0 = spu_msub(t1, t23r, cof0); /* JOH NKD BGP FCL - cof0 */ + cof1 = spu_msub(t0, t23r, cof1); /* AOH EKD IGP MCL - cof1 */ + cof1 = spu_rlqwbyte(cof1, 8); /* IGP MCL AOH EKD - IOH MKD AGP ECL */ + + t12 = spu_mul(t1, t2); /* JC NG BK FO */ + t12 = spu_shuffle(t12, t12, _VECTORMATH_SHUF_YXWZ); /* NG JC FO BK */ + cof0 = spu_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ + cof3 = spu_mul(t0, t12); /* ANG EJC IFO MBK */ + t12r = spu_rlqwbyte(t12, 8); /* FO BK NG JC */ + cof0 = spu_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ + cof3 = spu_msub(t0, t12r, cof3); /* AFO EBK ING MJC - cof3 */ + cof3 = spu_rlqwbyte(cof3, 8); /* ING MJC AFO EBK - IFO MBK ANG EJC */ + t1r = spu_rlqwbyte(t1, 8); /* B F J N */ + t2r = spu_rlqwbyte(t2, 8); /* K O C G */ + t1r3 = spu_mul(t1r, t3); /* BL FP JD NH */ + t1r3 = spu_shuffle(t1r3, t1r3, _VECTORMATH_SHUF_YXWZ); /* FP BL NH JD */ + cof0 = spu_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ + cof2 = spu_mul(t0, t1r3); /* AFP EBL INH MJD */ + t1r3r = spu_rlqwbyte(t1r3, 8); /* NH JD FP BL */ + cof0 = spu_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ + cof2 = spu_msub(t0, t1r3r, cof2); /* ANH EJD IFP MBL - cof2 */ + cof2 = spu_rlqwbyte(cof2, 8); /* IFP MBL ANH EJD - INH MJD AFP EBL */ + t01 = spu_mul(t0, t1); /* AJ EN IB MF */ + t01 = spu_shuffle(t01, t01, _VECTORMATH_SHUF_YXWZ); /* EN AJ MF IB */ + cof2 = spu_madd(t3, t01, cof2); /* LEN PAJ DMF HIB + cof2 */ + cof3 = spu_msub(t2r, t01, cof3); /* KEN OAJ CMF GIB - cof3 */ + t01r = spu_rlqwbyte(t01, 8); /* MF IB EN AJ */ + cof2 = spu_msub(t3, t01r, cof2); /* LMF PIB DEN HAJ - cof2 */ + cof3 = spu_nmsub(t2r, t01r, cof3); /* cof3 - KMF OIB CEN GAJ */ + t03 = spu_mul(t0, t3); /* AL EP ID MH */ + t03 = spu_shuffle(t03, t03, _VECTORMATH_SHUF_YXWZ); /* EP AL MH ID */ + cof1 = spu_nmsub(t2r, t03, cof1); /* cof1 - KEP OAL CMH GID */ + cof2 = spu_madd(t1, t03, cof2); /* JEP NAL BMH FID + cof2 */ + t03r = spu_rlqwbyte(t03, 8); /* MH ID EP AL */ + cof1 = spu_madd(t2r, t03r, cof1); /* KMH OID CEP GAL + cof1 */ + cof2 = spu_nmsub(t1, t03r, cof2); /* cof2 - JMH NID BEP FAL */ + t02 = spu_mul(t0, t2r); /* AK EO IC MG */ + t02 = spu_shuffle(t02, t02, _VECTORMATH_SHUF_YXWZ); /* E0 AK MG IC */ + cof1 = spu_madd(t3, t02, cof1); /* LEO PAK DMG HIC + cof1 */ + cof3 = spu_nmsub(t1, t02, cof3); /* cof3 - JEO NAK BMG FIC */ + t02r = spu_rlqwbyte(t02, 8); /* MG IC EO AK */ + cof1 = spu_nmsub(t3, t02r, cof1); /* cof1 - LMG PIC DEO HAK */ + cof3 = spu_madd(t1, t02r, cof3); /* JMG NIC BEO FAK + cof3 */ + /* Compute the determinant of the matrix + * + * det = sum_across(t0 * cof0); + * + * We perform a sum across the entire vector so that + * we don't have to splat the result when multiplying the + * cofactors by the inverse of the determinant. + */ + det = spu_mul(t0, cof0); + det1 = spu_rlqwbyte(det, 4); + det2 = spu_rlqwbyte(det, 8); + det3 = spu_rlqwbyte(det, 12); + det = spu_add(det, det1); + det2 = spu_add(det2, det3); + det = spu_add(det, det2); + /* Compute the reciprocal of the determinant. + */ + invdet = recipf4(det); + /* Multiply the cofactors by the reciprocal of the determinant. + */ + return Matrix4( + Vector4( spu_mul(cof0, invdet) ), + Vector4( spu_mul(cof1, invdet) ), + Vector4( spu_mul(cof2, invdet) ), + Vector4( spu_mul(cof3, invdet) ) + ); +} + +inline const Matrix4 affineInverse( const Matrix4 & mat ) +{ + Transform3 affineMat; + affineMat.setCol0( mat.getCol0().getXYZ( ) ); + affineMat.setCol1( mat.getCol1().getXYZ( ) ); + affineMat.setCol2( mat.getCol2().getXYZ( ) ); + affineMat.setCol3( mat.getCol3().getXYZ( ) ); + return Matrix4( inverse( affineMat ) ); +} + +inline const Matrix4 orthoInverse( const Matrix4 & mat ) +{ + Transform3 affineMat; + affineMat.setCol0( mat.getCol0().getXYZ( ) ); + affineMat.setCol1( mat.getCol1().getXYZ( ) ); + affineMat.setCol2( mat.getCol2().getXYZ( ) ); + affineMat.setCol3( mat.getCol3().getXYZ( ) ); + return Matrix4( orthoInverse( affineMat ) ); +} + +inline float determinant( const Matrix4 & mat ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vec_float4 in0, in1, in2, in3; + vec_float4 tmp0, tmp1, tmp2, tmp3; + vec_float4 cof0; + vec_float4 t0, t1, t2, t3; + vec_float4 t12, t23; + vec_float4 t1r, t2r; + vec_float4 t12r, t23r; + vec_float4 t1r3, t1r3r; + in0 = mat.getCol0().get128(); + in1 = mat.getCol1().get128(); + in2 = mat.getCol2().get128(); + in3 = mat.getCol3().get128(); + /* Perform transform of the input matrix of the form: + * A B C D + * E F G H + * I J K L + * M N O P + * + * The pseudo transpose of the input matrix is trans: + * A E I M + * J N B F + * C G K O + * L P D H + */ + tmp0 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_XAZC); /* A E C G */ + tmp1 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_XAZC); /* I M K O */ + tmp2 = spu_shuffle(in0, in1, _VECTORMATH_SHUF_YBWD); /* B F D H */ + tmp3 = spu_shuffle(in2, in3, _VECTORMATH_SHUF_YBWD); /* J N L P */ + t0 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_XYAB); /* A E I M */ + t1 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_XYAB); /* J N B F */ + t2 = spu_shuffle(tmp0, tmp1, _VECTORMATH_SHUF_ZWCD); /* C G K O */ + t3 = spu_shuffle(tmp3, tmp2, _VECTORMATH_SHUF_ZWCD); /* L P D H */ + /* Generate a cofactor matrix. The computed cofactors reside in + * cof0, cof1, cof2, cof3. + */ + t23 = spu_mul(t2, t3); /* CL GP KD OH */ + t23 = spu_shuffle(t23, t23, _VECTORMATH_SHUF_YXWZ); /* GP CL OH KD */ + cof0 = spu_mul(t1, t23); /* JGP NCL BOH FKD */ + t23r = spu_rlqwbyte(t23, 8); /* OH KD GP CL */ + cof0 = spu_msub(t1, t23r, cof0); /* JOH NKD BGP FCL - cof0 */ + + t12 = spu_mul(t1, t2); /* JC NG BK FO */ + t12 = spu_shuffle(t12, t12, _VECTORMATH_SHUF_YXWZ); /* NG JC FO BK */ + cof0 = spu_madd(t3, t12, cof0); /* LNG PJC DFO HBK + cof0 */ + t12r = spu_rlqwbyte(t12, 8); /* FO BK NG JC */ + cof0 = spu_nmsub(t3, t12r, cof0); /* cof0 - LFO PBK DNG HJC */ + t1r = spu_rlqwbyte(t1, 8); /* B F J N */ + t2r = spu_rlqwbyte(t2, 8); /* K O C G */ + t1r3 = spu_mul(t1r, t3); /* BL FP JD NH */ + t1r3 = spu_shuffle(t1r3, t1r3, _VECTORMATH_SHUF_YXWZ); /* FP BL NH JD */ + cof0 = spu_madd(t2r, t1r3, cof0); /* KFP OBL CNH GJD + cof0 */ + t1r3r = spu_rlqwbyte(t1r3, 8); /* NH JD FP BL */ + cof0 = spu_nmsub(t2r, t1r3r, cof0); /* cof0 - KNH OJD CFP GBL */ + return spu_extract( _vmathVfDot4(t0,cof0), 0 ); +} + +inline const Matrix4 Matrix4::operator +( const Matrix4 & mat ) const +{ + return Matrix4( + ( mCol0 + mat.mCol0 ), + ( mCol1 + mat.mCol1 ), + ( mCol2 + mat.mCol2 ), + ( mCol3 + mat.mCol3 ) + ); +} + +inline const Matrix4 Matrix4::operator -( const Matrix4 & mat ) const +{ + return Matrix4( + ( mCol0 - mat.mCol0 ), + ( mCol1 - mat.mCol1 ), + ( mCol2 - mat.mCol2 ), + ( mCol3 - mat.mCol3 ) + ); +} + +inline Matrix4 & Matrix4::operator +=( const Matrix4 & mat ) +{ + *this = *this + mat; + return *this; +} + +inline Matrix4 & Matrix4::operator -=( const Matrix4 & mat ) +{ + *this = *this - mat; + return *this; +} + +inline const Matrix4 Matrix4::operator -( ) const +{ + return Matrix4( + ( -mCol0 ), + ( -mCol1 ), + ( -mCol2 ), + ( -mCol3 ) + ); +} + +inline const Matrix4 absPerElem( const Matrix4 & mat ) +{ + return Matrix4( + absPerElem( mat.getCol0() ), + absPerElem( mat.getCol1() ), + absPerElem( mat.getCol2() ), + absPerElem( mat.getCol3() ) + ); +} + +inline const Matrix4 Matrix4::operator *( float scalar ) const +{ + return Matrix4( + ( mCol0 * scalar ), + ( mCol1 * scalar ), + ( mCol2 * scalar ), + ( mCol3 * scalar ) + ); +} + +inline Matrix4 & Matrix4::operator *=( float scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Matrix4 operator *( float scalar, const Matrix4 & mat ) +{ + return mat * scalar; +} + +inline const Vector4 Matrix4::operator *( Vector4 vec ) const +{ + vec_float4 tmp0, tmp1, res; + vec_float4 xxxx, yyyy, zzzz, wwww; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); + xxxx = spu_shuffle( vec.get128(), vec.get128(), shuffle_xxxx ); + yyyy = spu_shuffle( vec.get128(), vec.get128(), shuffle_yyyy ); + zzzz = spu_shuffle( vec.get128(), vec.get128(), shuffle_zzzz ); + wwww = spu_shuffle( vec.get128(), vec.get128(), shuffle_wwww ); + tmp0 = spu_mul( mCol0.get128(), xxxx ); + tmp1 = spu_mul( mCol1.get128(), yyyy ); + tmp0 = spu_madd( mCol2.get128(), zzzz, tmp0 ); + tmp1 = spu_madd( mCol3.get128(), wwww, tmp1 ); + res = spu_add( tmp0, tmp1 ); + return Vector4( res ); +} + +inline const Vector4 Matrix4::operator *( Vector3 vec ) const +{ + vec_float4 res; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + xxxx = spu_shuffle( vec.get128(), vec.get128(), shuffle_xxxx ); + yyyy = spu_shuffle( vec.get128(), vec.get128(), shuffle_yyyy ); + zzzz = spu_shuffle( vec.get128(), vec.get128(), shuffle_zzzz ); + res = spu_mul( mCol0.get128(), xxxx ); + res = spu_madd( mCol1.get128(), yyyy, res ); + res = spu_madd( mCol2.get128(), zzzz, res ); + return Vector4( res ); +} + +inline const Vector4 Matrix4::operator *( Point3 pnt ) const +{ + vec_float4 tmp0, tmp1, res; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + xxxx = spu_shuffle( pnt.get128(), pnt.get128(), shuffle_xxxx ); + yyyy = spu_shuffle( pnt.get128(), pnt.get128(), shuffle_yyyy ); + zzzz = spu_shuffle( pnt.get128(), pnt.get128(), shuffle_zzzz ); + tmp0 = spu_mul( mCol0.get128(), xxxx ); + tmp1 = spu_mul( mCol1.get128(), yyyy ); + tmp0 = spu_madd( mCol2.get128(), zzzz, tmp0 ); + tmp1 = spu_add( mCol3.get128(), tmp1 ); + res = spu_add( tmp0, tmp1 ); + return Vector4( res ); +} + +inline const Matrix4 Matrix4::operator *( const Matrix4 & mat ) const +{ + return Matrix4( + ( *this * mat.mCol0 ), + ( *this * mat.mCol1 ), + ( *this * mat.mCol2 ), + ( *this * mat.mCol3 ) + ); +} + +inline Matrix4 & Matrix4::operator *=( const Matrix4 & mat ) +{ + *this = *this * mat; + return *this; +} + +inline const Matrix4 Matrix4::operator *( const Transform3 & tfrm ) const +{ + return Matrix4( + ( *this * tfrm.getCol0() ), + ( *this * tfrm.getCol1() ), + ( *this * tfrm.getCol2() ), + ( *this * Point3( tfrm.getCol3() ) ) + ); +} + +inline Matrix4 & Matrix4::operator *=( const Transform3 & tfrm ) +{ + *this = *this * tfrm; + return *this; +} + +inline const Matrix4 mulPerElem( const Matrix4 & mat0, const Matrix4 & mat1 ) +{ + return Matrix4( + mulPerElem( mat0.getCol0(), mat1.getCol0() ), + mulPerElem( mat0.getCol1(), mat1.getCol1() ), + mulPerElem( mat0.getCol2(), mat1.getCol2() ), + mulPerElem( mat0.getCol3(), mat1.getCol3() ) + ); +} + +inline const Matrix4 Matrix4::identity( ) +{ + return Matrix4( + Vector4::xAxis( ), + Vector4::yAxis( ), + Vector4::zAxis( ), + Vector4::wAxis( ) + ); +} + +inline Matrix4 & Matrix4::setUpper3x3( const Matrix3 & mat3 ) +{ + mCol0.setXYZ( mat3.getCol0() ); + mCol1.setXYZ( mat3.getCol1() ); + mCol2.setXYZ( mat3.getCol2() ); + return *this; +} + +inline const Matrix3 Matrix4::getUpper3x3( ) const +{ + return Matrix3( + mCol0.getXYZ( ), + mCol1.getXYZ( ), + mCol2.getXYZ( ) + ); +} + +inline Matrix4 & Matrix4::setTranslation( Vector3 translateVec ) +{ + mCol3.setXYZ( translateVec ); + return *this; +} + +inline const Vector3 Matrix4::getTranslation( ) const +{ + return mCol3.getXYZ( ); +} + +inline const Matrix4 Matrix4::rotationX( float radians ) +{ + vec_float4 s, c, res1, res2; + vec_uint4 select_y, select_z; + vec_float4 zero; + select_y = (vec_uint4)spu_maskb(0x0f00); + select_z = (vec_uint4)spu_maskb(0x00f0); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res1 = spu_sel( zero, c, select_y ); + res1 = spu_sel( res1, s, select_z ); + res2 = spu_sel( zero, negatef4(s), select_y ); + res2 = spu_sel( res2, c, select_z ); + return Matrix4( + Vector4::xAxis( ), + Vector4( res1 ), + Vector4( res2 ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotationY( float radians ) +{ + vec_float4 s, c, res0, res2; + vec_uint4 select_x, select_z; + vec_float4 zero; + select_x = (vec_uint4)spu_maskb(0xf000); + select_z = (vec_uint4)spu_maskb(0x00f0); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res0 = spu_sel( zero, c, select_x ); + res0 = spu_sel( res0, negatef4(s), select_z ); + res2 = spu_sel( zero, s, select_x ); + res2 = spu_sel( res2, c, select_z ); + return Matrix4( + Vector4( res0 ), + Vector4::yAxis( ), + Vector4( res2 ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotationZ( float radians ) +{ + vec_float4 s, c, res0, res1; + vec_uint4 select_x, select_y; + vec_float4 zero; + select_x = (vec_uint4)spu_maskb(0xf000); + select_y = (vec_uint4)spu_maskb(0x0f00); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res0 = spu_sel( zero, c, select_x ); + res0 = spu_sel( res0, s, select_y ); + res1 = spu_sel( zero, negatef4(s), select_x ); + res1 = spu_sel( res1, c, select_y ); + return Matrix4( + Vector4( res0 ), + Vector4( res1 ), + Vector4::zAxis( ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotationZYX( Vector3 radiansXYZ ) +{ + vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + angles = radiansXYZ.get128(); + angles = spu_insert( 0.0f, angles, 3 ); + sincosf4( angles, &s, &c ); + negS = negatef4( s ); + Z0 = spu_shuffle( s, c, _VECTORMATH_SHUF_CZD0 ); + Z1 = spu_shuffle( c, negS, _VECTORMATH_SHUF_CZD0 ); + Y0 = spu_shuffle( negS, c, _VECTORMATH_SHUF_BBY0 ); + Y1 = spu_shuffle( c, s, _VECTORMATH_SHUF_BBY0 ); + X0 = spu_shuffle( s, s, shuffle_xxxx ); + X1 = spu_shuffle( c, c, shuffle_xxxx ); + tmp = spu_mul( Z0, Y1 ); + return Matrix4( + Vector4( spu_mul( Z0, Y0 ) ), + Vector4( spu_madd( Z1, X1, spu_mul( tmp, X0 ) ) ), + Vector4( spu_nmsub( Z1, X0, spu_mul( tmp, X1 ) ) ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotation( float radians, Vector3 unitVec ) +{ + vec_float4 axis, s, c, oneMinusC, axisS, negAxisS, xxxx, yyyy, zzzz, tmp0, tmp1, tmp2, zeroW; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + axis = unitVec.get128(); + sincosf4( spu_splats( radians ), &s, &c ); + xxxx = spu_shuffle( axis, axis, shuffle_xxxx ); + yyyy = spu_shuffle( axis, axis, shuffle_yyyy ); + zzzz = spu_shuffle( axis, axis, shuffle_zzzz ); + oneMinusC = spu_sub( spu_splats(1.0f), c ); + axisS = spu_mul( axis, s ); + negAxisS = negatef4( axisS ); + tmp0 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_0ZB0 ); + tmp1 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_C0X0 ); + tmp2 = spu_shuffle( axisS, negAxisS, _VECTORMATH_SHUF_YA00 ); + tmp0 = spu_sel( tmp0, c, (vec_uint4)spu_maskb(0xf000) ); + tmp1 = spu_sel( tmp1, c, (vec_uint4)spu_maskb(0x0f00) ); + tmp2 = spu_sel( tmp2, c, (vec_uint4)spu_maskb(0x00f0) ); + zeroW = (vec_float4)spu_maskb(0x000f); + axis = spu_andc( axis, zeroW ); + return Matrix4( + Vector4( spu_madd( spu_mul( axis, xxxx ), oneMinusC, tmp0 ) ), + Vector4( spu_madd( spu_mul( axis, yyyy ), oneMinusC, tmp1 ) ), + Vector4( spu_madd( spu_mul( axis, zzzz ), oneMinusC, tmp2 ) ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotation( Quat unitQuat ) +{ + return Matrix4( Transform3::rotation( unitQuat ) ); +} + +inline const Matrix4 Matrix4::scale( Vector3 scaleVec ) +{ + vec_float4 zero = spu_splats(0.0f); + return Matrix4( + Vector4( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0xf000) ) ), + Vector4( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0x0f00) ) ), + Vector4( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0x00f0) ) ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 appendScale( const Matrix4 & mat, Vector3 scaleVec ) +{ + return Matrix4( + ( mat.getCol0() * scaleVec.getX( ) ), + ( mat.getCol1() * scaleVec.getY( ) ), + ( mat.getCol2() * scaleVec.getZ( ) ), + mat.getCol3() + ); +} + +inline const Matrix4 prependScale( Vector3 scaleVec, const Matrix4 & mat ) +{ + Vector4 scale4; + scale4 = Vector4( scaleVec, 1.0f ); + return Matrix4( + mulPerElem( mat.getCol0(), scale4 ), + mulPerElem( mat.getCol1(), scale4 ), + mulPerElem( mat.getCol2(), scale4 ), + mulPerElem( mat.getCol3(), scale4 ) + ); +} + +inline const Matrix4 Matrix4::translation( Vector3 translateVec ) +{ + return Matrix4( + Vector4::xAxis( ), + Vector4::yAxis( ), + Vector4::zAxis( ), + Vector4( translateVec, 1.0f ) + ); +} + +inline const Matrix4 Matrix4::lookAt( Point3 eyePos, Point3 lookAtPos, Vector3 upVec ) +{ + Matrix4 m4EyeFrame; + Vector3 v3X, v3Y, v3Z; + v3Y = normalize( upVec ); + v3Z = normalize( ( eyePos - lookAtPos ) ); + v3X = normalize( cross( v3Y, v3Z ) ); + v3Y = cross( v3Z, v3X ); + m4EyeFrame = Matrix4( Vector4( v3X ), Vector4( v3Y ), Vector4( v3Z ), Vector4( eyePos ) ); + return orthoInverse( m4EyeFrame ); +} + +inline const Matrix4 Matrix4::perspective( float fovyRadians, float aspect, float zNear, float zFar ) +{ + float f, rangeInv; + vec_float4 zero, col0, col1, col2, col3; + f = tanf( _VECTORMATH_PI_OVER_2 - fovyRadians * 0.5f ); + rangeInv = 1.0f / ( zNear - zFar ); + zero = spu_splats(0.0f); + col0 = zero; + col1 = zero; + col2 = zero; + col3 = zero; + col0 = spu_insert( f / aspect, col0, 0 ); + col1 = spu_insert( f, col1, 1 ); + col2 = spu_insert( ( zNear + zFar ) * rangeInv, col2, 2 ); + col2 = spu_insert( -1.0f, col2, 3 ); + col3 = spu_insert( zNear * zFar * rangeInv * 2.0f, col3, 2 ); + return Matrix4( + Vector4( col0 ), + Vector4( col1 ), + Vector4( col2 ), + Vector4( col3 ) + ); +} + +inline const Matrix4 Matrix4::frustum( float left, float right, float bottom, float top, float zNear, float zFar ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vec_float4 lbf, rtn; + vec_float4 diff, sum, inv_diff; + vec_float4 diagonal, column, near2; + vec_float4 zero = spu_splats(0.0f); + lbf = spu_shuffle( spu_promote(left,0), spu_promote(zFar,0), _VECTORMATH_SHUF_XAYB ); + rtn = spu_shuffle( spu_promote(right,0), spu_promote(zNear,0), _VECTORMATH_SHUF_XAYB ); + lbf = spu_shuffle( lbf, spu_promote(bottom,0), _VECTORMATH_SHUF_XAYB ); + rtn = spu_shuffle( rtn, spu_promote(top,0), _VECTORMATH_SHUF_XAYB ); + diff = spu_sub( rtn, lbf ); + sum = spu_add( rtn, lbf ); + inv_diff = recipf4( diff ); + near2 = spu_splats( zNear ); + near2 = spu_add( near2, near2 ); + diagonal = spu_mul( near2, inv_diff ); + column = spu_mul( sum, inv_diff ); + return Matrix4( + Vector4( spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0xf000) ) ), + Vector4( spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0x0f00) ) ), + Vector4( spu_sel( column, spu_splats(-1.0f), (vec_uint4)spu_maskb(0x000f) ) ), + Vector4( spu_sel( zero, spu_mul( diagonal, spu_splats(zFar) ), (vec_uint4)spu_maskb(0x00f0) ) ) + ); +} + +inline const Matrix4 Matrix4::orthographic( float left, float right, float bottom, float top, float zNear, float zFar ) +{ + /* function implementation based on code from STIDC SDK: */ + /* -------------------------------------------------------------- */ + /* PLEASE DO NOT MODIFY THIS SECTION */ + /* This prolog section is automatically generated. */ + /* */ + /* (C)Copyright */ + /* Sony Computer Entertainment, Inc., */ + /* Toshiba Corporation, */ + /* International Business Machines Corporation, */ + /* 2001,2002. */ + /* S/T/I Confidential Information */ + /* -------------------------------------------------------------- */ + vec_float4 lbf, rtn; + vec_float4 diff, sum, inv_diff, neg_inv_diff; + vec_float4 diagonal, column; + vec_float4 zero = spu_splats(0.0f); + lbf = spu_shuffle( spu_promote(left,0), spu_promote(zFar,0), _VECTORMATH_SHUF_XAYB ); + rtn = spu_shuffle( spu_promote(right,0), spu_promote(zNear,0), _VECTORMATH_SHUF_XAYB ); + lbf = spu_shuffle( lbf, spu_promote(bottom,0), _VECTORMATH_SHUF_XAYB ); + rtn = spu_shuffle( rtn, spu_promote(top,0), _VECTORMATH_SHUF_XAYB ); + diff = spu_sub( rtn, lbf ); + sum = spu_add( rtn, lbf ); + inv_diff = recipf4( diff ); + neg_inv_diff = negatef4( inv_diff ); + diagonal = spu_add( inv_diff, inv_diff ); + column = spu_mul( sum, spu_sel( neg_inv_diff, inv_diff, (vec_uint4)spu_maskb(0x00f0) ) ); + return Matrix4( + Vector4( spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0xf000) ) ), + Vector4( spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0x0f00) ) ), + Vector4( spu_sel( zero, diagonal, (vec_uint4)spu_maskb(0x00f0) ) ), + Vector4( spu_sel( column, spu_splats(1.0f), (vec_uint4)spu_maskb(0x000f) ) ) + ); +} + +inline const Matrix4 select( const Matrix4 & mat0, const Matrix4 & mat1, bool select1 ) +{ + return Matrix4( + select( mat0.getCol0(), mat1.getCol0(), select1 ), + select( mat0.getCol1(), mat1.getCol1(), select1 ), + select( mat0.getCol2(), mat1.getCol2(), select1 ), + select( mat0.getCol3(), mat1.getCol3(), select1 ) + ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( const Matrix4 & mat ) +{ + print( mat.getRow( 0 ) ); + print( mat.getRow( 1 ) ); + print( mat.getRow( 2 ) ); + print( mat.getRow( 3 ) ); +} + +inline void print( const Matrix4 & mat, const char * name ) +{ + printf("%s:\n", name); + print( mat ); +} + +#endif + +inline Transform3::Transform3( const Transform3 & tfrm ) +{ + mCol0 = tfrm.mCol0; + mCol1 = tfrm.mCol1; + mCol2 = tfrm.mCol2; + mCol3 = tfrm.mCol3; +} + +inline Transform3::Transform3( float scalar ) +{ + mCol0 = Vector3( scalar ); + mCol1 = Vector3( scalar ); + mCol2 = Vector3( scalar ); + mCol3 = Vector3( scalar ); +} + +inline Transform3::Transform3( Vector3 _col0, Vector3 _col1, Vector3 _col2, Vector3 _col3 ) +{ + mCol0 = _col0; + mCol1 = _col1; + mCol2 = _col2; + mCol3 = _col3; +} + +inline Transform3::Transform3( const Matrix3 & tfrm, Vector3 translateVec ) +{ + this->setUpper3x3( tfrm ); + this->setTranslation( translateVec ); +} + +inline Transform3::Transform3( Quat unitQuat, Vector3 translateVec ) +{ + this->setUpper3x3( Matrix3( unitQuat ) ); + this->setTranslation( translateVec ); +} + +inline Transform3 & Transform3::setCol0( Vector3 _col0 ) +{ + mCol0 = _col0; + return *this; +} + +inline Transform3 & Transform3::setCol1( Vector3 _col1 ) +{ + mCol1 = _col1; + return *this; +} + +inline Transform3 & Transform3::setCol2( Vector3 _col2 ) +{ + mCol2 = _col2; + return *this; +} + +inline Transform3 & Transform3::setCol3( Vector3 _col3 ) +{ + mCol3 = _col3; + return *this; +} + +inline Transform3 & Transform3::setCol( int col, Vector3 vec ) +{ + *(&mCol0 + col) = vec; + return *this; +} + +inline Transform3 & Transform3::setRow( int row, Vector4 vec ) +{ + mCol0.setElem( row, vec.getElem( 0 ) ); + mCol1.setElem( row, vec.getElem( 1 ) ); + mCol2.setElem( row, vec.getElem( 2 ) ); + mCol3.setElem( row, vec.getElem( 3 ) ); + return *this; +} + +inline Transform3 & Transform3::setElem( int col, int row, float val ) +{ + (*this)[col].setElem(row, val); + return *this; +} + +inline float Transform3::getElem( int col, int row ) const +{ + return this->getCol( col ).getElem( row ); +} + +inline const Vector3 Transform3::getCol0( ) const +{ + return mCol0; +} + +inline const Vector3 Transform3::getCol1( ) const +{ + return mCol1; +} + +inline const Vector3 Transform3::getCol2( ) const +{ + return mCol2; +} + +inline const Vector3 Transform3::getCol3( ) const +{ + return mCol3; +} + +inline const Vector3 Transform3::getCol( int col ) const +{ + return *(&mCol0 + col); +} + +inline const Vector4 Transform3::getRow( int row ) const +{ + return Vector4( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ), mCol3.getElem( row ) ); +} + +inline Vector3 & Transform3::operator []( int col ) +{ + return *(&mCol0 + col); +} + +inline const Vector3 Transform3::operator []( int col ) const +{ + return *(&mCol0 + col); +} + +inline Transform3 & Transform3::operator =( const Transform3 & tfrm ) +{ + mCol0 = tfrm.mCol0; + mCol1 = tfrm.mCol1; + mCol2 = tfrm.mCol2; + mCol3 = tfrm.mCol3; + return *this; +} + +inline const Transform3 inverse( const Transform3 & tfrm ) +{ + vec_float4 inv0, inv1, inv2, inv3; + vec_float4 tmp0, tmp1, tmp2, tmp3, tmp4, dot, invdet; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + tmp2 = _vmathVfCross( tfrm.getCol0().get128(), tfrm.getCol1().get128() ); + tmp0 = _vmathVfCross( tfrm.getCol1().get128(), tfrm.getCol2().get128() ); + tmp1 = _vmathVfCross( tfrm.getCol2().get128(), tfrm.getCol0().get128() ); + inv3 = negatef4( tfrm.getCol3().get128() ); + dot = _vmathVfDot3( tmp2, tfrm.getCol2().get128() ); + dot = spu_shuffle( dot, dot, shuffle_xxxx ); + invdet = recipf4( dot ); + tmp3 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_XAYB ); + tmp4 = spu_shuffle( tmp0, tmp2, _VECTORMATH_SHUF_ZCWD ); + inv0 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_XAYB ); + xxxx = spu_shuffle( inv3, inv3, shuffle_xxxx ); + inv1 = spu_shuffle( tmp3, tmp1, _VECTORMATH_SHUF_ZBW0 ); + inv2 = spu_shuffle( tmp4, tmp1, _VECTORMATH_SHUF_XCY0 ); + yyyy = spu_shuffle( inv3, inv3, shuffle_yyyy ); + zzzz = spu_shuffle( inv3, inv3, shuffle_zzzz ); + inv3 = spu_mul( inv0, xxxx ); + inv3 = spu_madd( inv1, yyyy, inv3 ); + inv3 = spu_madd( inv2, zzzz, inv3 ); + inv0 = spu_mul( inv0, invdet ); + inv1 = spu_mul( inv1, invdet ); + inv2 = spu_mul( inv2, invdet ); + inv3 = spu_mul( inv3, invdet ); + return Transform3( + Vector3( inv0 ), + Vector3( inv1 ), + Vector3( inv2 ), + Vector3( inv3 ) + ); +} + +inline const Transform3 orthoInverse( const Transform3 & tfrm ) +{ + vec_float4 inv0, inv1, inv2, inv3; + vec_float4 tmp0, tmp1; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + tmp0 = spu_shuffle( tfrm.getCol0().get128(), tfrm.getCol2().get128(), _VECTORMATH_SHUF_XAYB ); + tmp1 = spu_shuffle( tfrm.getCol0().get128(), tfrm.getCol2().get128(), _VECTORMATH_SHUF_ZCWD ); + inv3 = negatef4( tfrm.getCol3().get128() ); + inv0 = spu_shuffle( tmp0, tfrm.getCol1().get128(), _VECTORMATH_SHUF_XAYB ); + xxxx = spu_shuffle( inv3, inv3, shuffle_xxxx ); + inv1 = spu_shuffle( tmp0, tfrm.getCol1().get128(), _VECTORMATH_SHUF_ZBW0 ); + inv2 = spu_shuffle( tmp1, tfrm.getCol1().get128(), _VECTORMATH_SHUF_XCY0 ); + yyyy = spu_shuffle( inv3, inv3, shuffle_yyyy ); + zzzz = spu_shuffle( inv3, inv3, shuffle_zzzz ); + inv3 = spu_mul( inv0, xxxx ); + inv3 = spu_madd( inv1, yyyy, inv3 ); + inv3 = spu_madd( inv2, zzzz, inv3 ); + return Transform3( + Vector3( inv0 ), + Vector3( inv1 ), + Vector3( inv2 ), + Vector3( inv3 ) + ); +} + +inline const Transform3 absPerElem( const Transform3 & tfrm ) +{ + return Transform3( + absPerElem( tfrm.getCol0() ), + absPerElem( tfrm.getCol1() ), + absPerElem( tfrm.getCol2() ), + absPerElem( tfrm.getCol3() ) + ); +} + +inline const Vector3 Transform3::operator *( Vector3 vec ) const +{ + vec_float4 res; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + xxxx = spu_shuffle( vec.get128(), vec.get128(), shuffle_xxxx ); + yyyy = spu_shuffle( vec.get128(), vec.get128(), shuffle_yyyy ); + zzzz = spu_shuffle( vec.get128(), vec.get128(), shuffle_zzzz ); + res = spu_mul( mCol0.get128(), xxxx ); + res = spu_madd( mCol1.get128(), yyyy, res ); + res = spu_madd( mCol2.get128(), zzzz, res ); + return Vector3( res ); +} + +inline const Point3 Transform3::operator *( Point3 pnt ) const +{ + vec_float4 tmp0, tmp1, res; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + xxxx = spu_shuffle( pnt.get128(), pnt.get128(), shuffle_xxxx ); + yyyy = spu_shuffle( pnt.get128(), pnt.get128(), shuffle_yyyy ); + zzzz = spu_shuffle( pnt.get128(), pnt.get128(), shuffle_zzzz ); + tmp0 = spu_mul( mCol0.get128(), xxxx ); + tmp1 = spu_mul( mCol1.get128(), yyyy ); + tmp0 = spu_madd( mCol2.get128(), zzzz, tmp0 ); + tmp1 = spu_add( mCol3.get128(), tmp1 ); + res = spu_add( tmp0, tmp1 ); + return Point3( res ); +} + +inline const Transform3 Transform3::operator *( const Transform3 & tfrm ) const +{ + return Transform3( + ( *this * tfrm.mCol0 ), + ( *this * tfrm.mCol1 ), + ( *this * tfrm.mCol2 ), + Vector3( ( *this * Point3( tfrm.mCol3 ) ) ) + ); +} + +inline Transform3 & Transform3::operator *=( const Transform3 & tfrm ) +{ + *this = *this * tfrm; + return *this; +} + +inline const Transform3 mulPerElem( const Transform3 & tfrm0, const Transform3 & tfrm1 ) +{ + return Transform3( + mulPerElem( tfrm0.getCol0(), tfrm1.getCol0() ), + mulPerElem( tfrm0.getCol1(), tfrm1.getCol1() ), + mulPerElem( tfrm0.getCol2(), tfrm1.getCol2() ), + mulPerElem( tfrm0.getCol3(), tfrm1.getCol3() ) + ); +} + +inline const Transform3 Transform3::identity( ) +{ + return Transform3( + Vector3::xAxis( ), + Vector3::yAxis( ), + Vector3::zAxis( ), + Vector3( 0.0f ) + ); +} + +inline Transform3 & Transform3::setUpper3x3( const Matrix3 & tfrm ) +{ + mCol0 = tfrm.getCol0(); + mCol1 = tfrm.getCol1(); + mCol2 = tfrm.getCol2(); + return *this; +} + +inline const Matrix3 Transform3::getUpper3x3( ) const +{ + return Matrix3( mCol0, mCol1, mCol2 ); +} + +inline Transform3 & Transform3::setTranslation( Vector3 translateVec ) +{ + mCol3 = translateVec; + return *this; +} + +inline const Vector3 Transform3::getTranslation( ) const +{ + return mCol3; +} + +inline const Transform3 Transform3::rotationX( float radians ) +{ + vec_float4 s, c, res1, res2; + vec_uint4 select_y, select_z; + vec_float4 zero; + select_y = (vec_uint4)spu_maskb(0x0f00); + select_z = (vec_uint4)spu_maskb(0x00f0); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res1 = spu_sel( zero, c, select_y ); + res1 = spu_sel( res1, s, select_z ); + res2 = spu_sel( zero, negatef4(s), select_y ); + res2 = spu_sel( res2, c, select_z ); + return Transform3( + Vector3::xAxis( ), + Vector3( res1 ), + Vector3( res2 ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 Transform3::rotationY( float radians ) +{ + vec_float4 s, c, res0, res2; + vec_uint4 select_x, select_z; + vec_float4 zero; + select_x = (vec_uint4)spu_maskb(0xf000); + select_z = (vec_uint4)spu_maskb(0x00f0); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res0 = spu_sel( zero, c, select_x ); + res0 = spu_sel( res0, negatef4(s), select_z ); + res2 = spu_sel( zero, s, select_x ); + res2 = spu_sel( res2, c, select_z ); + return Transform3( + Vector3( res0 ), + Vector3::yAxis( ), + Vector3( res2 ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 Transform3::rotationZ( float radians ) +{ + vec_float4 s, c, res0, res1; + vec_uint4 select_x, select_y; + vec_float4 zero; + select_x = (vec_uint4)spu_maskb(0xf000); + select_y = (vec_uint4)spu_maskb(0x0f00); + zero = spu_splats(0.0f); + sincosf4( spu_splats(radians), &s, &c ); + res0 = spu_sel( zero, c, select_x ); + res0 = spu_sel( res0, s, select_y ); + res1 = spu_sel( zero, negatef4(s), select_x ); + res1 = spu_sel( res1, c, select_y ); + return Transform3( + Vector3( res0 ), + Vector3( res1 ), + Vector3::zAxis( ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 Transform3::rotationZYX( Vector3 radiansXYZ ) +{ + vec_float4 angles, s, negS, c, X0, X1, Y0, Y1, Z0, Z1, tmp; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + angles = radiansXYZ.get128(); + angles = spu_insert( 0.0f, angles, 3 ); + sincosf4( angles, &s, &c ); + negS = negatef4( s ); + Z0 = spu_shuffle( s, c, _VECTORMATH_SHUF_CZD0 ); + Z1 = spu_shuffle( c, negS, _VECTORMATH_SHUF_CZD0 ); + Y0 = spu_shuffle( negS, c, _VECTORMATH_SHUF_BBY0 ); + Y1 = spu_shuffle( c, s, _VECTORMATH_SHUF_BBY0 ); + X0 = spu_shuffle( s, s, shuffle_xxxx ); + X1 = spu_shuffle( c, c, shuffle_xxxx ); + tmp = spu_mul( Z0, Y1 ); + return Transform3( + Vector3( spu_mul( Z0, Y0 ) ), + Vector3( spu_madd( Z1, X1, spu_mul( tmp, X0 ) ) ), + Vector3( spu_nmsub( Z1, X0, spu_mul( tmp, X1 ) ) ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 Transform3::rotation( float radians, Vector3 unitVec ) +{ + return Transform3( Matrix3::rotation( radians, unitVec ), Vector3( 0.0f ) ); +} + +inline const Transform3 Transform3::rotation( Quat unitQuat ) +{ + return Transform3( Matrix3( unitQuat ), Vector3( 0.0f ) ); +} + +inline const Transform3 Transform3::scale( Vector3 scaleVec ) +{ + vec_float4 zero = spu_splats(0.0f); + return Transform3( + Vector3( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0xf000) ) ), + Vector3( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0x0f00) ) ), + Vector3( spu_sel( zero, scaleVec.get128(), (vec_uint4)spu_maskb(0x00f0) ) ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 appendScale( const Transform3 & tfrm, Vector3 scaleVec ) +{ + return Transform3( + ( tfrm.getCol0() * scaleVec.getX( ) ), + ( tfrm.getCol1() * scaleVec.getY( ) ), + ( tfrm.getCol2() * scaleVec.getZ( ) ), + tfrm.getCol3() + ); +} + +inline const Transform3 prependScale( Vector3 scaleVec, const Transform3 & tfrm ) +{ + return Transform3( + mulPerElem( tfrm.getCol0(), scaleVec ), + mulPerElem( tfrm.getCol1(), scaleVec ), + mulPerElem( tfrm.getCol2(), scaleVec ), + mulPerElem( tfrm.getCol3(), scaleVec ) + ); +} + +inline const Transform3 Transform3::translation( Vector3 translateVec ) +{ + return Transform3( + Vector3::xAxis( ), + Vector3::yAxis( ), + Vector3::zAxis( ), + translateVec + ); +} + +inline const Transform3 select( const Transform3 & tfrm0, const Transform3 & tfrm1, bool select1 ) +{ + return Transform3( + select( tfrm0.getCol0(), tfrm1.getCol0(), select1 ), + select( tfrm0.getCol1(), tfrm1.getCol1(), select1 ), + select( tfrm0.getCol2(), tfrm1.getCol2(), select1 ), + select( tfrm0.getCol3(), tfrm1.getCol3(), select1 ) + ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( const Transform3 & tfrm ) +{ + print( tfrm.getRow( 0 ) ); + print( tfrm.getRow( 1 ) ); + print( tfrm.getRow( 2 ) ); +} + +inline void print( const Transform3 & tfrm, const char * name ) +{ + printf("%s:\n", name); + print( tfrm ); +} + +#endif + +inline Quat::Quat( const Matrix3 & tfrm ) +{ + vec_float4 res; + vec_float4 col0, col1, col2; + vec_float4 xx_yy, xx_yy_zz_xx, yy_zz_xx_yy, zz_xx_yy_zz, diagSum, diagDiff; + vec_float4 zy_xz_yx, yz_zx_xy, sum, diff; + vec_float4 radicand, invSqrt, scale; + vec_float4 res0, res1, res2, res3; + vec_float4 xx, yy, zz; + vec_uint4 select_x = (vec_uint4)spu_maskb( 0xf000 ); + vec_uint4 select_y = (vec_uint4)spu_maskb( 0x0f00 ); + vec_uint4 select_z = (vec_uint4)spu_maskb( 0x00f0 ); + vec_uint4 select_w = (vec_uint4)spu_maskb( 0x000f ); + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((unsigned int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((unsigned int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((unsigned int)0x08090a0b); + vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((unsigned int)0x0c0d0e0f); + + col0 = tfrm.getCol0().get128(); + col1 = tfrm.getCol1().get128(); + col2 = tfrm.getCol2().get128(); + + /* four cases: */ + /* trace > 0 */ + /* else */ + /* xx largest diagonal element */ + /* yy largest diagonal element */ + /* zz largest diagonal element */ + + /* compute quaternion for each case */ + + xx_yy = spu_sel( col0, col1, select_y ); + xx_yy_zz_xx = spu_shuffle( xx_yy, col2, _VECTORMATH_SHUF_XYCX ); + yy_zz_xx_yy = spu_shuffle( xx_yy, col2, _VECTORMATH_SHUF_YCXY ); + zz_xx_yy_zz = spu_shuffle( xx_yy, col2, _VECTORMATH_SHUF_CXYC ); + + diagSum = spu_add( spu_add( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); + diagDiff = spu_sub( spu_sub( xx_yy_zz_xx, yy_zz_xx_yy ), zz_xx_yy_zz ); + radicand = spu_add( spu_sel( diagDiff, diagSum, select_w ), spu_splats(1.0f) ); + invSqrt = rsqrtf4( radicand ); + + zy_xz_yx = spu_sel( col0, col1, select_z ); + zy_xz_yx = spu_shuffle( zy_xz_yx, col2, _VECTORMATH_SHUF_ZAY0 ); + yz_zx_xy = spu_sel( col0, col1, select_x ); + yz_zx_xy = spu_shuffle( yz_zx_xy, col2, _VECTORMATH_SHUF_BZX0 ); + + sum = spu_add( zy_xz_yx, yz_zx_xy ); + diff = spu_sub( zy_xz_yx, yz_zx_xy ); + + scale = spu_mul( invSqrt, spu_splats(0.5f) ); + res0 = spu_shuffle( sum, diff, _VECTORMATH_SHUF_0ZYA ); + res1 = spu_shuffle( sum, diff, _VECTORMATH_SHUF_Z0XB ); + res2 = spu_shuffle( sum, diff, _VECTORMATH_SHUF_YX0C ); + res3 = diff; + res0 = spu_sel( res0, radicand, select_x ); + res1 = spu_sel( res1, radicand, select_y ); + res2 = spu_sel( res2, radicand, select_z ); + res3 = spu_sel( res3, radicand, select_w ); + res0 = spu_mul( res0, spu_shuffle( scale, scale, shuffle_xxxx ) ); + res1 = spu_mul( res1, spu_shuffle( scale, scale, shuffle_yyyy ) ); + res2 = spu_mul( res2, spu_shuffle( scale, scale, shuffle_zzzz ) ); + res3 = spu_mul( res3, spu_shuffle( scale, scale, shuffle_wwww ) ); + + /* determine case and select answer */ + + xx = spu_shuffle( col0, col0, shuffle_xxxx ); + yy = spu_shuffle( col1, col1, shuffle_yyyy ); + zz = spu_shuffle( col2, col2, shuffle_zzzz ); + res = spu_sel( res0, res1, spu_cmpgt( yy, xx ) ); + res = spu_sel( res, res2, spu_and( spu_cmpgt( zz, xx ), spu_cmpgt( zz, yy ) ) ); + res = spu_sel( res, res3, spu_cmpgt( spu_shuffle( diagSum, diagSum, shuffle_xxxx ), spu_splats(0.0f) ) ); + mVec128 = res; +} + +inline const Matrix3 outer( Vector3 tfrm0, Vector3 tfrm1 ) +{ + return Matrix3( + ( tfrm0 * tfrm1.getX( ) ), + ( tfrm0 * tfrm1.getY( ) ), + ( tfrm0 * tfrm1.getZ( ) ) + ); +} + +inline const Matrix4 outer( Vector4 tfrm0, Vector4 tfrm1 ) +{ + return Matrix4( + ( tfrm0 * tfrm1.getX( ) ), + ( tfrm0 * tfrm1.getY( ) ), + ( tfrm0 * tfrm1.getZ( ) ), + ( tfrm0 * tfrm1.getW( ) ) + ); +} + +inline const Vector3 rowMul( Vector3 vec, const Matrix3 & mat ) +{ + vec_float4 tmp0, tmp1, mcol0, mcol1, mcol2, res; + vec_float4 xxxx, yyyy, zzzz; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + tmp0 = spu_shuffle( mat.getCol0().get128(), mat.getCol2().get128(), _VECTORMATH_SHUF_XAYB ); + tmp1 = spu_shuffle( mat.getCol0().get128(), mat.getCol2().get128(), _VECTORMATH_SHUF_ZCWD ); + xxxx = spu_shuffle( vec.get128(), vec.get128(), shuffle_xxxx ); + mcol0 = spu_shuffle( tmp0, mat.getCol1().get128(), _VECTORMATH_SHUF_XAYB ); + mcol1 = spu_shuffle( tmp0, mat.getCol1().get128(), _VECTORMATH_SHUF_ZBW0 ); + mcol2 = spu_shuffle( tmp1, mat.getCol1().get128(), _VECTORMATH_SHUF_XCY0 ); + yyyy = spu_shuffle( vec.get128(), vec.get128(), shuffle_yyyy ); + res = spu_mul( mcol0, xxxx ); + zzzz = spu_shuffle( vec.get128(), vec.get128(), shuffle_zzzz ); + res = spu_madd( mcol1, yyyy, res ); + res = spu_madd( mcol2, zzzz, res ); + return Vector3( res ); +} + +inline const Matrix3 crossMatrix( Vector3 vec ) +{ + vec_float4 neg, res0, res1, res2; + neg = negatef4( vec.get128() ); + res0 = spu_shuffle( vec.get128(), neg, _VECTORMATH_SHUF_0ZB0 ); + res1 = spu_shuffle( vec.get128(), neg, _VECTORMATH_SHUF_C0X0 ); + res2 = spu_shuffle( vec.get128(), neg, _VECTORMATH_SHUF_YA00 ); + return Matrix3( + Vector3( res0 ), + Vector3( res1 ), + Vector3( res2 ) + ); +} + +inline const Matrix3 crossMatrixMul( Vector3 vec, const Matrix3 & mat ) +{ + return Matrix3( cross( vec, mat.getCol0() ), cross( vec, mat.getCol1() ), cross( vec, mat.getCol2() ) ); +} + +} // namespace Aos +} // namespace Vectormath + +#endif diff --git a/common/vectormath/spu/cpp/quat_aos.h b/common/vectormath/spu/cpp/quat_aos.h index c150f4a2..55afc6d8 100644 --- a/common/vectormath/spu/cpp/quat_aos.h +++ b/common/vectormath/spu/cpp/quat_aos.h @@ -1,417 +1,449 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_QUAT_AOS_CPP_H -#define _VECTORMATH_QUAT_AOS_CPP_H -//----------------------------------------------------------------------------- -// Definitions - -#ifndef _VECTORMATH_INTERNAL_FUNCTIONS -#define _VECTORMATH_INTERNAL_FUNCTIONS - -#endif - -namespace Vectormath { -namespace Aos { - -inline Quat::Quat( float _x, float _y, float _z, float _w ) -{ - mVec128 = (vec_float4){ _x, _y, _z, _w }; -} - -inline Quat::Quat( Vector3 xyz, float _w ) -{ - mVec128 = spu_shuffle( xyz.get128(), spu_promote( _w, 0 ), _VECTORMATH_SHUF_XYZA ); -} - -inline Quat::Quat( Vector4 vec ) -{ - mVec128 = vec.get128(); -} - -inline Quat::Quat( float scalar ) -{ - mVec128 = spu_splats( scalar ); -} - -inline Quat::Quat( vec_float4 vf4 ) -{ - mVec128 = vf4; -} - -inline const Quat Quat::identity( ) -{ - return Quat( _VECTORMATH_UNIT_0001 ); -} - -inline const Quat lerp( float t, Quat quat0, Quat quat1 ) -{ - return ( quat0 + ( ( quat1 - quat0 ) * t ) ); -} - -inline const Quat slerp( float t, Quat unitQuat0, Quat unitQuat1 ) -{ - Quat start; - vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; - vec_uint4 selectMask; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - cosAngle = _vmathVfDot4( unitQuat0.get128(), unitQuat1.get128() ); - cosAngle = spu_shuffle( cosAngle, cosAngle, shuffle_xxxx ); - selectMask = (vec_uint4)spu_cmpgt( spu_splats(0.0f), cosAngle ); - cosAngle = spu_sel( cosAngle, negatef4( cosAngle ), selectMask ); - start = Quat( spu_sel( unitQuat0.get128(), negatef4( unitQuat0.get128() ), selectMask ) ); - selectMask = (vec_uint4)spu_cmpgt( spu_splats(_VECTORMATH_SLERP_TOL), cosAngle ); - angle = acosf4( cosAngle ); - tttt = spu_splats(t); - oneMinusT = spu_sub( spu_splats(1.0f), tttt ); - angles = spu_sel( spu_splats(1.0f), oneMinusT, (vec_uint4)spu_maskb(0x0f00) ); - angles = spu_sel( angles, tttt, (vec_uint4)spu_maskb(0x00f0) ); - angles = spu_mul( angles, angle ); - sines = sinf4( angles ); - scales = divf4( sines, spu_shuffle( sines, sines, shuffle_xxxx ) ); - scale0 = spu_sel( oneMinusT, spu_shuffle( scales, scales, shuffle_yyyy ), selectMask ); - scale1 = spu_sel( tttt, spu_shuffle( scales, scales, shuffle_zzzz ), selectMask ); - return Quat( spu_madd( start.get128(), scale0, spu_mul( unitQuat1.get128(), scale1 ) ) ); -} - -inline const Quat squad( float t, Quat unitQuat0, Quat unitQuat1, Quat unitQuat2, Quat unitQuat3 ) -{ - Quat tmp0, tmp1; - tmp0 = slerp( t, unitQuat0, unitQuat3 ); - tmp1 = slerp( t, unitQuat1, unitQuat2 ); - return slerp( ( ( 2.0f * t ) * ( 1.0f - t ) ), tmp0, tmp1 ); -} - -inline vec_float4 Quat::get128( ) const -{ - return mVec128; -} - -inline Quat & Quat::operator =( Quat quat ) -{ - mVec128 = quat.mVec128; - return *this; -} - -inline Quat & Quat::setXYZ( Vector3 vec ) -{ - mVec128 = spu_sel( vec.get128(), mVec128, (vec_uint4)spu_maskb(0x000f) ); - return *this; -} - -inline const Vector3 Quat::getXYZ( ) const -{ - return Vector3( mVec128 ); -} - -inline Quat & Quat::setX( float _x ) -{ - mVec128 = spu_insert( _x, mVec128, 0 ); - return *this; -} - -inline float Quat::getX( ) const -{ - return spu_extract( mVec128, 0 ); -} - -inline Quat & Quat::setY( float _y ) -{ - mVec128 = spu_insert( _y, mVec128, 1 ); - return *this; -} - -inline float Quat::getY( ) const -{ - return spu_extract( mVec128, 1 ); -} - -inline Quat & Quat::setZ( float _z ) -{ - mVec128 = spu_insert( _z, mVec128, 2 ); - return *this; -} - -inline float Quat::getZ( ) const -{ - return spu_extract( mVec128, 2 ); -} - -inline Quat & Quat::setW( float _w ) -{ - mVec128 = spu_insert( _w, mVec128, 3 ); - return *this; -} - -inline float Quat::getW( ) const -{ - return spu_extract( mVec128, 3 ); -} - -inline Quat & Quat::setElem( int idx, float value ) -{ - mVec128 = spu_insert( value, mVec128, idx ); - return *this; -} - -inline float Quat::getElem( int idx ) const -{ - return spu_extract( mVec128, idx ); -} - -inline VecIdx Quat::operator []( int idx ) -{ - return VecIdx( mVec128, idx ); -} - -inline float Quat::operator []( int idx ) const -{ - return spu_extract( mVec128, idx ); -} - -inline const Quat Quat::operator +( Quat quat ) const -{ - return Quat( spu_add( mVec128, quat.mVec128 ) ); -} - -inline const Quat Quat::operator -( Quat quat ) const -{ - return Quat( spu_sub( mVec128, quat.mVec128 ) ); -} - -inline const Quat Quat::operator *( float scalar ) const -{ - return Quat( spu_mul( mVec128, spu_splats(scalar) ) ); -} - -inline Quat & Quat::operator +=( Quat quat ) -{ - *this = *this + quat; - return *this; -} - -inline Quat & Quat::operator -=( Quat quat ) -{ - *this = *this - quat; - return *this; -} - -inline Quat & Quat::operator *=( float scalar ) -{ - *this = *this * scalar; - return *this; -} - -inline const Quat Quat::operator /( float scalar ) const -{ - return Quat( divf4( mVec128, spu_splats(scalar) ) ); -} - -inline Quat & Quat::operator /=( float scalar ) -{ - *this = *this / scalar; - return *this; -} - -inline const Quat Quat::operator -( ) const -{ - return Quat( negatef4( mVec128 ) ); -} - -inline const Quat operator *( float scalar, Quat quat ) -{ - return quat * scalar; -} - -inline float dot( Quat quat0, Quat quat1 ) -{ - return spu_extract( _vmathVfDot4( quat0.get128(), quat1.get128() ), 0 ); -} - -inline float norm( Quat quat ) -{ - return spu_extract( _vmathVfDot4( quat.get128(), quat.get128() ), 0 ); -} - -inline float length( Quat quat ) -{ - return sqrtf( norm( quat ) ); -} - -inline const Quat normalize( Quat quat ) -{ - vec_float4 dot = _vmathVfDot4( quat.get128(), quat.get128() ); - return Quat( spu_mul( quat.get128(), rsqrtf4( dot ) ) ); -} - -inline const Quat Quat::rotation( Vector3 unitVec0, Vector3 unitVec1 ) -{ - Vector3 crossVec; - vec_float4 cosAngle, cosAngleX2Plus2, recipCosHalfAngleX2, cosHalfAngleX2, res; - cosAngle = _vmathVfDot3( unitVec0.get128(), unitVec1.get128() ); - cosAngle = spu_shuffle( cosAngle, cosAngle, (vec_uchar16)spu_splats(0x00010203) ); - cosAngleX2Plus2 = spu_madd( cosAngle, spu_splats(2.0f), spu_splats(2.0f) ); - recipCosHalfAngleX2 = rsqrtf4( cosAngleX2Plus2 ); - cosHalfAngleX2 = spu_mul( recipCosHalfAngleX2, cosAngleX2Plus2 ); - crossVec = cross( unitVec0, unitVec1 ); - res = spu_mul( crossVec.get128(), recipCosHalfAngleX2 ); - res = spu_sel( res, spu_mul( cosHalfAngleX2, spu_splats(0.5f) ), (vec_uint4)spu_maskb(0x000f) ); - return Quat( res ); -} - -inline const Quat Quat::rotation( float radians, Vector3 unitVec ) -{ - vec_float4 s, c, angle, res; - angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); - sincosf4( angle, &s, &c ); - res = spu_sel( spu_mul( unitVec.get128(), s ), c, (vec_uint4)spu_maskb(0x000f) ); - return Quat( res ); -} - -inline const Quat Quat::rotationX( float radians ) -{ - vec_float4 s, c, angle, res; - angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); - sincosf4( angle, &s, &c ); - res = spu_sel( spu_splats(0.0f), s, (vec_uint4)spu_maskb(0xf000) ); - res = spu_sel( res, c, (vec_uint4)spu_maskb(0x000f) ); - return Quat( res ); -} - -inline const Quat Quat::rotationY( float radians ) -{ - vec_float4 s, c, angle, res; - angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); - sincosf4( angle, &s, &c ); - res = spu_sel( spu_splats(0.0f), s, (vec_uint4)spu_maskb(0x0f00) ); - res = spu_sel( res, c, (vec_uint4)spu_maskb(0x000f) ); - return Quat( res ); -} - -inline const Quat Quat::rotationZ( float radians ) -{ - vec_float4 s, c, angle, res; - angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); - sincosf4( angle, &s, &c ); - res = spu_sel( spu_splats(0.0f), s, (vec_uint4)spu_maskb(0x00f0) ); - res = spu_sel( res, c, (vec_uint4)spu_maskb(0x000f) ); - return Quat( res ); -} - -inline const Quat Quat::operator *( Quat quat ) const -{ - vec_float4 ldata, rdata, qv, tmp0, tmp1, tmp2, tmp3; - vec_float4 product, l_wxyz, r_wxyz, xy, qw; - ldata = mVec128; - rdata = quat.mVec128; - vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); - tmp0 = spu_shuffle( ldata, ldata, _VECTORMATH_SHUF_YZXW ); - tmp1 = spu_shuffle( rdata, rdata, _VECTORMATH_SHUF_ZXYW ); - tmp2 = spu_shuffle( ldata, ldata, _VECTORMATH_SHUF_ZXYW ); - tmp3 = spu_shuffle( rdata, rdata, _VECTORMATH_SHUF_YZXW ); - qv = spu_mul( spu_shuffle( ldata, ldata, shuffle_wwww ), rdata ); - qv = spu_madd( spu_shuffle( rdata, rdata, shuffle_wwww ), ldata, qv ); - qv = spu_madd( tmp0, tmp1, qv ); - qv = spu_nmsub( tmp2, tmp3, qv ); - product = spu_mul( ldata, rdata ); - l_wxyz = spu_rlqwbyte( ldata, 12 ); - r_wxyz = spu_rlqwbyte( rdata, 12 ); - qw = spu_nmsub( l_wxyz, r_wxyz, product ); - xy = spu_madd( l_wxyz, r_wxyz, product ); - qw = spu_sub( qw, spu_rlqwbyte( xy, 8 ) ); - return Quat( spu_sel( qv, qw, (vec_uint4)spu_maskb( 0x000f ) ) ); -} - -inline Quat & Quat::operator *=( Quat quat ) -{ - *this = *this * quat; - return *this; -} - -inline const Vector3 rotate( Quat quat, Vector3 vec ) -{ - vec_float4 qdata, vdata, product, tmp0, tmp1, tmp2, tmp3, wwww, qv, qw, res; - qdata = quat.get128(); - vdata = vec.get128(); - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); - tmp0 = spu_shuffle( qdata, qdata, _VECTORMATH_SHUF_YZXW ); - tmp1 = spu_shuffle( vdata, vdata, _VECTORMATH_SHUF_ZXYW ); - tmp2 = spu_shuffle( qdata, qdata, _VECTORMATH_SHUF_ZXYW ); - tmp3 = spu_shuffle( vdata, vdata, _VECTORMATH_SHUF_YZXW ); - wwww = spu_shuffle( qdata, qdata, shuffle_wwww ); - qv = spu_mul( wwww, vdata ); - qv = spu_madd( tmp0, tmp1, qv ); - qv = spu_nmsub( tmp2, tmp3, qv ); - product = spu_mul( qdata, vdata ); - qw = spu_madd( spu_rlqwbyte( qdata, 4 ), spu_rlqwbyte( vdata, 4 ), product ); - qw = spu_add( spu_rlqwbyte( product, 8 ), qw ); - tmp1 = spu_shuffle( qv, qv, _VECTORMATH_SHUF_ZXYW ); - tmp3 = spu_shuffle( qv, qv, _VECTORMATH_SHUF_YZXW ); - res = spu_mul( spu_shuffle( qw, qw, shuffle_xxxx ), qdata ); - res = spu_madd( wwww, qv, res ); - res = spu_madd( tmp0, tmp1, res ); - res = spu_nmsub( tmp2, tmp3, res ); - return Vector3( res ); -} - -inline const Quat conj( Quat quat ) -{ - return Quat( spu_xor( quat.get128(), ((vec_float4)(vec_uint4){0x80000000,0x80000000,0x80000000,0}) ) ); -} - -inline const Quat select( Quat quat0, Quat quat1, bool select1 ) -{ - return Quat( spu_sel( quat0.get128(), quat1.get128(), spu_splats( (unsigned int)-(select1 > 0) ) ) ); -} - -#ifdef _VECTORMATH_DEBUG - -inline void print( Quat quat ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = quat.get128(); - printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -inline void print( Quat quat, const char * name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = quat.get128(); - printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -#endif - -} // namespace Aos -} // namespace Vectormath - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_QUAT_AOS_CPP_H +#define _VECTORMATH_QUAT_AOS_CPP_H +//----------------------------------------------------------------------------- +// Definitions + +#ifndef _VECTORMATH_INTERNAL_FUNCTIONS +#define _VECTORMATH_INTERNAL_FUNCTIONS + +#endif + +namespace Vectormath { +namespace Aos { + +inline Quat::Quat( float _x, float _y, float _z, float _w ) +{ + mVec128 = (vec_float4){ _x, _y, _z, _w }; +} + +inline Quat::Quat( Vector3 xyz, float _w ) +{ + mVec128 = spu_shuffle( xyz.get128(), spu_promote( _w, 0 ), _VECTORMATH_SHUF_XYZA ); +} + +inline Quat::Quat( Vector4 vec ) +{ + mVec128 = vec.get128(); +} + +inline Quat::Quat( float scalar ) +{ + mVec128 = spu_splats( scalar ); +} + +inline Quat::Quat( vec_float4 vf4 ) +{ + mVec128 = vf4; +} + +inline const Quat Quat::identity( ) +{ + return Quat( _VECTORMATH_UNIT_0001 ); +} + +inline const Quat lerp( float t, Quat quat0, Quat quat1 ) +{ + return ( quat0 + ( ( quat1 - quat0 ) * t ) ); +} + +inline const Quat slerp( float t, Quat unitQuat0, Quat unitQuat1 ) +{ + Quat start; + vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; + vec_uint4 selectMask; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + cosAngle = _vmathVfDot4( unitQuat0.get128(), unitQuat1.get128() ); + cosAngle = spu_shuffle( cosAngle, cosAngle, shuffle_xxxx ); + selectMask = (vec_uint4)spu_cmpgt( spu_splats(0.0f), cosAngle ); + cosAngle = spu_sel( cosAngle, negatef4( cosAngle ), selectMask ); + start = Quat( spu_sel( unitQuat0.get128(), negatef4( unitQuat0.get128() ), selectMask ) ); + selectMask = (vec_uint4)spu_cmpgt( spu_splats(_VECTORMATH_SLERP_TOL), cosAngle ); + angle = acosf4( cosAngle ); + tttt = spu_splats(t); + oneMinusT = spu_sub( spu_splats(1.0f), tttt ); + angles = spu_sel( spu_splats(1.0f), oneMinusT, (vec_uint4)spu_maskb(0x0f00) ); + angles = spu_sel( angles, tttt, (vec_uint4)spu_maskb(0x00f0) ); + angles = spu_mul( angles, angle ); + sines = sinf4( angles ); + scales = divf4( sines, spu_shuffle( sines, sines, shuffle_xxxx ) ); + scale0 = spu_sel( oneMinusT, spu_shuffle( scales, scales, shuffle_yyyy ), selectMask ); + scale1 = spu_sel( tttt, spu_shuffle( scales, scales, shuffle_zzzz ), selectMask ); + return Quat( spu_madd( start.get128(), scale0, spu_mul( unitQuat1.get128(), scale1 ) ) ); +} + +inline const Quat squad( float t, Quat unitQuat0, Quat unitQuat1, Quat unitQuat2, Quat unitQuat3 ) +{ + Quat tmp0, tmp1; + tmp0 = slerp( t, unitQuat0, unitQuat3 ); + tmp1 = slerp( t, unitQuat1, unitQuat2 ); + return slerp( ( ( 2.0f * t ) * ( 1.0f - t ) ), tmp0, tmp1 ); +} + +inline vec_float4 Quat::get128( ) const +{ + return mVec128; +} + +inline void loadXYZW( Quat & quat, const vec_float4 * quad ) +{ + quat = Quat( *quad ); +} + +inline void loadXYZW( Quat & quat, const float * fptr ) +{ + const vec_float4 vfsrc0 = *((vec_float4*)((uintptr_t)fptr)); + const vec_float4 vfsrc1 = *((vec_float4*)(((uintptr_t)fptr) + 16)); + const vec_uchar16 vucpat = (vec_uchar16)spu_add((vec_ushort8)(spu_splats((unsigned char)(((int)fptr)&0xf))),((vec_ushort8){0x0001, 0x0203, 0x0405, 0x0607, 0x0809, 0x0A0B, 0x0C0D, 0x0E0F})); + const vec_float4 vfval = spu_shuffle(vfsrc0, vfsrc1, vucpat); + quat = Quat( vfval ); +} + +inline void storeXYZW( Quat quat, float * fptr ) +{ + vec_float4 * vptr0 = (vec_float4*)((uintptr_t)fptr); + vec_float4 * vptr1 = (vec_float4*)(((uintptr_t)fptr) + 16); + vec_float4 dstVec0 = *vptr0; + vec_float4 dstVec1 = *vptr1; + uint32_t offset = (uint32_t)fptr & 0xf; + vec_uint4 mask = (vec_uint4)spu_splats(0xffffffff); + vec_uint4 mask0 = (vec_uint4)spu_rlmaskqwbyte(mask, -offset); + vec_uint4 mask1 = (vec_uint4)spu_slqwbyte(mask, 16 - offset); + vec_float4 vec0 = spu_rlmaskqwbyte(quat.get128(), -offset); + vec_float4 vec1 = spu_slqwbyte(quat.get128(), 16 - offset); + dstVec0 = spu_sel(dstVec0, vec0, mask0); + dstVec1 = spu_sel(dstVec1, vec1, mask1); + *vptr0 = dstVec0; + *vptr1 = dstVec1; +} + +inline Quat & Quat::operator =( Quat quat ) +{ + mVec128 = quat.mVec128; + return *this; +} + +inline Quat & Quat::setXYZ( Vector3 vec ) +{ + mVec128 = spu_sel( vec.get128(), mVec128, (vec_uint4)spu_maskb(0x000f) ); + return *this; +} + +inline const Vector3 Quat::getXYZ( ) const +{ + return Vector3( mVec128 ); +} + +inline Quat & Quat::setX( float _x ) +{ + mVec128 = spu_insert( _x, mVec128, 0 ); + return *this; +} + +inline float Quat::getX( ) const +{ + return spu_extract( mVec128, 0 ); +} + +inline Quat & Quat::setY( float _y ) +{ + mVec128 = spu_insert( _y, mVec128, 1 ); + return *this; +} + +inline float Quat::getY( ) const +{ + return spu_extract( mVec128, 1 ); +} + +inline Quat & Quat::setZ( float _z ) +{ + mVec128 = spu_insert( _z, mVec128, 2 ); + return *this; +} + +inline float Quat::getZ( ) const +{ + return spu_extract( mVec128, 2 ); +} + +inline Quat & Quat::setW( float _w ) +{ + mVec128 = spu_insert( _w, mVec128, 3 ); + return *this; +} + +inline float Quat::getW( ) const +{ + return spu_extract( mVec128, 3 ); +} + +inline Quat & Quat::setElem( int idx, float value ) +{ + mVec128 = spu_insert( value, mVec128, idx ); + return *this; +} + +inline float Quat::getElem( int idx ) const +{ + return spu_extract( mVec128, idx ); +} + +inline VecIdx Quat::operator []( int idx ) +{ + return VecIdx( mVec128, idx ); +} + +inline float Quat::operator []( int idx ) const +{ + return spu_extract( mVec128, idx ); +} + +inline const Quat Quat::operator +( Quat quat ) const +{ + return Quat( spu_add( mVec128, quat.mVec128 ) ); +} + +inline const Quat Quat::operator -( Quat quat ) const +{ + return Quat( spu_sub( mVec128, quat.mVec128 ) ); +} + +inline const Quat Quat::operator *( float scalar ) const +{ + return Quat( spu_mul( mVec128, spu_splats(scalar) ) ); +} + +inline Quat & Quat::operator +=( Quat quat ) +{ + *this = *this + quat; + return *this; +} + +inline Quat & Quat::operator -=( Quat quat ) +{ + *this = *this - quat; + return *this; +} + +inline Quat & Quat::operator *=( float scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Quat Quat::operator /( float scalar ) const +{ + return Quat( divf4( mVec128, spu_splats(scalar) ) ); +} + +inline Quat & Quat::operator /=( float scalar ) +{ + *this = *this / scalar; + return *this; +} + +inline const Quat Quat::operator -( ) const +{ + return Quat( negatef4( mVec128 ) ); +} + +inline const Quat operator *( float scalar, Quat quat ) +{ + return quat * scalar; +} + +inline float dot( Quat quat0, Quat quat1 ) +{ + return spu_extract( _vmathVfDot4( quat0.get128(), quat1.get128() ), 0 ); +} + +inline float norm( Quat quat ) +{ + return spu_extract( _vmathVfDot4( quat.get128(), quat.get128() ), 0 ); +} + +inline float length( Quat quat ) +{ + return sqrtf( norm( quat ) ); +} + +inline const Quat normalize( Quat quat ) +{ + vec_float4 dot = _vmathVfDot4( quat.get128(), quat.get128() ); + return Quat( spu_mul( quat.get128(), rsqrtf4( dot ) ) ); +} + +inline const Quat Quat::rotation( Vector3 unitVec0, Vector3 unitVec1 ) +{ + Vector3 crossVec; + vec_float4 cosAngle, cosAngleX2Plus2, recipCosHalfAngleX2, cosHalfAngleX2, res; + cosAngle = _vmathVfDot3( unitVec0.get128(), unitVec1.get128() ); + cosAngle = spu_shuffle( cosAngle, cosAngle, (vec_uchar16)spu_splats(0x00010203) ); + cosAngleX2Plus2 = spu_madd( cosAngle, spu_splats(2.0f), spu_splats(2.0f) ); + recipCosHalfAngleX2 = rsqrtf4( cosAngleX2Plus2 ); + cosHalfAngleX2 = spu_mul( recipCosHalfAngleX2, cosAngleX2Plus2 ); + crossVec = cross( unitVec0, unitVec1 ); + res = spu_mul( crossVec.get128(), recipCosHalfAngleX2 ); + res = spu_sel( res, spu_mul( cosHalfAngleX2, spu_splats(0.5f) ), (vec_uint4)spu_maskb(0x000f) ); + return Quat( res ); +} + +inline const Quat Quat::rotation( float radians, Vector3 unitVec ) +{ + vec_float4 s, c, angle, res; + angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); + sincosf4( angle, &s, &c ); + res = spu_sel( spu_mul( unitVec.get128(), s ), c, (vec_uint4)spu_maskb(0x000f) ); + return Quat( res ); +} + +inline const Quat Quat::rotationX( float radians ) +{ + vec_float4 s, c, angle, res; + angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); + sincosf4( angle, &s, &c ); + res = spu_sel( spu_splats(0.0f), s, (vec_uint4)spu_maskb(0xf000) ); + res = spu_sel( res, c, (vec_uint4)spu_maskb(0x000f) ); + return Quat( res ); +} + +inline const Quat Quat::rotationY( float radians ) +{ + vec_float4 s, c, angle, res; + angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); + sincosf4( angle, &s, &c ); + res = spu_sel( spu_splats(0.0f), s, (vec_uint4)spu_maskb(0x0f00) ); + res = spu_sel( res, c, (vec_uint4)spu_maskb(0x000f) ); + return Quat( res ); +} + +inline const Quat Quat::rotationZ( float radians ) +{ + vec_float4 s, c, angle, res; + angle = spu_mul( spu_splats(radians), spu_splats(0.5f) ); + sincosf4( angle, &s, &c ); + res = spu_sel( spu_splats(0.0f), s, (vec_uint4)spu_maskb(0x00f0) ); + res = spu_sel( res, c, (vec_uint4)spu_maskb(0x000f) ); + return Quat( res ); +} + +inline const Quat Quat::operator *( Quat quat ) const +{ + vec_float4 ldata, rdata, qv, tmp0, tmp1, tmp2, tmp3; + vec_float4 product, l_wxyz, r_wxyz, xy, qw; + ldata = mVec128; + rdata = quat.mVec128; + vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); + tmp0 = spu_shuffle( ldata, ldata, _VECTORMATH_SHUF_YZXW ); + tmp1 = spu_shuffle( rdata, rdata, _VECTORMATH_SHUF_ZXYW ); + tmp2 = spu_shuffle( ldata, ldata, _VECTORMATH_SHUF_ZXYW ); + tmp3 = spu_shuffle( rdata, rdata, _VECTORMATH_SHUF_YZXW ); + qv = spu_mul( spu_shuffle( ldata, ldata, shuffle_wwww ), rdata ); + qv = spu_madd( spu_shuffle( rdata, rdata, shuffle_wwww ), ldata, qv ); + qv = spu_madd( tmp0, tmp1, qv ); + qv = spu_nmsub( tmp2, tmp3, qv ); + product = spu_mul( ldata, rdata ); + l_wxyz = spu_rlqwbyte( ldata, 12 ); + r_wxyz = spu_rlqwbyte( rdata, 12 ); + qw = spu_nmsub( l_wxyz, r_wxyz, product ); + xy = spu_madd( l_wxyz, r_wxyz, product ); + qw = spu_sub( qw, spu_rlqwbyte( xy, 8 ) ); + return Quat( spu_sel( qv, qw, (vec_uint4)spu_maskb( 0x000f ) ) ); +} + +inline Quat & Quat::operator *=( Quat quat ) +{ + *this = *this * quat; + return *this; +} + +inline const Vector3 rotate( Quat quat, Vector3 vec ) +{ + vec_float4 qdata, vdata, product, tmp0, tmp1, tmp2, tmp3, wwww, qv, qw, res; + qdata = quat.get128(); + vdata = vec.get128(); + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_wwww = (vec_uchar16)spu_splats((int)0x0c0d0e0f); + tmp0 = spu_shuffle( qdata, qdata, _VECTORMATH_SHUF_YZXW ); + tmp1 = spu_shuffle( vdata, vdata, _VECTORMATH_SHUF_ZXYW ); + tmp2 = spu_shuffle( qdata, qdata, _VECTORMATH_SHUF_ZXYW ); + tmp3 = spu_shuffle( vdata, vdata, _VECTORMATH_SHUF_YZXW ); + wwww = spu_shuffle( qdata, qdata, shuffle_wwww ); + qv = spu_mul( wwww, vdata ); + qv = spu_madd( tmp0, tmp1, qv ); + qv = spu_nmsub( tmp2, tmp3, qv ); + product = spu_mul( qdata, vdata ); + qw = spu_madd( spu_rlqwbyte( qdata, 4 ), spu_rlqwbyte( vdata, 4 ), product ); + qw = spu_add( spu_rlqwbyte( product, 8 ), qw ); + tmp1 = spu_shuffle( qv, qv, _VECTORMATH_SHUF_ZXYW ); + tmp3 = spu_shuffle( qv, qv, _VECTORMATH_SHUF_YZXW ); + res = spu_mul( spu_shuffle( qw, qw, shuffle_xxxx ), qdata ); + res = spu_madd( wwww, qv, res ); + res = spu_madd( tmp0, tmp1, res ); + res = spu_nmsub( tmp2, tmp3, res ); + return Vector3( res ); +} + +inline const Quat conj( Quat quat ) +{ + return Quat( spu_xor( quat.get128(), ((vec_float4)(vec_uint4){0x80000000,0x80000000,0x80000000,0}) ) ); +} + +inline const Quat select( Quat quat0, Quat quat1, bool select1 ) +{ + return Quat( spu_sel( quat0.get128(), quat1.get128(), spu_splats( (unsigned int)-(select1 > 0) ) ) ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( Quat quat ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = quat.get128(); + printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +inline void print( Quat quat, const char * name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = quat.get128(); + printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +#endif + +} // namespace Aos +} // namespace Vectormath + +#endif diff --git a/common/vectormath/spu/cpp/vec_aos.h b/common/vectormath/spu/cpp/vec_aos.h index c983f181..1724a9c5 100644 --- a/common/vectormath/spu/cpp/vec_aos.h +++ b/common/vectormath/spu/cpp/vec_aos.h @@ -1,1167 +1,1263 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_VEC_AOS_CPP_H -#define _VECTORMATH_VEC_AOS_CPP_H -//----------------------------------------------------------------------------- -// Constants -// for shuffles, words are labeled [x,y,z,w] [a,b,c,d] - -#define _VECTORMATH_SHUF_X 0x00010203 -#define _VECTORMATH_SHUF_Y 0x04050607 -#define _VECTORMATH_SHUF_Z 0x08090a0b -#define _VECTORMATH_SHUF_W 0x0c0d0e0f -#define _VECTORMATH_SHUF_A 0x10111213 -#define _VECTORMATH_SHUF_B 0x14151617 -#define _VECTORMATH_SHUF_C 0x18191a1b -#define _VECTORMATH_SHUF_D 0x1c1d1e1f -#define _VECTORMATH_SHUF_0 0x80808080 -#define _VECTORMATH_SHUF_XYZA (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A } -#define _VECTORMATH_SHUF_ZXYW (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_W } -#define _VECTORMATH_SHUF_YZXW (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_W } -#define _VECTORMATH_SHUF_WABC (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_C } -#define _VECTORMATH_SHUF_ZWAB (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B } -#define _VECTORMATH_SHUF_XYZA (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A } -#define _VECTORMATH_SHUF_YZAB (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B } -#define _VECTORMATH_SHUF_ZABC (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_C } -#define _VECTORMATH_UNIT_1000 (vec_float4){ 1.0f, 0.0f, 0.0f, 0.0f } -#define _VECTORMATH_UNIT_0100 (vec_float4){ 0.0f, 1.0f, 0.0f, 0.0f } -#define _VECTORMATH_UNIT_0010 (vec_float4){ 0.0f, 0.0f, 1.0f, 0.0f } -#define _VECTORMATH_UNIT_0001 (vec_float4){ 0.0f, 0.0f, 0.0f, 1.0f } -#define _VECTORMATH_SLERP_TOL 0.999f - -//----------------------------------------------------------------------------- -// Definitions - -#ifndef _VECTORMATH_INTERNAL_FUNCTIONS -#define _VECTORMATH_INTERNAL_FUNCTIONS - -static inline vec_float4 _vmathVfDot3( vec_float4 vec0, vec_float4 vec1 ) -{ - vec_float4 result; - result = spu_mul( vec0, vec1 ); - result = spu_madd( spu_rlqwbyte( vec0, 4 ), spu_rlqwbyte( vec1, 4 ), result ); - return spu_madd( spu_rlqwbyte( vec0, 8 ), spu_rlqwbyte( vec1, 8 ), result ); -} - -static inline vec_float4 _vmathVfDot4( vec_float4 vec0, vec_float4 vec1 ) -{ - vec_float4 result; - result = spu_mul( vec0, vec1 ); - result = spu_madd( spu_rlqwbyte( vec0, 4 ), spu_rlqwbyte( vec1, 4 ), result ); - return spu_add( spu_rlqwbyte( result, 8 ), result ); -} - -static inline vec_float4 _vmathVfCross( vec_float4 vec0, vec_float4 vec1 ) -{ - vec_float4 tmp0, tmp1, tmp2, tmp3, result; - tmp0 = spu_shuffle( vec0, vec0, _VECTORMATH_SHUF_YZXW ); - tmp1 = spu_shuffle( vec1, vec1, _VECTORMATH_SHUF_ZXYW ); - tmp2 = spu_shuffle( vec0, vec0, _VECTORMATH_SHUF_ZXYW ); - tmp3 = spu_shuffle( vec1, vec1, _VECTORMATH_SHUF_YZXW ); - result = spu_mul( tmp0, tmp1 ); - result = spu_nmsub( tmp2, tmp3, result ); - return result; -} - -static inline vec_uint4 _vmathVfToHalfFloatsUnpacked(vec_float4 v) -{ - vec_int4 bexp; - vec_uint4 mant, sign, hfloat; - vec_uint4 notZero, isInf; - const vec_uint4 hfloatInf = spu_splats(0x00007c00u); - const vec_uint4 mergeMant = spu_splats(0x000003ffu); - const vec_uint4 mergeSign = spu_splats(0x00008000u); - - sign = spu_rlmask((vec_uint4)v, -16); - mant = spu_rlmask((vec_uint4)v, -13); - bexp = spu_and(spu_rlmask((vec_int4)v, -23), 0xff); - - notZero = spu_cmpgt(bexp, 112); - isInf = spu_cmpgt(bexp, 142); - - bexp = spu_add(bexp, -112); - bexp = spu_sl(bexp, 10); - - hfloat = spu_sel((vec_uint4)bexp, mant, mergeMant); - hfloat = spu_sel(spu_splats(0u), hfloat, notZero); - hfloat = spu_sel(hfloat, hfloatInf, isInf); - hfloat = spu_sel(hfloat, sign, mergeSign); - - return hfloat; -} - -static inline vec_ushort8 _vmath2VfToHalfFloats(vec_float4 u, vec_float4 v) -{ - vec_uint4 hfloat_u, hfloat_v; - const vec_uchar16 pack = (vec_uchar16){2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31}; - hfloat_u = _vmathVfToHalfFloatsUnpacked(u); - hfloat_v = _vmathVfToHalfFloatsUnpacked(v); - return (vec_ushort8)spu_shuffle(hfloat_u, hfloat_v, pack); -} - -#endif - -namespace Vectormath { -namespace Aos { - -inline VecIdx::operator float() const -{ - return spu_extract( ref, i ); -} - -inline float VecIdx::operator =( float scalar ) -{ - ref = spu_insert( scalar, ref, i ); - return scalar; -} - -inline float VecIdx::operator =( const VecIdx& scalar ) -{ - return *this = float(scalar); -} - -inline float VecIdx::operator *=( float scalar ) -{ - float tmp = spu_extract( ref, i ) * scalar; - ref = spu_insert( tmp, ref, i ); - return tmp; -} - -inline float VecIdx::operator /=( float scalar ) -{ - float tmp = spu_extract( ref, i ) / scalar; - ref = spu_insert( tmp, ref, i ); - return tmp; -} - -inline float VecIdx::operator +=( float scalar ) -{ - float tmp = spu_extract( ref, i ) + scalar; - ref = spu_insert( tmp, ref, i ); - return tmp; -} - -inline float VecIdx::operator -=( float scalar ) -{ - float tmp = spu_extract( ref, i ) - scalar; - ref = spu_insert( tmp, ref, i ); - return tmp; -} - -inline Vector3::Vector3( float _x, float _y, float _z ) -{ - mVec128 = (vec_float4){ _x, _y, _z, 0.0f }; -} - -inline Vector3::Vector3( Point3 pnt ) -{ - mVec128 = pnt.get128(); -} - -inline Vector3::Vector3( float scalar ) -{ - mVec128 = spu_splats( scalar ); -} - -inline Vector3::Vector3( vec_float4 vf4 ) -{ - mVec128 = vf4; -} - -inline const Vector3 Vector3::xAxis( ) -{ - return Vector3( _VECTORMATH_UNIT_1000 ); -} - -inline const Vector3 Vector3::yAxis( ) -{ - return Vector3( _VECTORMATH_UNIT_0100 ); -} - -inline const Vector3 Vector3::zAxis( ) -{ - return Vector3( _VECTORMATH_UNIT_0010 ); -} - -inline const Vector3 lerp( float t, Vector3 vec0, Vector3 vec1 ) -{ - return ( vec0 + ( ( vec1 - vec0 ) * t ) ); -} - -inline const Vector3 slerp( float t, Vector3 unitVec0, Vector3 unitVec1 ) -{ - vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; - vec_uint4 selectMask; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - cosAngle = _vmathVfDot3( unitVec0.get128(), unitVec1.get128() ); - cosAngle = spu_shuffle( cosAngle, cosAngle, shuffle_xxxx ); - selectMask = (vec_uint4)spu_cmpgt( spu_splats(_VECTORMATH_SLERP_TOL), cosAngle ); - angle = acosf4( cosAngle ); - tttt = spu_splats(t); - oneMinusT = spu_sub( spu_splats(1.0f), tttt ); - angles = spu_sel( spu_splats(1.0f), oneMinusT, (vec_uint4)spu_maskb(0x0f00) ); - angles = spu_sel( angles, tttt, (vec_uint4)spu_maskb(0x00f0) ); - angles = spu_mul( angles, angle ); - sines = sinf4( angles ); - scales = divf4( sines, spu_shuffle( sines, sines, shuffle_xxxx ) ); - scale0 = spu_sel( oneMinusT, spu_shuffle( scales, scales, shuffle_yyyy ), selectMask ); - scale1 = spu_sel( tttt, spu_shuffle( scales, scales, shuffle_zzzz ), selectMask ); - return Vector3( spu_madd( unitVec0.get128(), scale0, spu_mul( unitVec1.get128(), scale1 ) ) ); -} - -inline vec_float4 Vector3::get128( ) const -{ - return mVec128; -} - -inline void storeXYZ( Vector3 vec, vec_float4 * quad ) -{ - vec_float4 dstVec = *quad; - vec_uint4 mask = (vec_uint4)spu_maskb(0x000f); - dstVec = spu_sel(vec.get128(), dstVec, mask); - *quad = dstVec; -} - -inline void loadXYZArray( Vector3 & vec0, Vector3 & vec1, Vector3 & vec2, Vector3 & vec3, const vec_float4 * threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; - xyzx = threeQuads[0]; - yzxy = threeQuads[1]; - zxyz = threeQuads[2]; - xyz1 = spu_shuffle( xyzx, yzxy, _VECTORMATH_SHUF_WABC ); - xyz2 = spu_shuffle( yzxy, zxyz, _VECTORMATH_SHUF_ZWAB ); - xyz3 = spu_rlqwbyte( zxyz, 4 ); - vec0 = Vector3( xyzx ); - vec1 = Vector3( xyz1 ); - vec2 = Vector3( xyz2 ); - vec3 = Vector3( xyz3 ); -} - -inline void storeXYZArray( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, vec_float4 * threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz; - xyzx = spu_shuffle( vec0.get128(), vec1.get128(), _VECTORMATH_SHUF_XYZA ); - yzxy = spu_shuffle( vec1.get128(), vec2.get128(), _VECTORMATH_SHUF_YZAB ); - zxyz = spu_shuffle( vec2.get128(), vec3.get128(), _VECTORMATH_SHUF_ZABC ); - threeQuads[0] = xyzx; - threeQuads[1] = yzxy; - threeQuads[2] = zxyz; -} - -inline void storeHalfFloats( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, Vector3 vec4, Vector3 vec5, Vector3 vec6, Vector3 vec7, vec_ushort8 * threeQuads ) -{ - vec_float4 xyz0[3]; - vec_float4 xyz1[3]; - storeXYZArray( vec0, vec1, vec2, vec3, xyz0 ); - storeXYZArray( vec4, vec5, vec6, vec7, xyz1 ); - threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); - threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); - threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); -} - -inline Vector3 & Vector3::operator =( Vector3 vec ) -{ - mVec128 = vec.mVec128; - return *this; -} - -inline Vector3 & Vector3::setX( float _x ) -{ - mVec128 = spu_insert( _x, mVec128, 0 ); - return *this; -} - -inline float Vector3::getX( ) const -{ - return spu_extract( mVec128, 0 ); -} - -inline Vector3 & Vector3::setY( float _y ) -{ - mVec128 = spu_insert( _y, mVec128, 1 ); - return *this; -} - -inline float Vector3::getY( ) const -{ - return spu_extract( mVec128, 1 ); -} - -inline Vector3 & Vector3::setZ( float _z ) -{ - mVec128 = spu_insert( _z, mVec128, 2 ); - return *this; -} - -inline float Vector3::getZ( ) const -{ - return spu_extract( mVec128, 2 ); -} - -inline Vector3 & Vector3::setElem( int idx, float value ) -{ - mVec128 = spu_insert( value, mVec128, idx ); - return *this; -} - -inline float Vector3::getElem( int idx ) const -{ - return spu_extract( mVec128, idx ); -} - -inline VecIdx Vector3::operator []( int idx ) -{ - return VecIdx( mVec128, idx ); -} - -inline float Vector3::operator []( int idx ) const -{ - return spu_extract( mVec128, idx ); -} - -inline const Vector3 Vector3::operator +( Vector3 vec ) const -{ - return Vector3( spu_add( mVec128, vec.mVec128 ) ); -} - -inline const Vector3 Vector3::operator -( Vector3 vec ) const -{ - return Vector3( spu_sub( mVec128, vec.mVec128 ) ); -} - -inline const Point3 Vector3::operator +( Point3 pnt ) const -{ - return Point3( spu_add( mVec128, pnt.get128() ) ); -} - -inline const Vector3 Vector3::operator *( float scalar ) const -{ - return Vector3( spu_mul( mVec128, spu_splats(scalar) ) ); -} - -inline Vector3 & Vector3::operator +=( Vector3 vec ) -{ - *this = *this + vec; - return *this; -} - -inline Vector3 & Vector3::operator -=( Vector3 vec ) -{ - *this = *this - vec; - return *this; -} - -inline Vector3 & Vector3::operator *=( float scalar ) -{ - *this = *this * scalar; - return *this; -} - -inline const Vector3 Vector3::operator /( float scalar ) const -{ - return Vector3( divf4( mVec128, spu_splats(scalar) ) ); -} - -inline Vector3 & Vector3::operator /=( float scalar ) -{ - *this = *this / scalar; - return *this; -} - -inline const Vector3 Vector3::operator -( ) const -{ - return Vector3( negatef4( mVec128 ) ); -} - -inline const Vector3 operator *( float scalar, Vector3 vec ) -{ - return vec * scalar; -} - -inline const Vector3 mulPerElem( Vector3 vec0, Vector3 vec1 ) -{ - return Vector3( spu_mul( vec0.get128(), vec1.get128() ) ); -} - -inline const Vector3 divPerElem( Vector3 vec0, Vector3 vec1 ) -{ - return Vector3( divf4( vec0.get128(), vec1.get128() ) ); -} - -inline const Vector3 recipPerElem( Vector3 vec ) -{ - return Vector3( recipf4( vec.get128() ) ); -} - -inline const Vector3 sqrtPerElem( Vector3 vec ) -{ - return Vector3( sqrtf4( vec.get128() ) ); -} - -inline const Vector3 rsqrtPerElem( Vector3 vec ) -{ - return Vector3( rsqrtf4( vec.get128() ) ); -} - -inline const Vector3 absPerElem( Vector3 vec ) -{ - return Vector3( fabsf4( vec.get128() ) ); -} - -inline const Vector3 copySignPerElem( Vector3 vec0, Vector3 vec1 ) -{ - return Vector3( copysignf4( vec0.get128(), vec1.get128() ) ); -} - -inline const Vector3 maxPerElem( Vector3 vec0, Vector3 vec1 ) -{ - return Vector3( fmaxf4( vec0.get128(), vec1.get128() ) ); -} - -inline float maxElem( Vector3 vec ) -{ - vec_float4 result; - result = fmaxf4( spu_promote( spu_extract( vec.get128(), 1 ), 0 ), vec.get128() ); - result = fmaxf4( spu_promote( spu_extract( vec.get128(), 2 ), 0 ), result ); - return spu_extract( result, 0 ); -} - -inline const Vector3 minPerElem( Vector3 vec0, Vector3 vec1 ) -{ - return Vector3( fminf4( vec0.get128(), vec1.get128() ) ); -} - -inline float minElem( Vector3 vec ) -{ - vec_float4 result; - result = fminf4( spu_promote( spu_extract( vec.get128(), 1 ), 0 ), vec.get128() ); - result = fminf4( spu_promote( spu_extract( vec.get128(), 2 ), 0 ), result ); - return spu_extract( result, 0 ); -} - -inline float sum( Vector3 vec ) -{ - return - spu_extract( vec.get128(), 0 ) + - spu_extract( vec.get128(), 1 ) + - spu_extract( vec.get128(), 2 ); -} - -inline float dot( Vector3 vec0, Vector3 vec1 ) -{ - return spu_extract( _vmathVfDot3( vec0.get128(), vec1.get128() ), 0 ); -} - -inline float lengthSqr( Vector3 vec ) -{ - return spu_extract( _vmathVfDot3( vec.get128(), vec.get128() ), 0 ); -} - -inline float length( Vector3 vec ) -{ - return sqrtf( lengthSqr( vec ) ); -} - -inline const Vector3 normalize( Vector3 vec ) -{ - vec_float4 dot = _vmathVfDot3( vec.get128(), vec.get128() ); - dot = spu_shuffle( dot, dot, (vec_uchar16)spu_splats(0x00010203) ); - return Vector3( spu_mul( vec.get128(), rsqrtf4( dot ) ) ); -} - -inline const Vector3 cross( Vector3 vec0, Vector3 vec1 ) -{ - return Vector3( _vmathVfCross( vec0.get128(), vec1.get128() ) ); -} - -inline const Vector3 select( Vector3 vec0, Vector3 vec1, bool select1 ) -{ - return Vector3( spu_sel( vec0.get128(), vec1.get128(), spu_splats( (unsigned int)-(select1 > 0) ) ) ); -} - -#ifdef _VECTORMATH_DEBUG - -inline void print( Vector3 vec ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec.get128(); - printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -inline void print( Vector3 vec, const char * name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec.get128(); - printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -#endif - -inline Vector4::Vector4( float _x, float _y, float _z, float _w ) -{ - mVec128 = (vec_float4){ _x, _y, _z, _w }; -} - -inline Vector4::Vector4( Vector3 xyz, float _w ) -{ - mVec128 = spu_shuffle( xyz.get128(), spu_promote( _w, 0 ), _VECTORMATH_SHUF_XYZA ); -} - -inline Vector4::Vector4( Vector3 vec ) -{ - mVec128 = spu_sel( vec.get128(), spu_splats(0.0f), (vec_uint4)spu_maskb(0x000f) ); -} - -inline Vector4::Vector4( Point3 pnt ) -{ - mVec128 = spu_sel( pnt.get128(), spu_splats(1.0f), (vec_uint4)spu_maskb(0x000f) ); -} - -inline Vector4::Vector4( Quat quat ) -{ - mVec128 = quat.get128(); -} - -inline Vector4::Vector4( float scalar ) -{ - mVec128 = spu_splats( scalar ); -} - -inline Vector4::Vector4( vec_float4 vf4 ) -{ - mVec128 = vf4; -} - -inline const Vector4 Vector4::xAxis( ) -{ - return Vector4( _VECTORMATH_UNIT_1000 ); -} - -inline const Vector4 Vector4::yAxis( ) -{ - return Vector4( _VECTORMATH_UNIT_0100 ); -} - -inline const Vector4 Vector4::zAxis( ) -{ - return Vector4( _VECTORMATH_UNIT_0010 ); -} - -inline const Vector4 Vector4::wAxis( ) -{ - return Vector4( _VECTORMATH_UNIT_0001 ); -} - -inline const Vector4 lerp( float t, Vector4 vec0, Vector4 vec1 ) -{ - return ( vec0 + ( ( vec1 - vec0 ) * t ) ); -} - -inline const Vector4 slerp( float t, Vector4 unitVec0, Vector4 unitVec1 ) -{ - vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; - vec_uint4 selectMask; - vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); - vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); - vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); - cosAngle = _vmathVfDot4( unitVec0.get128(), unitVec1.get128() ); - cosAngle = spu_shuffle( cosAngle, cosAngle, shuffle_xxxx ); - selectMask = (vec_uint4)spu_cmpgt( spu_splats(_VECTORMATH_SLERP_TOL), cosAngle ); - angle = acosf4( cosAngle ); - tttt = spu_splats(t); - oneMinusT = spu_sub( spu_splats(1.0f), tttt ); - angles = spu_sel( spu_splats(1.0f), oneMinusT, (vec_uint4)spu_maskb(0x0f00) ); - angles = spu_sel( angles, tttt, (vec_uint4)spu_maskb(0x00f0) ); - angles = spu_mul( angles, angle ); - sines = sinf4( angles ); - scales = divf4( sines, spu_shuffle( sines, sines, shuffle_xxxx ) ); - scale0 = spu_sel( oneMinusT, spu_shuffle( scales, scales, shuffle_yyyy ), selectMask ); - scale1 = spu_sel( tttt, spu_shuffle( scales, scales, shuffle_zzzz ), selectMask ); - return Vector4( spu_madd( unitVec0.get128(), scale0, spu_mul( unitVec1.get128(), scale1 ) ) ); -} - -inline vec_float4 Vector4::get128( ) const -{ - return mVec128; -} - -inline void storeHalfFloats( Vector4 vec0, Vector4 vec1, Vector4 vec2, Vector4 vec3, vec_ushort8 * twoQuads ) -{ - twoQuads[0] = _vmath2VfToHalfFloats(vec0.get128(), vec1.get128()); - twoQuads[1] = _vmath2VfToHalfFloats(vec2.get128(), vec3.get128()); -} - -inline Vector4 & Vector4::operator =( Vector4 vec ) -{ - mVec128 = vec.mVec128; - return *this; -} - -inline Vector4 & Vector4::setXYZ( Vector3 vec ) -{ - mVec128 = spu_sel( vec.get128(), mVec128, (vec_uint4)spu_maskb(0x000f) ); - return *this; -} - -inline const Vector3 Vector4::getXYZ( ) const -{ - return Vector3( mVec128 ); -} - -inline Vector4 & Vector4::setX( float _x ) -{ - mVec128 = spu_insert( _x, mVec128, 0 ); - return *this; -} - -inline float Vector4::getX( ) const -{ - return spu_extract( mVec128, 0 ); -} - -inline Vector4 & Vector4::setY( float _y ) -{ - mVec128 = spu_insert( _y, mVec128, 1 ); - return *this; -} - -inline float Vector4::getY( ) const -{ - return spu_extract( mVec128, 1 ); -} - -inline Vector4 & Vector4::setZ( float _z ) -{ - mVec128 = spu_insert( _z, mVec128, 2 ); - return *this; -} - -inline float Vector4::getZ( ) const -{ - return spu_extract( mVec128, 2 ); -} - -inline Vector4 & Vector4::setW( float _w ) -{ - mVec128 = spu_insert( _w, mVec128, 3 ); - return *this; -} - -inline float Vector4::getW( ) const -{ - return spu_extract( mVec128, 3 ); -} - -inline Vector4 & Vector4::setElem( int idx, float value ) -{ - mVec128 = spu_insert( value, mVec128, idx ); - return *this; -} - -inline float Vector4::getElem( int idx ) const -{ - return spu_extract( mVec128, idx ); -} - -inline VecIdx Vector4::operator []( int idx ) -{ - return VecIdx( mVec128, idx ); -} - -inline float Vector4::operator []( int idx ) const -{ - return spu_extract( mVec128, idx ); -} - -inline const Vector4 Vector4::operator +( Vector4 vec ) const -{ - return Vector4( spu_add( mVec128, vec.mVec128 ) ); -} - -inline const Vector4 Vector4::operator -( Vector4 vec ) const -{ - return Vector4( spu_sub( mVec128, vec.mVec128 ) ); -} - -inline const Vector4 Vector4::operator *( float scalar ) const -{ - return Vector4( spu_mul( mVec128, spu_splats(scalar) ) ); -} - -inline Vector4 & Vector4::operator +=( Vector4 vec ) -{ - *this = *this + vec; - return *this; -} - -inline Vector4 & Vector4::operator -=( Vector4 vec ) -{ - *this = *this - vec; - return *this; -} - -inline Vector4 & Vector4::operator *=( float scalar ) -{ - *this = *this * scalar; - return *this; -} - -inline const Vector4 Vector4::operator /( float scalar ) const -{ - return Vector4( divf4( mVec128, spu_splats(scalar) ) ); -} - -inline Vector4 & Vector4::operator /=( float scalar ) -{ - *this = *this / scalar; - return *this; -} - -inline const Vector4 Vector4::operator -( ) const -{ - return Vector4( negatef4( mVec128 ) ); -} - -inline const Vector4 operator *( float scalar, Vector4 vec ) -{ - return vec * scalar; -} - -inline const Vector4 mulPerElem( Vector4 vec0, Vector4 vec1 ) -{ - return Vector4( spu_mul( vec0.get128(), vec1.get128() ) ); -} - -inline const Vector4 divPerElem( Vector4 vec0, Vector4 vec1 ) -{ - return Vector4( divf4( vec0.get128(), vec1.get128() ) ); -} - -inline const Vector4 recipPerElem( Vector4 vec ) -{ - return Vector4( recipf4( vec.get128() ) ); -} - -inline const Vector4 sqrtPerElem( Vector4 vec ) -{ - return Vector4( sqrtf4( vec.get128() ) ); -} - -inline const Vector4 rsqrtPerElem( Vector4 vec ) -{ - return Vector4( rsqrtf4( vec.get128() ) ); -} - -inline const Vector4 absPerElem( Vector4 vec ) -{ - return Vector4( fabsf4( vec.get128() ) ); -} - -inline const Vector4 copySignPerElem( Vector4 vec0, Vector4 vec1 ) -{ - return Vector4( copysignf4( vec0.get128(), vec1.get128() ) ); -} - -inline const Vector4 maxPerElem( Vector4 vec0, Vector4 vec1 ) -{ - return Vector4( fmaxf4( vec0.get128(), vec1.get128() ) ); -} - -inline float maxElem( Vector4 vec ) -{ - vec_float4 result; - result = fmaxf4( spu_promote( spu_extract( vec.get128(), 1 ), 0 ), vec.get128() ); - result = fmaxf4( spu_promote( spu_extract( vec.get128(), 2 ), 0 ), result ); - result = fmaxf4( spu_promote( spu_extract( vec.get128(), 3 ), 0 ), result ); - return spu_extract( result, 0 ); -} - -inline const Vector4 minPerElem( Vector4 vec0, Vector4 vec1 ) -{ - return Vector4( fminf4( vec0.get128(), vec1.get128() ) ); -} - -inline float minElem( Vector4 vec ) -{ - vec_float4 result; - result = fminf4( spu_promote( spu_extract( vec.get128(), 1 ), 0 ), vec.get128() ); - result = fminf4( spu_promote( spu_extract( vec.get128(), 2 ), 0 ), result ); - result = fminf4( spu_promote( spu_extract( vec.get128(), 3 ), 0 ), result ); - return spu_extract( result, 0 ); -} - -inline float sum( Vector4 vec ) -{ - return - spu_extract( vec.get128(), 0 ) + - spu_extract( vec.get128(), 1 ) + - spu_extract( vec.get128(), 2 ) + - spu_extract( vec.get128(), 3 ); -} - -inline float dot( Vector4 vec0, Vector4 vec1 ) -{ - return spu_extract( _vmathVfDot4( vec0.get128(), vec1.get128() ), 0 ); -} - -inline float lengthSqr( Vector4 vec ) -{ - return spu_extract( _vmathVfDot4( vec.get128(), vec.get128() ), 0 ); -} - -inline float length( Vector4 vec ) -{ - return sqrtf( lengthSqr( vec ) ); -} - -inline const Vector4 normalize( Vector4 vec ) -{ - vec_float4 dot = _vmathVfDot4( vec.get128(), vec.get128() ); - return Vector4( spu_mul( vec.get128(), rsqrtf4( dot ) ) ); -} - -inline const Vector4 select( Vector4 vec0, Vector4 vec1, bool select1 ) -{ - return Vector4( spu_sel( vec0.get128(), vec1.get128(), spu_splats( (unsigned int)-(select1 > 0) ) ) ); -} - -#ifdef _VECTORMATH_DEBUG - -inline void print( Vector4 vec ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec.get128(); - printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -inline void print( Vector4 vec, const char * name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = vec.get128(); - printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); -} - -#endif - -inline Point3::Point3( float _x, float _y, float _z ) -{ - mVec128 = (vec_float4){ _x, _y, _z, 0.0f }; -} - -inline Point3::Point3( Vector3 vec ) -{ - mVec128 = vec.get128(); -} - -inline Point3::Point3( float scalar ) -{ - mVec128 = spu_splats( scalar ); -} - -inline Point3::Point3( vec_float4 vf4 ) -{ - mVec128 = vf4; -} - -inline const Point3 lerp( float t, Point3 pnt0, Point3 pnt1 ) -{ - return ( pnt0 + ( ( pnt1 - pnt0 ) * t ) ); -} - -inline vec_float4 Point3::get128( ) const -{ - return mVec128; -} - -inline void storeXYZ( Point3 pnt, vec_float4 * quad ) -{ - vec_float4 dstVec = *quad; - vec_uint4 mask = (vec_uint4)spu_maskb(0x000f); - dstVec = spu_sel(pnt.get128(), dstVec, mask); - *quad = dstVec; -} - -inline void loadXYZArray( Point3 & pnt0, Point3 & pnt1, Point3 & pnt2, Point3 & pnt3, const vec_float4 * threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; - xyzx = threeQuads[0]; - yzxy = threeQuads[1]; - zxyz = threeQuads[2]; - xyz1 = spu_shuffle( xyzx, yzxy, _VECTORMATH_SHUF_WABC ); - xyz2 = spu_shuffle( yzxy, zxyz, _VECTORMATH_SHUF_ZWAB ); - xyz3 = spu_rlqwbyte( zxyz, 4 ); - pnt0 = Point3( xyzx ); - pnt1 = Point3( xyz1 ); - pnt2 = Point3( xyz2 ); - pnt3 = Point3( xyz3 ); -} - -inline void storeXYZArray( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, vec_float4 * threeQuads ) -{ - vec_float4 xyzx, yzxy, zxyz; - xyzx = spu_shuffle( pnt0.get128(), pnt1.get128(), _VECTORMATH_SHUF_XYZA ); - yzxy = spu_shuffle( pnt1.get128(), pnt2.get128(), _VECTORMATH_SHUF_YZAB ); - zxyz = spu_shuffle( pnt2.get128(), pnt3.get128(), _VECTORMATH_SHUF_ZABC ); - threeQuads[0] = xyzx; - threeQuads[1] = yzxy; - threeQuads[2] = zxyz; -} - -inline void storeHalfFloats( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, Point3 pnt4, Point3 pnt5, Point3 pnt6, Point3 pnt7, vec_ushort8 * threeQuads ) -{ - vec_float4 xyz0[3]; - vec_float4 xyz1[3]; - storeXYZArray( pnt0, pnt1, pnt2, pnt3, xyz0 ); - storeXYZArray( pnt4, pnt5, pnt6, pnt7, xyz1 ); - threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); - threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); - threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); -} - -inline Point3 & Point3::operator =( Point3 pnt ) -{ - mVec128 = pnt.mVec128; - return *this; -} - -inline Point3 & Point3::setX( float _x ) -{ - mVec128 = spu_insert( _x, mVec128, 0 ); - return *this; -} - -inline float Point3::getX( ) const -{ - return spu_extract( mVec128, 0 ); -} - -inline Point3 & Point3::setY( float _y ) -{ - mVec128 = spu_insert( _y, mVec128, 1 ); - return *this; -} - -inline float Point3::getY( ) const -{ - return spu_extract( mVec128, 1 ); -} - -inline Point3 & Point3::setZ( float _z ) -{ - mVec128 = spu_insert( _z, mVec128, 2 ); - return *this; -} - -inline float Point3::getZ( ) const -{ - return spu_extract( mVec128, 2 ); -} - -inline Point3 & Point3::setElem( int idx, float value ) -{ - mVec128 = spu_insert( value, mVec128, idx ); - return *this; -} - -inline float Point3::getElem( int idx ) const -{ - return spu_extract( mVec128, idx ); -} - -inline VecIdx Point3::operator []( int idx ) -{ - return VecIdx( mVec128, idx ); -} - -inline float Point3::operator []( int idx ) const -{ - return spu_extract( mVec128, idx ); -} - -inline const Vector3 Point3::operator -( Point3 pnt ) const -{ - return Vector3( spu_sub( mVec128, pnt.mVec128 ) ); -} - -inline const Point3 Point3::operator +( Vector3 vec ) const -{ - return Point3( spu_add( mVec128, vec.get128() ) ); -} - -inline const Point3 Point3::operator -( Vector3 vec ) const -{ - return Point3( spu_sub( mVec128, vec.get128() ) ); -} - -inline Point3 & Point3::operator +=( Vector3 vec ) -{ - *this = *this + vec; - return *this; -} - -inline Point3 & Point3::operator -=( Vector3 vec ) -{ - *this = *this - vec; - return *this; -} - -inline const Point3 mulPerElem( Point3 pnt0, Point3 pnt1 ) -{ - return Point3( spu_mul( pnt0.get128(), pnt1.get128() ) ); -} - -inline const Point3 divPerElem( Point3 pnt0, Point3 pnt1 ) -{ - return Point3( divf4( pnt0.get128(), pnt1.get128() ) ); -} - -inline const Point3 recipPerElem( Point3 pnt ) -{ - return Point3( recipf4( pnt.get128() ) ); -} - -inline const Point3 sqrtPerElem( Point3 pnt ) -{ - return Point3( sqrtf4( pnt.get128() ) ); -} - -inline const Point3 rsqrtPerElem( Point3 pnt ) -{ - return Point3( rsqrtf4( pnt.get128() ) ); -} - -inline const Point3 absPerElem( Point3 pnt ) -{ - return Point3( fabsf4( pnt.get128() ) ); -} - -inline const Point3 copySignPerElem( Point3 pnt0, Point3 pnt1 ) -{ - return Point3( copysignf4( pnt0.get128(), pnt1.get128() ) ); -} - -inline const Point3 maxPerElem( Point3 pnt0, Point3 pnt1 ) -{ - return Point3( fmaxf4( pnt0.get128(), pnt1.get128() ) ); -} - -inline float maxElem( Point3 pnt ) -{ - vec_float4 result; - result = fmaxf4( spu_promote( spu_extract( pnt.get128(), 1 ), 0 ), pnt.get128() ); - result = fmaxf4( spu_promote( spu_extract( pnt.get128(), 2 ), 0 ), result ); - return spu_extract( result, 0 ); -} - -inline const Point3 minPerElem( Point3 pnt0, Point3 pnt1 ) -{ - return Point3( fminf4( pnt0.get128(), pnt1.get128() ) ); -} - -inline float minElem( Point3 pnt ) -{ - vec_float4 result; - result = fminf4( spu_promote( spu_extract( pnt.get128(), 1 ), 0 ), pnt.get128() ); - result = fminf4( spu_promote( spu_extract( pnt.get128(), 2 ), 0 ), result ); - return spu_extract( result, 0 ); -} - -inline float sum( Point3 pnt ) -{ - return - spu_extract( pnt.get128(), 0 ) + - spu_extract( pnt.get128(), 1 ) + - spu_extract( pnt.get128(), 2 ); -} - -inline const Point3 scale( Point3 pnt, float scaleVal ) -{ - return mulPerElem( pnt, Point3( scaleVal ) ); -} - -inline const Point3 scale( Point3 pnt, Vector3 scaleVec ) -{ - return mulPerElem( pnt, Point3( scaleVec ) ); -} - -inline float projection( Point3 pnt, Vector3 unitVec ) -{ - return spu_extract( _vmathVfDot3( pnt.get128(), unitVec.get128() ), 0 ); -} - -inline float distSqrFromOrigin( Point3 pnt ) -{ - return lengthSqr( Vector3( pnt ) ); -} - -inline float distFromOrigin( Point3 pnt ) -{ - return length( Vector3( pnt ) ); -} - -inline float distSqr( Point3 pnt0, Point3 pnt1 ) -{ - return lengthSqr( ( pnt1 - pnt0 ) ); -} - -inline float dist( Point3 pnt0, Point3 pnt1 ) -{ - return length( ( pnt1 - pnt0 ) ); -} - -inline const Point3 select( Point3 pnt0, Point3 pnt1, bool select1 ) -{ - return Point3( spu_sel( pnt0.get128(), pnt1.get128(), spu_splats( (unsigned int)-(select1 > 0) ) ) ); -} - -#ifdef _VECTORMATH_DEBUG - -inline void print( Point3 pnt ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = pnt.get128(); - printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -inline void print( Point3 pnt, const char * name ) -{ - union { vec_float4 v; float s[4]; } tmp; - tmp.v = pnt.get128(); - printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); -} - -#endif - -} // namespace Aos -} // namespace Vectormath - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_VEC_AOS_CPP_H +#define _VECTORMATH_VEC_AOS_CPP_H +//----------------------------------------------------------------------------- +// Constants +// for shuffles, words are labeled [x,y,z,w] [a,b,c,d] + +#define _VECTORMATH_SHUF_X 0x00010203 +#define _VECTORMATH_SHUF_Y 0x04050607 +#define _VECTORMATH_SHUF_Z 0x08090a0b +#define _VECTORMATH_SHUF_W 0x0c0d0e0f +#define _VECTORMATH_SHUF_A 0x10111213 +#define _VECTORMATH_SHUF_B 0x14151617 +#define _VECTORMATH_SHUF_C 0x18191a1b +#define _VECTORMATH_SHUF_D 0x1c1d1e1f +#define _VECTORMATH_SHUF_0 0x80808080 +#define _VECTORMATH_SHUF_XYZA (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A } +#define _VECTORMATH_SHUF_ZXYW (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_W } +#define _VECTORMATH_SHUF_YZXW (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_W } +#define _VECTORMATH_SHUF_WABC (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_C } +#define _VECTORMATH_SHUF_ZWAB (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_W, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B } +#define _VECTORMATH_SHUF_XYZA (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_X, _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A } +#define _VECTORMATH_SHUF_YZAB (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Y, _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B } +#define _VECTORMATH_SHUF_ZABC (vec_uchar16)(vec_uint4){ _VECTORMATH_SHUF_Z, _VECTORMATH_SHUF_A, _VECTORMATH_SHUF_B, _VECTORMATH_SHUF_C } +#define _VECTORMATH_UNIT_1000 (vec_float4){ 1.0f, 0.0f, 0.0f, 0.0f } +#define _VECTORMATH_UNIT_0100 (vec_float4){ 0.0f, 1.0f, 0.0f, 0.0f } +#define _VECTORMATH_UNIT_0010 (vec_float4){ 0.0f, 0.0f, 1.0f, 0.0f } +#define _VECTORMATH_UNIT_0001 (vec_float4){ 0.0f, 0.0f, 0.0f, 1.0f } +#define _VECTORMATH_SLERP_TOL 0.999f + +//----------------------------------------------------------------------------- +// Definitions + +#ifndef _VECTORMATH_INTERNAL_FUNCTIONS +#define _VECTORMATH_INTERNAL_FUNCTIONS + +static inline vec_float4 _vmathVfDot3( vec_float4 vec0, vec_float4 vec1 ) +{ + vec_float4 result; + result = spu_mul( vec0, vec1 ); + result = spu_madd( spu_rlqwbyte( vec0, 4 ), spu_rlqwbyte( vec1, 4 ), result ); + return spu_madd( spu_rlqwbyte( vec0, 8 ), spu_rlqwbyte( vec1, 8 ), result ); +} + +static inline vec_float4 _vmathVfDot4( vec_float4 vec0, vec_float4 vec1 ) +{ + vec_float4 result; + result = spu_mul( vec0, vec1 ); + result = spu_madd( spu_rlqwbyte( vec0, 4 ), spu_rlqwbyte( vec1, 4 ), result ); + return spu_add( spu_rlqwbyte( result, 8 ), result ); +} + +static inline vec_float4 _vmathVfCross( vec_float4 vec0, vec_float4 vec1 ) +{ + vec_float4 tmp0, tmp1, tmp2, tmp3, result; + tmp0 = spu_shuffle( vec0, vec0, _VECTORMATH_SHUF_YZXW ); + tmp1 = spu_shuffle( vec1, vec1, _VECTORMATH_SHUF_ZXYW ); + tmp2 = spu_shuffle( vec0, vec0, _VECTORMATH_SHUF_ZXYW ); + tmp3 = spu_shuffle( vec1, vec1, _VECTORMATH_SHUF_YZXW ); + result = spu_mul( tmp0, tmp1 ); + result = spu_nmsub( tmp2, tmp3, result ); + return result; +} + +static inline vec_uint4 _vmathVfToHalfFloatsUnpacked(vec_float4 v) +{ + vec_int4 bexp; + vec_uint4 mant, sign, hfloat; + vec_uint4 notZero, isInf; + const vec_uint4 hfloatInf = spu_splats(0x00007c00u); + const vec_uint4 mergeMant = spu_splats(0x000003ffu); + const vec_uint4 mergeSign = spu_splats(0x00008000u); + + sign = spu_rlmask((vec_uint4)v, -16); + mant = spu_rlmask((vec_uint4)v, -13); + bexp = spu_and(spu_rlmask((vec_int4)v, -23), 0xff); + + notZero = spu_cmpgt(bexp, 112); + isInf = spu_cmpgt(bexp, 142); + + bexp = spu_add(bexp, -112); + bexp = spu_sl(bexp, 10); + + hfloat = spu_sel((vec_uint4)bexp, mant, mergeMant); + hfloat = spu_sel(spu_splats(0u), hfloat, notZero); + hfloat = spu_sel(hfloat, hfloatInf, isInf); + hfloat = spu_sel(hfloat, sign, mergeSign); + + return hfloat; +} + +static inline vec_ushort8 _vmath2VfToHalfFloats(vec_float4 u, vec_float4 v) +{ + vec_uint4 hfloat_u, hfloat_v; + const vec_uchar16 pack = (vec_uchar16){2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31}; + hfloat_u = _vmathVfToHalfFloatsUnpacked(u); + hfloat_v = _vmathVfToHalfFloatsUnpacked(v); + return (vec_ushort8)spu_shuffle(hfloat_u, hfloat_v, pack); +} + +#endif + +namespace Vectormath { +namespace Aos { + +inline VecIdx::operator float() const +{ + return spu_extract( ref, i ); +} + +inline float VecIdx::operator =( float scalar ) +{ + ref = spu_insert( scalar, ref, i ); + return scalar; +} + +inline float VecIdx::operator =( const VecIdx& scalar ) +{ + return *this = float(scalar); +} + +inline float VecIdx::operator *=( float scalar ) +{ + float tmp = spu_extract( ref, i ) * scalar; + ref = spu_insert( tmp, ref, i ); + return tmp; +} + +inline float VecIdx::operator /=( float scalar ) +{ + float tmp = spu_extract( ref, i ) / scalar; + ref = spu_insert( tmp, ref, i ); + return tmp; +} + +inline float VecIdx::operator +=( float scalar ) +{ + float tmp = spu_extract( ref, i ) + scalar; + ref = spu_insert( tmp, ref, i ); + return tmp; +} + +inline float VecIdx::operator -=( float scalar ) +{ + float tmp = spu_extract( ref, i ) - scalar; + ref = spu_insert( tmp, ref, i ); + return tmp; +} + +inline Vector3::Vector3( float _x, float _y, float _z ) +{ + mVec128 = (vec_float4){ _x, _y, _z, 0.0f }; +} + +inline Vector3::Vector3( Point3 pnt ) +{ + mVec128 = pnt.get128(); +} + +inline Vector3::Vector3( float scalar ) +{ + mVec128 = spu_splats( scalar ); +} + +inline Vector3::Vector3( vec_float4 vf4 ) +{ + mVec128 = vf4; +} + +inline const Vector3 Vector3::xAxis( ) +{ + return Vector3( _VECTORMATH_UNIT_1000 ); +} + +inline const Vector3 Vector3::yAxis( ) +{ + return Vector3( _VECTORMATH_UNIT_0100 ); +} + +inline const Vector3 Vector3::zAxis( ) +{ + return Vector3( _VECTORMATH_UNIT_0010 ); +} + +inline const Vector3 lerp( float t, Vector3 vec0, Vector3 vec1 ) +{ + return ( vec0 + ( ( vec1 - vec0 ) * t ) ); +} + +inline const Vector3 slerp( float t, Vector3 unitVec0, Vector3 unitVec1 ) +{ + vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; + vec_uint4 selectMask; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + cosAngle = _vmathVfDot3( unitVec0.get128(), unitVec1.get128() ); + cosAngle = spu_shuffle( cosAngle, cosAngle, shuffle_xxxx ); + selectMask = (vec_uint4)spu_cmpgt( spu_splats(_VECTORMATH_SLERP_TOL), cosAngle ); + angle = acosf4( cosAngle ); + tttt = spu_splats(t); + oneMinusT = spu_sub( spu_splats(1.0f), tttt ); + angles = spu_sel( spu_splats(1.0f), oneMinusT, (vec_uint4)spu_maskb(0x0f00) ); + angles = spu_sel( angles, tttt, (vec_uint4)spu_maskb(0x00f0) ); + angles = spu_mul( angles, angle ); + sines = sinf4( angles ); + scales = divf4( sines, spu_shuffle( sines, sines, shuffle_xxxx ) ); + scale0 = spu_sel( oneMinusT, spu_shuffle( scales, scales, shuffle_yyyy ), selectMask ); + scale1 = spu_sel( tttt, spu_shuffle( scales, scales, shuffle_zzzz ), selectMask ); + return Vector3( spu_madd( unitVec0.get128(), scale0, spu_mul( unitVec1.get128(), scale1 ) ) ); +} + +inline vec_float4 Vector3::get128( ) const +{ + return mVec128; +} + +inline void loadXYZ( Vector3 & vec, const vec_float4 * quad ) +{ + vec = Vector3( *quad ); +} + +inline void loadXYZ( Vector3 & vec, const float * fptr ) +{ + const vec_float4 vfsrc0 = *((vec_float4*)((uintptr_t)fptr)); + const vec_float4 vfsrc1 = *((vec_float4*)(((uintptr_t)fptr) + 16)); + const vec_uchar16 vucpat = (vec_uchar16)spu_add((vec_ushort8)(spu_splats((unsigned char)(((int)fptr) & 0xf))),((vec_ushort8){0x0001, 0x0203, 0x0405, 0x0607, 0x0809, 0x0A0B, 0x0C0D, 0x0E0F})); + const vec_float4 vfval = spu_shuffle(vfsrc0, vfsrc1, vucpat); + vec = Vector3( vfval ); +} + +inline void storeXYZ( Vector3 vec, vec_float4 * quad ) +{ + vec_float4 dstVec = *quad; + vec_uint4 mask = (vec_uint4)spu_maskb(0x000f); + dstVec = spu_sel(vec.get128(), dstVec, mask); + *quad = dstVec; +} + +inline void storeXYZ( Vector3 vec, float * fptr ) +{ + vec_float4 * vptr0 = (vec_float4*)((uintptr_t)fptr); + vec_float4 * vptr1 = (vec_float4*)(((uintptr_t)fptr) + 16); + vec_float4 dstVec0 = *vptr0; + vec_float4 dstVec1 = *vptr1; + uint32_t offset = (uint32_t)fptr & 0xf; + vec_uint4 mask = (vec_uint4)spu_maskb(0xfff0); + vec_uint4 mask0 = (vec_uint4)spu_rlmaskqwbyte(mask, -offset); + vec_uint4 mask1 = (vec_uint4)spu_slqwbyte(mask, 16 - offset); + vec_float4 vec0 = spu_rlmaskqwbyte(vec.get128(), -offset); + vec_float4 vec1 = spu_slqwbyte(vec.get128(), 16 - offset); + dstVec0 = spu_sel(dstVec0, vec0, mask0); + dstVec1 = spu_sel(dstVec1, vec1, mask1); + *vptr0 = dstVec0; + *vptr1 = dstVec1; +} + +inline void loadXYZArray( Vector3 & vec0, Vector3 & vec1, Vector3 & vec2, Vector3 & vec3, const vec_float4 * threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; + xyzx = threeQuads[0]; + yzxy = threeQuads[1]; + zxyz = threeQuads[2]; + xyz1 = spu_shuffle( xyzx, yzxy, _VECTORMATH_SHUF_WABC ); + xyz2 = spu_shuffle( yzxy, zxyz, _VECTORMATH_SHUF_ZWAB ); + xyz3 = spu_rlqwbyte( zxyz, 4 ); + vec0 = Vector3( xyzx ); + vec1 = Vector3( xyz1 ); + vec2 = Vector3( xyz2 ); + vec3 = Vector3( xyz3 ); +} + +inline void storeXYZArray( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, vec_float4 * threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz; + xyzx = spu_shuffle( vec0.get128(), vec1.get128(), _VECTORMATH_SHUF_XYZA ); + yzxy = spu_shuffle( vec1.get128(), vec2.get128(), _VECTORMATH_SHUF_YZAB ); + zxyz = spu_shuffle( vec2.get128(), vec3.get128(), _VECTORMATH_SHUF_ZABC ); + threeQuads[0] = xyzx; + threeQuads[1] = yzxy; + threeQuads[2] = zxyz; +} + +inline void storeHalfFloats( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, Vector3 vec4, Vector3 vec5, Vector3 vec6, Vector3 vec7, vec_ushort8 * threeQuads ) +{ + vec_float4 xyz0[3]; + vec_float4 xyz1[3]; + storeXYZArray( vec0, vec1, vec2, vec3, xyz0 ); + storeXYZArray( vec4, vec5, vec6, vec7, xyz1 ); + threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); + threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); + threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); +} + +inline Vector3 & Vector3::operator =( Vector3 vec ) +{ + mVec128 = vec.mVec128; + return *this; +} + +inline Vector3 & Vector3::setX( float _x ) +{ + mVec128 = spu_insert( _x, mVec128, 0 ); + return *this; +} + +inline float Vector3::getX( ) const +{ + return spu_extract( mVec128, 0 ); +} + +inline Vector3 & Vector3::setY( float _y ) +{ + mVec128 = spu_insert( _y, mVec128, 1 ); + return *this; +} + +inline float Vector3::getY( ) const +{ + return spu_extract( mVec128, 1 ); +} + +inline Vector3 & Vector3::setZ( float _z ) +{ + mVec128 = spu_insert( _z, mVec128, 2 ); + return *this; +} + +inline float Vector3::getZ( ) const +{ + return spu_extract( mVec128, 2 ); +} + +inline Vector3 & Vector3::setElem( int idx, float value ) +{ + mVec128 = spu_insert( value, mVec128, idx ); + return *this; +} + +inline float Vector3::getElem( int idx ) const +{ + return spu_extract( mVec128, idx ); +} + +inline VecIdx Vector3::operator []( int idx ) +{ + return VecIdx( mVec128, idx ); +} + +inline float Vector3::operator []( int idx ) const +{ + return spu_extract( mVec128, idx ); +} + +inline const Vector3 Vector3::operator +( Vector3 vec ) const +{ + return Vector3( spu_add( mVec128, vec.mVec128 ) ); +} + +inline const Vector3 Vector3::operator -( Vector3 vec ) const +{ + return Vector3( spu_sub( mVec128, vec.mVec128 ) ); +} + +inline const Point3 Vector3::operator +( Point3 pnt ) const +{ + return Point3( spu_add( mVec128, pnt.get128() ) ); +} + +inline const Vector3 Vector3::operator *( float scalar ) const +{ + return Vector3( spu_mul( mVec128, spu_splats(scalar) ) ); +} + +inline Vector3 & Vector3::operator +=( Vector3 vec ) +{ + *this = *this + vec; + return *this; +} + +inline Vector3 & Vector3::operator -=( Vector3 vec ) +{ + *this = *this - vec; + return *this; +} + +inline Vector3 & Vector3::operator *=( float scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Vector3 Vector3::operator /( float scalar ) const +{ + return Vector3( divf4( mVec128, spu_splats(scalar) ) ); +} + +inline Vector3 & Vector3::operator /=( float scalar ) +{ + *this = *this / scalar; + return *this; +} + +inline const Vector3 Vector3::operator -( ) const +{ + return Vector3( negatef4( mVec128 ) ); +} + +inline const Vector3 operator *( float scalar, Vector3 vec ) +{ + return vec * scalar; +} + +inline const Vector3 mulPerElem( Vector3 vec0, Vector3 vec1 ) +{ + return Vector3( spu_mul( vec0.get128(), vec1.get128() ) ); +} + +inline const Vector3 divPerElem( Vector3 vec0, Vector3 vec1 ) +{ + return Vector3( divf4( vec0.get128(), vec1.get128() ) ); +} + +inline const Vector3 recipPerElem( Vector3 vec ) +{ + return Vector3( recipf4( vec.get128() ) ); +} + +inline const Vector3 sqrtPerElem( Vector3 vec ) +{ + return Vector3( sqrtf4( vec.get128() ) ); +} + +inline const Vector3 rsqrtPerElem( Vector3 vec ) +{ + return Vector3( rsqrtf4( vec.get128() ) ); +} + +inline const Vector3 absPerElem( Vector3 vec ) +{ + return Vector3( fabsf4( vec.get128() ) ); +} + +inline const Vector3 copySignPerElem( Vector3 vec0, Vector3 vec1 ) +{ + return Vector3( copysignf4( vec0.get128(), vec1.get128() ) ); +} + +inline const Vector3 maxPerElem( Vector3 vec0, Vector3 vec1 ) +{ + return Vector3( fmaxf4( vec0.get128(), vec1.get128() ) ); +} + +inline float maxElem( Vector3 vec ) +{ + vec_float4 result; + result = fmaxf4( spu_promote( spu_extract( vec.get128(), 1 ), 0 ), vec.get128() ); + result = fmaxf4( spu_promote( spu_extract( vec.get128(), 2 ), 0 ), result ); + return spu_extract( result, 0 ); +} + +inline const Vector3 minPerElem( Vector3 vec0, Vector3 vec1 ) +{ + return Vector3( fminf4( vec0.get128(), vec1.get128() ) ); +} + +inline float minElem( Vector3 vec ) +{ + vec_float4 result; + result = fminf4( spu_promote( spu_extract( vec.get128(), 1 ), 0 ), vec.get128() ); + result = fminf4( spu_promote( spu_extract( vec.get128(), 2 ), 0 ), result ); + return spu_extract( result, 0 ); +} + +inline float sum( Vector3 vec ) +{ + return + spu_extract( vec.get128(), 0 ) + + spu_extract( vec.get128(), 1 ) + + spu_extract( vec.get128(), 2 ); +} + +inline float dot( Vector3 vec0, Vector3 vec1 ) +{ + return spu_extract( _vmathVfDot3( vec0.get128(), vec1.get128() ), 0 ); +} + +inline float lengthSqr( Vector3 vec ) +{ + return spu_extract( _vmathVfDot3( vec.get128(), vec.get128() ), 0 ); +} + +inline float length( Vector3 vec ) +{ + return sqrtf( lengthSqr( vec ) ); +} + +inline const Vector3 normalize( Vector3 vec ) +{ + vec_float4 dot = _vmathVfDot3( vec.get128(), vec.get128() ); + dot = spu_shuffle( dot, dot, (vec_uchar16)spu_splats(0x00010203) ); + return Vector3( spu_mul( vec.get128(), rsqrtf4( dot ) ) ); +} + +inline const Vector3 cross( Vector3 vec0, Vector3 vec1 ) +{ + return Vector3( _vmathVfCross( vec0.get128(), vec1.get128() ) ); +} + +inline const Vector3 select( Vector3 vec0, Vector3 vec1, bool select1 ) +{ + return Vector3( spu_sel( vec0.get128(), vec1.get128(), spu_splats( (unsigned int)-(select1 > 0) ) ) ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( Vector3 vec ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec.get128(); + printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +inline void print( Vector3 vec, const char * name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec.get128(); + printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +#endif + +inline Vector4::Vector4( float _x, float _y, float _z, float _w ) +{ + mVec128 = (vec_float4){ _x, _y, _z, _w }; +} + +inline Vector4::Vector4( Vector3 xyz, float _w ) +{ + mVec128 = spu_shuffle( xyz.get128(), spu_promote( _w, 0 ), _VECTORMATH_SHUF_XYZA ); +} + +inline Vector4::Vector4( Vector3 vec ) +{ + mVec128 = spu_sel( vec.get128(), spu_splats(0.0f), (vec_uint4)spu_maskb(0x000f) ); +} + +inline Vector4::Vector4( Point3 pnt ) +{ + mVec128 = spu_sel( pnt.get128(), spu_splats(1.0f), (vec_uint4)spu_maskb(0x000f) ); +} + +inline Vector4::Vector4( Quat quat ) +{ + mVec128 = quat.get128(); +} + +inline Vector4::Vector4( float scalar ) +{ + mVec128 = spu_splats( scalar ); +} + +inline Vector4::Vector4( vec_float4 vf4 ) +{ + mVec128 = vf4; +} + +inline const Vector4 Vector4::xAxis( ) +{ + return Vector4( _VECTORMATH_UNIT_1000 ); +} + +inline const Vector4 Vector4::yAxis( ) +{ + return Vector4( _VECTORMATH_UNIT_0100 ); +} + +inline const Vector4 Vector4::zAxis( ) +{ + return Vector4( _VECTORMATH_UNIT_0010 ); +} + +inline const Vector4 Vector4::wAxis( ) +{ + return Vector4( _VECTORMATH_UNIT_0001 ); +} + +inline const Vector4 lerp( float t, Vector4 vec0, Vector4 vec1 ) +{ + return ( vec0 + ( ( vec1 - vec0 ) * t ) ); +} + +inline const Vector4 slerp( float t, Vector4 unitVec0, Vector4 unitVec1 ) +{ + vec_float4 scales, scale0, scale1, cosAngle, angle, tttt, oneMinusT, angles, sines; + vec_uint4 selectMask; + vec_uchar16 shuffle_xxxx = (vec_uchar16)spu_splats((int)0x00010203); + vec_uchar16 shuffle_yyyy = (vec_uchar16)spu_splats((int)0x04050607); + vec_uchar16 shuffle_zzzz = (vec_uchar16)spu_splats((int)0x08090a0b); + cosAngle = _vmathVfDot4( unitVec0.get128(), unitVec1.get128() ); + cosAngle = spu_shuffle( cosAngle, cosAngle, shuffle_xxxx ); + selectMask = (vec_uint4)spu_cmpgt( spu_splats(_VECTORMATH_SLERP_TOL), cosAngle ); + angle = acosf4( cosAngle ); + tttt = spu_splats(t); + oneMinusT = spu_sub( spu_splats(1.0f), tttt ); + angles = spu_sel( spu_splats(1.0f), oneMinusT, (vec_uint4)spu_maskb(0x0f00) ); + angles = spu_sel( angles, tttt, (vec_uint4)spu_maskb(0x00f0) ); + angles = spu_mul( angles, angle ); + sines = sinf4( angles ); + scales = divf4( sines, spu_shuffle( sines, sines, shuffle_xxxx ) ); + scale0 = spu_sel( oneMinusT, spu_shuffle( scales, scales, shuffle_yyyy ), selectMask ); + scale1 = spu_sel( tttt, spu_shuffle( scales, scales, shuffle_zzzz ), selectMask ); + return Vector4( spu_madd( unitVec0.get128(), scale0, spu_mul( unitVec1.get128(), scale1 ) ) ); +} + +inline vec_float4 Vector4::get128( ) const +{ + return mVec128; +} + +inline void loadXYZW( Vector4 & vec, const vec_float4 * quad ) +{ + vec = Vector4( *quad ); +} + +inline void loadXYZW( Vector4 & vec, const float * fptr ) +{ + const vec_float4 vfsrc0 = *((vec_float4*)((uintptr_t)fptr)); + const vec_float4 vfsrc1 = *((vec_float4*)(((uintptr_t)fptr) + 16)); + const vec_uchar16 vucpat = (vec_uchar16)spu_add((vec_ushort8)(spu_splats((unsigned char)(((int)fptr) & 0xf))), ((vec_ushort8){0x0001, 0x0203, 0x0405, 0x0607, 0x0809, 0x0A0B, 0x0C0D, 0x0E0F})); + const vec_float4 vfval = spu_shuffle(vfsrc0, vfsrc1, vucpat); + vec = Vector4( vfval ); +} + +inline void storeXYZW( Vector4 vec, float * fptr ) +{ + vec_float4 * vptr0 = (vec_float4*)((uintptr_t)fptr); + vec_float4 * vptr1 = (vec_float4*)(((uintptr_t)fptr) + 16); + vec_float4 dstVec0 = *vptr0; + vec_float4 dstVec1 = *vptr1; + uint32_t offset = (uint32_t)fptr & 0xf; + vec_uint4 mask = (vec_uint4)spu_splats(0xffffffff); + vec_uint4 mask0 = (vec_uint4)spu_rlmaskqwbyte(mask, -offset); + vec_uint4 mask1 = (vec_uint4)spu_slqwbyte(mask, 16 - offset); + vec_float4 vec0 = spu_rlmaskqwbyte(vec.get128(), -offset); + vec_float4 vec1 = spu_slqwbyte(vec.get128(), 16 - offset); + dstVec0 = spu_sel(dstVec0, vec0, mask0); + dstVec1 = spu_sel(dstVec1, vec1, mask1); + *vptr0 = dstVec0; + *vptr1 = dstVec1; +} + +inline void storeHalfFloats( Vector4 vec0, Vector4 vec1, Vector4 vec2, Vector4 vec3, vec_ushort8 * twoQuads ) +{ + twoQuads[0] = _vmath2VfToHalfFloats(vec0.get128(), vec1.get128()); + twoQuads[1] = _vmath2VfToHalfFloats(vec2.get128(), vec3.get128()); +} + +inline Vector4 & Vector4::operator =( Vector4 vec ) +{ + mVec128 = vec.mVec128; + return *this; +} + +inline Vector4 & Vector4::setXYZ( Vector3 vec ) +{ + mVec128 = spu_sel( vec.get128(), mVec128, (vec_uint4)spu_maskb(0x000f) ); + return *this; +} + +inline const Vector3 Vector4::getXYZ( ) const +{ + return Vector3( mVec128 ); +} + +inline Vector4 & Vector4::setX( float _x ) +{ + mVec128 = spu_insert( _x, mVec128, 0 ); + return *this; +} + +inline float Vector4::getX( ) const +{ + return spu_extract( mVec128, 0 ); +} + +inline Vector4 & Vector4::setY( float _y ) +{ + mVec128 = spu_insert( _y, mVec128, 1 ); + return *this; +} + +inline float Vector4::getY( ) const +{ + return spu_extract( mVec128, 1 ); +} + +inline Vector4 & Vector4::setZ( float _z ) +{ + mVec128 = spu_insert( _z, mVec128, 2 ); + return *this; +} + +inline float Vector4::getZ( ) const +{ + return spu_extract( mVec128, 2 ); +} + +inline Vector4 & Vector4::setW( float _w ) +{ + mVec128 = spu_insert( _w, mVec128, 3 ); + return *this; +} + +inline float Vector4::getW( ) const +{ + return spu_extract( mVec128, 3 ); +} + +inline Vector4 & Vector4::setElem( int idx, float value ) +{ + mVec128 = spu_insert( value, mVec128, idx ); + return *this; +} + +inline float Vector4::getElem( int idx ) const +{ + return spu_extract( mVec128, idx ); +} + +inline VecIdx Vector4::operator []( int idx ) +{ + return VecIdx( mVec128, idx ); +} + +inline float Vector4::operator []( int idx ) const +{ + return spu_extract( mVec128, idx ); +} + +inline const Vector4 Vector4::operator +( Vector4 vec ) const +{ + return Vector4( spu_add( mVec128, vec.mVec128 ) ); +} + +inline const Vector4 Vector4::operator -( Vector4 vec ) const +{ + return Vector4( spu_sub( mVec128, vec.mVec128 ) ); +} + +inline const Vector4 Vector4::operator *( float scalar ) const +{ + return Vector4( spu_mul( mVec128, spu_splats(scalar) ) ); +} + +inline Vector4 & Vector4::operator +=( Vector4 vec ) +{ + *this = *this + vec; + return *this; +} + +inline Vector4 & Vector4::operator -=( Vector4 vec ) +{ + *this = *this - vec; + return *this; +} + +inline Vector4 & Vector4::operator *=( float scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Vector4 Vector4::operator /( float scalar ) const +{ + return Vector4( divf4( mVec128, spu_splats(scalar) ) ); +} + +inline Vector4 & Vector4::operator /=( float scalar ) +{ + *this = *this / scalar; + return *this; +} + +inline const Vector4 Vector4::operator -( ) const +{ + return Vector4( negatef4( mVec128 ) ); +} + +inline const Vector4 operator *( float scalar, Vector4 vec ) +{ + return vec * scalar; +} + +inline const Vector4 mulPerElem( Vector4 vec0, Vector4 vec1 ) +{ + return Vector4( spu_mul( vec0.get128(), vec1.get128() ) ); +} + +inline const Vector4 divPerElem( Vector4 vec0, Vector4 vec1 ) +{ + return Vector4( divf4( vec0.get128(), vec1.get128() ) ); +} + +inline const Vector4 recipPerElem( Vector4 vec ) +{ + return Vector4( recipf4( vec.get128() ) ); +} + +inline const Vector4 sqrtPerElem( Vector4 vec ) +{ + return Vector4( sqrtf4( vec.get128() ) ); +} + +inline const Vector4 rsqrtPerElem( Vector4 vec ) +{ + return Vector4( rsqrtf4( vec.get128() ) ); +} + +inline const Vector4 absPerElem( Vector4 vec ) +{ + return Vector4( fabsf4( vec.get128() ) ); +} + +inline const Vector4 copySignPerElem( Vector4 vec0, Vector4 vec1 ) +{ + return Vector4( copysignf4( vec0.get128(), vec1.get128() ) ); +} + +inline const Vector4 maxPerElem( Vector4 vec0, Vector4 vec1 ) +{ + return Vector4( fmaxf4( vec0.get128(), vec1.get128() ) ); +} + +inline float maxElem( Vector4 vec ) +{ + vec_float4 result; + result = fmaxf4( spu_promote( spu_extract( vec.get128(), 1 ), 0 ), vec.get128() ); + result = fmaxf4( spu_promote( spu_extract( vec.get128(), 2 ), 0 ), result ); + result = fmaxf4( spu_promote( spu_extract( vec.get128(), 3 ), 0 ), result ); + return spu_extract( result, 0 ); +} + +inline const Vector4 minPerElem( Vector4 vec0, Vector4 vec1 ) +{ + return Vector4( fminf4( vec0.get128(), vec1.get128() ) ); +} + +inline float minElem( Vector4 vec ) +{ + vec_float4 result; + result = fminf4( spu_promote( spu_extract( vec.get128(), 1 ), 0 ), vec.get128() ); + result = fminf4( spu_promote( spu_extract( vec.get128(), 2 ), 0 ), result ); + result = fminf4( spu_promote( spu_extract( vec.get128(), 3 ), 0 ), result ); + return spu_extract( result, 0 ); +} + +inline float sum( Vector4 vec ) +{ + return + spu_extract( vec.get128(), 0 ) + + spu_extract( vec.get128(), 1 ) + + spu_extract( vec.get128(), 2 ) + + spu_extract( vec.get128(), 3 ); +} + +inline float dot( Vector4 vec0, Vector4 vec1 ) +{ + return spu_extract( _vmathVfDot4( vec0.get128(), vec1.get128() ), 0 ); +} + +inline float lengthSqr( Vector4 vec ) +{ + return spu_extract( _vmathVfDot4( vec.get128(), vec.get128() ), 0 ); +} + +inline float length( Vector4 vec ) +{ + return sqrtf( lengthSqr( vec ) ); +} + +inline const Vector4 normalize( Vector4 vec ) +{ + vec_float4 dot = _vmathVfDot4( vec.get128(), vec.get128() ); + return Vector4( spu_mul( vec.get128(), rsqrtf4( dot ) ) ); +} + +inline const Vector4 select( Vector4 vec0, Vector4 vec1, bool select1 ) +{ + return Vector4( spu_sel( vec0.get128(), vec1.get128(), spu_splats( (unsigned int)-(select1 > 0) ) ) ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( Vector4 vec ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec.get128(); + printf( "( %f %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +inline void print( Vector4 vec, const char * name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = vec.get128(); + printf( "%s: ( %f %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2], tmp.s[3] ); +} + +#endif + +inline Point3::Point3( float _x, float _y, float _z ) +{ + mVec128 = (vec_float4){ _x, _y, _z, 0.0f }; +} + +inline Point3::Point3( Vector3 vec ) +{ + mVec128 = vec.get128(); +} + +inline Point3::Point3( float scalar ) +{ + mVec128 = spu_splats( scalar ); +} + +inline Point3::Point3( vec_float4 vf4 ) +{ + mVec128 = vf4; +} + +inline const Point3 lerp( float t, Point3 pnt0, Point3 pnt1 ) +{ + return ( pnt0 + ( ( pnt1 - pnt0 ) * t ) ); +} + +inline vec_float4 Point3::get128( ) const +{ + return mVec128; +} + +inline void loadXYZ( Point3 & pnt, const vec_float4 * quad ) +{ + pnt = Point3( *quad ); +} + +inline void loadXYZ( Point3 & pnt, const float * fptr ) +{ + const vec_float4 vfsrc0 = *((vec_float4*)((uintptr_t)fptr)); + const vec_float4 vfsrc1 = *((vec_float4*)(((uintptr_t)fptr) + 16)); + const vec_uchar16 vucpat = (vec_uchar16)spu_add((vec_ushort8)(spu_splats((unsigned char)(((int)fptr) & 0xf))),((vec_ushort8){0x0001, 0x0203, 0x0405, 0x0607, 0x0809, 0x0A0B, 0x0C0D, 0x0E0F})); + const vec_float4 vfval = spu_shuffle(vfsrc0, vfsrc1, vucpat); + pnt = Point3( vfval ); +} + +inline void storeXYZ( Point3 pnt, vec_float4 * quad ) +{ + vec_float4 dstVec = *quad; + vec_uint4 mask = (vec_uint4)spu_maskb(0x000f); + dstVec = spu_sel(pnt.get128(), dstVec, mask); + *quad = dstVec; +} + +inline void storeXYZ( Point3 pnt, float * fptr ) +{ + vec_float4 * vptr0 = (vec_float4*)((uintptr_t)fptr); + vec_float4 * vptr1 = (vec_float4*)(((uintptr_t)fptr) + 16); + vec_float4 dstVec0 = *vptr0; + vec_float4 dstVec1 = *vptr1; + uint32_t offset = (uint32_t)fptr & 0xf; + vec_uint4 mask = (vec_uint4)spu_maskb(0xfff0); + vec_uint4 mask0 = (vec_uint4)spu_rlmaskqwbyte(mask, -offset); + vec_uint4 mask1 = (vec_uint4)spu_slqwbyte(mask, 16 - offset); + vec_float4 vec0 = spu_rlmaskqwbyte(pnt.get128(), -offset); + vec_float4 vec1 = spu_slqwbyte(pnt.get128(), 16 - offset); + dstVec0 = spu_sel(dstVec0, vec0, mask0); + dstVec1 = spu_sel(dstVec1, vec1, mask1); + *vptr0 = dstVec0; + *vptr1 = dstVec1; +} + +inline void loadXYZArray( Point3 & pnt0, Point3 & pnt1, Point3 & pnt2, Point3 & pnt3, const vec_float4 * threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz, xyz1, xyz2, xyz3; + xyzx = threeQuads[0]; + yzxy = threeQuads[1]; + zxyz = threeQuads[2]; + xyz1 = spu_shuffle( xyzx, yzxy, _VECTORMATH_SHUF_WABC ); + xyz2 = spu_shuffle( yzxy, zxyz, _VECTORMATH_SHUF_ZWAB ); + xyz3 = spu_rlqwbyte( zxyz, 4 ); + pnt0 = Point3( xyzx ); + pnt1 = Point3( xyz1 ); + pnt2 = Point3( xyz2 ); + pnt3 = Point3( xyz3 ); +} + +inline void storeXYZArray( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, vec_float4 * threeQuads ) +{ + vec_float4 xyzx, yzxy, zxyz; + xyzx = spu_shuffle( pnt0.get128(), pnt1.get128(), _VECTORMATH_SHUF_XYZA ); + yzxy = spu_shuffle( pnt1.get128(), pnt2.get128(), _VECTORMATH_SHUF_YZAB ); + zxyz = spu_shuffle( pnt2.get128(), pnt3.get128(), _VECTORMATH_SHUF_ZABC ); + threeQuads[0] = xyzx; + threeQuads[1] = yzxy; + threeQuads[2] = zxyz; +} + +inline void storeHalfFloats( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, Point3 pnt4, Point3 pnt5, Point3 pnt6, Point3 pnt7, vec_ushort8 * threeQuads ) +{ + vec_float4 xyz0[3]; + vec_float4 xyz1[3]; + storeXYZArray( pnt0, pnt1, pnt2, pnt3, xyz0 ); + storeXYZArray( pnt4, pnt5, pnt6, pnt7, xyz1 ); + threeQuads[0] = _vmath2VfToHalfFloats(xyz0[0], xyz0[1]); + threeQuads[1] = _vmath2VfToHalfFloats(xyz0[2], xyz1[0]); + threeQuads[2] = _vmath2VfToHalfFloats(xyz1[1], xyz1[2]); +} + +inline Point3 & Point3::operator =( Point3 pnt ) +{ + mVec128 = pnt.mVec128; + return *this; +} + +inline Point3 & Point3::setX( float _x ) +{ + mVec128 = spu_insert( _x, mVec128, 0 ); + return *this; +} + +inline float Point3::getX( ) const +{ + return spu_extract( mVec128, 0 ); +} + +inline Point3 & Point3::setY( float _y ) +{ + mVec128 = spu_insert( _y, mVec128, 1 ); + return *this; +} + +inline float Point3::getY( ) const +{ + return spu_extract( mVec128, 1 ); +} + +inline Point3 & Point3::setZ( float _z ) +{ + mVec128 = spu_insert( _z, mVec128, 2 ); + return *this; +} + +inline float Point3::getZ( ) const +{ + return spu_extract( mVec128, 2 ); +} + +inline Point3 & Point3::setElem( int idx, float value ) +{ + mVec128 = spu_insert( value, mVec128, idx ); + return *this; +} + +inline float Point3::getElem( int idx ) const +{ + return spu_extract( mVec128, idx ); +} + +inline VecIdx Point3::operator []( int idx ) +{ + return VecIdx( mVec128, idx ); +} + +inline float Point3::operator []( int idx ) const +{ + return spu_extract( mVec128, idx ); +} + +inline const Vector3 Point3::operator -( Point3 pnt ) const +{ + return Vector3( spu_sub( mVec128, pnt.mVec128 ) ); +} + +inline const Point3 Point3::operator +( Vector3 vec ) const +{ + return Point3( spu_add( mVec128, vec.get128() ) ); +} + +inline const Point3 Point3::operator -( Vector3 vec ) const +{ + return Point3( spu_sub( mVec128, vec.get128() ) ); +} + +inline Point3 & Point3::operator +=( Vector3 vec ) +{ + *this = *this + vec; + return *this; +} + +inline Point3 & Point3::operator -=( Vector3 vec ) +{ + *this = *this - vec; + return *this; +} + +inline const Point3 mulPerElem( Point3 pnt0, Point3 pnt1 ) +{ + return Point3( spu_mul( pnt0.get128(), pnt1.get128() ) ); +} + +inline const Point3 divPerElem( Point3 pnt0, Point3 pnt1 ) +{ + return Point3( divf4( pnt0.get128(), pnt1.get128() ) ); +} + +inline const Point3 recipPerElem( Point3 pnt ) +{ + return Point3( recipf4( pnt.get128() ) ); +} + +inline const Point3 sqrtPerElem( Point3 pnt ) +{ + return Point3( sqrtf4( pnt.get128() ) ); +} + +inline const Point3 rsqrtPerElem( Point3 pnt ) +{ + return Point3( rsqrtf4( pnt.get128() ) ); +} + +inline const Point3 absPerElem( Point3 pnt ) +{ + return Point3( fabsf4( pnt.get128() ) ); +} + +inline const Point3 copySignPerElem( Point3 pnt0, Point3 pnt1 ) +{ + return Point3( copysignf4( pnt0.get128(), pnt1.get128() ) ); +} + +inline const Point3 maxPerElem( Point3 pnt0, Point3 pnt1 ) +{ + return Point3( fmaxf4( pnt0.get128(), pnt1.get128() ) ); +} + +inline float maxElem( Point3 pnt ) +{ + vec_float4 result; + result = fmaxf4( spu_promote( spu_extract( pnt.get128(), 1 ), 0 ), pnt.get128() ); + result = fmaxf4( spu_promote( spu_extract( pnt.get128(), 2 ), 0 ), result ); + return spu_extract( result, 0 ); +} + +inline const Point3 minPerElem( Point3 pnt0, Point3 pnt1 ) +{ + return Point3( fminf4( pnt0.get128(), pnt1.get128() ) ); +} + +inline float minElem( Point3 pnt ) +{ + vec_float4 result; + result = fminf4( spu_promote( spu_extract( pnt.get128(), 1 ), 0 ), pnt.get128() ); + result = fminf4( spu_promote( spu_extract( pnt.get128(), 2 ), 0 ), result ); + return spu_extract( result, 0 ); +} + +inline float sum( Point3 pnt ) +{ + return + spu_extract( pnt.get128(), 0 ) + + spu_extract( pnt.get128(), 1 ) + + spu_extract( pnt.get128(), 2 ); +} + +inline const Point3 scale( Point3 pnt, float scaleVal ) +{ + return mulPerElem( pnt, Point3( scaleVal ) ); +} + +inline const Point3 scale( Point3 pnt, Vector3 scaleVec ) +{ + return mulPerElem( pnt, Point3( scaleVec ) ); +} + +inline float projection( Point3 pnt, Vector3 unitVec ) +{ + return spu_extract( _vmathVfDot3( pnt.get128(), unitVec.get128() ), 0 ); +} + +inline float distSqrFromOrigin( Point3 pnt ) +{ + return lengthSqr( Vector3( pnt ) ); +} + +inline float distFromOrigin( Point3 pnt ) +{ + return length( Vector3( pnt ) ); +} + +inline float distSqr( Point3 pnt0, Point3 pnt1 ) +{ + return lengthSqr( ( pnt1 - pnt0 ) ); +} + +inline float dist( Point3 pnt0, Point3 pnt1 ) +{ + return length( ( pnt1 - pnt0 ) ); +} + +inline const Point3 select( Point3 pnt0, Point3 pnt1, bool select1 ) +{ + return Point3( spu_sel( pnt0.get128(), pnt1.get128(), spu_splats( (unsigned int)-(select1 > 0) ) ) ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( Point3 pnt ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = pnt.get128(); + printf( "( %f %f %f )\n", tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +inline void print( Point3 pnt, const char * name ) +{ + union { vec_float4 v; float s[4]; } tmp; + tmp.v = pnt.get128(); + printf( "%s: ( %f %f %f )\n", name, tmp.s[0], tmp.s[1], tmp.s[2] ); +} + +#endif + +} // namespace Aos +} // namespace Vectormath + +#endif diff --git a/common/vectormath/spu/cpp/vecidx_aos.h b/common/vectormath/spu/cpp/vecidx_aos.h index f5309153..e46578ad 100644 --- a/common/vectormath/spu/cpp/vecidx_aos.h +++ b/common/vectormath/spu/cpp/vecidx_aos.h @@ -1,64 +1,64 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_VECIDX_AOS_H -#define _VECTORMATH_VECIDX_AOS_H - -#include - -namespace Vectormath { -namespace Aos { - -//----------------------------------------------------------------------------- -// VecIdx -// Used in setting elements of Vector3, Vector4, Point3, or Quat with the -// subscripting operator. -// - -class VecIdx -{ -private: - typedef vec_float4 vec_float4_t; - vec_float4_t &ref __attribute__ ((aligned(16))); - int i __attribute__ ((aligned(16))); -public: - inline VecIdx( vec_float4& vec, int idx ): ref(vec) { i = idx; } - inline operator float() const; - inline float operator =( float scalar ); - inline float operator =( const VecIdx& scalar ); - inline float operator *=( float scalar ); - inline float operator /=( float scalar ); - inline float operator +=( float scalar ); - inline float operator -=( float scalar ); -}; - -} // namespace Aos -} // namespace Vectormath - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_VECIDX_AOS_H +#define _VECTORMATH_VECIDX_AOS_H + +#include + +namespace Vectormath { +namespace Aos { + +//----------------------------------------------------------------------------- +// VecIdx +// Used in setting elements of Vector3, Vector4, Point3, or Quat with the +// subscripting operator. +// + +class VecIdx +{ +private: + typedef vec_float4 vec_float4_t; + vec_float4_t &ref __attribute__ ((aligned(16))); + int i __attribute__ ((aligned(16))); +public: + inline VecIdx( vec_float4& vec, int idx ): ref(vec) { i = idx; } + inline operator float() const; + inline float operator =( float scalar ); + inline float operator =( const VecIdx& scalar ); + inline float operator *=( float scalar ); + inline float operator /=( float scalar ); + inline float operator +=( float scalar ); + inline float operator -=( float scalar ); +}; + +} // namespace Aos +} // namespace Vectormath + +#endif diff --git a/common/vectormath/spu/cpp/vectormath_aos.h b/common/vectormath/spu/cpp/vectormath_aos.h index f876c538..7906da31 100644 --- a/common/vectormath/spu/cpp/vectormath_aos.h +++ b/common/vectormath/spu/cpp/vectormath_aos.h @@ -1,1851 +1,1901 @@ -/* - Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. - All rights reserved. - - Redistribution and use in source and binary forms, - with or without modification, are permitted provided that the - following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Sony Computer Entertainment Inc nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VECTORMATH_AOS_CPP_SPU_H -#define _VECTORMATH_AOS_CPP_SPU_H - -#include -#include -#include "floatInVec.h" -#include "boolInVec.h" -#include "vecidx_aos.h" -#include - -#ifdef _VECTORMATH_DEBUG -#endif - -namespace Vectormath { - -namespace Aos { - -//----------------------------------------------------------------------------- -// Forward Declarations -// - -class Vector3; -class Vector4; -class Point3; -class Quat; -class Matrix3; -class Matrix4; -class Transform3; - -// A 3-D vector in array-of-structures format -// -class Vector3 -{ - vec_float4 mVec128; - -public: - // Default constructor; does no initialization - // - inline Vector3( ) { }; - - // Construct a 3-D vector from x, y, and z elements - // - inline Vector3( float x, float y, float z ); - - // Copy elements from a 3-D point into a 3-D vector - // - explicit inline Vector3( Point3 pnt ); - - // Set all elements of a 3-D vector to the same scalar value - // - explicit inline Vector3( float scalar ); - - // Set vector float data in a 3-D vector - // - explicit inline Vector3( vec_float4 vf4 ); - - // Get vector float data from a 3-D vector - // - inline vec_float4 get128( ) const; - - // Assign one 3-D vector to another - // - inline Vector3 & operator =( Vector3 vec ); - - // Set the x element of a 3-D vector - // - inline Vector3 & setX( float x ); - - // Set the y element of a 3-D vector - // - inline Vector3 & setY( float y ); - - // Set the z element of a 3-D vector - // - inline Vector3 & setZ( float z ); - - // Get the x element of a 3-D vector - // - inline float getX( ) const; - - // Get the y element of a 3-D vector - // - inline float getY( ) const; - - // Get the z element of a 3-D vector - // - inline float getZ( ) const; - - // Set an x, y, or z element of a 3-D vector by index - // - inline Vector3 & setElem( int idx, float value ); - - // Get an x, y, or z element of a 3-D vector by index - // - inline float getElem( int idx ) const; - - // Subscripting operator to set or get an element - // - inline VecIdx operator []( int idx ); - - // Subscripting operator to get an element - // - inline float operator []( int idx ) const; - - // Add two 3-D vectors - // - inline const Vector3 operator +( Vector3 vec ) const; - - // Subtract a 3-D vector from another 3-D vector - // - inline const Vector3 operator -( Vector3 vec ) const; - - // Add a 3-D vector to a 3-D point - // - inline const Point3 operator +( Point3 pnt ) const; - - // Multiply a 3-D vector by a scalar - // - inline const Vector3 operator *( float scalar ) const; - - // Divide a 3-D vector by a scalar - // - inline const Vector3 operator /( float scalar ) const; - - // Perform compound assignment and addition with a 3-D vector - // - inline Vector3 & operator +=( Vector3 vec ); - - // Perform compound assignment and subtraction by a 3-D vector - // - inline Vector3 & operator -=( Vector3 vec ); - - // Perform compound assignment and multiplication by a scalar - // - inline Vector3 & operator *=( float scalar ); - - // Perform compound assignment and division by a scalar - // - inline Vector3 & operator /=( float scalar ); - - // Negate all elements of a 3-D vector - // - inline const Vector3 operator -( ) const; - - // Construct x axis - // - static inline const Vector3 xAxis( ); - - // Construct y axis - // - static inline const Vector3 yAxis( ); - - // Construct z axis - // - static inline const Vector3 zAxis( ); - -}; - -// Multiply a 3-D vector by a scalar -// -inline const Vector3 operator *( float scalar, Vector3 vec ); - -// Multiply two 3-D vectors per element -// -inline const Vector3 mulPerElem( Vector3 vec0, Vector3 vec1 ); - -// Divide two 3-D vectors per element -// NOTE: -// Floating-point behavior matches standard library function divf4. -// -inline const Vector3 divPerElem( Vector3 vec0, Vector3 vec1 ); - -// Compute the reciprocal of a 3-D vector per element -// NOTE: -// Floating-point behavior matches standard library function recipf4. -// -inline const Vector3 recipPerElem( Vector3 vec ); - -// Compute the square root of a 3-D vector per element -// NOTE: -// Floating-point behavior matches standard library function sqrtf4. -// -inline const Vector3 sqrtPerElem( Vector3 vec ); - -// Compute the reciprocal square root of a 3-D vector per element -// NOTE: -// Floating-point behavior matches standard library function rsqrtf4. -// -inline const Vector3 rsqrtPerElem( Vector3 vec ); - -// Compute the absolute value of a 3-D vector per element -// -inline const Vector3 absPerElem( Vector3 vec ); - -// Copy sign from one 3-D vector to another, per element -// -inline const Vector3 copySignPerElem( Vector3 vec0, Vector3 vec1 ); - -// Maximum of two 3-D vectors per element -// -inline const Vector3 maxPerElem( Vector3 vec0, Vector3 vec1 ); - -// Minimum of two 3-D vectors per element -// -inline const Vector3 minPerElem( Vector3 vec0, Vector3 vec1 ); - -// Maximum element of a 3-D vector -// -inline float maxElem( Vector3 vec ); - -// Minimum element of a 3-D vector -// -inline float minElem( Vector3 vec ); - -// Compute the sum of all elements of a 3-D vector -// -inline float sum( Vector3 vec ); - -// Compute the dot product of two 3-D vectors -// -inline float dot( Vector3 vec0, Vector3 vec1 ); - -// Compute the square of the length of a 3-D vector -// -inline float lengthSqr( Vector3 vec ); - -// Compute the length of a 3-D vector -// -inline float length( Vector3 vec ); - -// Normalize a 3-D vector -// NOTE: -// The result is unpredictable when all elements of vec are at or near zero. -// -inline const Vector3 normalize( Vector3 vec ); - -// Compute cross product of two 3-D vectors -// -inline const Vector3 cross( Vector3 vec0, Vector3 vec1 ); - -// Outer product of two 3-D vectors -// -inline const Matrix3 outer( Vector3 vec0, Vector3 vec1 ); - -// Pre-multiply a row vector by a 3x3 matrix -// NOTE: -// Slower than column post-multiply. -// -inline const Vector3 rowMul( Vector3 vec, const Matrix3 & mat ); - -// Cross-product matrix of a 3-D vector -// -inline const Matrix3 crossMatrix( Vector3 vec ); - -// Create cross-product matrix and multiply -// NOTE: -// Faster than separately creating a cross-product matrix and multiplying. -// -inline const Matrix3 crossMatrixMul( Vector3 vec, const Matrix3 & mat ); - -// Linear interpolation between two 3-D vectors -// NOTE: -// Does not clamp t between 0 and 1. -// -inline const Vector3 lerp( float t, Vector3 vec0, Vector3 vec1 ); - -// Spherical linear interpolation between two 3-D vectors -// NOTE: -// The result is unpredictable if the vectors point in opposite directions. -// Does not clamp t between 0 and 1. -// -inline const Vector3 slerp( float t, Vector3 unitVec0, Vector3 unitVec1 ); - -// Conditionally select between two 3-D vectors -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// -inline const Vector3 select( Vector3 vec0, Vector3 vec1, bool select1 ); - -// Store x, y, and z elements of a 3-D vector in the first three words of a quadword. -// The value of the fourth word (the word with the highest address) remains unchanged -// -inline void storeXYZ( Vector3 vec, vec_float4 * quad ); - -// Load four three-float 3-D vectors, stored in three quadwords -// -inline void loadXYZArray( Vector3 & vec0, Vector3 & vec1, Vector3 & vec2, Vector3 & vec3, const vec_float4 * threeQuads ); - -// Store four 3-D vectors in three quadwords -// -inline void storeXYZArray( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, vec_float4 * threeQuads ); - -// Store eight 3-D vectors as half-floats -// -inline void storeHalfFloats( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, Vector3 vec4, Vector3 vec5, Vector3 vec6, Vector3 vec7, vec_ushort8 * threeQuads ); - -#ifdef _VECTORMATH_DEBUG - -// Print a 3-D vector -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Vector3 vec ); - -// Print a 3-D vector and an associated string identifier -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Vector3 vec, const char * name ); - -#endif - -// A 4-D vector in array-of-structures format -// -class Vector4 -{ - vec_float4 mVec128; - -public: - // Default constructor; does no initialization - // - inline Vector4( ) { }; - - // Construct a 4-D vector from x, y, z, and w elements - // - inline Vector4( float x, float y, float z, float w ); - - // Construct a 4-D vector from a 3-D vector and a scalar - // - inline Vector4( Vector3 xyz, float w ); - - // Copy x, y, and z from a 3-D vector into a 4-D vector, and set w to 0 - // - explicit inline Vector4( Vector3 vec ); - - // Copy x, y, and z from a 3-D point into a 4-D vector, and set w to 1 - // - explicit inline Vector4( Point3 pnt ); - - // Copy elements from a quaternion into a 4-D vector - // - explicit inline Vector4( Quat quat ); - - // Set all elements of a 4-D vector to the same scalar value - // - explicit inline Vector4( float scalar ); - - // Set vector float data in a 4-D vector - // - explicit inline Vector4( vec_float4 vf4 ); - - // Get vector float data from a 4-D vector - // - inline vec_float4 get128( ) const; - - // Assign one 4-D vector to another - // - inline Vector4 & operator =( Vector4 vec ); - - // Set the x, y, and z elements of a 4-D vector - // NOTE: - // This function does not change the w element. - // - inline Vector4 & setXYZ( Vector3 vec ); - - // Get the x, y, and z elements of a 4-D vector - // - inline const Vector3 getXYZ( ) const; - - // Set the x element of a 4-D vector - // - inline Vector4 & setX( float x ); - - // Set the y element of a 4-D vector - // - inline Vector4 & setY( float y ); - - // Set the z element of a 4-D vector - // - inline Vector4 & setZ( float z ); - - // Set the w element of a 4-D vector - // - inline Vector4 & setW( float w ); - - // Get the x element of a 4-D vector - // - inline float getX( ) const; - - // Get the y element of a 4-D vector - // - inline float getY( ) const; - - // Get the z element of a 4-D vector - // - inline float getZ( ) const; - - // Get the w element of a 4-D vector - // - inline float getW( ) const; - - // Set an x, y, z, or w element of a 4-D vector by index - // - inline Vector4 & setElem( int idx, float value ); - - // Get an x, y, z, or w element of a 4-D vector by index - // - inline float getElem( int idx ) const; - - // Subscripting operator to set or get an element - // - inline VecIdx operator []( int idx ); - - // Subscripting operator to get an element - // - inline float operator []( int idx ) const; - - // Add two 4-D vectors - // - inline const Vector4 operator +( Vector4 vec ) const; - - // Subtract a 4-D vector from another 4-D vector - // - inline const Vector4 operator -( Vector4 vec ) const; - - // Multiply a 4-D vector by a scalar - // - inline const Vector4 operator *( float scalar ) const; - - // Divide a 4-D vector by a scalar - // - inline const Vector4 operator /( float scalar ) const; - - // Perform compound assignment and addition with a 4-D vector - // - inline Vector4 & operator +=( Vector4 vec ); - - // Perform compound assignment and subtraction by a 4-D vector - // - inline Vector4 & operator -=( Vector4 vec ); - - // Perform compound assignment and multiplication by a scalar - // - inline Vector4 & operator *=( float scalar ); - - // Perform compound assignment and division by a scalar - // - inline Vector4 & operator /=( float scalar ); - - // Negate all elements of a 4-D vector - // - inline const Vector4 operator -( ) const; - - // Construct x axis - // - static inline const Vector4 xAxis( ); - - // Construct y axis - // - static inline const Vector4 yAxis( ); - - // Construct z axis - // - static inline const Vector4 zAxis( ); - - // Construct w axis - // - static inline const Vector4 wAxis( ); - -}; - -// Multiply a 4-D vector by a scalar -// -inline const Vector4 operator *( float scalar, Vector4 vec ); - -// Multiply two 4-D vectors per element -// -inline const Vector4 mulPerElem( Vector4 vec0, Vector4 vec1 ); - -// Divide two 4-D vectors per element -// NOTE: -// Floating-point behavior matches standard library function divf4. -// -inline const Vector4 divPerElem( Vector4 vec0, Vector4 vec1 ); - -// Compute the reciprocal of a 4-D vector per element -// NOTE: -// Floating-point behavior matches standard library function recipf4. -// -inline const Vector4 recipPerElem( Vector4 vec ); - -// Compute the square root of a 4-D vector per element -// NOTE: -// Floating-point behavior matches standard library function sqrtf4. -// -inline const Vector4 sqrtPerElem( Vector4 vec ); - -// Compute the reciprocal square root of a 4-D vector per element -// NOTE: -// Floating-point behavior matches standard library function rsqrtf4. -// -inline const Vector4 rsqrtPerElem( Vector4 vec ); - -// Compute the absolute value of a 4-D vector per element -// -inline const Vector4 absPerElem( Vector4 vec ); - -// Copy sign from one 4-D vector to another, per element -// -inline const Vector4 copySignPerElem( Vector4 vec0, Vector4 vec1 ); - -// Maximum of two 4-D vectors per element -// -inline const Vector4 maxPerElem( Vector4 vec0, Vector4 vec1 ); - -// Minimum of two 4-D vectors per element -// -inline const Vector4 minPerElem( Vector4 vec0, Vector4 vec1 ); - -// Maximum element of a 4-D vector -// -inline float maxElem( Vector4 vec ); - -// Minimum element of a 4-D vector -// -inline float minElem( Vector4 vec ); - -// Compute the sum of all elements of a 4-D vector -// -inline float sum( Vector4 vec ); - -// Compute the dot product of two 4-D vectors -// -inline float dot( Vector4 vec0, Vector4 vec1 ); - -// Compute the square of the length of a 4-D vector -// -inline float lengthSqr( Vector4 vec ); - -// Compute the length of a 4-D vector -// -inline float length( Vector4 vec ); - -// Normalize a 4-D vector -// NOTE: -// The result is unpredictable when all elements of vec are at or near zero. -// -inline const Vector4 normalize( Vector4 vec ); - -// Outer product of two 4-D vectors -// -inline const Matrix4 outer( Vector4 vec0, Vector4 vec1 ); - -// Linear interpolation between two 4-D vectors -// NOTE: -// Does not clamp t between 0 and 1. -// -inline const Vector4 lerp( float t, Vector4 vec0, Vector4 vec1 ); - -// Spherical linear interpolation between two 4-D vectors -// NOTE: -// The result is unpredictable if the vectors point in opposite directions. -// Does not clamp t between 0 and 1. -// -inline const Vector4 slerp( float t, Vector4 unitVec0, Vector4 unitVec1 ); - -// Conditionally select between two 4-D vectors -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// -inline const Vector4 select( Vector4 vec0, Vector4 vec1, bool select1 ); - -// Store four 4-D vectors as half-floats -// -inline void storeHalfFloats( Vector4 vec0, Vector4 vec1, Vector4 vec2, Vector4 vec3, vec_ushort8 * twoQuads ); - -#ifdef _VECTORMATH_DEBUG - -// Print a 4-D vector -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Vector4 vec ); - -// Print a 4-D vector and an associated string identifier -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Vector4 vec, const char * name ); - -#endif - -// A 3-D point in array-of-structures format -// -class Point3 -{ - vec_float4 mVec128; - -public: - // Default constructor; does no initialization - // - inline Point3( ) { }; - - // Construct a 3-D point from x, y, and z elements - // - inline Point3( float x, float y, float z ); - - // Copy elements from a 3-D vector into a 3-D point - // - explicit inline Point3( Vector3 vec ); - - // Set all elements of a 3-D point to the same scalar value - // - explicit inline Point3( float scalar ); - - // Set vector float data in a 3-D point - // - explicit inline Point3( vec_float4 vf4 ); - - // Get vector float data from a 3-D point - // - inline vec_float4 get128( ) const; - - // Assign one 3-D point to another - // - inline Point3 & operator =( Point3 pnt ); - - // Set the x element of a 3-D point - // - inline Point3 & setX( float x ); - - // Set the y element of a 3-D point - // - inline Point3 & setY( float y ); - - // Set the z element of a 3-D point - // - inline Point3 & setZ( float z ); - - // Get the x element of a 3-D point - // - inline float getX( ) const; - - // Get the y element of a 3-D point - // - inline float getY( ) const; - - // Get the z element of a 3-D point - // - inline float getZ( ) const; - - // Set an x, y, or z element of a 3-D point by index - // - inline Point3 & setElem( int idx, float value ); - - // Get an x, y, or z element of a 3-D point by index - // - inline float getElem( int idx ) const; - - // Subscripting operator to set or get an element - // - inline VecIdx operator []( int idx ); - - // Subscripting operator to get an element - // - inline float operator []( int idx ) const; - - // Subtract a 3-D point from another 3-D point - // - inline const Vector3 operator -( Point3 pnt ) const; - - // Add a 3-D point to a 3-D vector - // - inline const Point3 operator +( Vector3 vec ) const; - - // Subtract a 3-D vector from a 3-D point - // - inline const Point3 operator -( Vector3 vec ) const; - - // Perform compound assignment and addition with a 3-D vector - // - inline Point3 & operator +=( Vector3 vec ); - - // Perform compound assignment and subtraction by a 3-D vector - // - inline Point3 & operator -=( Vector3 vec ); - -}; - -// Multiply two 3-D points per element -// -inline const Point3 mulPerElem( Point3 pnt0, Point3 pnt1 ); - -// Divide two 3-D points per element -// NOTE: -// Floating-point behavior matches standard library function divf4. -// -inline const Point3 divPerElem( Point3 pnt0, Point3 pnt1 ); - -// Compute the reciprocal of a 3-D point per element -// NOTE: -// Floating-point behavior matches standard library function recipf4. -// -inline const Point3 recipPerElem( Point3 pnt ); - -// Compute the square root of a 3-D point per element -// NOTE: -// Floating-point behavior matches standard library function sqrtf4. -// -inline const Point3 sqrtPerElem( Point3 pnt ); - -// Compute the reciprocal square root of a 3-D point per element -// NOTE: -// Floating-point behavior matches standard library function rsqrtf4. -// -inline const Point3 rsqrtPerElem( Point3 pnt ); - -// Compute the absolute value of a 3-D point per element -// -inline const Point3 absPerElem( Point3 pnt ); - -// Copy sign from one 3-D point to another, per element -// -inline const Point3 copySignPerElem( Point3 pnt0, Point3 pnt1 ); - -// Maximum of two 3-D points per element -// -inline const Point3 maxPerElem( Point3 pnt0, Point3 pnt1 ); - -// Minimum of two 3-D points per element -// -inline const Point3 minPerElem( Point3 pnt0, Point3 pnt1 ); - -// Maximum element of a 3-D point -// -inline float maxElem( Point3 pnt ); - -// Minimum element of a 3-D point -// -inline float minElem( Point3 pnt ); - -// Compute the sum of all elements of a 3-D point -// -inline float sum( Point3 pnt ); - -// Apply uniform scale to a 3-D point -// -inline const Point3 scale( Point3 pnt, float scaleVal ); - -// Apply non-uniform scale to a 3-D point -// -inline const Point3 scale( Point3 pnt, Vector3 scaleVec ); - -// Scalar projection of a 3-D point on a unit-length 3-D vector -// -inline float projection( Point3 pnt, Vector3 unitVec ); - -// Compute the square of the distance of a 3-D point from the coordinate-system origin -// -inline float distSqrFromOrigin( Point3 pnt ); - -// Compute the distance of a 3-D point from the coordinate-system origin -// -inline float distFromOrigin( Point3 pnt ); - -// Compute the square of the distance between two 3-D points -// -inline float distSqr( Point3 pnt0, Point3 pnt1 ); - -// Compute the distance between two 3-D points -// -inline float dist( Point3 pnt0, Point3 pnt1 ); - -// Linear interpolation between two 3-D points -// NOTE: -// Does not clamp t between 0 and 1. -// -inline const Point3 lerp( float t, Point3 pnt0, Point3 pnt1 ); - -// Conditionally select between two 3-D points -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// -inline const Point3 select( Point3 pnt0, Point3 pnt1, bool select1 ); - -// Store x, y, and z elements of a 3-D point in the first three words of a quadword. -// The value of the fourth word (the word with the highest address) remains unchanged -// -inline void storeXYZ( Point3 pnt, vec_float4 * quad ); - -// Load four three-float 3-D points, stored in three quadwords -// -inline void loadXYZArray( Point3 & pnt0, Point3 & pnt1, Point3 & pnt2, Point3 & pnt3, const vec_float4 * threeQuads ); - -// Store four 3-D points in three quadwords -// -inline void storeXYZArray( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, vec_float4 * threeQuads ); - -// Store eight 3-D points as half-floats -// -inline void storeHalfFloats( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, Point3 pnt4, Point3 pnt5, Point3 pnt6, Point3 pnt7, vec_ushort8 * threeQuads ); - -#ifdef _VECTORMATH_DEBUG - -// Print a 3-D point -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Point3 pnt ); - -// Print a 3-D point and an associated string identifier -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Point3 pnt, const char * name ); - -#endif - -// A quaternion in array-of-structures format -// -class Quat -{ - vec_float4 mVec128; - -public: - // Default constructor; does no initialization - // - inline Quat( ) { }; - - // Construct a quaternion from x, y, z, and w elements - // - inline Quat( float x, float y, float z, float w ); - - // Construct a quaternion from a 3-D vector and a scalar - // - inline Quat( Vector3 xyz, float w ); - - // Copy elements from a 4-D vector into a quaternion - // - explicit inline Quat( Vector4 vec ); - - // Convert a rotation matrix to a unit-length quaternion - // - explicit inline Quat( const Matrix3 & rotMat ); - - // Set all elements of a quaternion to the same scalar value - // - explicit inline Quat( float scalar ); - - // Set vector float data in a quaternion - // - explicit inline Quat( vec_float4 vf4 ); - - // Get vector float data from a quaternion - // - inline vec_float4 get128( ) const; - - // Assign one quaternion to another - // - inline Quat & operator =( Quat quat ); - - // Set the x, y, and z elements of a quaternion - // NOTE: - // This function does not change the w element. - // - inline Quat & setXYZ( Vector3 vec ); - - // Get the x, y, and z elements of a quaternion - // - inline const Vector3 getXYZ( ) const; - - // Set the x element of a quaternion - // - inline Quat & setX( float x ); - - // Set the y element of a quaternion - // - inline Quat & setY( float y ); - - // Set the z element of a quaternion - // - inline Quat & setZ( float z ); - - // Set the w element of a quaternion - // - inline Quat & setW( float w ); - - // Get the x element of a quaternion - // - inline float getX( ) const; - - // Get the y element of a quaternion - // - inline float getY( ) const; - - // Get the z element of a quaternion - // - inline float getZ( ) const; - - // Get the w element of a quaternion - // - inline float getW( ) const; - - // Set an x, y, z, or w element of a quaternion by index - // - inline Quat & setElem( int idx, float value ); - - // Get an x, y, z, or w element of a quaternion by index - // - inline float getElem( int idx ) const; - - // Subscripting operator to set or get an element - // - inline VecIdx operator []( int idx ); - - // Subscripting operator to get an element - // - inline float operator []( int idx ) const; - - // Add two quaternions - // - inline const Quat operator +( Quat quat ) const; - - // Subtract a quaternion from another quaternion - // - inline const Quat operator -( Quat quat ) const; - - // Multiply two quaternions - // - inline const Quat operator *( Quat quat ) const; - - // Multiply a quaternion by a scalar - // - inline const Quat operator *( float scalar ) const; - - // Divide a quaternion by a scalar - // - inline const Quat operator /( float scalar ) const; - - // Perform compound assignment and addition with a quaternion - // - inline Quat & operator +=( Quat quat ); - - // Perform compound assignment and subtraction by a quaternion - // - inline Quat & operator -=( Quat quat ); - - // Perform compound assignment and multiplication by a quaternion - // - inline Quat & operator *=( Quat quat ); - - // Perform compound assignment and multiplication by a scalar - // - inline Quat & operator *=( float scalar ); - - // Perform compound assignment and division by a scalar - // - inline Quat & operator /=( float scalar ); - - // Negate all elements of a quaternion - // - inline const Quat operator -( ) const; - - // Construct an identity quaternion - // - static inline const Quat identity( ); - - // Construct a quaternion to rotate between two unit-length 3-D vectors - // NOTE: - // The result is unpredictable if unitVec0 and unitVec1 point in opposite directions. - // - static inline const Quat rotation( Vector3 unitVec0, Vector3 unitVec1 ); - - // Construct a quaternion to rotate around a unit-length 3-D vector - // - static inline const Quat rotation( float radians, Vector3 unitVec ); - - // Construct a quaternion to rotate around the x axis - // - static inline const Quat rotationX( float radians ); - - // Construct a quaternion to rotate around the y axis - // - static inline const Quat rotationY( float radians ); - - // Construct a quaternion to rotate around the z axis - // - static inline const Quat rotationZ( float radians ); - -}; - -// Multiply a quaternion by a scalar -// -inline const Quat operator *( float scalar, Quat quat ); - -// Compute the conjugate of a quaternion -// -inline const Quat conj( Quat quat ); - -// Use a unit-length quaternion to rotate a 3-D vector -// -inline const Vector3 rotate( Quat unitQuat, Vector3 vec ); - -// Compute the dot product of two quaternions -// -inline float dot( Quat quat0, Quat quat1 ); - -// Compute the norm of a quaternion -// -inline float norm( Quat quat ); - -// Compute the length of a quaternion -// -inline float length( Quat quat ); - -// Normalize a quaternion -// NOTE: -// The result is unpredictable when all elements of quat are at or near zero. -// -inline const Quat normalize( Quat quat ); - -// Linear interpolation between two quaternions -// NOTE: -// Does not clamp t between 0 and 1. -// -inline const Quat lerp( float t, Quat quat0, Quat quat1 ); - -// Spherical linear interpolation between two quaternions -// NOTE: -// Interpolates along the shortest path between orientations. -// Does not clamp t between 0 and 1. -// -inline const Quat slerp( float t, Quat unitQuat0, Quat unitQuat1 ); - -// Spherical quadrangle interpolation -// -inline const Quat squad( float t, Quat unitQuat0, Quat unitQuat1, Quat unitQuat2, Quat unitQuat3 ); - -// Conditionally select between two quaternions -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// -inline const Quat select( Quat quat0, Quat quat1, bool select1 ); - -#ifdef _VECTORMATH_DEBUG - -// Print a quaternion -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Quat quat ); - -// Print a quaternion and an associated string identifier -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( Quat quat, const char * name ); - -#endif - -// A 3x3 matrix in array-of-structures format -// -class Matrix3 -{ - Vector3 mCol0; - Vector3 mCol1; - Vector3 mCol2; - -public: - // Default constructor; does no initialization - // - inline Matrix3( ) { }; - - // Copy a 3x3 matrix - // - inline Matrix3( const Matrix3 & mat ); - - // Construct a 3x3 matrix containing the specified columns - // - inline Matrix3( Vector3 col0, Vector3 col1, Vector3 col2 ); - - // Construct a 3x3 rotation matrix from a unit-length quaternion - // - explicit inline Matrix3( Quat unitQuat ); - - // Set all elements of a 3x3 matrix to the same scalar value - // - explicit inline Matrix3( float scalar ); - - // Assign one 3x3 matrix to another - // - inline Matrix3 & operator =( const Matrix3 & mat ); - - // Set column 0 of a 3x3 matrix - // - inline Matrix3 & setCol0( Vector3 col0 ); - - // Set column 1 of a 3x3 matrix - // - inline Matrix3 & setCol1( Vector3 col1 ); - - // Set column 2 of a 3x3 matrix - // - inline Matrix3 & setCol2( Vector3 col2 ); - - // Get column 0 of a 3x3 matrix - // - inline const Vector3 getCol0( ) const; - - // Get column 1 of a 3x3 matrix - // - inline const Vector3 getCol1( ) const; - - // Get column 2 of a 3x3 matrix - // - inline const Vector3 getCol2( ) const; - - // Set the column of a 3x3 matrix referred to by the specified index - // - inline Matrix3 & setCol( int col, Vector3 vec ); - - // Set the row of a 3x3 matrix referred to by the specified index - // - inline Matrix3 & setRow( int row, Vector3 vec ); - - // Get the column of a 3x3 matrix referred to by the specified index - // - inline const Vector3 getCol( int col ) const; - - // Get the row of a 3x3 matrix referred to by the specified index - // - inline const Vector3 getRow( int row ) const; - - // Subscripting operator to set or get a column - // - inline Vector3 & operator []( int col ); - - // Subscripting operator to get a column - // - inline const Vector3 operator []( int col ) const; - - // Set the element of a 3x3 matrix referred to by column and row indices - // - inline Matrix3 & setElem( int col, int row, float val ); - - // Get the element of a 3x3 matrix referred to by column and row indices - // - inline float getElem( int col, int row ) const; - - // Add two 3x3 matrices - // - inline const Matrix3 operator +( const Matrix3 & mat ) const; - - // Subtract a 3x3 matrix from another 3x3 matrix - // - inline const Matrix3 operator -( const Matrix3 & mat ) const; - - // Negate all elements of a 3x3 matrix - // - inline const Matrix3 operator -( ) const; - - // Multiply a 3x3 matrix by a scalar - // - inline const Matrix3 operator *( float scalar ) const; - - // Multiply a 3x3 matrix by a 3-D vector - // - inline const Vector3 operator *( Vector3 vec ) const; - - // Multiply two 3x3 matrices - // - inline const Matrix3 operator *( const Matrix3 & mat ) const; - - // Perform compound assignment and addition with a 3x3 matrix - // - inline Matrix3 & operator +=( const Matrix3 & mat ); - - // Perform compound assignment and subtraction by a 3x3 matrix - // - inline Matrix3 & operator -=( const Matrix3 & mat ); - - // Perform compound assignment and multiplication by a scalar - // - inline Matrix3 & operator *=( float scalar ); - - // Perform compound assignment and multiplication by a 3x3 matrix - // - inline Matrix3 & operator *=( const Matrix3 & mat ); - - // Construct an identity 3x3 matrix - // - static inline const Matrix3 identity( ); - - // Construct a 3x3 matrix to rotate around the x axis - // - static inline const Matrix3 rotationX( float radians ); - - // Construct a 3x3 matrix to rotate around the y axis - // - static inline const Matrix3 rotationY( float radians ); - - // Construct a 3x3 matrix to rotate around the z axis - // - static inline const Matrix3 rotationZ( float radians ); - - // Construct a 3x3 matrix to rotate around the x, y, and z axes - // - static inline const Matrix3 rotationZYX( Vector3 radiansXYZ ); - - // Construct a 3x3 matrix to rotate around a unit-length 3-D vector - // - static inline const Matrix3 rotation( float radians, Vector3 unitVec ); - - // Construct a rotation matrix from a unit-length quaternion - // - static inline const Matrix3 rotation( Quat unitQuat ); - - // Construct a 3x3 matrix to perform scaling - // - static inline const Matrix3 scale( Vector3 scaleVec ); - -}; -// Multiply a 3x3 matrix by a scalar -// -inline const Matrix3 operator *( float scalar, const Matrix3 & mat ); - -// Append (post-multiply) a scale transformation to a 3x3 matrix -// NOTE: -// Faster than creating and multiplying a scale transformation matrix. -// -inline const Matrix3 appendScale( const Matrix3 & mat, Vector3 scaleVec ); - -// Prepend (pre-multiply) a scale transformation to a 3x3 matrix -// NOTE: -// Faster than creating and multiplying a scale transformation matrix. -// -inline const Matrix3 prependScale( Vector3 scaleVec, const Matrix3 & mat ); - -// Multiply two 3x3 matrices per element -// -inline const Matrix3 mulPerElem( const Matrix3 & mat0, const Matrix3 & mat1 ); - -// Compute the absolute value of a 3x3 matrix per element -// -inline const Matrix3 absPerElem( const Matrix3 & mat ); - -// Transpose of a 3x3 matrix -// -inline const Matrix3 transpose( const Matrix3 & mat ); - -// Compute the inverse of a 3x3 matrix -// NOTE: -// Result is unpredictable when the determinant of mat is equal to or near 0. -// -inline const Matrix3 inverse( const Matrix3 & mat ); - -// Determinant of a 3x3 matrix -// -inline float determinant( const Matrix3 & mat ); - -// Conditionally select between two 3x3 matrices -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// -inline const Matrix3 select( const Matrix3 & mat0, const Matrix3 & mat1, bool select1 ); - -#ifdef _VECTORMATH_DEBUG - -// Print a 3x3 matrix -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( const Matrix3 & mat ); - -// Print a 3x3 matrix and an associated string identifier -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( const Matrix3 & mat, const char * name ); - -#endif - -// A 4x4 matrix in array-of-structures format -// -class Matrix4 -{ - Vector4 mCol0; - Vector4 mCol1; - Vector4 mCol2; - Vector4 mCol3; - -public: - // Default constructor; does no initialization - // - inline Matrix4( ) { }; - - // Copy a 4x4 matrix - // - inline Matrix4( const Matrix4 & mat ); - - // Construct a 4x4 matrix containing the specified columns - // - inline Matrix4( Vector4 col0, Vector4 col1, Vector4 col2, Vector4 col3 ); - - // Construct a 4x4 matrix from a 3x4 transformation matrix - // - explicit inline Matrix4( const Transform3 & mat ); - - // Construct a 4x4 matrix from a 3x3 matrix and a 3-D vector - // - inline Matrix4( const Matrix3 & mat, Vector3 translateVec ); - - // Construct a 4x4 matrix from a unit-length quaternion and a 3-D vector - // - inline Matrix4( Quat unitQuat, Vector3 translateVec ); - - // Set all elements of a 4x4 matrix to the same scalar value - // - explicit inline Matrix4( float scalar ); - - // Assign one 4x4 matrix to another - // - inline Matrix4 & operator =( const Matrix4 & mat ); - - // Set the upper-left 3x3 submatrix - // NOTE: - // This function does not change the bottom row elements. - // - inline Matrix4 & setUpper3x3( const Matrix3 & mat3 ); - - // Get the upper-left 3x3 submatrix of a 4x4 matrix - // - inline const Matrix3 getUpper3x3( ) const; - - // Set translation component - // NOTE: - // This function does not change the bottom row elements. - // - inline Matrix4 & setTranslation( Vector3 translateVec ); - - // Get the translation component of a 4x4 matrix - // - inline const Vector3 getTranslation( ) const; - - // Set column 0 of a 4x4 matrix - // - inline Matrix4 & setCol0( Vector4 col0 ); - - // Set column 1 of a 4x4 matrix - // - inline Matrix4 & setCol1( Vector4 col1 ); - - // Set column 2 of a 4x4 matrix - // - inline Matrix4 & setCol2( Vector4 col2 ); - - // Set column 3 of a 4x4 matrix - // - inline Matrix4 & setCol3( Vector4 col3 ); - - // Get column 0 of a 4x4 matrix - // - inline const Vector4 getCol0( ) const; - - // Get column 1 of a 4x4 matrix - // - inline const Vector4 getCol1( ) const; - - // Get column 2 of a 4x4 matrix - // - inline const Vector4 getCol2( ) const; - - // Get column 3 of a 4x4 matrix - // - inline const Vector4 getCol3( ) const; - - // Set the column of a 4x4 matrix referred to by the specified index - // - inline Matrix4 & setCol( int col, Vector4 vec ); - - // Set the row of a 4x4 matrix referred to by the specified index - // - inline Matrix4 & setRow( int row, Vector4 vec ); - - // Get the column of a 4x4 matrix referred to by the specified index - // - inline const Vector4 getCol( int col ) const; - - // Get the row of a 4x4 matrix referred to by the specified index - // - inline const Vector4 getRow( int row ) const; - - // Subscripting operator to set or get a column - // - inline Vector4 & operator []( int col ); - - // Subscripting operator to get a column - // - inline const Vector4 operator []( int col ) const; - - // Set the element of a 4x4 matrix referred to by column and row indices - // - inline Matrix4 & setElem( int col, int row, float val ); - - // Get the element of a 4x4 matrix referred to by column and row indices - // - inline float getElem( int col, int row ) const; - - // Add two 4x4 matrices - // - inline const Matrix4 operator +( const Matrix4 & mat ) const; - - // Subtract a 4x4 matrix from another 4x4 matrix - // - inline const Matrix4 operator -( const Matrix4 & mat ) const; - - // Negate all elements of a 4x4 matrix - // - inline const Matrix4 operator -( ) const; - - // Multiply a 4x4 matrix by a scalar - // - inline const Matrix4 operator *( float scalar ) const; - - // Multiply a 4x4 matrix by a 4-D vector - // - inline const Vector4 operator *( Vector4 vec ) const; - - // Multiply a 4x4 matrix by a 3-D vector - // - inline const Vector4 operator *( Vector3 vec ) const; - - // Multiply a 4x4 matrix by a 3-D point - // - inline const Vector4 operator *( Point3 pnt ) const; - - // Multiply two 4x4 matrices - // - inline const Matrix4 operator *( const Matrix4 & mat ) const; - - // Multiply a 4x4 matrix by a 3x4 transformation matrix - // - inline const Matrix4 operator *( const Transform3 & tfrm ) const; - - // Perform compound assignment and addition with a 4x4 matrix - // - inline Matrix4 & operator +=( const Matrix4 & mat ); - - // Perform compound assignment and subtraction by a 4x4 matrix - // - inline Matrix4 & operator -=( const Matrix4 & mat ); - - // Perform compound assignment and multiplication by a scalar - // - inline Matrix4 & operator *=( float scalar ); - - // Perform compound assignment and multiplication by a 4x4 matrix - // - inline Matrix4 & operator *=( const Matrix4 & mat ); - - // Perform compound assignment and multiplication by a 3x4 transformation matrix - // - inline Matrix4 & operator *=( const Transform3 & tfrm ); - - // Construct an identity 4x4 matrix - // - static inline const Matrix4 identity( ); - - // Construct a 4x4 matrix to rotate around the x axis - // - static inline const Matrix4 rotationX( float radians ); - - // Construct a 4x4 matrix to rotate around the y axis - // - static inline const Matrix4 rotationY( float radians ); - - // Construct a 4x4 matrix to rotate around the z axis - // - static inline const Matrix4 rotationZ( float radians ); - - // Construct a 4x4 matrix to rotate around the x, y, and z axes - // - static inline const Matrix4 rotationZYX( Vector3 radiansXYZ ); - - // Construct a 4x4 matrix to rotate around a unit-length 3-D vector - // - static inline const Matrix4 rotation( float radians, Vector3 unitVec ); - - // Construct a rotation matrix from a unit-length quaternion - // - static inline const Matrix4 rotation( Quat unitQuat ); - - // Construct a 4x4 matrix to perform scaling - // - static inline const Matrix4 scale( Vector3 scaleVec ); - - // Construct a 4x4 matrix to perform translation - // - static inline const Matrix4 translation( Vector3 translateVec ); - - // Construct viewing matrix based on eye position, position looked at, and up direction - // - static inline const Matrix4 lookAt( Point3 eyePos, Point3 lookAtPos, Vector3 upVec ); - - // Construct a perspective projection matrix - // - static inline const Matrix4 perspective( float fovyRadians, float aspect, float zNear, float zFar ); - - // Construct a perspective projection matrix based on frustum - // - static inline const Matrix4 frustum( float left, float right, float bottom, float top, float zNear, float zFar ); - - // Construct an orthographic projection matrix - // - static inline const Matrix4 orthographic( float left, float right, float bottom, float top, float zNear, float zFar ); - -}; -// Multiply a 4x4 matrix by a scalar -// -inline const Matrix4 operator *( float scalar, const Matrix4 & mat ); - -// Append (post-multiply) a scale transformation to a 4x4 matrix -// NOTE: -// Faster than creating and multiplying a scale transformation matrix. -// -inline const Matrix4 appendScale( const Matrix4 & mat, Vector3 scaleVec ); - -// Prepend (pre-multiply) a scale transformation to a 4x4 matrix -// NOTE: -// Faster than creating and multiplying a scale transformation matrix. -// -inline const Matrix4 prependScale( Vector3 scaleVec, const Matrix4 & mat ); - -// Multiply two 4x4 matrices per element -// -inline const Matrix4 mulPerElem( const Matrix4 & mat0, const Matrix4 & mat1 ); - -// Compute the absolute value of a 4x4 matrix per element -// -inline const Matrix4 absPerElem( const Matrix4 & mat ); - -// Transpose of a 4x4 matrix -// -inline const Matrix4 transpose( const Matrix4 & mat ); - -// Compute the inverse of a 4x4 matrix -// NOTE: -// Result is unpredictable when the determinant of mat is equal to or near 0. -// -inline const Matrix4 inverse( const Matrix4 & mat ); - -// Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix -// NOTE: -// This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. The result is unpredictable when the determinant of mat is equal to or near 0. -// -inline const Matrix4 affineInverse( const Matrix4 & mat ); - -// Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix with an orthogonal upper-left 3x3 submatrix -// NOTE: -// This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. -// -inline const Matrix4 orthoInverse( const Matrix4 & mat ); - -// Determinant of a 4x4 matrix -// -inline float determinant( const Matrix4 & mat ); - -// Conditionally select between two 4x4 matrices -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// -inline const Matrix4 select( const Matrix4 & mat0, const Matrix4 & mat1, bool select1 ); - -#ifdef _VECTORMATH_DEBUG - -// Print a 4x4 matrix -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( const Matrix4 & mat ); - -// Print a 4x4 matrix and an associated string identifier -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( const Matrix4 & mat, const char * name ); - -#endif - -// A 3x4 transformation matrix in array-of-structures format -// -class Transform3 -{ - Vector3 mCol0; - Vector3 mCol1; - Vector3 mCol2; - Vector3 mCol3; - -public: - // Default constructor; does no initialization - // - inline Transform3( ) { }; - - // Copy a 3x4 transformation matrix - // - inline Transform3( const Transform3 & tfrm ); - - // Construct a 3x4 transformation matrix containing the specified columns - // - inline Transform3( Vector3 col0, Vector3 col1, Vector3 col2, Vector3 col3 ); - - // Construct a 3x4 transformation matrix from a 3x3 matrix and a 3-D vector - // - inline Transform3( const Matrix3 & tfrm, Vector3 translateVec ); - - // Construct a 3x4 transformation matrix from a unit-length quaternion and a 3-D vector - // - inline Transform3( Quat unitQuat, Vector3 translateVec ); - - // Set all elements of a 3x4 transformation matrix to the same scalar value - // - explicit inline Transform3( float scalar ); - - // Assign one 3x4 transformation matrix to another - // - inline Transform3 & operator =( const Transform3 & tfrm ); - - // Set the upper-left 3x3 submatrix - // - inline Transform3 & setUpper3x3( const Matrix3 & mat3 ); - - // Get the upper-left 3x3 submatrix of a 3x4 transformation matrix - // - inline const Matrix3 getUpper3x3( ) const; - - // Set translation component - // - inline Transform3 & setTranslation( Vector3 translateVec ); - - // Get the translation component of a 3x4 transformation matrix - // - inline const Vector3 getTranslation( ) const; - - // Set column 0 of a 3x4 transformation matrix - // - inline Transform3 & setCol0( Vector3 col0 ); - - // Set column 1 of a 3x4 transformation matrix - // - inline Transform3 & setCol1( Vector3 col1 ); - - // Set column 2 of a 3x4 transformation matrix - // - inline Transform3 & setCol2( Vector3 col2 ); - - // Set column 3 of a 3x4 transformation matrix - // - inline Transform3 & setCol3( Vector3 col3 ); - - // Get column 0 of a 3x4 transformation matrix - // - inline const Vector3 getCol0( ) const; - - // Get column 1 of a 3x4 transformation matrix - // - inline const Vector3 getCol1( ) const; - - // Get column 2 of a 3x4 transformation matrix - // - inline const Vector3 getCol2( ) const; - - // Get column 3 of a 3x4 transformation matrix - // - inline const Vector3 getCol3( ) const; - - // Set the column of a 3x4 transformation matrix referred to by the specified index - // - inline Transform3 & setCol( int col, Vector3 vec ); - - // Set the row of a 3x4 transformation matrix referred to by the specified index - // - inline Transform3 & setRow( int row, Vector4 vec ); - - // Get the column of a 3x4 transformation matrix referred to by the specified index - // - inline const Vector3 getCol( int col ) const; - - // Get the row of a 3x4 transformation matrix referred to by the specified index - // - inline const Vector4 getRow( int row ) const; - - // Subscripting operator to set or get a column - // - inline Vector3 & operator []( int col ); - - // Subscripting operator to get a column - // - inline const Vector3 operator []( int col ) const; - - // Set the element of a 3x4 transformation matrix referred to by column and row indices - // - inline Transform3 & setElem( int col, int row, float val ); - - // Get the element of a 3x4 transformation matrix referred to by column and row indices - // - inline float getElem( int col, int row ) const; - - // Multiply a 3x4 transformation matrix by a 3-D vector - // - inline const Vector3 operator *( Vector3 vec ) const; - - // Multiply a 3x4 transformation matrix by a 3-D point - // - inline const Point3 operator *( Point3 pnt ) const; - - // Multiply two 3x4 transformation matrices - // - inline const Transform3 operator *( const Transform3 & tfrm ) const; - - // Perform compound assignment and multiplication by a 3x4 transformation matrix - // - inline Transform3 & operator *=( const Transform3 & tfrm ); - - // Construct an identity 3x4 transformation matrix - // - static inline const Transform3 identity( ); - - // Construct a 3x4 transformation matrix to rotate around the x axis - // - static inline const Transform3 rotationX( float radians ); - - // Construct a 3x4 transformation matrix to rotate around the y axis - // - static inline const Transform3 rotationY( float radians ); - - // Construct a 3x4 transformation matrix to rotate around the z axis - // - static inline const Transform3 rotationZ( float radians ); - - // Construct a 3x4 transformation matrix to rotate around the x, y, and z axes - // - static inline const Transform3 rotationZYX( Vector3 radiansXYZ ); - - // Construct a 3x4 transformation matrix to rotate around a unit-length 3-D vector - // - static inline const Transform3 rotation( float radians, Vector3 unitVec ); - - // Construct a rotation matrix from a unit-length quaternion - // - static inline const Transform3 rotation( Quat unitQuat ); - - // Construct a 3x4 transformation matrix to perform scaling - // - static inline const Transform3 scale( Vector3 scaleVec ); - - // Construct a 3x4 transformation matrix to perform translation - // - static inline const Transform3 translation( Vector3 translateVec ); - -}; -// Append (post-multiply) a scale transformation to a 3x4 transformation matrix -// NOTE: -// Faster than creating and multiplying a scale transformation matrix. -// -inline const Transform3 appendScale( const Transform3 & tfrm, Vector3 scaleVec ); - -// Prepend (pre-multiply) a scale transformation to a 3x4 transformation matrix -// NOTE: -// Faster than creating and multiplying a scale transformation matrix. -// -inline const Transform3 prependScale( Vector3 scaleVec, const Transform3 & tfrm ); - -// Multiply two 3x4 transformation matrices per element -// -inline const Transform3 mulPerElem( const Transform3 & tfrm0, const Transform3 & tfrm1 ); - -// Compute the absolute value of a 3x4 transformation matrix per element -// -inline const Transform3 absPerElem( const Transform3 & tfrm ); - -// Inverse of a 3x4 transformation matrix -// NOTE: -// Result is unpredictable when the determinant of the left 3x3 submatrix is equal to or near 0. -// -inline const Transform3 inverse( const Transform3 & tfrm ); - -// Compute the inverse of a 3x4 transformation matrix, expected to have an orthogonal upper-left 3x3 submatrix -// NOTE: -// This can be used to achieve better performance than a general inverse when the specified 3x4 transformation matrix meets the given restrictions. -// -inline const Transform3 orthoInverse( const Transform3 & tfrm ); - -// Conditionally select between two 3x4 transformation matrices -// NOTE: -// This function uses a conditional select instruction to avoid a branch. -// -inline const Transform3 select( const Transform3 & tfrm0, const Transform3 & tfrm1, bool select1 ); - -#ifdef _VECTORMATH_DEBUG - -// Print a 3x4 transformation matrix -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( const Transform3 & tfrm ); - -// Print a 3x4 transformation matrix and an associated string identifier -// NOTE: -// Function is only defined when _VECTORMATH_DEBUG is defined. -// -inline void print( const Transform3 & tfrm, const char * name ); - -#endif - -} // namespace Aos -} // namespace Vectormath - -#include "vec_aos.h" -#include "quat_aos.h" -#include "mat_aos.h" - -#endif +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_AOS_CPP_SPU_H +#define _VECTORMATH_AOS_CPP_SPU_H + +#include +#include +#include "floatInVec.h" +#include "boolInVec.h" +#include "vecidx_aos.h" +#include + +#ifdef _VECTORMATH_DEBUG +#endif + +namespace Vectormath { + +namespace Aos { + +//----------------------------------------------------------------------------- +// Forward Declarations +// + +class Vector3; +class Vector4; +class Point3; +class Quat; +class Matrix3; +class Matrix4; +class Transform3; + +// A 3-D vector in array-of-structures format +// +class Vector3 +{ + vec_float4 mVec128; + +public: + // Default constructor; does no initialization + // + inline Vector3( ) { }; + + // Construct a 3-D vector from x, y, and z elements + // + inline Vector3( float x, float y, float z ); + + // Copy elements from a 3-D point into a 3-D vector + // + explicit inline Vector3( Point3 pnt ); + + // Set all elements of a 3-D vector to the same scalar value + // + explicit inline Vector3( float scalar ); + + // Set vector float data in a 3-D vector + // + explicit inline Vector3( vec_float4 vf4 ); + + // Get vector float data from a 3-D vector + // + inline vec_float4 get128( ) const; + + // Assign one 3-D vector to another + // + inline Vector3 & operator =( Vector3 vec ); + + // Set the x element of a 3-D vector + // + inline Vector3 & setX( float x ); + + // Set the y element of a 3-D vector + // + inline Vector3 & setY( float y ); + + // Set the z element of a 3-D vector + // + inline Vector3 & setZ( float z ); + + // Get the x element of a 3-D vector + // + inline float getX( ) const; + + // Get the y element of a 3-D vector + // + inline float getY( ) const; + + // Get the z element of a 3-D vector + // + inline float getZ( ) const; + + // Set an x, y, or z element of a 3-D vector by index + // + inline Vector3 & setElem( int idx, float value ); + + // Get an x, y, or z element of a 3-D vector by index + // + inline float getElem( int idx ) const; + + // Subscripting operator to set or get an element + // + inline VecIdx operator []( int idx ); + + // Subscripting operator to get an element + // + inline float operator []( int idx ) const; + + // Add two 3-D vectors + // + inline const Vector3 operator +( Vector3 vec ) const; + + // Subtract a 3-D vector from another 3-D vector + // + inline const Vector3 operator -( Vector3 vec ) const; + + // Add a 3-D vector to a 3-D point + // + inline const Point3 operator +( Point3 pnt ) const; + + // Multiply a 3-D vector by a scalar + // + inline const Vector3 operator *( float scalar ) const; + + // Divide a 3-D vector by a scalar + // + inline const Vector3 operator /( float scalar ) const; + + // Perform compound assignment and addition with a 3-D vector + // + inline Vector3 & operator +=( Vector3 vec ); + + // Perform compound assignment and subtraction by a 3-D vector + // + inline Vector3 & operator -=( Vector3 vec ); + + // Perform compound assignment and multiplication by a scalar + // + inline Vector3 & operator *=( float scalar ); + + // Perform compound assignment and division by a scalar + // + inline Vector3 & operator /=( float scalar ); + + // Negate all elements of a 3-D vector + // + inline const Vector3 operator -( ) const; + + // Construct x axis + // + static inline const Vector3 xAxis( ); + + // Construct y axis + // + static inline const Vector3 yAxis( ); + + // Construct z axis + // + static inline const Vector3 zAxis( ); + +}; + +// Multiply a 3-D vector by a scalar +// +inline const Vector3 operator *( float scalar, Vector3 vec ); + +// Multiply two 3-D vectors per element +// +inline const Vector3 mulPerElem( Vector3 vec0, Vector3 vec1 ); + +// Divide two 3-D vectors per element +// NOTE: +// Floating-point behavior matches standard library function divf4. +// +inline const Vector3 divPerElem( Vector3 vec0, Vector3 vec1 ); + +// Compute the reciprocal of a 3-D vector per element +// NOTE: +// Floating-point behavior matches standard library function recipf4. +// +inline const Vector3 recipPerElem( Vector3 vec ); + +// Compute the square root of a 3-D vector per element +// NOTE: +// Floating-point behavior matches standard library function sqrtf4. +// +inline const Vector3 sqrtPerElem( Vector3 vec ); + +// Compute the reciprocal square root of a 3-D vector per element +// NOTE: +// Floating-point behavior matches standard library function rsqrtf4. +// +inline const Vector3 rsqrtPerElem( Vector3 vec ); + +// Compute the absolute value of a 3-D vector per element +// +inline const Vector3 absPerElem( Vector3 vec ); + +// Copy sign from one 3-D vector to another, per element +// +inline const Vector3 copySignPerElem( Vector3 vec0, Vector3 vec1 ); + +// Maximum of two 3-D vectors per element +// +inline const Vector3 maxPerElem( Vector3 vec0, Vector3 vec1 ); + +// Minimum of two 3-D vectors per element +// +inline const Vector3 minPerElem( Vector3 vec0, Vector3 vec1 ); + +// Maximum element of a 3-D vector +// +inline float maxElem( Vector3 vec ); + +// Minimum element of a 3-D vector +// +inline float minElem( Vector3 vec ); + +// Compute the sum of all elements of a 3-D vector +// +inline float sum( Vector3 vec ); + +// Compute the dot product of two 3-D vectors +// +inline float dot( Vector3 vec0, Vector3 vec1 ); + +// Compute the square of the length of a 3-D vector +// +inline float lengthSqr( Vector3 vec ); + +// Compute the length of a 3-D vector +// +inline float length( Vector3 vec ); + +// Normalize a 3-D vector +// NOTE: +// The result is unpredictable when all elements of vec are at or near zero. +// +inline const Vector3 normalize( Vector3 vec ); + +// Compute cross product of two 3-D vectors +// +inline const Vector3 cross( Vector3 vec0, Vector3 vec1 ); + +// Outer product of two 3-D vectors +// +inline const Matrix3 outer( Vector3 vec0, Vector3 vec1 ); + +// Pre-multiply a row vector by a 3x3 matrix +// NOTE: +// Slower than column post-multiply. +// +inline const Vector3 rowMul( Vector3 vec, const Matrix3 & mat ); + +// Cross-product matrix of a 3-D vector +// +inline const Matrix3 crossMatrix( Vector3 vec ); + +// Create cross-product matrix and multiply +// NOTE: +// Faster than separately creating a cross-product matrix and multiplying. +// +inline const Matrix3 crossMatrixMul( Vector3 vec, const Matrix3 & mat ); + +// Linear interpolation between two 3-D vectors +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Vector3 lerp( float t, Vector3 vec0, Vector3 vec1 ); + +// Spherical linear interpolation between two 3-D vectors +// NOTE: +// The result is unpredictable if the vectors point in opposite directions. +// Does not clamp t between 0 and 1. +// +inline const Vector3 slerp( float t, Vector3 unitVec0, Vector3 unitVec1 ); + +// Conditionally select between two 3-D vectors +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// +inline const Vector3 select( Vector3 vec0, Vector3 vec1, bool select1 ); + +// Load x, y, and z elements from the first three words of a quadword. +// +// +inline void loadXYZ( Vector3 & vec, const vec_float4 * quad ); + +// Load x, y, and z elements from the first three words of a float array. +// +// +inline void loadXYZ( Vector3 & vec, const float * fptr ); + +// Store x, y, and z elements of a 3-D vector in the first three words of a quadword. +// The value of the fourth word (the word with the highest address) remains unchanged +// +inline void storeXYZ( Vector3 vec, vec_float4 * quad ); + +// Store x, y, and z elements of a 3-D vector in the first three words of a float array. +// Memory area of previous 16 bytes and next 32 bytes from fptr might be accessed +// +inline void storeXYZ( Vector3 vec, float * fptr ); + +// Load four three-float 3-D vectors, stored in three quadwords +// +inline void loadXYZArray( Vector3 & vec0, Vector3 & vec1, Vector3 & vec2, Vector3 & vec3, const vec_float4 * threeQuads ); + +// Store four 3-D vectors in three quadwords +// +inline void storeXYZArray( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, vec_float4 * threeQuads ); + +// Store eight 3-D vectors as half-floats +// +inline void storeHalfFloats( Vector3 vec0, Vector3 vec1, Vector3 vec2, Vector3 vec3, Vector3 vec4, Vector3 vec5, Vector3 vec6, Vector3 vec7, vec_ushort8 * threeQuads ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 3-D vector +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Vector3 vec ); + +// Print a 3-D vector and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Vector3 vec, const char * name ); + +#endif + +// A 4-D vector in array-of-structures format +// +class Vector4 +{ + vec_float4 mVec128; + +public: + // Default constructor; does no initialization + // + inline Vector4( ) { }; + + // Construct a 4-D vector from x, y, z, and w elements + // + inline Vector4( float x, float y, float z, float w ); + + // Construct a 4-D vector from a 3-D vector and a scalar + // + inline Vector4( Vector3 xyz, float w ); + + // Copy x, y, and z from a 3-D vector into a 4-D vector, and set w to 0 + // + explicit inline Vector4( Vector3 vec ); + + // Copy x, y, and z from a 3-D point into a 4-D vector, and set w to 1 + // + explicit inline Vector4( Point3 pnt ); + + // Copy elements from a quaternion into a 4-D vector + // + explicit inline Vector4( Quat quat ); + + // Set all elements of a 4-D vector to the same scalar value + // + explicit inline Vector4( float scalar ); + + // Set vector float data in a 4-D vector + // + explicit inline Vector4( vec_float4 vf4 ); + + // Get vector float data from a 4-D vector + // + inline vec_float4 get128( ) const; + + // Assign one 4-D vector to another + // + inline Vector4 & operator =( Vector4 vec ); + + // Set the x, y, and z elements of a 4-D vector + // NOTE: + // This function does not change the w element. + // + inline Vector4 & setXYZ( Vector3 vec ); + + // Get the x, y, and z elements of a 4-D vector + // + inline const Vector3 getXYZ( ) const; + + // Set the x element of a 4-D vector + // + inline Vector4 & setX( float x ); + + // Set the y element of a 4-D vector + // + inline Vector4 & setY( float y ); + + // Set the z element of a 4-D vector + // + inline Vector4 & setZ( float z ); + + // Set the w element of a 4-D vector + // + inline Vector4 & setW( float w ); + + // Get the x element of a 4-D vector + // + inline float getX( ) const; + + // Get the y element of a 4-D vector + // + inline float getY( ) const; + + // Get the z element of a 4-D vector + // + inline float getZ( ) const; + + // Get the w element of a 4-D vector + // + inline float getW( ) const; + + // Set an x, y, z, or w element of a 4-D vector by index + // + inline Vector4 & setElem( int idx, float value ); + + // Get an x, y, z, or w element of a 4-D vector by index + // + inline float getElem( int idx ) const; + + // Subscripting operator to set or get an element + // + inline VecIdx operator []( int idx ); + + // Subscripting operator to get an element + // + inline float operator []( int idx ) const; + + // Add two 4-D vectors + // + inline const Vector4 operator +( Vector4 vec ) const; + + // Subtract a 4-D vector from another 4-D vector + // + inline const Vector4 operator -( Vector4 vec ) const; + + // Multiply a 4-D vector by a scalar + // + inline const Vector4 operator *( float scalar ) const; + + // Divide a 4-D vector by a scalar + // + inline const Vector4 operator /( float scalar ) const; + + // Perform compound assignment and addition with a 4-D vector + // + inline Vector4 & operator +=( Vector4 vec ); + + // Perform compound assignment and subtraction by a 4-D vector + // + inline Vector4 & operator -=( Vector4 vec ); + + // Perform compound assignment and multiplication by a scalar + // + inline Vector4 & operator *=( float scalar ); + + // Perform compound assignment and division by a scalar + // + inline Vector4 & operator /=( float scalar ); + + // Negate all elements of a 4-D vector + // + inline const Vector4 operator -( ) const; + + // Construct x axis + // + static inline const Vector4 xAxis( ); + + // Construct y axis + // + static inline const Vector4 yAxis( ); + + // Construct z axis + // + static inline const Vector4 zAxis( ); + + // Construct w axis + // + static inline const Vector4 wAxis( ); + +}; + +// Multiply a 4-D vector by a scalar +// +inline const Vector4 operator *( float scalar, Vector4 vec ); + +// Multiply two 4-D vectors per element +// +inline const Vector4 mulPerElem( Vector4 vec0, Vector4 vec1 ); + +// Divide two 4-D vectors per element +// NOTE: +// Floating-point behavior matches standard library function divf4. +// +inline const Vector4 divPerElem( Vector4 vec0, Vector4 vec1 ); + +// Compute the reciprocal of a 4-D vector per element +// NOTE: +// Floating-point behavior matches standard library function recipf4. +// +inline const Vector4 recipPerElem( Vector4 vec ); + +// Compute the square root of a 4-D vector per element +// NOTE: +// Floating-point behavior matches standard library function sqrtf4. +// +inline const Vector4 sqrtPerElem( Vector4 vec ); + +// Compute the reciprocal square root of a 4-D vector per element +// NOTE: +// Floating-point behavior matches standard library function rsqrtf4. +// +inline const Vector4 rsqrtPerElem( Vector4 vec ); + +// Compute the absolute value of a 4-D vector per element +// +inline const Vector4 absPerElem( Vector4 vec ); + +// Copy sign from one 4-D vector to another, per element +// +inline const Vector4 copySignPerElem( Vector4 vec0, Vector4 vec1 ); + +// Maximum of two 4-D vectors per element +// +inline const Vector4 maxPerElem( Vector4 vec0, Vector4 vec1 ); + +// Minimum of two 4-D vectors per element +// +inline const Vector4 minPerElem( Vector4 vec0, Vector4 vec1 ); + +// Maximum element of a 4-D vector +// +inline float maxElem( Vector4 vec ); + +// Minimum element of a 4-D vector +// +inline float minElem( Vector4 vec ); + +// Compute the sum of all elements of a 4-D vector +// +inline float sum( Vector4 vec ); + +// Compute the dot product of two 4-D vectors +// +inline float dot( Vector4 vec0, Vector4 vec1 ); + +// Compute the square of the length of a 4-D vector +// +inline float lengthSqr( Vector4 vec ); + +// Compute the length of a 4-D vector +// +inline float length( Vector4 vec ); + +// Normalize a 4-D vector +// NOTE: +// The result is unpredictable when all elements of vec are at or near zero. +// +inline const Vector4 normalize( Vector4 vec ); + +// Outer product of two 4-D vectors +// +inline const Matrix4 outer( Vector4 vec0, Vector4 vec1 ); + +// Linear interpolation between two 4-D vectors +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Vector4 lerp( float t, Vector4 vec0, Vector4 vec1 ); + +// Spherical linear interpolation between two 4-D vectors +// NOTE: +// The result is unpredictable if the vectors point in opposite directions. +// Does not clamp t between 0 and 1. +// +inline const Vector4 slerp( float t, Vector4 unitVec0, Vector4 unitVec1 ); + +// Conditionally select between two 4-D vectors +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// +inline const Vector4 select( Vector4 vec0, Vector4 vec1, bool select1 ); + +// Load x, y, z, and w elements from the first four words of a float array. +// +// +inline void loadXYZW( Vector4 & vec, const float * fptr ); + +// Store x, y, z, and w elements of a 4-D vector in the first four words of a float array. +// Memory area of previous 16 bytes and next 32 bytes from fptr might be accessed +// +inline void storeXYZW( Vector4 vec, float * fptr ); + +// Store four 4-D vectors as half-floats +// +inline void storeHalfFloats( Vector4 vec0, Vector4 vec1, Vector4 vec2, Vector4 vec3, vec_ushort8 * twoQuads ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 4-D vector +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Vector4 vec ); + +// Print a 4-D vector and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Vector4 vec, const char * name ); + +#endif + +// A 3-D point in array-of-structures format +// +class Point3 +{ + vec_float4 mVec128; + +public: + // Default constructor; does no initialization + // + inline Point3( ) { }; + + // Construct a 3-D point from x, y, and z elements + // + inline Point3( float x, float y, float z ); + + // Copy elements from a 3-D vector into a 3-D point + // + explicit inline Point3( Vector3 vec ); + + // Set all elements of a 3-D point to the same scalar value + // + explicit inline Point3( float scalar ); + + // Set vector float data in a 3-D point + // + explicit inline Point3( vec_float4 vf4 ); + + // Get vector float data from a 3-D point + // + inline vec_float4 get128( ) const; + + // Assign one 3-D point to another + // + inline Point3 & operator =( Point3 pnt ); + + // Set the x element of a 3-D point + // + inline Point3 & setX( float x ); + + // Set the y element of a 3-D point + // + inline Point3 & setY( float y ); + + // Set the z element of a 3-D point + // + inline Point3 & setZ( float z ); + + // Get the x element of a 3-D point + // + inline float getX( ) const; + + // Get the y element of a 3-D point + // + inline float getY( ) const; + + // Get the z element of a 3-D point + // + inline float getZ( ) const; + + // Set an x, y, or z element of a 3-D point by index + // + inline Point3 & setElem( int idx, float value ); + + // Get an x, y, or z element of a 3-D point by index + // + inline float getElem( int idx ) const; + + // Subscripting operator to set or get an element + // + inline VecIdx operator []( int idx ); + + // Subscripting operator to get an element + // + inline float operator []( int idx ) const; + + // Subtract a 3-D point from another 3-D point + // + inline const Vector3 operator -( Point3 pnt ) const; + + // Add a 3-D point to a 3-D vector + // + inline const Point3 operator +( Vector3 vec ) const; + + // Subtract a 3-D vector from a 3-D point + // + inline const Point3 operator -( Vector3 vec ) const; + + // Perform compound assignment and addition with a 3-D vector + // + inline Point3 & operator +=( Vector3 vec ); + + // Perform compound assignment and subtraction by a 3-D vector + // + inline Point3 & operator -=( Vector3 vec ); + +}; + +// Multiply two 3-D points per element +// +inline const Point3 mulPerElem( Point3 pnt0, Point3 pnt1 ); + +// Divide two 3-D points per element +// NOTE: +// Floating-point behavior matches standard library function divf4. +// +inline const Point3 divPerElem( Point3 pnt0, Point3 pnt1 ); + +// Compute the reciprocal of a 3-D point per element +// NOTE: +// Floating-point behavior matches standard library function recipf4. +// +inline const Point3 recipPerElem( Point3 pnt ); + +// Compute the square root of a 3-D point per element +// NOTE: +// Floating-point behavior matches standard library function sqrtf4. +// +inline const Point3 sqrtPerElem( Point3 pnt ); + +// Compute the reciprocal square root of a 3-D point per element +// NOTE: +// Floating-point behavior matches standard library function rsqrtf4. +// +inline const Point3 rsqrtPerElem( Point3 pnt ); + +// Compute the absolute value of a 3-D point per element +// +inline const Point3 absPerElem( Point3 pnt ); + +// Copy sign from one 3-D point to another, per element +// +inline const Point3 copySignPerElem( Point3 pnt0, Point3 pnt1 ); + +// Maximum of two 3-D points per element +// +inline const Point3 maxPerElem( Point3 pnt0, Point3 pnt1 ); + +// Minimum of two 3-D points per element +// +inline const Point3 minPerElem( Point3 pnt0, Point3 pnt1 ); + +// Maximum element of a 3-D point +// +inline float maxElem( Point3 pnt ); + +// Minimum element of a 3-D point +// +inline float minElem( Point3 pnt ); + +// Compute the sum of all elements of a 3-D point +// +inline float sum( Point3 pnt ); + +// Apply uniform scale to a 3-D point +// +inline const Point3 scale( Point3 pnt, float scaleVal ); + +// Apply non-uniform scale to a 3-D point +// +inline const Point3 scale( Point3 pnt, Vector3 scaleVec ); + +// Scalar projection of a 3-D point on a unit-length 3-D vector +// +inline float projection( Point3 pnt, Vector3 unitVec ); + +// Compute the square of the distance of a 3-D point from the coordinate-system origin +// +inline float distSqrFromOrigin( Point3 pnt ); + +// Compute the distance of a 3-D point from the coordinate-system origin +// +inline float distFromOrigin( Point3 pnt ); + +// Compute the square of the distance between two 3-D points +// +inline float distSqr( Point3 pnt0, Point3 pnt1 ); + +// Compute the distance between two 3-D points +// +inline float dist( Point3 pnt0, Point3 pnt1 ); + +// Linear interpolation between two 3-D points +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Point3 lerp( float t, Point3 pnt0, Point3 pnt1 ); + +// Conditionally select between two 3-D points +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// +inline const Point3 select( Point3 pnt0, Point3 pnt1, bool select1 ); + +// Load x, y, and z elements from the first three words of a quadword. +// +// +inline void loadXYZ( Point3 & pnt, const vec_float4 * quad ); + +// Load x, y, and z elements from the first three words of a float array. +// +// +inline void loadXYZ( Point3 & pnt, const float * fptr ); + +// Store x, y, and z elements of a 3-D point in the first three words of a quadword. +// The value of the fourth word (the word with the highest address) remains unchanged +// +inline void storeXYZ( Point3 pnt, vec_float4 * quad ); + +// Store x, y, and z elements of a 3-D point in the first three words of a float array. +// Memory area of previous 16 bytes and next 32 bytes from fptr might be accessed +// +inline void storeXYZ( Point3 pnt, float * fptr ); + +// Load four three-float 3-D points, stored in three quadwords +// +inline void loadXYZArray( Point3 & pnt0, Point3 & pnt1, Point3 & pnt2, Point3 & pnt3, const vec_float4 * threeQuads ); + +// Store four 3-D points in three quadwords +// +inline void storeXYZArray( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, vec_float4 * threeQuads ); + +// Store eight 3-D points as half-floats +// +inline void storeHalfFloats( Point3 pnt0, Point3 pnt1, Point3 pnt2, Point3 pnt3, Point3 pnt4, Point3 pnt5, Point3 pnt6, Point3 pnt7, vec_ushort8 * threeQuads ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 3-D point +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Point3 pnt ); + +// Print a 3-D point and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Point3 pnt, const char * name ); + +#endif + +// A quaternion in array-of-structures format +// +class Quat +{ + vec_float4 mVec128; + +public: + // Default constructor; does no initialization + // + inline Quat( ) { }; + + // Construct a quaternion from x, y, z, and w elements + // + inline Quat( float x, float y, float z, float w ); + + // Construct a quaternion from a 3-D vector and a scalar + // + inline Quat( Vector3 xyz, float w ); + + // Copy elements from a 4-D vector into a quaternion + // + explicit inline Quat( Vector4 vec ); + + // Convert a rotation matrix to a unit-length quaternion + // + explicit inline Quat( const Matrix3 & rotMat ); + + // Set all elements of a quaternion to the same scalar value + // + explicit inline Quat( float scalar ); + + // Set vector float data in a quaternion + // + explicit inline Quat( vec_float4 vf4 ); + + // Get vector float data from a quaternion + // + inline vec_float4 get128( ) const; + + // Assign one quaternion to another + // + inline Quat & operator =( Quat quat ); + + // Set the x, y, and z elements of a quaternion + // NOTE: + // This function does not change the w element. + // + inline Quat & setXYZ( Vector3 vec ); + + // Get the x, y, and z elements of a quaternion + // + inline const Vector3 getXYZ( ) const; + + // Set the x element of a quaternion + // + inline Quat & setX( float x ); + + // Set the y element of a quaternion + // + inline Quat & setY( float y ); + + // Set the z element of a quaternion + // + inline Quat & setZ( float z ); + + // Set the w element of a quaternion + // + inline Quat & setW( float w ); + + // Get the x element of a quaternion + // + inline float getX( ) const; + + // Get the y element of a quaternion + // + inline float getY( ) const; + + // Get the z element of a quaternion + // + inline float getZ( ) const; + + // Get the w element of a quaternion + // + inline float getW( ) const; + + // Set an x, y, z, or w element of a quaternion by index + // + inline Quat & setElem( int idx, float value ); + + // Get an x, y, z, or w element of a quaternion by index + // + inline float getElem( int idx ) const; + + // Subscripting operator to set or get an element + // + inline VecIdx operator []( int idx ); + + // Subscripting operator to get an element + // + inline float operator []( int idx ) const; + + // Add two quaternions + // + inline const Quat operator +( Quat quat ) const; + + // Subtract a quaternion from another quaternion + // + inline const Quat operator -( Quat quat ) const; + + // Multiply two quaternions + // + inline const Quat operator *( Quat quat ) const; + + // Multiply a quaternion by a scalar + // + inline const Quat operator *( float scalar ) const; + + // Divide a quaternion by a scalar + // + inline const Quat operator /( float scalar ) const; + + // Perform compound assignment and addition with a quaternion + // + inline Quat & operator +=( Quat quat ); + + // Perform compound assignment and subtraction by a quaternion + // + inline Quat & operator -=( Quat quat ); + + // Perform compound assignment and multiplication by a quaternion + // + inline Quat & operator *=( Quat quat ); + + // Perform compound assignment and multiplication by a scalar + // + inline Quat & operator *=( float scalar ); + + // Perform compound assignment and division by a scalar + // + inline Quat & operator /=( float scalar ); + + // Negate all elements of a quaternion + // + inline const Quat operator -( ) const; + + // Construct an identity quaternion + // + static inline const Quat identity( ); + + // Construct a quaternion to rotate between two unit-length 3-D vectors + // NOTE: + // The result is unpredictable if unitVec0 and unitVec1 point in opposite directions. + // + static inline const Quat rotation( Vector3 unitVec0, Vector3 unitVec1 ); + + // Construct a quaternion to rotate around a unit-length 3-D vector + // + static inline const Quat rotation( float radians, Vector3 unitVec ); + + // Construct a quaternion to rotate around the x axis + // + static inline const Quat rotationX( float radians ); + + // Construct a quaternion to rotate around the y axis + // + static inline const Quat rotationY( float radians ); + + // Construct a quaternion to rotate around the z axis + // + static inline const Quat rotationZ( float radians ); + +}; + +// Multiply a quaternion by a scalar +// +inline const Quat operator *( float scalar, Quat quat ); + +// Compute the conjugate of a quaternion +// +inline const Quat conj( Quat quat ); + +// Use a unit-length quaternion to rotate a 3-D vector +// +inline const Vector3 rotate( Quat unitQuat, Vector3 vec ); + +// Compute the dot product of two quaternions +// +inline float dot( Quat quat0, Quat quat1 ); + +// Compute the norm of a quaternion +// +inline float norm( Quat quat ); + +// Compute the length of a quaternion +// +inline float length( Quat quat ); + +// Normalize a quaternion +// NOTE: +// The result is unpredictable when all elements of quat are at or near zero. +// +inline const Quat normalize( Quat quat ); + +// Linear interpolation between two quaternions +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Quat lerp( float t, Quat quat0, Quat quat1 ); + +// Spherical linear interpolation between two quaternions +// NOTE: +// Interpolates along the shortest path between orientations. +// Does not clamp t between 0 and 1. +// +inline const Quat slerp( float t, Quat unitQuat0, Quat unitQuat1 ); + +// Spherical quadrangle interpolation +// +inline const Quat squad( float t, Quat unitQuat0, Quat unitQuat1, Quat unitQuat2, Quat unitQuat3 ); + +// Conditionally select between two quaternions +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// +inline const Quat select( Quat quat0, Quat quat1, bool select1 ); + +// Load x, y, z, and w elements from the first four words of a float array. +// +// +inline void loadXYZW( Quat & quat, const float * fptr ); + +// Store x, y, z, and w elements of a quaternion in the first four words of a float array. +// Memory area of previous 16 bytes and next 32 bytes from fptr might be accessed +// +inline void storeXYZW( Quat quat, float * fptr ); + +#ifdef _VECTORMATH_DEBUG + +// Print a quaternion +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Quat quat ); + +// Print a quaternion and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( Quat quat, const char * name ); + +#endif + +// A 3x3 matrix in array-of-structures format +// +class Matrix3 +{ + Vector3 mCol0; + Vector3 mCol1; + Vector3 mCol2; + +public: + // Default constructor; does no initialization + // + inline Matrix3( ) { }; + + // Copy a 3x3 matrix + // + inline Matrix3( const Matrix3 & mat ); + + // Construct a 3x3 matrix containing the specified columns + // + inline Matrix3( Vector3 col0, Vector3 col1, Vector3 col2 ); + + // Construct a 3x3 rotation matrix from a unit-length quaternion + // + explicit inline Matrix3( Quat unitQuat ); + + // Set all elements of a 3x3 matrix to the same scalar value + // + explicit inline Matrix3( float scalar ); + + // Assign one 3x3 matrix to another + // + inline Matrix3 & operator =( const Matrix3 & mat ); + + // Set column 0 of a 3x3 matrix + // + inline Matrix3 & setCol0( Vector3 col0 ); + + // Set column 1 of a 3x3 matrix + // + inline Matrix3 & setCol1( Vector3 col1 ); + + // Set column 2 of a 3x3 matrix + // + inline Matrix3 & setCol2( Vector3 col2 ); + + // Get column 0 of a 3x3 matrix + // + inline const Vector3 getCol0( ) const; + + // Get column 1 of a 3x3 matrix + // + inline const Vector3 getCol1( ) const; + + // Get column 2 of a 3x3 matrix + // + inline const Vector3 getCol2( ) const; + + // Set the column of a 3x3 matrix referred to by the specified index + // + inline Matrix3 & setCol( int col, Vector3 vec ); + + // Set the row of a 3x3 matrix referred to by the specified index + // + inline Matrix3 & setRow( int row, Vector3 vec ); + + // Get the column of a 3x3 matrix referred to by the specified index + // + inline const Vector3 getCol( int col ) const; + + // Get the row of a 3x3 matrix referred to by the specified index + // + inline const Vector3 getRow( int row ) const; + + // Subscripting operator to set or get a column + // + inline Vector3 & operator []( int col ); + + // Subscripting operator to get a column + // + inline const Vector3 operator []( int col ) const; + + // Set the element of a 3x3 matrix referred to by column and row indices + // + inline Matrix3 & setElem( int col, int row, float val ); + + // Get the element of a 3x3 matrix referred to by column and row indices + // + inline float getElem( int col, int row ) const; + + // Add two 3x3 matrices + // + inline const Matrix3 operator +( const Matrix3 & mat ) const; + + // Subtract a 3x3 matrix from another 3x3 matrix + // + inline const Matrix3 operator -( const Matrix3 & mat ) const; + + // Negate all elements of a 3x3 matrix + // + inline const Matrix3 operator -( ) const; + + // Multiply a 3x3 matrix by a scalar + // + inline const Matrix3 operator *( float scalar ) const; + + // Multiply a 3x3 matrix by a 3-D vector + // + inline const Vector3 operator *( Vector3 vec ) const; + + // Multiply two 3x3 matrices + // + inline const Matrix3 operator *( const Matrix3 & mat ) const; + + // Perform compound assignment and addition with a 3x3 matrix + // + inline Matrix3 & operator +=( const Matrix3 & mat ); + + // Perform compound assignment and subtraction by a 3x3 matrix + // + inline Matrix3 & operator -=( const Matrix3 & mat ); + + // Perform compound assignment and multiplication by a scalar + // + inline Matrix3 & operator *=( float scalar ); + + // Perform compound assignment and multiplication by a 3x3 matrix + // + inline Matrix3 & operator *=( const Matrix3 & mat ); + + // Construct an identity 3x3 matrix + // + static inline const Matrix3 identity( ); + + // Construct a 3x3 matrix to rotate around the x axis + // + static inline const Matrix3 rotationX( float radians ); + + // Construct a 3x3 matrix to rotate around the y axis + // + static inline const Matrix3 rotationY( float radians ); + + // Construct a 3x3 matrix to rotate around the z axis + // + static inline const Matrix3 rotationZ( float radians ); + + // Construct a 3x3 matrix to rotate around the x, y, and z axes + // + static inline const Matrix3 rotationZYX( Vector3 radiansXYZ ); + + // Construct a 3x3 matrix to rotate around a unit-length 3-D vector + // + static inline const Matrix3 rotation( float radians, Vector3 unitVec ); + + // Construct a rotation matrix from a unit-length quaternion + // + static inline const Matrix3 rotation( Quat unitQuat ); + + // Construct a 3x3 matrix to perform scaling + // + static inline const Matrix3 scale( Vector3 scaleVec ); + +}; +// Multiply a 3x3 matrix by a scalar +// +inline const Matrix3 operator *( float scalar, const Matrix3 & mat ); + +// Append (post-multiply) a scale transformation to a 3x3 matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Matrix3 appendScale( const Matrix3 & mat, Vector3 scaleVec ); + +// Prepend (pre-multiply) a scale transformation to a 3x3 matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Matrix3 prependScale( Vector3 scaleVec, const Matrix3 & mat ); + +// Multiply two 3x3 matrices per element +// +inline const Matrix3 mulPerElem( const Matrix3 & mat0, const Matrix3 & mat1 ); + +// Compute the absolute value of a 3x3 matrix per element +// +inline const Matrix3 absPerElem( const Matrix3 & mat ); + +// Transpose of a 3x3 matrix +// +inline const Matrix3 transpose( const Matrix3 & mat ); + +// Compute the inverse of a 3x3 matrix +// NOTE: +// Result is unpredictable when the determinant of mat is equal to or near 0. +// +inline const Matrix3 inverse( const Matrix3 & mat ); + +// Determinant of a 3x3 matrix +// +inline float determinant( const Matrix3 & mat ); + +// Conditionally select between two 3x3 matrices +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// +inline const Matrix3 select( const Matrix3 & mat0, const Matrix3 & mat1, bool select1 ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 3x3 matrix +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Matrix3 & mat ); + +// Print a 3x3 matrix and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Matrix3 & mat, const char * name ); + +#endif + +// A 4x4 matrix in array-of-structures format +// +class Matrix4 +{ + Vector4 mCol0; + Vector4 mCol1; + Vector4 mCol2; + Vector4 mCol3; + +public: + // Default constructor; does no initialization + // + inline Matrix4( ) { }; + + // Copy a 4x4 matrix + // + inline Matrix4( const Matrix4 & mat ); + + // Construct a 4x4 matrix containing the specified columns + // + inline Matrix4( Vector4 col0, Vector4 col1, Vector4 col2, Vector4 col3 ); + + // Construct a 4x4 matrix from a 3x4 transformation matrix + // + explicit inline Matrix4( const Transform3 & mat ); + + // Construct a 4x4 matrix from a 3x3 matrix and a 3-D vector + // + inline Matrix4( const Matrix3 & mat, Vector3 translateVec ); + + // Construct a 4x4 matrix from a unit-length quaternion and a 3-D vector + // + inline Matrix4( Quat unitQuat, Vector3 translateVec ); + + // Set all elements of a 4x4 matrix to the same scalar value + // + explicit inline Matrix4( float scalar ); + + // Assign one 4x4 matrix to another + // + inline Matrix4 & operator =( const Matrix4 & mat ); + + // Set the upper-left 3x3 submatrix + // NOTE: + // This function does not change the bottom row elements. + // + inline Matrix4 & setUpper3x3( const Matrix3 & mat3 ); + + // Get the upper-left 3x3 submatrix of a 4x4 matrix + // + inline const Matrix3 getUpper3x3( ) const; + + // Set translation component + // NOTE: + // This function does not change the bottom row elements. + // + inline Matrix4 & setTranslation( Vector3 translateVec ); + + // Get the translation component of a 4x4 matrix + // + inline const Vector3 getTranslation( ) const; + + // Set column 0 of a 4x4 matrix + // + inline Matrix4 & setCol0( Vector4 col0 ); + + // Set column 1 of a 4x4 matrix + // + inline Matrix4 & setCol1( Vector4 col1 ); + + // Set column 2 of a 4x4 matrix + // + inline Matrix4 & setCol2( Vector4 col2 ); + + // Set column 3 of a 4x4 matrix + // + inline Matrix4 & setCol3( Vector4 col3 ); + + // Get column 0 of a 4x4 matrix + // + inline const Vector4 getCol0( ) const; + + // Get column 1 of a 4x4 matrix + // + inline const Vector4 getCol1( ) const; + + // Get column 2 of a 4x4 matrix + // + inline const Vector4 getCol2( ) const; + + // Get column 3 of a 4x4 matrix + // + inline const Vector4 getCol3( ) const; + + // Set the column of a 4x4 matrix referred to by the specified index + // + inline Matrix4 & setCol( int col, Vector4 vec ); + + // Set the row of a 4x4 matrix referred to by the specified index + // + inline Matrix4 & setRow( int row, Vector4 vec ); + + // Get the column of a 4x4 matrix referred to by the specified index + // + inline const Vector4 getCol( int col ) const; + + // Get the row of a 4x4 matrix referred to by the specified index + // + inline const Vector4 getRow( int row ) const; + + // Subscripting operator to set or get a column + // + inline Vector4 & operator []( int col ); + + // Subscripting operator to get a column + // + inline const Vector4 operator []( int col ) const; + + // Set the element of a 4x4 matrix referred to by column and row indices + // + inline Matrix4 & setElem( int col, int row, float val ); + + // Get the element of a 4x4 matrix referred to by column and row indices + // + inline float getElem( int col, int row ) const; + + // Add two 4x4 matrices + // + inline const Matrix4 operator +( const Matrix4 & mat ) const; + + // Subtract a 4x4 matrix from another 4x4 matrix + // + inline const Matrix4 operator -( const Matrix4 & mat ) const; + + // Negate all elements of a 4x4 matrix + // + inline const Matrix4 operator -( ) const; + + // Multiply a 4x4 matrix by a scalar + // + inline const Matrix4 operator *( float scalar ) const; + + // Multiply a 4x4 matrix by a 4-D vector + // + inline const Vector4 operator *( Vector4 vec ) const; + + // Multiply a 4x4 matrix by a 3-D vector + // + inline const Vector4 operator *( Vector3 vec ) const; + + // Multiply a 4x4 matrix by a 3-D point + // + inline const Vector4 operator *( Point3 pnt ) const; + + // Multiply two 4x4 matrices + // + inline const Matrix4 operator *( const Matrix4 & mat ) const; + + // Multiply a 4x4 matrix by a 3x4 transformation matrix + // + inline const Matrix4 operator *( const Transform3 & tfrm ) const; + + // Perform compound assignment and addition with a 4x4 matrix + // + inline Matrix4 & operator +=( const Matrix4 & mat ); + + // Perform compound assignment and subtraction by a 4x4 matrix + // + inline Matrix4 & operator -=( const Matrix4 & mat ); + + // Perform compound assignment and multiplication by a scalar + // + inline Matrix4 & operator *=( float scalar ); + + // Perform compound assignment and multiplication by a 4x4 matrix + // + inline Matrix4 & operator *=( const Matrix4 & mat ); + + // Perform compound assignment and multiplication by a 3x4 transformation matrix + // + inline Matrix4 & operator *=( const Transform3 & tfrm ); + + // Construct an identity 4x4 matrix + // + static inline const Matrix4 identity( ); + + // Construct a 4x4 matrix to rotate around the x axis + // + static inline const Matrix4 rotationX( float radians ); + + // Construct a 4x4 matrix to rotate around the y axis + // + static inline const Matrix4 rotationY( float radians ); + + // Construct a 4x4 matrix to rotate around the z axis + // + static inline const Matrix4 rotationZ( float radians ); + + // Construct a 4x4 matrix to rotate around the x, y, and z axes + // + static inline const Matrix4 rotationZYX( Vector3 radiansXYZ ); + + // Construct a 4x4 matrix to rotate around a unit-length 3-D vector + // + static inline const Matrix4 rotation( float radians, Vector3 unitVec ); + + // Construct a rotation matrix from a unit-length quaternion + // + static inline const Matrix4 rotation( Quat unitQuat ); + + // Construct a 4x4 matrix to perform scaling + // + static inline const Matrix4 scale( Vector3 scaleVec ); + + // Construct a 4x4 matrix to perform translation + // + static inline const Matrix4 translation( Vector3 translateVec ); + + // Construct viewing matrix based on eye position, position looked at, and up direction + // + static inline const Matrix4 lookAt( Point3 eyePos, Point3 lookAtPos, Vector3 upVec ); + + // Construct a perspective projection matrix + // + static inline const Matrix4 perspective( float fovyRadians, float aspect, float zNear, float zFar ); + + // Construct a perspective projection matrix based on frustum + // + static inline const Matrix4 frustum( float left, float right, float bottom, float top, float zNear, float zFar ); + + // Construct an orthographic projection matrix + // + static inline const Matrix4 orthographic( float left, float right, float bottom, float top, float zNear, float zFar ); + +}; +// Multiply a 4x4 matrix by a scalar +// +inline const Matrix4 operator *( float scalar, const Matrix4 & mat ); + +// Append (post-multiply) a scale transformation to a 4x4 matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Matrix4 appendScale( const Matrix4 & mat, Vector3 scaleVec ); + +// Prepend (pre-multiply) a scale transformation to a 4x4 matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Matrix4 prependScale( Vector3 scaleVec, const Matrix4 & mat ); + +// Multiply two 4x4 matrices per element +// +inline const Matrix4 mulPerElem( const Matrix4 & mat0, const Matrix4 & mat1 ); + +// Compute the absolute value of a 4x4 matrix per element +// +inline const Matrix4 absPerElem( const Matrix4 & mat ); + +// Transpose of a 4x4 matrix +// +inline const Matrix4 transpose( const Matrix4 & mat ); + +// Compute the inverse of a 4x4 matrix +// NOTE: +// Result is unpredictable when the determinant of mat is equal to or near 0. +// +inline const Matrix4 inverse( const Matrix4 & mat ); + +// Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix +// NOTE: +// This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. The result is unpredictable when the determinant of mat is equal to or near 0. +// +inline const Matrix4 affineInverse( const Matrix4 & mat ); + +// Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix with an orthogonal upper-left 3x3 submatrix +// NOTE: +// This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. +// +inline const Matrix4 orthoInverse( const Matrix4 & mat ); + +// Determinant of a 4x4 matrix +// +inline float determinant( const Matrix4 & mat ); + +// Conditionally select between two 4x4 matrices +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// +inline const Matrix4 select( const Matrix4 & mat0, const Matrix4 & mat1, bool select1 ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 4x4 matrix +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Matrix4 & mat ); + +// Print a 4x4 matrix and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Matrix4 & mat, const char * name ); + +#endif + +// A 3x4 transformation matrix in array-of-structures format +// +class Transform3 +{ + Vector3 mCol0; + Vector3 mCol1; + Vector3 mCol2; + Vector3 mCol3; + +public: + // Default constructor; does no initialization + // + inline Transform3( ) { }; + + // Copy a 3x4 transformation matrix + // + inline Transform3( const Transform3 & tfrm ); + + // Construct a 3x4 transformation matrix containing the specified columns + // + inline Transform3( Vector3 col0, Vector3 col1, Vector3 col2, Vector3 col3 ); + + // Construct a 3x4 transformation matrix from a 3x3 matrix and a 3-D vector + // + inline Transform3( const Matrix3 & tfrm, Vector3 translateVec ); + + // Construct a 3x4 transformation matrix from a unit-length quaternion and a 3-D vector + // + inline Transform3( Quat unitQuat, Vector3 translateVec ); + + // Set all elements of a 3x4 transformation matrix to the same scalar value + // + explicit inline Transform3( float scalar ); + + // Assign one 3x4 transformation matrix to another + // + inline Transform3 & operator =( const Transform3 & tfrm ); + + // Set the upper-left 3x3 submatrix + // + inline Transform3 & setUpper3x3( const Matrix3 & mat3 ); + + // Get the upper-left 3x3 submatrix of a 3x4 transformation matrix + // + inline const Matrix3 getUpper3x3( ) const; + + // Set translation component + // + inline Transform3 & setTranslation( Vector3 translateVec ); + + // Get the translation component of a 3x4 transformation matrix + // + inline const Vector3 getTranslation( ) const; + + // Set column 0 of a 3x4 transformation matrix + // + inline Transform3 & setCol0( Vector3 col0 ); + + // Set column 1 of a 3x4 transformation matrix + // + inline Transform3 & setCol1( Vector3 col1 ); + + // Set column 2 of a 3x4 transformation matrix + // + inline Transform3 & setCol2( Vector3 col2 ); + + // Set column 3 of a 3x4 transformation matrix + // + inline Transform3 & setCol3( Vector3 col3 ); + + // Get column 0 of a 3x4 transformation matrix + // + inline const Vector3 getCol0( ) const; + + // Get column 1 of a 3x4 transformation matrix + // + inline const Vector3 getCol1( ) const; + + // Get column 2 of a 3x4 transformation matrix + // + inline const Vector3 getCol2( ) const; + + // Get column 3 of a 3x4 transformation matrix + // + inline const Vector3 getCol3( ) const; + + // Set the column of a 3x4 transformation matrix referred to by the specified index + // + inline Transform3 & setCol( int col, Vector3 vec ); + + // Set the row of a 3x4 transformation matrix referred to by the specified index + // + inline Transform3 & setRow( int row, Vector4 vec ); + + // Get the column of a 3x4 transformation matrix referred to by the specified index + // + inline const Vector3 getCol( int col ) const; + + // Get the row of a 3x4 transformation matrix referred to by the specified index + // + inline const Vector4 getRow( int row ) const; + + // Subscripting operator to set or get a column + // + inline Vector3 & operator []( int col ); + + // Subscripting operator to get a column + // + inline const Vector3 operator []( int col ) const; + + // Set the element of a 3x4 transformation matrix referred to by column and row indices + // + inline Transform3 & setElem( int col, int row, float val ); + + // Get the element of a 3x4 transformation matrix referred to by column and row indices + // + inline float getElem( int col, int row ) const; + + // Multiply a 3x4 transformation matrix by a 3-D vector + // + inline const Vector3 operator *( Vector3 vec ) const; + + // Multiply a 3x4 transformation matrix by a 3-D point + // + inline const Point3 operator *( Point3 pnt ) const; + + // Multiply two 3x4 transformation matrices + // + inline const Transform3 operator *( const Transform3 & tfrm ) const; + + // Perform compound assignment and multiplication by a 3x4 transformation matrix + // + inline Transform3 & operator *=( const Transform3 & tfrm ); + + // Construct an identity 3x4 transformation matrix + // + static inline const Transform3 identity( ); + + // Construct a 3x4 transformation matrix to rotate around the x axis + // + static inline const Transform3 rotationX( float radians ); + + // Construct a 3x4 transformation matrix to rotate around the y axis + // + static inline const Transform3 rotationY( float radians ); + + // Construct a 3x4 transformation matrix to rotate around the z axis + // + static inline const Transform3 rotationZ( float radians ); + + // Construct a 3x4 transformation matrix to rotate around the x, y, and z axes + // + static inline const Transform3 rotationZYX( Vector3 radiansXYZ ); + + // Construct a 3x4 transformation matrix to rotate around a unit-length 3-D vector + // + static inline const Transform3 rotation( float radians, Vector3 unitVec ); + + // Construct a rotation matrix from a unit-length quaternion + // + static inline const Transform3 rotation( Quat unitQuat ); + + // Construct a 3x4 transformation matrix to perform scaling + // + static inline const Transform3 scale( Vector3 scaleVec ); + + // Construct a 3x4 transformation matrix to perform translation + // + static inline const Transform3 translation( Vector3 translateVec ); + +}; +// Append (post-multiply) a scale transformation to a 3x4 transformation matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Transform3 appendScale( const Transform3 & tfrm, Vector3 scaleVec ); + +// Prepend (pre-multiply) a scale transformation to a 3x4 transformation matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Transform3 prependScale( Vector3 scaleVec, const Transform3 & tfrm ); + +// Multiply two 3x4 transformation matrices per element +// +inline const Transform3 mulPerElem( const Transform3 & tfrm0, const Transform3 & tfrm1 ); + +// Compute the absolute value of a 3x4 transformation matrix per element +// +inline const Transform3 absPerElem( const Transform3 & tfrm ); + +// Inverse of a 3x4 transformation matrix +// NOTE: +// Result is unpredictable when the determinant of the left 3x3 submatrix is equal to or near 0. +// +inline const Transform3 inverse( const Transform3 & tfrm ); + +// Compute the inverse of a 3x4 transformation matrix, expected to have an orthogonal upper-left 3x3 submatrix +// NOTE: +// This can be used to achieve better performance than a general inverse when the specified 3x4 transformation matrix meets the given restrictions. +// +inline const Transform3 orthoInverse( const Transform3 & tfrm ); + +// Conditionally select between two 3x4 transformation matrices +// NOTE: +// This function uses a conditional select instruction to avoid a branch. +// +inline const Transform3 select( const Transform3 & tfrm0, const Transform3 & tfrm1, bool select1 ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 3x4 transformation matrix +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Transform3 & tfrm ); + +// Print a 3x4 transformation matrix and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Transform3 & tfrm, const char * name ); + +#endif + +} // namespace Aos +} // namespace Vectormath + +#include "vec_aos.h" +#include "quat_aos.h" +#include "mat_aos.h" + +#endif From 14a996996dcb28017bce72dfbfeb6755582b3a79 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Tue, 14 Jul 2020 12:06:01 +0200 Subject: [PATCH 15/56] - add libfont - add libfontFT --- .gitignore | 2 + ppu/include/font/font.h | 266 ++++++++++++++++++++++++++++ ppu/include/font/fontFT.h | 43 +++++ ppu/include/font/fontset.h | 41 +++++ ppu/sprx/Makefile | 6 + ppu/sprx/libfont/Makefile | 101 +++++++++++ ppu/sprx/libfont/config.h | 5 + ppu/sprx/libfont/exports.h | 67 +++++++ ppu/sprx/libfont/font_wrapper.c | 70 ++++++++ ppu/sprx/libfontFT/Makefile | 101 +++++++++++ ppu/sprx/libfontFT/config.h | 5 + ppu/sprx/libfontFT/exports.h | 8 + ppu/sprx/libfontFT/fontft_wrapper.c | 29 +++ 13 files changed, 744 insertions(+) create mode 100644 ppu/include/font/font.h create mode 100644 ppu/include/font/fontFT.h create mode 100644 ppu/include/font/fontset.h create mode 100644 ppu/sprx/libfont/Makefile create mode 100644 ppu/sprx/libfont/config.h create mode 100644 ppu/sprx/libfont/exports.h create mode 100644 ppu/sprx/libfont/font_wrapper.c create mode 100644 ppu/sprx/libfontFT/Makefile create mode 100644 ppu/sprx/libfontFT/config.h create mode 100644 ppu/sprx/libfontFT/exports.h create mode 100644 ppu/sprx/libfontFT/fontft_wrapper.c diff --git a/.gitignore b/.gitignore index 5b36bec6..fb2e714f 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,8 @@ ppu/sprx/libsysmodule/ppu/ ppu/sprx/libsysutil/ppu/ ppu/sprx/libusb/ppu/ ppu/sprx/libvdec/ppu/ +ppu/sprx/libfont/ppu/ +ppu/sprx/libfontFT/ppu/ tools/generic/bin2s tools/cgcomp/cgcomp tools/geohot/make_self diff --git a/ppu/include/font/font.h b/ppu/include/font/font.h new file mode 100644 index 00000000..fa188f35 --- /dev/null +++ b/ppu/include/font/font.h @@ -0,0 +1,266 @@ +#ifndef __LV2_FONT_H__ +#define __LV2_FONT_H__ + +#include + +#include + +#define FONT_LIBRARY_TYPE_NONE 0 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* (*fontMallocCallback)(void *object,u32 size); +typedef void (*fontFreeCallback)(void *object,void *ptr); +typedef void* (*fontReallocCallback)(void *object,void *p,u32 reallocSize); +typedef void* (*fontCallocCallback)(void *object,u32 num,u32 size); + +typedef struct _font_memory_interface +{ + void *object ATTRIBUTE_PRXPTR; + fontMallocCallback malloc_func ATTRIBUTE_PRXPTR; + fontFreeCallback free_func ATTRIBUTE_PRXPTR; + fontReallocCallback realloc_func ATTRIBUTE_PRXPTR; + fontCallocCallback calloc_func ATTRIBUTE_PRXPTR; +} fontMemoryInterface; + +typedef struct _font_entry +{ + u32 lock; + u32 uniqueId; + const void* fontLib ATTRIBUTE_PRXPTR; + void* fontH ATTRIBUTE_PRXPTR; +} fontEntry; + +typedef struct _font_config +{ + struct { + u32 *buffer ATTRIBUTE_PRXPTR; + u32 size; + } fileCache; + + u32 userFontEntryMax; + fontEntry *userFontEntries ATTRIBUTE_PRXPTR; + u32 flags; +} fontConfig; + +typedef struct _font_library +{ + u32 libraryType, libraryVersion; + u32 systemClosed[]; +} fontLibrary; + +typedef struct _font_type +{ + u32 type; + u32 map; +} fontType; + +typedef struct _font +{ + void* ATTRIBUTE_PRXPTR systemReserved[64]; +} font; + +typedef struct _font_renderer +{ + void* ATTRIBUTE_PRXPTR systemReserved[64]; +} fontRenderer; + +typedef struct _font_renderer_config +{ + struct { + void* buffer ATTRIBUTE_PRXPTR; + u32 initSize; + u32 maxSize; + u32 expandSize; + u32 resetSize; + } bufferingPolicy; +} fontRendererConfig; + +typedef struct _font_horizontal_layout +{ + f32 baseLineY; + f32 lineHeight; + f32 effectHeight; +} fontHorizontalLayout; + +typedef struct _font_vertical_layout +{ + f32 baseLineX; + f32 lineWidth; + f32 effectWidth; +} fontVerticalLayout; + +typedef struct _font_glyph_metrics +{ + f32 width; + f32 height; + + struct { + f32 bearingX; + f32 bearingY; + f32 advance; + } horizontal; + struct { + f32 bearingX; + f32 bearingY; + f32 advance; + } vertical; +} fontGlyphMetrics; + +typedef struct _font_glyph_outline +{ + s16 contoursCount; + s16 pointsCount; + + struct { + f32 x, y; + } *Points ATTRIBUTE_PRXPTR; + + u8 * pointTags ATTRIBUTE_PRXPTR; + u16* contourIndexs ATTRIBUTE_PRXPTR; + u32 flags; + void* generateEnv ATTRIBUTE_PRXPTR; +} fontGlyphOutline; + +typedef struct _font_glyph +{ + u16 cf_type, type; + u32 size; + fontGlyphMetrics metrics; + fontGlyphOutline outline; +} fontGlyph; + +typedef struct _font_kerning +{ + f32 offsetX; + f32 offsetY; +} fontKerning; + +typedef struct _font_glyph_style +{ + struct { + f32 widthPixel; + f32 heightPixel; + } Scale; + struct { + f32 weight; + f32 slant; + } Effect; +} fontGlyphStyle; + +typedef struct _font_render_surface +{ + void* buffer ATTRIBUTE_PRXPTR; + s32 widthByte; + s32 pixelSizeByte; + s32 width, height; + struct { + u32 x0, y0; + u32 x1, y1; + } Scissor; +} fontRenderSurface; + +typedef struct _font_image_trans_info +{ + u8 *image ATTRIBUTE_PRXPTR; + u32 imageWidthByte; + u32 imageWidth; + u32 imageHeight; + void *surface ATTRIBUTE_PRXPTR; + u32 surfWidthByte; +} fontImageTransInfo; + +static inline void fontMemoryInterface_initialize(fontMemoryInterface *mIF) +{ + mIF->object = NULL; + mIF->malloc_func = NULL; + mIF->free_func = NULL; + mIF->realloc_func = NULL; + mIF->calloc_func = NULL; +} + +static inline void fontConfig_initialize(fontConfig *config) +{ + config->fileCache.buffer = NULL; + config->fileCache.size = 0; + config->userFontEntryMax = 0; + config->userFontEntries = NULL; + config->flags = 0x00000000; +} + +static inline s32 fontInit(fontConfig *config) +{ + extern void fontGetStubRevisionFlags(u64 *revisionFlags); + extern s32 fontInitializeWithRevision(u64 revision, fontConfig *config); + u64 revisionFlags = 0LL; + + fontGetStubRevisionFlags(&revisionFlags); + return fontInitializeWithRevision(revisionFlags, config); +} + +s32 fontOpenFontset(const fontLibrary *lib,fontType *type,font *f); +s32 fontOpenFontsetOnMemory(const fontLibrary *lib,fontType *type,font *f); +s32 fontOpenFontFile(const fontLibrary *lib,const char *fontPath,u32 subNum,s32 uniqueID,font *f); +s32 fontOpenFontMemory(const fontLibrary *lib,void *fontAddr,u32 fontSize,u32 subNum,s32 uniqueID,font *f); +s32 fontOpenFontInstance(font *openedFont,font *f); +s32 fontGetLibrary(font *f,const fontLibrary **lib,u32 *type); +s32 fontAdjustGlyphExpandBuffer(font *f,s32 pointN,s32 contourN); +s32 fontGetGlyphExpandBufferInfo(font *f,s32 *pointN,s32 *contourN); +s32 fontAdjustFontScaling(font *f,f32 fontScale); +s32 fontSetResolutionDpi(font *f,u32 hDpi,u32 vDpi); +s32 fontSetScalePoint(font *f,f32 w,f32 h); +s32 fontontSetScalePixel(font *f,f32 w,f32 h); +s32 fontSetEffectWeight(font *f,f32 effectWeight); +s32 fontSetEffectSlant(font *f,f32 effectSlant); +s32 fontGetResolutionDpi(font *f,u32 *hDpi,u32 *vDpi); +s32 fontGetScalePoint(font *f,f32 *w,f32 *h); +s32 fontGetScalePixel(font *f,f32 *w,f32 *h); +s32 fontGetEffectWeight(font *f,f32 *effectWeight); +s32 fontGetEffectSlant(font *f,f32 *effectSlant); +s32 fontGetHorizontalLayout(font *f,fontHorizontalLayout *layout); +s32 fontGetVerticalLayout(font *f,fontVerticalLayout *layout); +s32 fontGetFontIdCode(font *f,u32 code,u32 *fontId,u32 *fontcode); +s32 fontGetCharGlyphMetrics(font *f,u32 code,fontGlyphMetrics *metrics); +s32 fontGetCharGlyphMetricsVertical(font *f,u32 code,fontGlyphMetrics *metrics); +s32 fontGetKerning(font *f,u32 preCode,u32 code,fontKerning *kerning); +s32 fontCreateRenderer(const fontLibrary *lib,fontRendererConfig *confing,fontRenderer *renderer); +s32 fontBindRenderer(font *f,fontRenderer *renderer); +s32 fontGetBindingRenderer(font *f,fontRenderer **renderer); +s32 fontSetupRenderScalePoint(font *f,f32 w,f32 h); +s32 fontSetupRenderScalePixel(font *f,f32 w,f32 h); +s32 fontSetupRenderEffectWeight(font *f,f32 additionalWeight); +s32 fontSetupRenderEffectSlant(font *f,f32 effectSlant); +s32 fontGetRenderScalePoint(font *f,f32 *w,f32 *h); +s32 fontGetRenderScalePixel(font *f,f32 *w,f32 *h); +s32 fontGetRenderEffectWeight(font *f,f32 *effectWeight); +s32 fontGetRenderEffectSlant(font *f,f32 *effectSlant); +s32 fontGetRenderCharGlyphMetrics(font *f,u32 code,fontGlyphMetrics *metrics); +s32 fontGetRenderCharGlyphMetricsVertical(font *cfEx,u32 code,fontGlyphMetrics *metrics); +s32 fontGetRenderScaledKerning(font *f,u32 preCode,u32 code,fontKerning *kerning); +s32 fontGenerateCharGlyph(font *f,u32 code,fontGlyph **glyph); +s32 fontGenerateCharGlyphVertical(font *f,u32 code,fontGlyph **glyph); +s32 fontDeleteGlyph(font *f,fontGlyph *glyph); +s32 fontDelete(const fontLibrary *library,void *p); +void fontRenderSurfaceInit(fontRenderSurface *surface,void *buffer,s32 bufWidthByte,s32 pixelSizeByte,s32 w,s32 h); +void fontRenderSurfaceSetScissor(fontRenderSurface *surface,s32 x0,s32 y0,u32 w,u32 h); +s32 fontRenderCharGlyphImage(font *f,u32 code,fontRenderSurface *surface,f32 x,f32 y,fontGlyphMetrics *metrics,fontImageTransInfo *transInfo); +s32 fontRenderCharGlyphImageHorizontal(font *f,u32 code,fontRenderSurface *surface,f32 x,f32 y,fontGlyphMetrics *metrics,fontImageTransInfo *transInfo); +s32 fontRenderCharGlyphImageVertical(font *f,u32 code,fontRenderSurface *surface,f32 x,f32 y,fontGlyphMetrics *metrics,fontImageTransInfo *transInfo); +s32 fontUnbindRenderer(font *f); +s32 fontGlyphRenderImage(fontGlyph *glyph,fontGlyphStyle *style,fontRenderer *renderer,fontRenderSurface *surface,f32 x,f32 y,fontGlyphMetrics *metrics,fontImageTransInfo *transInfo); +s32 fontGlyphRenderImageHorizontal(fontGlyph *glyph,fontGlyphStyle *style,fontRenderer *renderer,fontRenderSurface *surface,f32 x,f32 y,fontGlyphMetrics *metrics,fontImageTransInfo *transInfo); +s32 fontGlyphRenderImageVertical(fontGlyph *glyph,fontGlyphStyle *style,fontRenderer *renderer,fontRenderSurface *surface,f32 x,f32 y,fontGlyphMetrics *metrics,fontImageTransInfo *transInfo); +s32 fontGlyphGetHorizontalShift(fontGlyph *glyph,f32 *shiftX,f32 *shiftY); +s32 fontGlyphGetVerticalShift(fontGlyph *glyph,f32 *shiftX,f32 *shiftY); +s32 fontDestroyRenderer(fontRenderer *renderer); +s32 fontCloseFont(font *cf); +s32 fontEndLibrary(const fontLibrary *lib); +s32 fontEnd(); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/ppu/include/font/fontFT.h b/ppu/include/font/fontFT.h new file mode 100644 index 00000000..735e734b --- /dev/null +++ b/ppu/include/font/fontFT.h @@ -0,0 +1,43 @@ +#ifndef __LV2_FONTFT_H__ +#define __LV2_FONTFT_H__ + +#include + +#include + +#define FONT_LIBRARY_TYPE_FREETYPE 2 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _font_library_configFT +{ + void *library ATTRIBUTE_PRXPTR; + fontMemoryInterface memoryIF; +} fontLibraryConfigFT; + +typedef struct _font_renderer_configFT +{ + struct { + void *buffer ATTRIBUTE_PRXPTR; + u32 initSize; + u32 maxSize; + u32 expandSize; + u32 resetSize; + } bufferingPolicy; +} fontRendererConfigFT; + +static inline void fontLibraryConfigFT_initialize(fontLibraryConfigFT *config) +{ + config->library = NULL; + fontMemoryInterface_initialize(&config->memoryIF); +} + +s32 fontInitLibraryFreeType(fontLibraryConfigFT *config,const fontLibrary **lib); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/ppu/include/font/fontset.h b/ppu/include/font/fontset.h new file mode 100644 index 00000000..ae435091 --- /dev/null +++ b/ppu/include/font/fontset.h @@ -0,0 +1,41 @@ +#ifndef __LV2_FONTSET_H__ +#define __LV2_FONTSET_H__ + +#define FONT_TYPE_RODIN_SANS_SERIF_LATIN 0x00000000 +#define FONT_TYPE_MATISSE_SERIF_LATIN 0x00000020 +#define FONT_TYPE_NEWRODIN_GOTHIC_JAPANESE 0x00000008 +#define FONT_TYPE_YD_GOTHIC_KOREAN 0x0000000c + +#define FONT_TYPE_NEWRODIN_GOTHIC_JP_SET 0x00000100 +#define FONT_TYPE_NEWRODIN_GOTHIC_LATIN_SET 0x00000101 +#define FONT_TYPE_NEWRODIN_GOTHIC_YG_DFHEI5_SET 0x00000108 //SDK1.60- +#define FONT_TYPE_NEWRODIN_GOTHIC_YG_DFHEI5_RODIN_SET 0x00000109 //SDK1.60- +#define FONT_TYPE_DFHEI5_GOTHIC_YG_NEWRODIN_TCH_SET 0x0000010a //SDK1.60- +#define FONT_TYPE_DFHEI5_GOTHIC_YG_NEWRODIN_RODIN_TCH_SET 0x0000010b //SDK1.60- +#define FONT_TYPE_DFHEI5_GOTHIC_YG_NEWRODIN_SCH_SET 0x0000010c //SDK1.60- +#define FONT_TYPE_DFHEI5_GOTHIC_YG_NEWRODIN_RODIN_SCH_SET 0x0000010d //SDK1.60- + +//SDK1.00- +// ========================================================================================================== +#define FONT_TYPE_DEFAULT_GOTHIC_SET FONT_TYPE_NEWRODIN_GOTHIC_LATIN_SET +#define FONT_TYPE_DEFAULT_GOTHIC_LATIN_SET FONT_TYPE_NEWRODIN_GOTHIC_LATIN_SET +#define FONT_TYPE_DEFAULT_GOTHIC_JP_SET FONT_TYPE_NEWRODIN_GOTHIC_JP_SET +#define FONT_TYPE_DEFAULT_SANS_SERIF FONT_TYPE_RODIN_SANS_SERIF_LATIN +#define FONT_TYPE_DEFAULT_SERIF FONT_TYPE_MATISSE_SERIF_LATIN + +//SDK1.60- +// ========================================================================================================== +#define FONT_TYPE_GOTHIC_JAPANESE_CJK_JP_SET FONT_TYPE_NEWRODIN_GOTHIC_YG_DFHEI5_SET +#define FONT_TYPE_GOTHIC_JAPANESE_CJK_LATIN_SET FONT_TYPE_NEWRODIN_GOTHIC_YG_DFHEI5_RODIN_SET +#define FONT_TYPE_GOTHIC_TCHINESE_CJK_JP_SET FONT_TYPE_DFHEI5_GOTHIC_YG_NEWRODIN_TCH_SET +#define FONT_TYPE_GOTHIC_TCHINESE_CJK_LATIN_SET FONT_TYPE_DFHEI5_GOTHIC_YG_NEWRODIN_RODIN_TCH_SET +#define FONT_TYPE_GOTHIC_SCHINESE_CJK_JP_SET FONT_TYPE_DFHEI5_GOTHIC_YG_NEWRODIN_SCH_SET +#define FONT_TYPE_GOTHIC_SCHINESE_CJK_LATIN_SET FONT_TYPE_DFHEI5_GOTHIC_YG_NEWRODIN_RODIN_SCH_SET +// ---------------------------------------------------------------------------------------------------------- + +//----------------------------------- +#define FONT_MAP_FONT 0x0 +#define FONT_MAP_UNICODE 0x1 +//----------------------------------- + +#endif diff --git a/ppu/sprx/Makefile b/ppu/sprx/Makefile index 886efb58..ca837e33 100644 --- a/ppu/sprx/Makefile +++ b/ppu/sprx/Makefile @@ -20,6 +20,8 @@ all: @$(MAKE) -C libvdec --no-print-directory @$(MAKE) -C libusb --no-print-directory @$(MAKE) -C libspurs --no-print-directory + @$(MAKE) -C libfont --no-print-directory + @$(MAKE) -C libfontFT --no-print-directory install: @$(MAKE) -C libio install --no-print-directory @@ -43,6 +45,8 @@ install: @$(MAKE) -C libvdec install --no-print-directory @$(MAKE) -C libusb install --no-print-directory @$(MAKE) -C libspurs install --no-print-directory + @$(MAKE) -C libfont install --no-print-directory + @$(MAKE) -C libfontFT install --no-print-directory clean: @$(MAKE) -C libio clean --no-print-directory @@ -66,5 +70,7 @@ clean: @$(MAKE) -C libvdec clean --no-print-directory @$(MAKE) -C libusb clean --no-print-directory @$(MAKE) -C libspurs clean --no-print-directory + @$(MAKE) -C libfont clean --no-print-directory + @$(MAKE) -C libfontFT clean --no-print-directory .PHONY: all clean install diff --git a/ppu/sprx/libfont/Makefile b/ppu/sprx/libfont/Makefile new file mode 100644 index 00000000..0be145cb --- /dev/null +++ b/ppu/sprx/libfont/Makefile @@ -0,0 +1,101 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PSL1GHT)),) +$(error "Please set PSL1GHT in your environment. export PSL1GHT=") +endif + +include $(PSL1GHT)/ppu_rules + +BUILD := build +SOURCES := ../common . +INCLUDES := . + +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PLATFORM)),) +#--------------------------------------------------------------------------------- +export BASEDIR := $(CURDIR) +export DEPS := $(BASEDIR)/deps +export LIBS := $(BASEDIR)/lib + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +export CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +export CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +export sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +export SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) + +export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) + +export INCLUDE := $(foreach dir,$(INCLUDES), -iquote $(CURDIR)/$(dir)) \ + -I$(BASEDIR)/../../include + +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + +export LIBDIR := $(LIBS)/$(PLATFORM) +export DEPSDIR := $(DEPS)/$(PLATFORM) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + + +LIBRARY := $(LIBDIR)/libfont + +#--------------------------------------------------------------------------------- +LD := $(PREFIX)ld + +CFLAGS := -O2 -mregnames -Wall -mcpu=cell -DSPRX_INTERNAL $(MACHDEP) $(INCLUDE) -Wa,-mcell +ASFLAGS := $(MACHDEP) -mregnames -mcpu=cell -DSPRX_INTERNAL -D__ASSEMBLY__ -Wa,-mcell $(INCLUDE) + +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- + +all: ppu + +#--------------------------------------------------------------------------------- +ppu: +#--------------------------------------------------------------------------------- + @[ -d $(LIBS)/ppu ] || mkdir -p $(LIBS)/ppu + @[ -d $(DEPS)/ppu ] || mkdir -p $(DEPS)/ppu + @[ -d ppu ] || mkdir -p ppu + @$(MAKE) PLATFORM=ppu lib -C ppu -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +install: all +#--------------------------------------------------------------------------------- + @[ -d $(PSL1GHT)/ppu/lib ] || mkdir -p $(PSL1GHT)/ppu/lib + @cp -frv $(CURDIR)/lib/ppu/*.a $(PSL1GHT)/ppu/lib + +#--------------------------------------------------------------------------------- +$(LIBRARY).a: sprx.o font_wrapper.o +#--------------------------------------------------------------------------------- + +.PHONY: lib ppu install + +#--------------------------------------------------------------------------------- +lib: $(LIBRARY).a +#--------------------------------------------------------------------------------- + +libexport.o: libexport.c + @$(CC) $(DEPSOPT) -S -m32 $(INCLUDE) $< -o libexport.S + @$(CC) $(DEPSOPT) -c libexport.S -o $@ + +sprx.o: exports.o libexport.o + @$(LD) -r exports.o libexport.o -o $@ +#--------------------------------------------------------------------------------- +clean: +#--------------------------------------------------------------------------------- + @echo clean ... + @rm -rf ppu + @rm -rf $(DEPS) + @rm -rf $(LIBS) + +-include $(DEPSDIR)/*.d diff --git a/ppu/sprx/libfont/config.h b/ppu/sprx/libfont/config.h new file mode 100644 index 00000000..9a9a0e02 --- /dev/null +++ b/ppu/sprx/libfont/config.h @@ -0,0 +1,5 @@ +#define LIBRARY_NAME "cellFont" +#define LIBRARY_SYMBOL cellFont + +#define LIBRARY_HEADER_1 0x2c000001 +#define LIBRARY_HEADER_2 0x0009 diff --git a/ppu/sprx/libfont/exports.h b/ppu/sprx/libfont/exports.h new file mode 100644 index 00000000..08e9086b --- /dev/null +++ b/ppu/sprx/libfont/exports.h @@ -0,0 +1,67 @@ +#ifndef __EXPORTS_H__ +#define __EXPORTS_H__ + +EXPORT(fontGetRenderEffectWeight, 0x0109f3d3); +EXPORT(fontCreateRenderer, 0x042e74e3); +EXPORT(fontGetKerning, 0x06be743d); +EXPORT(fontOpenFontsetOnMemory, 0x073fa321); +EXPORT(fontOpenFontFile, 0x0a7306a4); +EXPORT(fontGetRenderScaledKerning, 0x0baf90fe); +EXPORT(fontGetRenderScalePixel, 0x0d106a11); +EXPORT(fontGetHorizontalLayout, 0x1387c45c); +EXPORT(fontRenderCharGlyphImageHorizontal, 0x1a218fe4); +EXPORT(fontDestroyRenderer, 0x21ebb248); +EXPORT(fontSetupRenderScalePixel, 0x227e1e3c); +EXPORT(fontGlyphGetHorizontalShift, 0x231d5941); +EXPORT(fontSetEffectWeight, 0x25253fe4); +EXPORT(fontGetEffectWeight, 0x25dbeff9); +EXPORT(fontGetScalePixel, 0x285d30d6); +EXPORT(fontOpenFontInstance, 0x29329541); +EXPORT(fontSetScalePixel, 0x297f0e93); +EXPORT(fontGetRenderCharGlyphMetrics, 0x2da9fd9d); +EXPORT(fontEndLibrary, 0x40d40544); +EXPORT(fontAdjustFontScaling, 0x47ca71ef); +EXPORT(fontSetupRenderScalePoint, 0x4d19c631); +EXPORT(fontGlyphGetVerticalShift, 0x534e785f); +EXPORT(fontGetGlyphExpandBufferInfo, 0x59ef0073); +EXPORT(fontGetLibraryEx, 0x5abd8b1e); +EXPORT(fontBindRenderer, 0x66a23100); +EXPORT(fontGetVerticalLayout, 0x698897f8); +EXPORT(fontGetRenderCharGlyphMetricsVertical, 0x700e6223); +EXPORT(fontSetScalePoint, 0x70f3e728); +EXPORT(fontSetupRenderEffectSlant, 0x78d05e08); +EXPORT(fontEnd, 0x7ab47f7e); +EXPORT(fontGetInitializedRevisionFlags, 0x7c5df0d8); +EXPORT(fontSetEffectSlant, 0x8657c8f5); +EXPORT(fontRenderCharGlyphImage, 0x88be4799); +EXPORT(fontGetResolutionDpi, 0x8a632038); +EXPORT(fontGlyphRenderImageVertical, 0x8e3f2c40); +EXPORT(fontRenderSurfaceInit, 0x90b9465e); +EXPORT(fontGlyphRenderImageHorizontal, 0x97b95244); +EXPORT(fontGetFontIdCode, 0x98ac5524); +EXPORT(fontOpenFontMemory, 0x9e19072b); +EXPORT(fontAdjustGlyphExpandBuffer, 0x9e3b1e16); +EXPORT(fontGetRenderScalePoint, 0xa165daae); +EXPORT(fontSetupRenderEffectWeight, 0xa6dc25d1); +EXPORT(fontDelete, 0xa7b2103a); +EXPORT(fontOpenFontset, 0xa885cc9b); +EXPORT(fontGetRevisionFlags, 0xb015a84e); +EXPORT(fontCloseFont, 0xb276f1f6); +EXPORT(fontPatchWorks, 0xb3d304b2); +EXPORT(fontRenderSurfaceSetScissor, 0xb422b005); +EXPORT(fontGenerateCharGlyphEx, 0xc17259de); +EXPORT(fontGetBindingRendererEx, 0xc91c8ece); +EXPORT(fontGenerateCharGlyphVerticalEx, 0xcaed32c1); +EXPORT(fontGetRenderEffectSlant, 0xced4dda9); +EXPORT(fontDeleteGlyph, 0xd62f5d76); +EXPORT(fontGetCharGlyphMetrics, 0xd8eaee9f); +EXPORT(fontGlyphRenderImage, 0xe01b199e); +EXPORT(fontGetEffectSlant, 0xe16e679a); +EXPORT(fontRenderCharGlyphImageVertical, 0xe857a0ca); +EXPORT(fontInitializeWithRevision, 0xf03dcc29); +EXPORT(fontUnbindRenderer, 0xf16379fa); +EXPORT(fontGetScalePoint, 0xf7a19060); +EXPORT(fontSetResolutionDpi, 0xfb3341ba); +EXPORT(fontGetCharGlyphMetricsVertical, 0xfe9a6dd7); + +#endif diff --git a/ppu/sprx/libfont/font_wrapper.c b/ppu/sprx/libfont/font_wrapper.c new file mode 100644 index 00000000..4816e2f2 --- /dev/null +++ b/ppu/sprx/libfont/font_wrapper.c @@ -0,0 +1,70 @@ +#include +#include + +#include + +extern s32 fontGetLibraryEx(font *f,const fontLibrary* ATTRIBUTE_PRXPTR *lib,u32 *type); +extern s32 fontGetBindingRendererEx(font *f,fontRenderer* ATTRIBUTE_PRXPTR *renderer); +extern s32 fontGenerateCharGlyphEx(font *f,u32 code,fontGlyph* ATTRIBUTE_PRXPTR *glyph); +extern s32 fontGenerateCharGlyphVerticalEx(font *f,u32 code,fontGlyph* ATTRIBUTE_PRXPTR *glyph); + +void fontGetStubRevisionFlags(u64 *revisionFlags) +{ + if(revisionFlags == NULL) return; + *revisionFlags = 0x14; +} + +s32 fontGetLibrary(font *f,const fontLibrary **lib,u32 *type) +{ + s32 ret; + const fontLibrary *l ATTRIBUTE_PRXPTR; + + if(f == NULL || lib == NULL || type == NULL) return 0x80540002; + + ret = fontGetLibraryEx(f,&l,type); + *lib = ret == 0 ? l : NULL; + + return ret; +} + +s32 fontGetBindingRenderer(font *f,fontRenderer **renderer) +{ + s32 ret; + fontRenderer *r ATTRIBUTE_PRXPTR; + + if(f == NULL || renderer == NULL) return 0x80540002; + + ret = fontGetBindingRendererEx(f,&r); + *renderer = ret == 0 ? r : NULL; + + return ret; +} + +s32 fontGenerateCharGlyph(font *f,u32 code,fontGlyph **glyph) +{ + s32 ret; + fontGlyph *g ATTRIBUTE_PRXPTR; + + if(f == NULL || glyph == NULL) return 0x80540002; + + ret = fontGenerateCharGlyphEx(f,code,&g); + *glyph = ret == 0 ? g : NULL; + + return ret; +} + +s32 fontGenerateCharGlyphVertical(font *f,u32 code,fontGlyph **glyph) +{ + s32 ret; + fontGlyph *g ATTRIBUTE_PRXPTR; + + if(f == NULL || glyph == NULL) return 0x80540002; + + ret = fontGenerateCharGlyphVerticalEx(f,code,&g); + *glyph = ret == 0 ? g : NULL; + + return ret; +} + + + diff --git a/ppu/sprx/libfontFT/Makefile b/ppu/sprx/libfontFT/Makefile new file mode 100644 index 00000000..c6a478ee --- /dev/null +++ b/ppu/sprx/libfontFT/Makefile @@ -0,0 +1,101 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PSL1GHT)),) +$(error "Please set PSL1GHT in your environment. export PSL1GHT=") +endif + +include $(PSL1GHT)/ppu_rules + +BUILD := build +SOURCES := ../common . +INCLUDES := . + +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PLATFORM)),) +#--------------------------------------------------------------------------------- +export BASEDIR := $(CURDIR) +export DEPS := $(BASEDIR)/deps +export LIBS := $(BASEDIR)/lib + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +export CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +export CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +export sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +export SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) + +export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) + +export INCLUDE := $(foreach dir,$(INCLUDES), -iquote $(CURDIR)/$(dir)) \ + -I$(BASEDIR)/../../include + +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + +export LIBDIR := $(LIBS)/$(PLATFORM) +export DEPSDIR := $(DEPS)/$(PLATFORM) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + + +LIBRARY := $(LIBDIR)/libfontFT + +#--------------------------------------------------------------------------------- +LD := $(PREFIX)ld + +CFLAGS := -O2 -mregnames -Wall -mcpu=cell -DSPRX_INTERNAL $(MACHDEP) $(INCLUDE) -Wa,-mcell +ASFLAGS := $(MACHDEP) -mregnames -mcpu=cell -DSPRX_INTERNAL -D__ASSEMBLY__ -Wa,-mcell $(INCLUDE) + +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- + +all: ppu + +#--------------------------------------------------------------------------------- +ppu: +#--------------------------------------------------------------------------------- + @[ -d $(LIBS)/ppu ] || mkdir -p $(LIBS)/ppu + @[ -d $(DEPS)/ppu ] || mkdir -p $(DEPS)/ppu + @[ -d ppu ] || mkdir -p ppu + @$(MAKE) PLATFORM=ppu lib -C ppu -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +install: all +#--------------------------------------------------------------------------------- + @[ -d $(PSL1GHT)/ppu/lib ] || mkdir -p $(PSL1GHT)/ppu/lib + @cp -frv $(CURDIR)/lib/ppu/*.a $(PSL1GHT)/ppu/lib + +#--------------------------------------------------------------------------------- +$(LIBRARY).a: sprx.o fontft_wrapper.o +#--------------------------------------------------------------------------------- + +.PHONY: lib ppu install + +#--------------------------------------------------------------------------------- +lib: $(LIBRARY).a +#--------------------------------------------------------------------------------- + +libexport.o: libexport.c + @$(CC) $(DEPSOPT) -S -m32 $(INCLUDE) $< -o libexport.S + @$(CC) $(DEPSOPT) -c libexport.S -o $@ + +sprx.o: exports.o libexport.o + @$(LD) -r exports.o libexport.o -o $@ +#--------------------------------------------------------------------------------- +clean: +#--------------------------------------------------------------------------------- + @echo clean ... + @rm -rf ppu + @rm -rf $(DEPS) + @rm -rf $(LIBS) + +-include $(DEPSDIR)/*.d diff --git a/ppu/sprx/libfontFT/config.h b/ppu/sprx/libfontFT/config.h new file mode 100644 index 00000000..79b106be --- /dev/null +++ b/ppu/sprx/libfontFT/config.h @@ -0,0 +1,5 @@ +#define LIBRARY_NAME "cellFontFT" +#define LIBRARY_SYMBOL cellFontFT + +#define LIBRARY_HEADER_1 0x2c000001 +#define LIBRARY_HEADER_2 0x0009 diff --git a/ppu/sprx/libfontFT/exports.h b/ppu/sprx/libfontFT/exports.h new file mode 100644 index 00000000..7ae76089 --- /dev/null +++ b/ppu/sprx/libfontFT/exports.h @@ -0,0 +1,8 @@ +#ifndef __EXPORTS_H__ +#define __EXPORTS_H__ + +EXPORT(fontInitLibraryFreeTypeWithRevision, 0x7a0a83c4); +EXPORT(fontFTGetRevisionFlags, 0xec89a187); +EXPORT(fontFTGetInitializedRevisionFlags, 0xfa0c2de0); + +#endif diff --git a/ppu/sprx/libfontFT/fontft_wrapper.c b/ppu/sprx/libfontFT/fontft_wrapper.c new file mode 100644 index 00000000..00297222 --- /dev/null +++ b/ppu/sprx/libfontFT/fontft_wrapper.c @@ -0,0 +1,29 @@ +#include +#include + +#include +#include + +extern s32 fontInitLibraryFreeTypeWithRevision(u64 revisionFlags,fontLibraryConfigFT *config,const fontLibrary* ATTRIBUTE_PRXPTR *lib); + +void fontFTGetStubRevisionFlags(u64 *revisionFlags) +{ + if(revisionFlags == NULL) return; + *revisionFlags = 0x14; +} + +s32 fontInitLibraryFreeType(fontLibraryConfigFT *config,const fontLibrary **lib) +{ + s32 ret; + u64 revisionFlags = 0LL; + const fontLibrary *l ATTRIBUTE_PRXPTR; + + if(config == NULL || lib == NULL) return 0x80540002; + + fontFTGetStubRevisionFlags(&revisionFlags); + + ret = fontInitLibraryFreeTypeWithRevision(revisionFlags,config,&l); + *lib = ret == 0 ? l : NULL; + + return ret; +} From ead0713444df4dd9bb40e0e14fecbf72eaf934b5 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Thu, 16 Jul 2020 15:48:36 +0200 Subject: [PATCH 16/56] - add shader binaries to .gitignore - fixes to vectormath - cast intptr_t for pointer calculations --- .gitignore | 2 ++ common/vectormath/ppu/cpp/vectormath_aos.h | 8 +++---- common/vectormath/spu/cpp/vectormath_aos.h | 8 +++---- ppu/include/ppu-asm.h | 28 +++++++++++++++++++++- 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index fb2e714f..34b28df5 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ *.dll *.task *.elf.map +*.fpo* +*.vpo* build/ .idea/ .vscode/ diff --git a/common/vectormath/ppu/cpp/vectormath_aos.h b/common/vectormath/ppu/cpp/vectormath_aos.h index 2e73f034..8d0ed834 100644 --- a/common/vectormath/ppu/cpp/vectormath_aos.h +++ b/common/vectormath/ppu/cpp/vectormath_aos.h @@ -65,7 +65,7 @@ class Vector3 public: // Default constructor; does no initialization // - inline Vector3( ) { }; + inline Vector3( ) : mVec128((vec_float4){0.0f, 0.0f, 0.0f, 0.0f}) { }; // Construct a 3-D vector from x, y, and z elements // @@ -452,7 +452,7 @@ class Vector4 public: // Default constructor; does no initialization // - inline Vector4( ) { }; + inline Vector4( ) : mVec128((vec_float4){0.0f, 0.0f, 0.0f, 0.0f}) { }; // Construct a 4-D vector from x, y, z, and w elements // @@ -839,7 +839,7 @@ class Point3 public: // Default constructor; does no initialization // - inline Point3( ) { }; + inline Point3( ) : mVec128((vec_float4){0.0f, 0.0f, 0.0f, 0.0f}) { }; // Construct a 3-D point from x, y, and z elements // @@ -1145,7 +1145,7 @@ class Quat public: // Default constructor; does no initialization // - inline Quat( ) { }; + inline Quat( ) : mVec128((vec_float4){0.0f, 0.0f, 0.0f, 0.0f}) { }; // Construct a quaternion from x, y, z, and w elements // diff --git a/common/vectormath/spu/cpp/vectormath_aos.h b/common/vectormath/spu/cpp/vectormath_aos.h index 7906da31..f7ab35ab 100644 --- a/common/vectormath/spu/cpp/vectormath_aos.h +++ b/common/vectormath/spu/cpp/vectormath_aos.h @@ -65,7 +65,7 @@ class Vector3 public: // Default constructor; does no initialization // - inline Vector3( ) { }; + inline Vector3( ) : mVec128((vec_float4){0.0f, 0.0f, 0.0f, 0.0f}) { }; // Construct a 3-D vector from x, y, and z elements // @@ -363,7 +363,7 @@ class Vector4 public: // Default constructor; does no initialization // - inline Vector4( ) { }; + inline Vector4( ) : mVec128((vec_float4){0.0f, 0.0f, 0.0f, 0.0f}) { }; // Construct a 4-D vector from x, y, z, and w elements // @@ -653,7 +653,7 @@ class Point3 public: // Default constructor; does no initialization // - inline Point3( ) { }; + inline Point3( ) : mVec128((vec_float4){0.0f, 0.0f, 0.0f, 0.0f}) { }; // Construct a 3-D point from x, y, and z elements // @@ -894,7 +894,7 @@ class Quat public: // Default constructor; does no initialization // - inline Quat( ) { }; + inline Quat( ) : mVec128((vec_float4){0.0f, 0.0f, 0.0f, 0.0f}) { }; // Construct a quaternion from x, y, z, and w elements // diff --git a/ppu/include/ppu-asm.h b/ppu/include/ppu-asm.h index 1eea0c6a..f8b41c1e 100644 --- a/ppu/include/ppu-asm.h +++ b/ppu/include/ppu-asm.h @@ -5,7 +5,7 @@ #define PPU_ALIGNMENT 8 -#define __get_opd32(opd64) ((unsigned long long)((opd64) + 16)) +#define __get_opd32(opd64) ((unsigned long long)(((intptr_t)(opd64)) + 16)) #define __get_addr32(addr) (unsigned int)((unsigned long long)(addr)) @@ -52,4 +52,30 @@ asm volatile("1: mftb %[current_tb]; cmpwi 7,%[current_tb],0; beq- 7,1b" : [current_tb] "=r"(tb) : : "cr7"); \ tb;}) +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +static inline unsigned short bswap16(unsigned short val) +{ + unsigned short tmp = val; + return __lhbrx(&tmp); +} + +static inline unsigned int bswap32(unsigned int val) +{ + unsigned int tmp = val; + return __lwbrx(&tmp); +} + +static inline unsigned long long bswap64(unsigned long long val) +{ + unsigned long long tmp = val; + return __ldbrx(&tmp); +} + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + #endif From 75c3c56d2e458cd278eef25fc3a03b5f18de4016 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Thu, 16 Jul 2020 15:50:32 +0200 Subject: [PATCH 17/56] - add extended methods to jpgdec - improve jpgegdec/pngdec --- ppu/include/jpgdec/jpgdec.h | 186 +++++++++++++++++++++++++----- ppu/include/pngdec/pngdec.h | 211 ++++++++++++++++++++++++++++++----- ppu/sprx/libjpgdec/exports.h | 5 + ppu/sprx/libjpgdec/jpgdec.c | 49 ++++---- ppu/sprx/libpngdec/pngdec.c | 45 ++++---- 5 files changed, 397 insertions(+), 99 deletions(-) diff --git a/ppu/include/jpgdec/jpgdec.h b/ppu/include/jpgdec/jpgdec.h index 8a5d189e..04d1a40f 100644 --- a/ppu/include/jpgdec/jpgdec.h +++ b/ppu/include/jpgdec/jpgdec.h @@ -3,25 +3,44 @@ #include -#define JPGDEC_DISABLE 0 -#define JPGDEC_ENABLE 1 - -#define JPGDEC_TOP_TO_BOTTOM 0 -#define JPGDEC_BOTTOM_TO_TOP 1 - -#define JPGDEC_HIGH_QUALITY 0 -#define JPGDEC_LOW_QUALITY 5 +#define JPGDEC_ERROR_OK 0 +#define JPGDEC_ERROR_HEADER 0x80611101 +#define JPGDEC_ERROR_STREAM_FORMAT 0x80611102 +#define JPGDEC_ERROR_ARG 0x80611103 +#define JPGDEC_ERROR_SEQ 0x80611104 +#define JPGDEC_ERROR_BUSY 0x80611105 +#define JPGDEC_ERROR_FATAL 0x80611106 +#define JPGDEC_ERROR_OPEN_FILE 0x80611107 +#define JPGDEC_ERROR_SPU_UNSUPPORT 0x80611108 +#define JPGDEC_ERROR_CB_PARAM 0x80611109 #ifdef __cplusplus extern "C" { #endif +typedef struct _jpgdec_strm_info jpgDecStrmInfo; +typedef struct _jpgdec_strm_param jpgDecStrmParam; +typedef struct _jpgdec_disp_info jpgDecDispInfo; +typedef struct _jpgdec_disp_param jpgDecDispParam; + +typedef void* (*jpgCbCtrlMalloc)(u32 size,void *cbCtrlArg); +typedef void (*jpgCbCtrlFree)(void *ptr,void *cbCtrlArg); + +typedef s32 (*jpgCbCtrlStrm)(jpgDecStrmInfo *strmInfo,jpgDecStrmParam *strmParam,void *cbCtrlArg); +typedef s32 (*jpgCbCtrlDisp)(jpgDecDispInfo *dispInfo,jpgDecDispParam *dispParam,void *cbDispArg); + typedef enum { JPGDEC_FILE = 0, JPGDEC_BUFFER = 1 } jpgStreamSel; +typedef enum +{ + JPGDEC_SPU_THREAD_DISABLE = 0, + JPGDEC_SPU_THREAD_ENABLE = 1 +} jpgSpuThreadEna; + typedef enum { JPGDEC_GRAYSCALE = 1, @@ -32,15 +51,51 @@ typedef enum JPGDEC_ARGB = 20, } jpgColorSpace; +typedef enum +{ + JPGDEC_STATUS_FINISH = 0, + JPGDEC_STATUS_STOP = 1 +} jpgDecodeStatus; + +typedef enum +{ + JPGDEC_CONTINUE = 0, + JPGDEC_STOP = 1 +} jpgCommand; + +typedef enum +{ + JPGDEC_QUALITY = 0, + JPGDEC_FAST = 5 +} jpgMethod; + +typedef enum +{ + JPGDEC_TOP_TO_BOTTOM = 0, + JPGDEC_BOTTOM_TO_TOP = 1 +} jpgOutputMode; + +typedef enum +{ + JPGDEC_MCU_MODE = 0, + JPGDEC_LINE_MODE = 1 +} jpgBufferMode; + +typedef enum +{ + JPGDEC_RECEIVE_EVENT = 0, + JPGDEC_TRYRECEIVE_EVENT = 1 +} jpgSpuMode; + typedef struct _jpgdec_thread_in_param { - s32 enable; + u32 spu_enable; u32 ppu_prio; u32 spu_prio; - u32 malloc_func; - u32 malloc_arg; - u32 free_func; - u32 free_arg; + jpgCbCtrlMalloc malloc_func ATTRIBUTE_PRXPTR; + void *malloc_arg ATTRIBUTE_PRXPTR; + jpgCbCtrlFree free_func ATTRIBUTE_PRXPTR; + void *free_arg ATTRIBUTE_PRXPTR; } jpgDecThreadInParam; typedef struct _jpgdec_thread_out_param @@ -50,13 +105,13 @@ typedef struct _jpgdec_thread_out_param typedef struct _jpgdec_src { - jpgStreamSel stream; - u32 file_name; + u32 stream_sel; + const char *file_name ATTRIBUTE_PRXPTR; s64 file_offset; u32 file_size; - u32 stream_ptr; + void *stream_ptr ATTRIBUTE_PRXPTR; u32 stream_size; - s32 enable; + u32 spu_enable; } jpgDecSource; typedef struct _jpgdec_info @@ -64,23 +119,28 @@ typedef struct _jpgdec_info u32 width; u32 height; u32 num_comp; - jpgColorSpace space; + u32 color_space; } jpgDecInfo; typedef struct _jpgdec_data_info { f32 value; u32 output_lines; - s32 status; + u32 decode_status; } jpgDecDataInfo; +typedef struct _jpgdec_opn_info +{ + u32 init_space_allocated; +} jpgDecOpnInfo; + typedef struct _jpgdec_in_param { - u32 cmd_ptr; + vu32 *cmd_ptr ATTRIBUTE_PRXPTR; u32 down_scale; - s32 quality; - s32 mode; - jpgColorSpace space; + u32 quality_mode; + u32 output_mode; + u32 color_space; u8 alpha; u8 pad[3]; } jpgDecInParam; @@ -91,12 +151,79 @@ typedef struct _jpgdec_out_param u32 width; u32 height; u32 num_comp; - s32 mode; - jpgColorSpace space; + u32 output_mode; + u32 color_space; u32 down_scale; u32 use_memory_space; } jpgDecOutParam; +typedef struct _jpgdec_datactrl_param +{ + u64 output_bytes_per_line; +} jpgDecDataCtrlParam; + +typedef struct _jpgdec_ctrl_strm +{ + jpgCbCtrlStrm strm_func ATTRIBUTE_PRXPTR; + void *strm_arg ATTRIBUTE_PRXPTR; +} jpgDecCtrlStrm; + +typedef struct _jpgdec_ext_info +{ + u64 coeff_buffer_size; + u32 mcu_width; +} jpgDecExtInfo; + +typedef struct _jpgdec_extin_param +{ + void *coeff_buffer ATTRIBUTE_PRXPTR; + u32 buffer_mode; + u32 output_counts; + u32 spu_mode; +} jpgDecExtInParam; + +typedef struct _jpgdec_extout_param +{ + u64 output_width_byte; + u32 output_height; + u32 one_mcu_width; + u32 one_mcu_height; +} jpgDecExtOutParam; + +typedef struct _jpgdec_ctrl_disp +{ + jpgCbCtrlDisp disp_func ATTRIBUTE_PRXPTR; + void *disp_arg ATTRIBUTE_PRXPTR; +} jpgDecCtrlDisp; + +struct _jpgdec_strm_info +{ + u32 decoded_strm_size; +}; + +struct _jpgdec_strm_param +{ + void *strm_ptr ATTRIBUTE_PRXPTR; + u32 strm_size; +}; + +struct _jpgdec_disp_info +{ + u64 output_frame_width_byte; + u32 output_frame_height; + u64 output_start_xbyte; + u32 output_start_y; + u64 output_width_byte; + u32 output_height; + u32 output_components; + void *output_image ATTRIBUTE_PRXPTR; +}; + +struct _jpgdec_disp_param +{ + void *next_output_image ATTRIBUTE_PRXPTR; +}; + typedef struct _jpg_data { void *bmp_out; @@ -107,13 +234,18 @@ typedef struct _jpg_data } jpgData; s32 jpgDecCreate(s32 *handle,jpgDecThreadInParam *in,jpgDecThreadOutParam *out); -s32 jpgDecOpen(s32 handle,s32 *subhandle,const jpgDecSource *src,u32 *space_allocated); +s32 jpgDecOpen(s32 handle,s32 *subhandle,const jpgDecSource *src,jpgDecOpnInfo *openInfo); s32 jpgDecReadHeader(s32 handle,s32 subhandle,jpgDecInfo *info); s32 jpgDecSetParameter(s32 handle,s32 subhandle,const jpgDecInParam *in,jpgDecOutParam *out); -s32 jpgDecDecodeData(s32 handle,s32 subhandle,u8 *data,const u64 *bytes_per_line,jpgDecDataInfo *info); +s32 jpgDecDecodeData(s32 handle,s32 subhandle,u8 *data,const jpgDecDataCtrlParam *dataCtrlParam,jpgDecDataInfo *info); s32 jpgDecClose(s32 handle,s32 subhandle); s32 jpgDecDestroy(s32 handle); +s32 jpgDecExtOpen(s32 handle,s32 *subhandle,const jpgDecSource *src,jpgDecOpnInfo *openInfo,const jpgDecCtrlStrm *cbCtrlStrm); +s32 jpgDecExtReadHeader(s32 handle,s32 subhandle,jpgDecInfo *info,jpgDecExtInfo *extInfo); +s32 jpgDecExtSetParameter(s32 handle,s32 subhandle,const jpgDecInParam *inParam,jpgDecOutParam *outParam,const jpgDecExtInParam *extInParam,jpgDecExtOutParam *extOutParam); +s32 jpgDecExtDecodeData(s32 handle,s32 subhandle,u8 *data,const jpgDecDataCtrlParam *dataCtrlParam,jpgDecDataInfo *info,const jpgDecCtrlDisp *cbCtrlDisp,jpgDecDispParam *dispParam); + s32 jpgLoadFromFile(const char *filename,jpgData *out); s32 jpgLoadFromBuffer(const void *buffer,u32 size,jpgData *out); diff --git a/ppu/include/pngdec/pngdec.h b/ppu/include/pngdec/pngdec.h index 7db0cd7e..6d238086 100644 --- a/ppu/include/pngdec/pngdec.h +++ b/ppu/include/pngdec/pngdec.h @@ -3,16 +3,40 @@ #include -#define PNGDEC_DISABLE 0 -#define PNGDEC_ENABLE 1 - -#define PNGDEC_TOP_TO_BOTTOM 0 -#define PNGDEC_BOTTOM_TO_TOP 1 +#define PNGDEC_ERROR_OK 0 +#define PNGDEC_ERROR_HEADER 0x80611201 +#define PNGDEC_ERROR_STREAM_FORMAT 0x80611202 +#define PNGDEC_ERROR_ARG 0x80611203 +#define PNGDEC_ERROR_SEQ 0x80611204 +#define PNGDEC_ERROR_BUSY 0x80611205 +#define PNGDEC_ERROR_FATAL 0x80611206 +#define PNGDEC_ERROR_OPEN_FILE 0x80611207 +#define PNGDEC_ERROR_SPU_UNSUPPORT 0x80611208 +#define PNGDEC_ERROR_SPU_ERROR 0x80611209 +#define PNGDEC_ERROR_CB_PARAM 0x8061120a #ifdef __cplusplus extern "C" { #endif +typedef struct _pngdec_stream_info pngDecStreamInfo; +typedef struct _pngdec_stream_param pngDecStreamParam; +typedef struct _pngdec_disp_info pngDecDispInfo; +typedef struct _pngdec_disp_param pngDecDispParam; + +typedef void* (*pngCbCtrlMalloc)(u32 size,void *cbCtrlArg); +typedef void (*pngCbCtrlFree)(void *ptr,void *cbCtrlArg); + +typedef s32 (*pngCbCtrlStrm)(pngDecStreamInfo *strmInfo,pngDecStreamParam *strmParam,void *cbStrmArg); + +typedef s32 (*pngCbCtrlDisp)(pngDecDispInfo *dispInfo,pngDecDispParam *dispParam,void *cbDispArg); + +typedef enum +{ + PNGDEC_SPU_THREAD_DISABLE = 0, + PNGDEC_SPU_THREAD_ENABLE = 1 +} pngSpuThreadEna; + typedef enum { PNGDEC_FILE = 0, @@ -29,15 +53,62 @@ typedef enum PNGDEC_ARGB = 20 } pngColorSpace; +typedef enum +{ + PNGDEC_NO_INTERLACE = 0, + PNGDEC_ADAM7_INTERLACE = 1 +} pngInterlaceMode; + +typedef enum +{ + PNGDEC_STATUS_FINISH = 0, + PNGDEC_STATUS_STOP = 1 +} pngDecodeStatus; + +typedef enum +{ + PNGDEC_CONTINUE = 0, + PNGDEC_STOP = 1 +} pngCommand; + +typedef enum +{ + PNGDEC_TOP_TO_BOTTOM = 0, + PNGDEC_BOTTOM_TO_TOP = 1 +} pngOutputMode; + +typedef enum +{ + PNGDEC_1BYTE_PER_NPIXEL = 0, + PNGDEC_1BYTE_PER_1PIXEL = 1 +} pngPackFlag; + +typedef enum +{ + PNGDEC_STREAM_ALPHA = 0, + PNGDEC_FIX_ALPHA = 1 +} pngAlphaSelect; + +typedef enum +{ + PNGDEC_LINE_MODE = 1 /*! \brief LINE mode.
Decode the number of lines specified with output_counts at one time. */ +} pngBufferMode; + +typedef enum +{ + PNGDEC_RECEIVE_EVENT = 0, /*! \brief Decoder uses \ref spursQueuePopBegin to wait for termination of Disp callback function.
Transistion to WAITING state and may be affected by the behavior of other SPU threads. */ + PNGDEC_TRYRECEIVE_EVENT = 1 /*! \brief Decoder uses \ref spursQueueTryPopBegin to wait for termination of Disp callback function.
Since there is no state transistion, it is not affected by the behavior of other SPU threads. */ +} pngSpuMode; + typedef struct _pngdec_thread_in_param { - s32 enable; + u32 spu_enable; u32 ppu_prio; u32 spu_prio; - u32 malloc_func; - u32 malloc_arg; - u32 free_func; - u32 free_arg; + pngCbCtrlMalloc malloc_func ATTRIBUTE_PRXPTR; + void *malloc_arg ATTRIBUTE_PRXPTR; + pngCbCtrlFree free_func ATTRIBUTE_PRXPTR; + void *free_arg ATTRIBUTE_PRXPTR; } pngDecThreadInParam; typedef struct _pngdec_thread_out_param @@ -47,13 +118,17 @@ typedef struct _pngdec_thread_out_param typedef struct _pngdec_src { - pngStreamSel stream; - u32 file_name; + /*! \brief Input stream selection. Possible values: + - \ref PNGDEC_FILE + - \ref PNGDEC_BUFFER + */ + u32 stream_sel; + const char *file_name ATTRIBUTE_PRXPTR; s64 file_offset; u32 file_size; - u32 stream_ptr; + void *stream_ptr ATTRIBUTE_PRXPTR; u32 stream_size; - s32 enable; + u32 spu_enable; } pngDecSource; typedef struct _pngdec_info @@ -61,9 +136,9 @@ typedef struct _pngdec_info u32 width; u32 height; u32 num_comp; - pngColorSpace space; + u32 color_space; u32 bit_depth; - s32 interlaced; + u32 interlace_mode; u32 chunk_info; } pngDecInfo; @@ -72,17 +147,17 @@ typedef struct _pngdec_data_info u32 chunk_info; u32 num_text; u32 num_unk_chunk; - s32 status; + u32 decode_status; } pngDecDataInfo; typedef struct _pngdec_in_param { - u32 cmd_ptr; - s32 mode; - pngColorSpace space; + vu32 *cmd_ptr ATTRIBUTE_PRXPTR; + u32 output_mode; + u32 color_space; u32 bit_depth; - s32 pack_flag; - s32 alpha_select; + u32 pack_flag; + u32 alpha_select; u32 alpha; } pngDecInParam; @@ -93,11 +168,90 @@ typedef struct _pngdec_out_param u32 height; u32 num_comp; u32 bit_depth; - s32 mode; - pngColorSpace space; + u32 output_mode; + u32 color_space; u32 use_memory_space; } pngDecOutParam; +typedef struct _pngdec_datactrl_param +{ + u64 output_bytes_per_line; +} pngDecDataCtrlParam; + +typedef struct _pngdec_opn_info +{ + u32 init_space_allocated; +} pngDecOpnInfo; + +typedef struct _pngdec_opn_param +{ + u32 select_chunk; +} pngDecOpnParam; + +typedef struct _pngdec_ctrl_strm +{ + pngCbCtrlStrm stream_func ATTRIBUTE_PRXPTR; + void *stream_arg ATTRIBUTE_PRXPTR; +} pngDecCtrlStrm; + +typedef struct _pngdec_ext_info +{ + u64 reserved; +} pngDecExtInfo; + +typedef struct _pngdec_extin_param +{ + /*! \brief Mode of output of partial images. Possible values: + - \ref PNGDEC_LINE_MODE + */ + u32 buffer_mode; + u32 output_counts; + u32 spu_mode; +} pngDecExtInParam; + +typedef struct _pngdec_extout_param +{ + u64 output_width_bytes; + u32 output_height; +} pngDecExtOutParam; + +typedef struct _pngdec_ctrl_disp +{ + pngCbCtrlDisp disp_func ATTRIBUTE_PRXPTR; + void *disp_arg ATTRIBUTE_PRXPTR; +} pngDecCtrlDisp; + +struct _pngdec_stream_info +{ + u32 decoded_stream_size; +}; + +struct _pngdec_stream_param +{ + void *strm_ptr ATTRIBUTE_PRXPTR; + u32 strm_size; +}; + +struct _pngdec_disp_param +{ + void *next_output_image ATTRIBUTE_PRXPTR; +}; + +struct _pngdec_disp_info +{ + u64 output_frame_width_bytes; + u32 output_frame_height; + u64 output_start_xbyte; + u32 output_start_y; + u64 output_width_byte; + u32 output_height; + u32 output_bit_depth; + u32 output_components; + u32 next_output_start_y; + u32 scan_pass_count; + void *output_image ATTRIBUTE_PRXPTR; +}; + typedef struct _png_data { void *bmp_out; @@ -108,13 +262,18 @@ typedef struct _png_data } pngData; s32 pngDecCreate(s32 *handle,pngDecThreadInParam *in,pngDecThreadOutParam *out); -s32 pngDecOpen(s32 handle,s32 *subhandle,const pngDecSource *src,u32 *space_allocated); +s32 pngDecOpen(s32 handle,s32 *subhandle,const pngDecSource *src,pngDecOpnInfo *open_info); s32 pngDecReadHeader(s32 handle,s32 subhandle,pngDecInfo *info); s32 pngDecSetParameter(s32 handle,s32 subhandle,const pngDecInParam *in,pngDecOutParam *out); -s32 pngDecDecodeData(s32 handle,s32 subhandle,u8 *data,const u64 *bytes_per_line,pngDecDataInfo *info); +s32 pngDecDecodeData(s32 handle,s32 subhandle,u8 *data,const pngDecDataCtrlParam *dataCtrlParam,pngDecDataInfo *info); s32 pngDecClose(s32 handle,s32 subhandle); s32 pngDecDestroy(s32 handle); +s32 pngDecExtOpen(s32 handle,s32 *subhandle,const pngDecSource *src,pngDecOpnInfo *open_info,const pngDecCtrlStrm *cbCtrlStrm,const pngDecOpnParam *opnParam); +s32 pngDecExtReadHeader(s32 handle,s32 subhandle,pngDecInfo *info,pngDecExtInfo *extInfo); +s32 pngDecExtSetParameter(s32 handle,s32 subhandle,const pngDecInParam *inParam,pngDecOutParam *outParam,const pngDecExtInParam *extInParam,pngDecExtOutParam *extOutParam); +s32 pngDecExtDecodeData(s32 handle,s32 subhandle,u8 *data,const pngDecDataCtrlParam *dataCtrlParam,pngDecDataInfo *dataOutInfo,const pngDecCtrlDisp *cbCtrlDisp,pngDecDispParam *dispParam); + s32 pngLoadFromFile(const char *filename,pngData *out); s32 pngLoadFromBuffer(const void *buffer,u32 size,pngData *out); diff --git a/ppu/sprx/libjpgdec/exports.h b/ppu/sprx/libjpgdec/exports.h index 0154aea7..9f78f1de 100644 --- a/ppu/sprx/libjpgdec/exports.h +++ b/ppu/sprx/libjpgdec/exports.h @@ -9,4 +9,9 @@ EXPORT(jpgDecReadHeader, 0x6d9ebccf); EXPORT(jpgDecDecodeData, 0xaf8bb012); EXPORT(jpgDecSetParameter, 0xe08f3910); +EXPORT(jpgDecExtSetParameter, 0x65cbbb16); +EXPORT(jpgDecExtDecodeData, 0x716f8792); +EXPORT(jpgDecExtOpen, 0xa9f703e3); +EXPORT(jpgDecExtReadHeader, 0xb91eb3d2); + #endif diff --git a/ppu/sprx/libjpgdec/jpgdec.c b/ppu/sprx/libjpgdec/jpgdec.c index a2f218fc..8314ddbb 100644 --- a/ppu/sprx/libjpgdec/jpgdec.c +++ b/ppu/sprx/libjpgdec/jpgdec.c @@ -45,53 +45,54 @@ static void jpg_free(void *ptr,void *usrdata) static s32 decodeJPEG(jpgDecSource *src,jpgData *out) { s32 mHandle,sHandle,ret; - u32 space_allocated; - u64 bytes_per_line; jpgDecInfo DecInfo; jpgDecInParam inParam; + jpgDecOpnInfo openInfo; jpgDecOutParam outParam; jpgDecDataInfo DecDataInfo; jpgDecThreadInParam InThdParam; jpgDecThreadOutParam OutThdParam; + jpgDecDataCtrlParam dataCtrlParam; - InThdParam.enable = 0; + InThdParam.spu_enable = JPGDEC_SPU_THREAD_DISABLE; InThdParam.ppu_prio = 512; InThdParam.spu_prio = 200; - InThdParam.malloc_func = __get_addr32(__get_opd32(jpg_malloc)); - InThdParam.malloc_arg = 0; // no args - InThdParam.free_func = __get_addr32(__get_opd32(jpg_free)); - InThdParam.free_arg = 0; // no args + InThdParam.malloc_func = (jpgCbCtrlMalloc)__get_opd32(jpg_malloc); + InThdParam.malloc_arg = NULL; + InThdParam.free_func = (jpgCbCtrlFree)__get_opd32(jpg_free); + InThdParam.free_arg = NULL; ret = jpgDecCreate(&mHandle,&InThdParam,&OutThdParam); out->bmp_out = NULL; if(ret==0) { - ret = jpgDecOpen(mHandle,&sHandle,src,&space_allocated); + ret = jpgDecOpen(mHandle,&sHandle,src,&openInfo); if(ret==0) { ret = jpgDecReadHeader(mHandle,sHandle,&DecInfo); - if(ret==0 && DecInfo.space==0) ret = -1; + if(ret==0 && DecInfo.color_space==0) ret = -1; if(ret==0) { - inParam.cmd_ptr = 0; + inParam.cmd_ptr = NULL; inParam.down_scale = 1; - inParam.quality = JPGDEC_LOW_QUALITY; - inParam.mode = JPGDEC_TOP_TO_BOTTOM; - inParam.space = JPGDEC_ARGB; + inParam.quality_mode = JPGDEC_FAST; + inParam.output_mode = JPGDEC_TOP_TO_BOTTOM; + inParam.color_space = JPGDEC_ARGB; inParam.alpha = 0xff; ret = jpgDecSetParameter(mHandle,sHandle,&inParam,&outParam); } if(ret==0) { - out->pitch = bytes_per_line = outParam.width*4; - out->bmp_out = malloc(bytes_per_line*outParam.height); + out->pitch = outParam.width*4; + out->bmp_out = malloc(out->pitch*outParam.height); if(!out->bmp_out) ret = -1; else { - memset(out->bmp_out,0,(bytes_per_line*outParam.height)); + memset(out->bmp_out,0,(out->pitch*outParam.height)); - ret = jpgDecDecodeData(mHandle,sHandle,out->bmp_out,&bytes_per_line,&DecDataInfo); - if(ret==0 && DecDataInfo.status==0) { + dataCtrlParam.output_bytes_per_line = out->pitch; + ret = jpgDecDecodeData(mHandle,sHandle,out->bmp_out,&dataCtrlParam,&DecDataInfo); + if(ret==0 && DecDataInfo.decode_status==0) { out->width = outParam.width; out->height = outParam.height; @@ -117,9 +118,9 @@ s32 jpgLoadFromFile(const char *filename,jpgData *out) memset(&source,0,sizeof(jpgDecSource)); - source.stream = JPGDEC_FILE; - source.file_name = __get_addr32(filename); - source.enable = JPGDEC_DISABLE; + source.stream_sel = JPGDEC_FILE; + source.file_name = filename; + source.spu_enable = JPGDEC_SPU_THREAD_DISABLE; return decodeJPEG(&source,out); } @@ -130,10 +131,10 @@ s32 jpgLoadFromBuffer(const void *buffer,u32 size,jpgData *out) memset(&source,0,sizeof(jpgDecSource)); - source.stream = JPGDEC_BUFFER; - source.stream_ptr = __get_addr32(buffer); + source.stream_sel = JPGDEC_BUFFER; + source.stream_ptr = (void*)buffer; source.stream_size = size; - source.enable = JPGDEC_DISABLE; + source.spu_enable = JPGDEC_SPU_THREAD_DISABLE; return decodeJPEG(&source,out); } diff --git a/ppu/sprx/libpngdec/pngdec.c b/ppu/sprx/libpngdec/pngdec.c index c51cea2d..78cad20b 100644 --- a/ppu/sprx/libpngdec/pngdec.c +++ b/ppu/sprx/libpngdec/pngdec.c @@ -45,37 +45,37 @@ static void png_free(void *ptr,void *usrdata) static s32 decodePNG(pngDecSource *src,pngData *out) { s32 mHandle,sHandle,ret; - u32 space_allocated; - u64 bytes_per_line; pngDecInfo DecInfo; + pngDecOpnInfo openInfo; pngDecInParam inParam; pngDecOutParam outParam; pngDecDataInfo DecDataInfo; pngDecThreadInParam InThdParam; pngDecThreadOutParam OutThdParam; + pngDecDataCtrlParam dataCtrlParam; - InThdParam.enable = 0; + InThdParam.spu_enable = PNGDEC_SPU_THREAD_DISABLE; InThdParam.ppu_prio = 512; InThdParam.spu_prio = 200; - InThdParam.malloc_func = __get_addr32(__get_opd32(png_malloc)); - InThdParam.malloc_arg = 0; // no args - InThdParam.free_func = __get_addr32(__get_opd32(png_free)); - InThdParam.free_arg = 0; // no args + InThdParam.malloc_func = (pngCbCtrlMalloc)__get_opd32(png_malloc); + InThdParam.malloc_arg = NULL; + InThdParam.free_func = (pngCbCtrlFree)__get_opd32(png_free); + InThdParam.free_arg = NULL; ret= pngDecCreate(&mHandle, &InThdParam, &OutThdParam); out->bmp_out = NULL; if(ret==0) { - ret = pngDecOpen(mHandle,&sHandle,src,&space_allocated); + ret = pngDecOpen(mHandle,&sHandle,src,&openInfo); if(ret==0) { ret = pngDecReadHeader(mHandle,sHandle,&DecInfo); if(ret==0) { inParam.cmd_ptr = 0; - inParam.mode = PNGDEC_TOP_TO_BOTTOM; - inParam.space = PNGDEC_ARGB; + inParam.output_mode = PNGDEC_TOP_TO_BOTTOM; + inParam.color_space = PNGDEC_ARGB; inParam.bit_depth = 8; - inParam.pack_flag = 1; - if(DecInfo.space==PNGDEC_GRAYSCALE_ALPHA || DecInfo.space==PNGDEC_RGBA || DecInfo.chunk_info&0x10) + inParam.pack_flag = PNGDEC_1BYTE_PER_1PIXEL; + if(DecInfo.color_space==PNGDEC_GRAYSCALE_ALPHA || DecInfo.color_space==PNGDEC_RGBA || DecInfo.chunk_info&0x10) inParam.alpha_select = 0; else inParam.alpha_select = 1; @@ -86,15 +86,16 @@ static s32 decodePNG(pngDecSource *src,pngData *out) } if(ret==0) { - out->pitch = bytes_per_line = outParam.width*4; + out->pitch = outParam.width*4; out->bmp_out = malloc(out->pitch*outParam.height); if(!out->bmp_out) ret = -1; else { - memset(out->bmp_out,0,(bytes_per_line*outParam.height)); + memset(out->bmp_out,0,(out->pitch*outParam.height)); - ret = pngDecDecodeData(mHandle,sHandle,out->bmp_out,&bytes_per_line,&DecDataInfo); - if(ret==0 && DecDataInfo.status==0) { + dataCtrlParam.output_bytes_per_line = out->pitch; + ret = pngDecDecodeData(mHandle,sHandle,out->bmp_out,&dataCtrlParam,&DecDataInfo); + if(ret==0 && DecDataInfo.decode_status==0) { out->width = outParam.width; out->height = outParam.height; @@ -120,9 +121,9 @@ s32 pngLoadFromFile(const char *filename,pngData *out) memset(&source,0,sizeof(pngDecSource)); - source.stream = PNGDEC_FILE; - source.file_name = __get_addr32(filename); - source.enable = PNGDEC_DISABLE; + source.stream_sel = PNGDEC_FILE; + source.file_name = filename; + source.spu_enable = PNGDEC_SPU_THREAD_DISABLE; return decodePNG(&source,out); } @@ -133,10 +134,10 @@ s32 pngLoadFromBuffer(const void *buffer,u32 size,pngData *out) memset(&source,0,sizeof(pngDecSource)); - source.stream = PNGDEC_BUFFER; - source.stream_ptr = __get_addr32(buffer); + source.stream_sel = PNGDEC_BUFFER; + source.stream_ptr = (void*)buffer; source.stream_size = size; - source.enable = PNGDEC_DISABLE; + source.spu_enable = PNGDEC_SPU_THREAD_DISABLE; return decodePNG(&source,out); } From 62b372bf03334972fc23583defa250499fa41479 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Thu, 16 Jul 2020 15:51:19 +0200 Subject: [PATCH 18/56] - improvements to the cg compiler --- tools/cgcomp/include/fpparser.h | 9 +- tools/cgcomp/source/compilerfp.cpp | 10 ++- tools/cgcomp/source/fpparser.cpp | 140 +++++++++++++++++++---------- 3 files changed, 108 insertions(+), 51 deletions(-) diff --git a/tools/cgcomp/include/fpparser.h b/tools/cgcomp/include/fpparser.h index 8223acfa..b15cc37e 100644 --- a/tools/cgcomp/include/fpparser.h +++ b/tools/cgcomp/include/fpparser.h @@ -11,6 +11,7 @@ typedef struct _oparam { std::string alias; s32 index; + u8 is_fp16; } oparam; class CFPParser : public CParser @@ -29,9 +30,13 @@ class CFPParser : public CParser void ParseOutput(const char *param_str); - const char* ParseOutputReg(const char *token,s32 *reg); + const char* ParseOutputReg(const char *token,s32 *reg,u8 *is_fp16); const char* ParseInputReg(const char *token,s32 *reg); - const char* ParseOutputRegAlias(const char *token,s32 *reg); + const char* ParseOutputRegAlias(const char *token,s32 *reg,u8 *is_fp16); + + void SetNoneDestReg(struct nvfx_insn *insn); + + u8 IsPCDisablingInstruction(struct nvfx_insn *insn); opcode FindOpcode(const char *mnemonic); diff --git a/tools/cgcomp/source/compilerfp.cpp b/tools/cgcomp/source/compilerfp.cpp index bf0a232d..cf4184c4 100644 --- a/tools/cgcomp/source/compilerfp.cpp +++ b/tools/cgcomp/source/compilerfp.cpp @@ -307,6 +307,8 @@ void CCompilerFP::emit_src(struct nvfx_insn *insn,s32 pos,bool *have_const) if(src->reg.index>=NVFX_FP_OP_INPUT_SRC_TC(0) && src->reg.index<=NVFX_FP_OP_INPUT_SRC_TC(7)) { param fpi = GetInputAttrib(src->reg.index); + hw[3] |= ((insn->disable_pc << NV40_FP_OP_DISABLE_PC_SHIFT) | (0x7fc << NV40_FP_OP_ADDR_INDEX_SHIFT)); + if((int)fpi.index!=-1) { if(fpi.type>PARAM_FLOAT2) m_nTexcoord3D |= (1 << (src->reg.index - NVFX_FP_OP_INPUT_SRC_TC0)); @@ -547,16 +549,16 @@ void CCompilerFP::emit_lrp(struct nvfx_insn *insn) void CCompilerFP::emit_pow(struct nvfx_insn *insn) { struct nvfx_insn tmp_insn; - struct nvfx_src tmp = nvfx_src(temp()); + struct nvfx_src src = nvfx_src(insn->dst); struct nvfx_src none = nvfx_src(nvfx_reg(NVFXSR_NONE,0)); - tmp_insn = arith(0,tmp.reg, NVFX_FP_MASK_X, insn->src[0], none, none); + tmp_insn = arith(0,insn->dst, NVFX_FP_MASK_X, insn->src[0], none, none); emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_LG2); - tmp_insn = arith(0,tmp.reg, NVFX_FP_MASK_X, swz(tmp, X, X, X, X),insn->src[1], none); + tmp_insn = arith(0,insn->dst, NVFX_FP_MASK_X, swz(src, X, X, X, X),insn->src[1], none); emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_MUL); - tmp_insn = arith_ctor(insn,insn->dst,swz(tmp, X, X, X, X), none, none); + tmp_insn = arith_ctor(insn,insn->dst,swz(src, X, X, X, X), none, none); emit_insn(&tmp_insn,NVFX_FP_OP_OPCODE_EX2); } diff --git a/tools/cgcomp/source/fpparser.cpp b/tools/cgcomp/source/fpparser.cpp index 57282555..75296a0d 100644 --- a/tools/cgcomp/source/fpparser.cpp +++ b/tools/cgcomp/source/fpparser.cpp @@ -111,8 +111,8 @@ struct _opcode { "XPD", OPCODE_X2D, INPUT_2V, OUTPUT_V, _R | _H | _C | _S }, // { "END", OPCODE_END,0,0,0 }, - { NULL, (enum nvfx_opcode) -1, 0, 0, 0 } }; +static const size_t FP_OPCODES_CNT = sizeof(fp_opcodes)/sizeof(struct _opcode); static ioset fp_inputs[] = { @@ -135,7 +135,7 @@ static ioset fp_inputs[] = { "TEX6", 10 }, { "TEX7", 11 } }; -static const u32 FP_INPUTS_CNT = sizeof(fp_inputs)/sizeof(ioset); +static const size_t FP_INPUTS_CNT = sizeof(fp_inputs)/sizeof(ioset); static ioset fp_outputs[] = { @@ -145,7 +145,7 @@ static ioset fp_outputs[] = { "COLH", 0}, { "DEPR", 1} }; -static const u32 FP_OUTPUTS_CNT = sizeof(fp_outputs)/sizeof(ioset); +static const size_t FP_OUTPUTS_CNT = sizeof(fp_outputs)/sizeof(ioset); CFPParser::CFPParser() : CParser() { @@ -195,9 +195,9 @@ int CFPParser::Parse(const char *str) continue; } - char *col_ptr = NULL; - char *opcode = NULL; - char *ptr = line; + const char *col_ptr = NULL; + const char *ptr = line; + const char *param_str = NULL; if((col_ptr = strstr((char*)ptr,":"))!=NULL) { int j = 0; @@ -210,41 +210,43 @@ int CFPParser::Parse(const char *str) } if(valid) { - (void)strtok(ptr,":\x20"); + (void)strtok((char*)ptr,":\x20"); ptr = col_ptr + 1; } } - opcode = strtok(ptr," "); + ptr = SkipSpaces(ptr); + if ((param_str = strstr(ptr, "OPTION"))!=NULL) { + param_str = SkipSpaces(param_str + 6); + if(strncasecmp(param_str,"NV_fragment_program2",20)==0) + m_nOption |= NV_OPTION_FP2; + continue; + } + else if ((param_str = strstr(ptr, "PARAM"))!=NULL) + continue; + else if ((param_str = strstr(ptr, "TEMP"))!=NULL) + continue; + else if ((param_str = strstr(ptr, "OUTPUT"))!=NULL) { + ParseOutput(ptr); + continue; + } else { + const char *opcode = strtok((char*)ptr," "); + struct _opcode opc = FindOpcode(opcode); - if(opcode) { - const char *param_str = SkipSpaces(strtok(NULL,"\0")); - if(strcasecmp(opcode,"OPTION")==0) { - if(strncasecmp(param_str,"NV_fragment_program2",20)==0) - m_nOption |= NV_OPTION_FP2; - continue; - } else if(strcasecmp(opcode,"PARAM")==0) - continue; - else if(strcasecmp(opcode,"TEMP")==0) - continue; - else if(strcasecmp(opcode,"OUTPUT")==0) { - ParseOutput(param_str); + if(opc.opcode>=MAX_OPCODE) continue; - } else { - struct _opcode opc = FindOpcode(opcode); - insn = &m_pInstructions[m_nInstructions]; - - if(opc.opcode>=MAX_OPCODE) continue; - - InitInstruction(insn,opc.opcode); - if(opc.opcode==OPCODE_END) { - m_nInstructions++; - break; - } + + insn = &m_pInstructions[m_nInstructions]; + param_str = SkipSpaces(strtok(NULL,"\0")); - ParseInstruction(insn,&opc,param_str); + InitInstruction(insn,opc.opcode); + if(opc.opcode==OPCODE_END) { m_nInstructions++; + break; } + + ParseInstruction(insn,&opc,param_str); + m_nInstructions++; } } return 0; @@ -257,9 +259,12 @@ void CFPParser::ParseInstruction(struct nvfx_insn *insn,opcode *opc,const char * insn->precision = opc->suffixes&(_R|_H|_X); insn->sat = ((opc->suffixes&_S) ? TRUE : FALSE); insn->cc_update = ((opc->suffixes&_C) ? TRUE : FALSE); + insn->disable_pc = IsPCDisablingInstruction(insn); if(opc->outputs==OUTPUT_S || opc->outputs==OUTPUT_V) { ParseMaskedDstReg(token,insn); + } else if(opc->outputs==OUTPUT_NONE) { + SetNoneDestReg(insn); } if(opc->outputs!=OUTPUT_NONE && opc->inputs!=INPUT_NONE) { @@ -317,7 +322,6 @@ void CFPParser::ParseInstruction(struct nvfx_insn *insn,opcode *opc,const char * opcode CFPParser::FindOpcode(const char *mnemonic) { - const struct _opcode *inst; struct _opcode result; result.name = NULL; @@ -326,7 +330,8 @@ opcode CFPParser::FindOpcode(const char *mnemonic) result.outputs = 0; result.suffixes = 0; - for(inst=fp_opcodes;inst->name;inst++) { + for(size_t i=0;iname,strlen(inst->name))==0) { int i = strlen(inst->name); @@ -393,19 +398,22 @@ s32 CFPParser::ConvertInputReg(const char *token) void CFPParser::ParseOutput(const char *param_str) { oparam p; + u8 is_fp16 = 0; s32 reg = -1; - const char *token = SkipSpaces(strtok((char*)param_str," =")); + const char *param = SkipSpaces(strstr(param_str, "OUTPUT") + 6); + const char *token = SkipSpaces(strtok((char*)param," =")); const char *name = SkipSpaces(strtok(NULL,"=\0")); - ParseOutputReg(name,®); + ParseOutputReg(name,®,&is_fp16); p.alias = token; p.index = reg; + p.is_fp16 = (strncmp(param_str, "SHORT", 5) == 0) || is_fp16; m_lOParameters.push_back(p); } -const char* CFPParser::ParseOutputReg(const char *token, s32 *reg) +const char* CFPParser::ParseOutputReg(const char *token, s32 *reg,u8 *is_fp16) { if(isdigit(*token)) { char *p = (char*)token; @@ -420,6 +428,21 @@ const char* CFPParser::ParseOutputReg(const char *token, s32 *reg) size_t tlen = strlen(fp_outputs[i].name.c_str()); if(strncmp(token,fp_outputs[i].name.c_str(),tlen)==0) { *reg = fp_outputs[i].index; + if(strcmp(fp_outputs[i].name.c_str(), "result.color")==0 || + strcmp(fp_outputs[i].name.c_str(), "COLR")==0 || + strcmp(fp_outputs[i].name.c_str(), "COLH")==0) + { + if(strcmp(fp_outputs[i].name.c_str(), "COLH")==0) + *is_fp16 = 1; + + if(token[tlen]=='[' && isdigit(token[tlen+1])) { + char *p = (char*)(token + tlen + 1); + while(isdigit(*p)) p++; + + *reg = *reg + atoi(token + tlen + 1) + 1; + tlen = (p - token) + 1; + } + } return (token + tlen); } } @@ -459,45 +482,47 @@ const char* CFPParser::ParseInputReg(const char *token, s32 *reg) return NULL; } -const char* CFPParser::ParseOutputRegAlias(const char *token,s32 *reg) +const char* CFPParser::ParseOutputRegAlias(const char *token,s32 *reg,u8 *is_fp16) { std::list::iterator it = m_lOParameters.begin(); + *is_fp16 = 0; + for(;it!=m_lOParameters.end();it++) { if(strncmp(token,it->alias.c_str(),it->alias.size())==0) { *reg = it->index; + *is_fp16 = it->is_fp16; return (token + it->alias.size()); } } - return NULL; + return ParseOutputReg(token,reg,is_fp16); } void CFPParser::ParseMaskedDstReg(const char *token,struct nvfx_insn *insn) { s32 idx; + u8 is_fp16 = 0; if(!token) return; if(strncmp(token,"RC",2)==0 || strncmp(token,"HC",2)==0) { - insn->dst.type = NVFXSR_NONE; - insn->dst.is_fp16 = (token[0]=='H'); - insn->dst.index = 0x3f; - + SetNoneDestReg(insn); token += 2; } else if(token[0]=='R' || token[0]=='H') { insn->dst.type = NVFXSR_TEMP; insn->dst.is_fp16 = (token[0]=='H'); token = ParseTempReg(token,&insn->dst.index); } else if(token[0]=='o' && token[1]=='[') { - token = ParseOutputReg(&token[2],&idx); + token = ParseOutputReg(&token[2],&idx,&is_fp16); token++; insn->dst.type = NVFXSR_OUTPUT; insn->dst.index = idx; + insn->dst.is_fp16 = is_fp16; } else { - token = ParseOutputRegAlias(token,&idx); + token = ParseOutputRegAlias(token,&idx,&is_fp16); insn->dst.type = NVFXSR_OUTPUT; insn->dst.index = idx; @@ -650,3 +675,28 @@ const char* CFPParser::ParseOutputMask(const char *token,u8 *mask) } return token; } + +void CFPParser::SetNoneDestReg(struct nvfx_insn *insn) +{ + insn->dst.type = NVFXSR_NONE; + insn->dst.index = 0x3f; + insn->dst.is_fp16 = 0; //always treat as fp32 (on RSX there's only RC) +} + +u8 CFPParser::IsPCDisablingInstruction(struct nvfx_insn *insn) +{ + switch(insn->op) { + case OPCODE_DP2: + case OPCODE_DP2A: + case OPCODE_DP3: + case OPCODE_DP4: + case OPCODE_MUL: + case OPCODE_DIV: + case OPCODE_NRM3: + return 1; + case OPCODE_TEX: + if(insn->tex_target == PARAM_SAMPLERCUBE) return 1; + default: + return 0; + } +} From bcbd2768fba02248fdb377c5d5ece951f353e917 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Thu, 16 Jul 2020 15:52:46 +0200 Subject: [PATCH 19/56] - add additional defines --- ppu/include/io/pad.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ppu/include/io/pad.h b/ppu/include/io/pad.h index 8deca0d1..61fabda6 100644 --- a/ppu/include/io/pad.h +++ b/ppu/include/io/pad.h @@ -17,6 +17,16 @@ #define PAD_TYPE_REMOTE (4) /*!< BD Remote Controller */ #define PAD_TYPE_LDD (5) /*!< Custom Controller */ +#define PAD_PRESS_MODE_ON (1) +#define PAD_PRESS_MODE_OFF (0) + +#define PAD_INFO_SUPPORTED_PRESS_MODE (1) + +#define PAD_SENSOR_MODE_ON (1) +#define PAD_SENSOR_MODE_OFF (0) + +#define PAD_INFO_SUPPORTED_SENSOR_MODE (1) + #ifdef __cplusplus extern "C" { #endif From f92c0f20043c0788b5647006a6a477b9d1a894f7 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Thu, 16 Jul 2020 16:13:50 +0200 Subject: [PATCH 20/56] - proper const'ing - refactor shader programs API (introduced *Index methods to get the index of a param) - RSX api refactorings according to the shader program API refactoring(s) --- ppu/include/rsx/commands_inc.h | 144 ++++++++++++----------- ppu/include/rsx/rsx.h | 18 ++- ppu/include/rsx/rsx_program.h | 88 +++++++++++--- ppu/librsx/Makefile | 3 +- ppu/librsx/commands_impl.h | 209 ++++++++++++++++++--------------- ppu/librsx/fragment_program.c | 62 +++++----- ppu/librsx/init.c | 6 +- ppu/librsx/rsx_internal.c | 48 ++++++++ ppu/librsx/rsx_internal.h | 21 ++++ ppu/librsx/vertex_program.c | 58 +++++---- 10 files changed, 408 insertions(+), 249 deletions(-) create mode 100644 ppu/librsx/rsx_internal.c diff --git a/ppu/include/rsx/commands_inc.h b/ppu/include/rsx/commands_inc.h index 59694cee..2d48b7f9 100644 --- a/ppu/include/rsx/commands_inc.h +++ b/ppu/include/rsx/commands_inc.h @@ -30,7 +30,7 @@ void RSX_FUNC(SetCullFaceEnable)(gcmContextData *context,u32 enable); - \ref GCM_POLYGON_MODE_LINE - \ref GCM_POLYGON_MODE_FILL */ -void RSX_FUNC(SetFrontPolygonMode)(gcmContextData *context,const u32 mode); +void RSX_FUNC(SetFrontPolygonMode)(gcmContextData *context,u32 mode); /*! \brief Control back-facing polygon rendering. \param context Pointer to the context object. @@ -39,10 +39,10 @@ void RSX_FUNC(SetFrontPolygonMode)(gcmContextData *context,const u32 mode); - \ref GCM_POLYGON_MODE_LINE - \ref GCM_POLYGON_MODE_FILL */ -void RSX_FUNC(SetBackPolygonMode)(gcmContextData *context,const u32 mode); +void RSX_FUNC(SetBackPolygonMode)(gcmContextData *context,u32 mode); -void RSX_FUNC(SetPolygonOffsetFillEnable)(gcmContextData *context,const u32 enable); -void RSX_FUNC(SetPolygonOffset)(gcmContextData *context,const f32 factor,const f32 units); +void RSX_FUNC(SetPolygonOffsetFillEnable)(gcmContextData *context,u32 enable); +void RSX_FUNC(SetPolygonOffset)(gcmContextData *context,f32 factor,f32 units); /*! \brief Enable/Disable write to depth buffer. \param context Pointer to the context object. @@ -84,14 +84,14 @@ Starts the rendering for a primitive. - \ref GCM_TYPE_POLYGON */ void RSX_FUNC(DrawVertexBegin)(gcmContextData *context,u32 type); -void RSX_FUNC(DrawVertex1f)(gcmContextData *context,const u8 idx,const f32 v); -void RSX_FUNC(DrawVertex2f)(gcmContextData *context,const u8 idx,const f32 v[2]); -void RSX_FUNC(DrawVertex3f)(gcmContextData *context,const u8 idx,const f32 v[3]); -void RSX_FUNC(DrawVertex4f)(gcmContextData *context,const u8 idx,const f32 v[4]); -void RSX_FUNC(DrawVertex4s)(gcmContextData *context,const u8 idx,const s16 v[4]); -void RSX_FUNC(DrawVertexScaled4s)(gcmContextData *context,const u8 idx,const s16 v[4]); -void RSX_FUNC(DrawVertex2s)(gcmContextData *context,const u8 idx,const s16 v[2]); -void RSX_FUNC(DrawVertex4ub)(gcmContextData *context,const u8 idx,const u8 v[4]); +void RSX_FUNC(DrawVertex1f)(gcmContextData *context,u8 idx,f32 v); +void RSX_FUNC(DrawVertex2f)(gcmContextData *context,u8 idx,const f32 v[2]); +void RSX_FUNC(DrawVertex3f)(gcmContextData *context,u8 idx,const f32 v[3]); +void RSX_FUNC(DrawVertex4f)(gcmContextData *context,u8 idx,const f32 v[4]); +void RSX_FUNC(DrawVertex4s)(gcmContextData *context,u8 idx,const s16 v[4]); +void RSX_FUNC(DrawVertexScaled4s)(gcmContextData *context,u8 idx,const s16 v[4]); +void RSX_FUNC(DrawVertex2s)(gcmContextData *context,u8 idx,const s16 v[2]); +void RSX_FUNC(DrawVertex4ub)(gcmContextData *context,u8 idx,const u8 v[4]); void RSX_FUNC(SetScissor)(gcmContextData *context,u16 x,u16 y,u16 w,u16 h); /*! \brief Specify the value used for depth buffer comparisons. @@ -148,12 +148,12 @@ This value is used by the \ref rsxClearSurface function. \param context Pointer to the context object. \param value Color value */ -void RSX_FUNC(SetClearDepthStencil)(gcmContextData *context,const u32 value); +void RSX_FUNC(SetClearDepthStencil)(gcmContextData *context,u32 value); void RSX_FUNC(SetReturnCommand)(gcmContextData *context); void RSX_FUNC(SetCallCommand)(gcmContextData *context,u32 offset); void RSX_FUNC(SetJumpCommand)(gcmContextData *context,u32 offset); void RSX_FUNC(SetNopCommand)(gcmContextData *context,u32 count); -void RSX_FUNC(SetSkipNop)(gcmContextData *context,const u32 count); +void RSX_FUNC(SetSkipNop)(gcmContextData *context,u32 count); /*! \brief Set the clear color. @@ -186,29 +186,29 @@ combination of the following values: */ void RSX_FUNC(SetColorMaskMRT)(gcmContextData *context,u32 mask); -void RSX_FUNC(SetPointSpriteControl)(gcmContextData *context,const u32 enable,const u32 rmode,const u32 texcoordMask); -void RSX_FUNC(SetPointSize)(gcmContextData *context,const f32 size); -void RSX_FUNC(SetAntialiasingControl)(gcmContextData *context,const u32 enable,const u32 alphaToCoverage,const u32 alphaToOne,const u32 sampleMask); -void RSX_FUNC(SetCylindricalWrap)(gcmContextData *context,const u32 enable); -void RSX_FUNC(SetStencilFunc)(gcmContextData *context,const u32 func,const u32 ref,const u32 mask); -void RSX_FUNC(SetStencilMask)(gcmContextData *context,const u32 mask); -void RSX_FUNC(SetStencilOp)(gcmContextData *context,const u32 fail,const u32 depthFail,const u32 depthPass); -void RSX_FUNC(SetStencilTestEnable)(gcmContextData *context,const u32 enable); -void RSX_FUNC(SetBackStencilFunc)(gcmContextData *context,const u32 func,const u32 ref,const u32 mask); -void RSX_FUNC(SetBackStencilMask)(gcmContextData *context,const u32 mask); -void RSX_FUNC(SetBackStencilOp)(gcmContextData *context,const u32 fail,const u32 depthFail,const u32 depthPass); -void RSX_FUNC(SetTwoSidedStencilTestEnable)(gcmContextData *context,const u32 enable); -void RSX_FUNC(SetTwoSideLightEnable)(gcmContextData *context,const u32 enable); -void RSX_FUNC(SetRenderEnable)(gcmContextData *context,const u8 mode,const u32 index); -void RSX_FUNC(SetReport)(gcmContextData *context,const u32 type,const u32 index); -void RSX_FUNC(SetClearReport)(gcmContextData *context,const u32 type); -void RSX_FUNC(SetSCullControl)(gcmContextData *context,const u8 sFunc,const u8 sRef,const u8 sMask); -void RSX_FUNC(SetZCullEnable)(gcmContextData *context, const u32 depth, const u32 stencil); -void RSX_FUNC(SetClearZCullSurface)(gcmContextData *context, const u32 depth, const u32 stencil); -void RSX_FUNC(SetZCullLimit)(gcmContextData *context,const u16 moveforwardlimit,const u16 pushbacklimit); -void RSX_FUNC(SetZCullControl)(gcmContextData *context,const u8 zculldir,const u8 zcullformat); -void RSX_FUNC(SetZCullStatsEnable)(gcmContextData *context,const u32 enable); -void RSX_FUNC(SetPolygonSmoothEnable)(gcmContextData *context,const u32 enable); +void RSX_FUNC(SetPointSpriteControl)(gcmContextData *context,u32 enable,u32 rmode,u32 texcoordMask); +void RSX_FUNC(SetPointSize)(gcmContextData *context,f32 size); +void RSX_FUNC(SetAntialiasingControl)(gcmContextData *context,u32 enable,u32 alphaToCoverage,u32 alphaToOne,u32 sampleMask); +void RSX_FUNC(SetCylindricalWrap)(gcmContextData *context,u32 enable); +void RSX_FUNC(SetStencilFunc)(gcmContextData *context,u32 func,u32 ref,u32 mask); +void RSX_FUNC(SetStencilMask)(gcmContextData *context,u32 mask); +void RSX_FUNC(SetStencilOp)(gcmContextData *context,u32 fail,u32 depthFail,u32 depthPass); +void RSX_FUNC(SetStencilTestEnable)(gcmContextData *context,u32 enable); +void RSX_FUNC(SetBackStencilFunc)(gcmContextData *context,u32 func,u32 ref,u32 mask); +void RSX_FUNC(SetBackStencilMask)(gcmContextData *context,u32 mask); +void RSX_FUNC(SetBackStencilOp)(gcmContextData *context,u32 fail,u32 depthFail,u32 depthPass); +void RSX_FUNC(SetTwoSidedStencilTestEnable)(gcmContextData *context,u32 enable); +void RSX_FUNC(SetTwoSideLightEnable)(gcmContextData *context,u32 enable); +void RSX_FUNC(SetRenderEnable)(gcmContextData *context,u8 mode,u32 index); +void RSX_FUNC(SetReport)(gcmContextData *context,u32 type,u32 index); +void RSX_FUNC(SetClearReport)(gcmContextData *context,u32 type); +void RSX_FUNC(SetSCullControl)(gcmContextData *context,u8 sFunc,u8 sRef,u8 sMask); +void RSX_FUNC(SetZCullEnable)(gcmContextData *context, u32 depth, u32 stencil); +void RSX_FUNC(SetClearZCullSurface)(gcmContextData *context, u32 depth, u32 stencil); +void RSX_FUNC(SetZCullLimit)(gcmContextData *context,u16 moveforwardlimit,u16 pushbacklimit); +void RSX_FUNC(SetZCullControl)(gcmContextData *context,u8 zculldir,u8 zcullformat); +void RSX_FUNC(SetZCullStatsEnable)(gcmContextData *context,u32 enable); +void RSX_FUNC(SetPolygonSmoothEnable)(gcmContextData *context,u32 enable); /*! \brief Setup the render surface. @@ -216,7 +216,7 @@ This function is used to setup the render target where RSX should render the fra \param context Pointer to the context object. \param surface Pointer to the surface object. */ -void RSX_FUNC(SetSurface)(gcmContextData *context,gcmSurface *surface); +void RSX_FUNC(SetSurface)(gcmContextData *context,const gcmSurface *surface); void RSX_FUNC(SetReferenceCommand)(gcmContextData *context,u32 ref_value); /*! \brief Enqueues a Wait for label command. @@ -240,7 +240,7 @@ void RSX_FUNC(SetWriteCommandLabel)(gcmContextData *context,u8 index,u32 value); */ void RSX_FUNC(SetWriteBackendLabel)(gcmContextData *context,u8 index,u32 value); -void RSX_FUNC(SetWriteTextureLabel)(gcmContextData *context,const u8 index,const u32 value); +void RSX_FUNC(SetWriteTextureLabel)(gcmContextData *context,u8 index,u32 value); void RSX_FUNC(SetViewportClip)(gcmContextData *context,u8 sel,u16 width,u16 height); @@ -328,25 +328,25 @@ void RSX_FUNC(LoadTexture)(gcmContextData *context,u8 index,const gcmTexture *te \todo finish args documentation. */ void RSX_FUNC(TextureControl)(gcmContextData *context,u8 index,u32 enable,u16 minlod,u16 maxlod,u8 maxaniso); -void RSX_FUNC(TextureFilter)(gcmContextData *context,u8 index,u8 min,u8 mag,u8 conv); +void RSX_FUNC(TextureFilter)(gcmContextData *context,u8 index,u16 bias,u8 min,u8 mag,u8 conv); void RSX_FUNC(TextureWrapMode)(gcmContextData *context,u8 index,u8 wraps,u8 wrapt,u8 wrapr,u8 unsignedRemap,u8 zfunc,u8 gamma); -void RSX_FUNC(TextureBorderColor)(gcmContextData *context,const u8 index,const u32 color); -void RSX_FUNC(TextureOptimization)(gcmContextData *context,const u8 index,const u8 slope,const u8 iso,const u8 aniso); -void RSX_FUNC(TextureAnisoSpread)(gcmContextData *context,const u8 index,const u8 reduceSamplesEnable,const u8 hReduceSamplesEnable,const u8 vReduceSamplesEnable,const u8 spacingSelect,const u8 hSpacingSelect,const u8 vSpacingSelect); +void RSX_FUNC(TextureBorderColor)(gcmContextData *context,u8 index,u32 color); +void RSX_FUNC(TextureOptimization)(gcmContextData *context,u8 index,u8 slope,u8 iso,u8 aniso); +void RSX_FUNC(TextureAnisoSpread)(gcmContextData *context,u8 index,u8 reduceSamplesEnable,u8 hReduceSamplesEnable,u8 vReduceSamplesEnable,u8 spacingSelect,u8 hSpacingSelect,u8 vSpacingSelect); -void RSX_FUNC(LoadVertexTexture)(gcmContextData *context,const u8 index,const gcmTexture *texture); -void RSX_FUNC(VertexTextureControl)(gcmContextData *context,const u8 index,const u32 enable,const u16 minlod,const u16 maxlod); -void RSX_FUNC(VertexTextureFilter)(gcmContextData *context,const u8 index,const u16 bias); -void RSX_FUNC(VertexTextureWrapMode)(gcmContextData *context,const u8 index,const u8 wraps,const u8 wrapt); -void RSX_FUNC(VertexTextureBorderColor)(gcmContextData *context,const u8 index,const u32 color); +void RSX_FUNC(LoadVertexTexture)(gcmContextData *context,u8 index,const gcmTexture *texture); +void RSX_FUNC(VertexTextureControl)(gcmContextData *context,u8 index,u32 enable,u16 minlod,u16 maxlod); +void RSX_FUNC(VertexTextureFilter)(gcmContextData *context,u8 index,u16 bias); +void RSX_FUNC(VertexTextureWrapMode)(gcmContextData *context,u8 index,u8 wraps,u8 wrapt); +void RSX_FUNC(VertexTextureBorderColor)(gcmContextData *context,u8 index,u32 color); /*! \brief Load a compiled vertex shader program. \param context Pointer to the context object \param program Pointer to the vertex program configuration \param ucode Pointer to the shader micro code */ -void RSX_FUNC(LoadVertexProgram)(gcmContextData *context,rsxVertexProgram *program,const void *ucode); +void RSX_FUNC(LoadVertexProgram)(gcmContextData *context,const rsxVertexProgram *program,const void *ucode); /*! \brief Load a compiled fragment shader program. \param context Pointer to the context object @@ -356,30 +356,32 @@ void RSX_FUNC(LoadVertexProgram)(gcmContextData *context,rsxVertexProgram *progr - \ref GCM_LOCATION_RSX - \ref GCM_LOCATION_CELL */ -void RSX_FUNC(LoadFragmentProgramLocation)(gcmContextData *context,rsxFragmentProgram *program,u32 offset,u32 location); -void RSX_FUNC(UpdateFragmentProgramLocation)(gcmContextData *context,const u32 offset,const u32 location); +void RSX_FUNC(LoadFragmentProgramLocation)(gcmContextData *context,const rsxFragmentProgram *program,u32 offset,u32 location); +void RSX_FUNC(UpdateFragmentProgramLocation)(gcmContextData *context,u32 offset,u32 location); void RSX_FUNC(SetZControl)(gcmContextData *context,u8 cullNearFar,u8 zClampEnable,u8 cullIgnoreW); -void RSX_FUNC(SetZPixelCountEnable)(gcmContextData *context,const u32 enable); -void RSX_FUNC(LoadVertexProgramBlock)(gcmContextData *context,rsxVertexProgram *program,const void *ucode); +void RSX_FUNC(SetZPixelCountEnable)(gcmContextData *context,u32 enable); +void RSX_FUNC(LoadVertexProgramBlock)(gcmContextData *context,const rsxVertexProgram *program,const void *ucode); void RSX_FUNC(LoadVertexProgramParameterBlock)(gcmContextData *context,u32 base_const,u32 const_cnt,const f32 *value); -void RSX_FUNC(SetVertexProgramParameter)(gcmContextData *context,rsxVertexProgram *program,s32 index,const f32 *value); -void RSX_FUNC(SetFragmentProgramParameter)(gcmContextData *context,rsxFragmentProgram *program,s32 index,const f32 *value,u32 offset,u32 location); +void RSX_FUNC(SetVertexProgramParameter)(gcmContextData *context,const rsxVertexProgram *program,const rsxProgramConst *param,const f32 *value); +void RSX_FUNC(SetVertexProgramParameterByIndex)(gcmContextData *context,const rsxVertexProgram *program,s32 index,const f32 *value); +void RSX_FUNC(SetFragmentProgramParameter)(gcmContextData *context,const rsxFragmentProgram *program,const rsxProgramConst *param,const f32 *value,u32 offset,u32 location); +void RSX_FUNC(SetFragmentProgramParameterByIndex)(gcmContextData *context,const rsxFragmentProgram *program,s32 index,const f32 *value,u32 offset,u32 location); void RSX_FUNC(DrawVertexArray)(gcmContextData *context,u32 type,u32 start,u32 count); -void RSX_FUNC(BindVertexArrayAttrib)(gcmContextData *context,u8 attr,u32 offset,u8 stride,u8 elems,u8 dtype,u8 location); +void RSX_FUNC(BindVertexArrayAttrib)(gcmContextData *context,u8 attr,u16 frequency,u32 offset,u8 stride,u8 elems,u8 dtype,u8 location); void RSX_FUNC(DrawIndexArray)(gcmContextData *context,u8 type,u32 offset,u32 count,u8 data_type,u8 location); -void RSX_FUNC(DrawInlineIndexArray16)(gcmContextData *context,u8 type,u32 start,u32 count,u16 *data); -void RSX_FUNC(DrawInlineIndexArray32)(gcmContextData *context,u8 type,u32 start,u32 count,u32 *data); -void RSX_FUNC(InlineTransfer)(gcmContextData *context,const u32 dstOffset,const void *srcAddress,const u32 sizeInWords,const u8 location); +void RSX_FUNC(DrawInlineIndexArray16)(gcmContextData *context,u8 type,u32 start,u32 count,const u16 *data); +void RSX_FUNC(DrawInlineIndexArray32)(gcmContextData *context,u8 type,u32 start,u32 count,const u32 *data); +void RSX_FUNC(InlineTransfer)(gcmContextData *context,u32 dstOffset,const void *srcAddress,u32 sizeInWords,u8 location); void RSX_FUNC(SetUserClipPlaneControl)(gcmContextData *context,u32 plane0,u32 plane1,u32 plane2,u32 plane3,u32 plane4,u32 plane5); -void RSX_FUNC(SetAlphaFunc)(gcmContextData *context,const u32 alphaFunc,const u32 ref); -void RSX_FUNC(SetAlphaTestEnable)(gcmContextData *context,const u32 enable); -void RSX_FUNC(SetBlendEnableMrt)(gcmContextData *context, const u32 mrt1, const u32 mrt2, const u32 mrt3); -void RSX_FUNC(SetBlendOptimization)(gcmContextData *context,const u32 enable); -void RSX_FUNC(SetLogicOp)(gcmContextData *context,const u32 op); -void RSX_FUNC(SetLogicOpEnable)(gcmContextData *context,const u32 enable); -void RSX_FUNC(SetFogMode)(gcmContextData *context,const u32 mode); -void RSX_FUNC(SetFogParams)(gcmContextData *context,const f32 p0,const f32 p1); -void RSX_FUNC(SetVertexAttribOutputMask)(gcmContextData *context,const u32 mask); +void RSX_FUNC(SetAlphaFunc)(gcmContextData *context,u32 alphaFunc,u32 ref); +void RSX_FUNC(SetAlphaTestEnable)(gcmContextData *context,u32 enable); +void RSX_FUNC(SetBlendEnableMrt)(gcmContextData *context, u32 mrt1, u32 mrt2, u32 mrt3); +void RSX_FUNC(SetBlendOptimization)(gcmContextData *context,u32 enable); +void RSX_FUNC(SetLogicOp)(gcmContextData *context,u32 op); +void RSX_FUNC(SetLogicOpEnable)(gcmContextData *context,u32 enable); +void RSX_FUNC(SetFogMode)(gcmContextData *context,u32 mode); +void RSX_FUNC(SetFogParams)(gcmContextData *context,f32 p0,f32 p1); +void RSX_FUNC(SetVertexAttribOutputMask)(gcmContextData *context,u32 mask); /*! \brief Specify pixel arithmetic. @@ -623,7 +625,7 @@ void RSX_FUNC(SetTransferData)(gcmContextData *context,u8 mode,u32 dst,u32 outpi - \ref GCM_TRANSFER_SURFACE - \ref GCM_TRANSFER_SWIZZLE */ -void RSX_FUNC(SetTransferScaleMode)(gcmContextData *context,const u8 mode,const u8 surface); +void RSX_FUNC(SetTransferScaleMode)(gcmContextData *context,u8 mode,u8 surface); /*! \brief Initiate an asynchronous RSX blit. \param context Pointer to the context object @@ -651,10 +653,10 @@ void RSX_FUNC(SetTransferScaleSurface)(gcmContextData *context,const gcmTransfer \param height Height of the transfer rectangle. \param bytesPerPixel Number of bytes per pixel to transfer: 2 or 4. */ -void RSX_FUNC(SetTransferImage)(gcmContextData *context,const u8 mode,const u32 dstOffset,const u32 dstPitch,const u32 dstX,const u32 dstY,const u32 srcOffset,const u32 srcPitch,const u32 srcX,const u32 srcY,const u32 width,const u32 height,const u32 bytesPerPixel); +void RSX_FUNC(SetTransferImage)(gcmContextData *context,u8 mode,u32 dstOffset,u32 dstPitch,u32 dstX,u32 dstY,u32 srcOffset,u32 srcPitch,u32 srcX,u32 srcY,u32 width,u32 height,u32 bytesPerPixel); void RSX_FUNC(SetTimeStamp)(gcmContextData *context,u32 index); -void RSX_FUNC(SetConvertSwizzleFormat)(gcmContextData *context,const u32 dstOffset,const u32 dstWidth,const u32 dstHeight,const u32 dstX,const u32 dstY,const u32 srcOffset,const u32 srcPitch,const u32 srcX,const u32 srcY,const u32 width,const u32 height,const u32 bytesPerPixel,const u32 mode); +void RSX_FUNC(SetConvertSwizzleFormat)(gcmContextData *context,u32 dstOffset,u32 dstWidth,u32 dstHeight,u32 dstX,u32 dstY,u32 srcOffset,u32 srcPitch,u32 srcX,u32 srcY,u32 width,u32 height,u32 bytesPerPixel,u32 mode); void RSX_FUNC(SetTransferScaleSwizzle)(gcmContextData *context,const gcmTransferScale *scale,const gcmTransferSwizzle *swizzle); diff --git a/ppu/include/rsx/rsx.h b/ppu/include/rsx/rsx.h index 6e1fb70a..ce7d6d61 100644 --- a/ppu/include/rsx/rsx.h +++ b/ppu/include/rsx/rsx.h @@ -100,12 +100,13 @@ dynamic memory allocation using \ref rsxMalloc, \ref rsxMemalign and \param ioAddress Pointer to an allocated buffer of \p ioSize bytes. \return zero if no error occured, nonzero otherwise. */ -s32 rsxInit(gcmContextData **context,const u32 cmdSize,const u32 ioSize,const void *ioAddress); +s32 rsxInit(gcmContextData **context,u32 cmdSize,u32 ioSize,const void *ioAddress); -void rsxSetupContextData(gcmContextData *context,const u32 *addr,const u32 size,gcmContextCallback cb); -void rsxSetCurrentBuffer(gcmContextData **context,const u32 *addr,const u32 size); +void rsxSetupContextData(gcmContextData *context,const u32 *addr,u32 size,gcmContextCallback cb); +void rsxSetCurrentBuffer(gcmContextData **context,const u32 *addr,u32 size); void rsxSetDefaultCommandBuffer(gcmContextData **context); void rsxSetUserCallback(gcmContextCallback cb); +void rsxSetupContextData(gcmContextData *context,const u32 *addr,u32 size,gcmContextCallback cb); u32* rsxGetCurrentBuffer(); @@ -114,23 +115,28 @@ u32* rsxGetCurrentBuffer(); \param offset A pointer to the returned offset value. \return zero if no error occured, nonzero otherwise. */ -static inline s32 rsxAddressToOffset(void *ptr,u32 *offset) +static inline s32 rsxAddressToOffset(const void *ptr,u32 *offset) { return gcmAddressToOffset(ptr,offset); } /*! \brief Convert a floating point coordinate into 32-bit signed fixed point format. */ -static inline s32 rsxGetFixedSint32(const f32 f) +static inline s32 rsxGetFixedSint32(f32 f) { return (s32)(f*1048576.0f); } /*! \brief Convert a floating point coordinate into 16-bit unsigned fixed point format. */ -static inline u16 rsxGetFixedUint16(const f32 f) +static inline u16 rsxGetFixedUint16(f32 f) { return (u16)(f*16.0f); } +static inline u32 rsxAlign(u32 alignment, u32 value) +{ + return (alignment==0 ? value : (value==0 ? 0 : (((u32)((value - 1)/alignment) + 1)*alignment))); +} + #ifdef __cplusplus } #endif diff --git a/ppu/include/rsx/rsx_program.h b/ppu/include/rsx/rsx_program.h index 460bb541..3e16b0d9 100644 --- a/ppu/include/rsx/rsx_program.h +++ b/ppu/include/rsx/rsx_program.h @@ -119,73 +119,125 @@ typedef struct rsx_attrib \param ucode Pointer-pointer to receive the vertex program ucode. \param size Pointer to receive the vertex program ucode size. */ -void rsxVertexProgramGetUCode(rsxVertexProgram *vp,void **ucode,u32 *size); +void rsxVertexProgramGetUCode(const rsxVertexProgram *vp,void **ucode,u32 *size); + +/*! \brief Get the count of vertex program consts. +\param vp Pointer the to vertex program structure. +\return Count of constants in the vertex program. +*/ +u16 rsxVertexProgramGetNumConst(const rsxVertexProgram *vp); /*! \brief Get the list of vertex program consts. \param vp Pointer the to vertex program structure. \return Pointer to the list of program const structures. */ -rsxProgramConst* rsxVertexProgramGetConsts(rsxVertexProgram *vp); +rsxProgramConst* rsxVertexProgramGetConsts(const rsxVertexProgram *vp); -/*! \brief Get id of vertex program const from its name. +/*! \brief Get index of vertex program const from its name. \param vp Pointer the to vertex program structure. \param name Name of the vertex program const. \return The requested vertex program const id. */ -s32 rsxVertexProgramGetConst(rsxVertexProgram *vp,const char *name); +s32 rsxVertexProgramGetConstIndex(const rsxVertexProgram *vp,const char *name); + +/*! \brief Get const value of vertex program const from its name. +\param vp Pointer the to vertex program structure. +\param name Name of the vertex program const. +\return The requested vertex program const value. +*/ +rsxProgramConst* rsxVertexProgramGetConst(const rsxVertexProgram *vp,const char *name); + +/*! \brief Get the count of vertex program attributes. +\param vp Pointer the to vertex program structure. +\return Count of attributes in the vertex program. +*/ +u16 rsxVertexProgramGetNumAttrib(const rsxVertexProgram *vp); /*! \brief Get the list of vertex program attributes. \param vp Pointer the to vertex program structure. \return Pointer to the list of program attribute structures. */ -rsxProgramAttrib* rsxVertexProgramGetAttribs(rsxVertexProgram *vp); +rsxProgramAttrib* rsxVertexProgramGetAttribs(const rsxVertexProgram *vp); + +/*! \brief Get attribute value of vertex program attribute from its name. +\param vp Pointer the to vertex program structure. +\param name Name of the vertex program attribute. +\return The requested vertex program attribute value. +*/ +rsxProgramAttrib* rsxVertexProgramGetAttrib(const rsxVertexProgram *vp,const char *name); -/*! \brief Get id of vertex program attribute from its name. +/*! \brief Get index of vertex program attribute from its name. \param vp Pointer the to vertex program structure. \param name Name of the vertex program attribute. -\return The requested vertex program attribute id. +\return The requested vertex program attribute index. */ -s32 rsxVertexProgramGetAttrib(rsxVertexProgram *vp,const char *name); +s32 rsxVertexProgramGetAttribIndex(const rsxVertexProgram *vp,const char *name); /*! \brief Get Ucode from RSX fragment program. \param fp Pointer the to fragment program structure. \param ucode Pointer-pointer to receive the fragment program ucode. \param size Pointer to receive the fragment program ucode size. */ -void rsxFragmentProgramGetUCode(rsxFragmentProgram *fp,void **ucode,u32 *size); +void rsxFragmentProgramGetUCode(const rsxFragmentProgram *fp,void **ucode,u32 *size); + +/*! \brief Get the count of fragment program consts. +\param fp Pointer the to fragment program structure. +\return Count of constants in the fragment program. +*/ +u16 rsxFragmentProgramGetNumConst(const rsxFragmentProgram *fp); /*! \brief Get the list of fragment program consts. \param fp Pointer the to fragment program structure. \return Pointer to the list of program const structures. */ -rsxProgramConst* rsxFragmentProgramGetConsts(rsxFragmentProgram *fp); +rsxProgramConst* rsxFragmentProgramGetConsts(const rsxFragmentProgram *fp); + +/*! \brief Get index of fragment program const from its name. +\param fp Pointer the to fragment program structure. +\param name Name of the fragment program const. +\return The requested fragment program const index. +*/ +s32 rsxFragmentProgramGetConstIndex(const rsxFragmentProgram *fp,const char *name); -/*! \brief Get id of fragment program const from its name. +/*! \brief Get const value of fragment program const from its name. \param fp Pointer the to fragment program structure. \param name Name of the fragment program const. -\return The requested fragment program const id. +\return The requested fragment program const value. */ -s32 rsxFragmentProgramGetConst(rsxFragmentProgram *fp,const char *name); +rsxProgramConst* rsxFragmentProgramGetConst(const rsxFragmentProgram *fp,const char *name); + +/*! \brief Get the count of fragment program attributes. +\param fp Pointer the to fragment program structure. +\return Count of attributes in the fragment program. +*/ +u16 rsxFragmentProgramGetNumAttrib(const rsxFragmentProgram *fp); /*! \brief Get the list of fragment program attributes. \param fp Pointer the to fragment program structure. \return Pointer to the list of program attribute structures. */ -rsxProgramAttrib* rsxFragmentProgramGetAttribs(rsxFragmentProgram *fp); +rsxProgramAttrib* rsxFragmentProgramGetAttribs(const rsxFragmentProgram *fp); + +/*! \brief Get index of fragment program attribute from its name. +\param fp Pointer the to fragment program structure. +\param name Name of the fragment program attribute. +\return The requested fragment program attribute index. +*/ +s32 rsxFragmentProgramGetAttribIndex(const rsxFragmentProgram *fp,const char *name); -/*! \brief Get id of fragment program attribute from its name. +/*! \brief Get attribute value of fragment program attribute from its name. \param fp Pointer the to fragment program structure. \param name Name of the fragment program attribute. -\return The requested fragment program attribute id. +\return The requested fragment program attribute value. */ -s32 rsxFragmentProgramGetAttrib(rsxFragmentProgram *fp,const char *name); +rsxProgramAttrib* rsxFragmentProgramGetAttrib(const rsxFragmentProgram *fp,const char *name); /*! \brief Get const offset table from a fragment program. \param fp Pointer the to fragment program structure. \param table_off Offset of the const offset table. \return Pointer to the requested const offset table. */ -rsxConstOffsetTable* rsxFragmentProgramGetConstOffsetTable(rsxFragmentProgram *fp,u32 table_off); +rsxConstOffsetTable* rsxFragmentProgramGetConstOffsetTable(const rsxFragmentProgram *fp,u32 table_off); #ifdef __cplusplus } diff --git a/ppu/librsx/Makefile b/ppu/librsx/Makefile index 61472c3d..fcfc3615 100644 --- a/ppu/librsx/Makefile +++ b/ppu/librsx/Makefile @@ -44,7 +44,8 @@ VPATH := $(BASEDIR) #--------------------------------------------------------------------------------- OBJS := \ - init.o buffer.o commands.o mm.o vertex_program.o fragment_program.o + init.o buffer.o commands.o mm.o vertex_program.o fragment_program.o \ + rsx_internal.o all: ppu diff --git a/ppu/librsx/commands_impl.h b/ppu/librsx/commands_impl.h index 79e1b0ca..2d10d85d 100644 --- a/ppu/librsx/commands_impl.h +++ b/ppu/librsx/commands_impl.h @@ -29,7 +29,7 @@ void RSX_FUNC(SetNopCommand)(gcmContextData *context,u32 count) RSX_CONTEXT_CURRENT_END(count); } -void RSX_FUNC(SetSkipNop)(gcmContextData *context,const u32 count) +void RSX_FUNC(SetSkipNop)(gcmContextData *context,u32 count) { RSX_CONTEXT_CURRENT_BEGIN(1 + count); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD_NI(NV40TCL_NOP, count); @@ -44,7 +44,7 @@ void RSX_FUNC(SetClearColor)(gcmContextData *context,u32 color) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetClearDepthStencil)(gcmContextData *context,const u32 value) +void RSX_FUNC(SetClearDepthStencil)(gcmContextData *context,u32 value) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_CLEAR_VALUE_DEPTH,1); @@ -73,7 +73,7 @@ void RSX_FUNC(SetWriteBackendLabel)(gcmContextData *context,u8 index,u32 value) RSX_CONTEXT_CURRENT_END(4); } -void RSX_FUNC(SetWriteTextureLabel)(gcmContextData *context,const u8 index,const u32 value) +void RSX_FUNC(SetWriteTextureLabel)(gcmContextData *context,u8 index,u32 value) { u32 offset = 0x10*index; @@ -109,7 +109,7 @@ void RSX_FUNC(SetWriteCommandLabel)(gcmContextData *context,u8 index,u32 value) RSX_CONTEXT_CURRENT_END(4); } -void RSX_FUNC(SetSurface)(gcmContextData *context,gcmSurface *surface) +void RSX_FUNC(SetSurface)(gcmContextData *context,const gcmSurface *surface) { u32 log2Width = 31 - __cntlzw(surface->width); u32 log2Height = 31 - __cntlzw(surface->height); @@ -329,7 +329,7 @@ void RSX_FUNC(SetFrontFace)(gcmContextData *context,u32 dir) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetFrontPolygonMode)(gcmContextData *context,const u32 mode) +void RSX_FUNC(SetFrontPolygonMode)(gcmContextData *context,u32 mode) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_POLYGON_MODE_FRONT,1); @@ -337,7 +337,7 @@ void RSX_FUNC(SetFrontPolygonMode)(gcmContextData *context,const u32 mode) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetBackPolygonMode)(gcmContextData *context,const u32 mode) +void RSX_FUNC(SetBackPolygonMode)(gcmContextData *context,u32 mode) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_POLYGON_MODE_BACK,1); @@ -345,7 +345,7 @@ void RSX_FUNC(SetBackPolygonMode)(gcmContextData *context,const u32 mode) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetPolygonOffsetFillEnable)(gcmContextData *context,const u32 enable) +void RSX_FUNC(SetPolygonOffsetFillEnable)(gcmContextData *context,u32 enable) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_POLYGON_OFFSET_FILL_ENABLE,1); @@ -353,7 +353,7 @@ void RSX_FUNC(SetPolygonOffsetFillEnable)(gcmContextData *context,const u32 enab RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetPolygonOffset)(gcmContextData *context,const f32 factor,const f32 units) +void RSX_FUNC(SetPolygonOffset)(gcmContextData *context,f32 factor,f32 units) { ieee32 d0,d1; @@ -379,7 +379,7 @@ void RSX_FUNC(ClearSurface)(gcmContextData *context,u32 clear_mask) RSX_CONTEXT_CURRENT_END(4); } -void RSX_FUNC(SetCylindricalWrap)(gcmContextData *context,const u32 enable) +void RSX_FUNC(SetCylindricalWrap)(gcmContextData *context,u32 enable) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_CYLINDRICAL_WRAP,1); @@ -387,7 +387,7 @@ void RSX_FUNC(SetCylindricalWrap)(gcmContextData *context,const u32 enable) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetTwoSideLightEnable)(gcmContextData *context,const u32 enable) +void RSX_FUNC(SetTwoSideLightEnable)(gcmContextData *context,u32 enable) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TWO_SIDE_LIGHT_EN,1); @@ -395,7 +395,7 @@ void RSX_FUNC(SetTwoSideLightEnable)(gcmContextData *context,const u32 enable) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetStencilFunc)(gcmContextData *context,const u32 func,const u32 ref,const u32 mask) +void RSX_FUNC(SetStencilFunc)(gcmContextData *context,u32 func,u32 ref,u32 mask) { RSX_CONTEXT_CURRENT_BEGIN(4); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_BACK_FUNC_FUNC,3); @@ -405,7 +405,7 @@ void RSX_FUNC(SetStencilFunc)(gcmContextData *context,const u32 func,const u32 r RSX_CONTEXT_CURRENT_END(4); } -void RSX_FUNC(SetStencilMask)(gcmContextData *context,const u32 mask) +void RSX_FUNC(SetStencilMask)(gcmContextData *context,u32 mask) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_BACK_MASK,1); @@ -413,7 +413,7 @@ void RSX_FUNC(SetStencilMask)(gcmContextData *context,const u32 mask) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetStencilOp)(gcmContextData *context,const u32 fail,const u32 depthFail,const u32 depthPass) +void RSX_FUNC(SetStencilOp)(gcmContextData *context,u32 fail,u32 depthFail,u32 depthPass) { RSX_CONTEXT_CURRENT_BEGIN(4); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_BACK_OP_FAIL,3); @@ -423,7 +423,7 @@ void RSX_FUNC(SetStencilOp)(gcmContextData *context,const u32 fail,const u32 dep RSX_CONTEXT_CURRENT_END(4); } -void RSX_FUNC(SetStencilTestEnable)(gcmContextData *context,const u32 enable) +void RSX_FUNC(SetStencilTestEnable)(gcmContextData *context,u32 enable) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_BACK_ENABLE,1); @@ -431,7 +431,7 @@ void RSX_FUNC(SetStencilTestEnable)(gcmContextData *context,const u32 enable) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetBackStencilFunc)(gcmContextData *context,const u32 func,const u32 ref,const u32 mask) +void RSX_FUNC(SetBackStencilFunc)(gcmContextData *context,u32 func,u32 ref,u32 mask) { RSX_CONTEXT_CURRENT_BEGIN(4); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_FRONT_FUNC_FUNC,3); @@ -441,7 +441,7 @@ void RSX_FUNC(SetBackStencilFunc)(gcmContextData *context,const u32 func,const u RSX_CONTEXT_CURRENT_END(4); } -void RSX_FUNC(SetBackStencilMask)(gcmContextData *context,const u32 mask) +void RSX_FUNC(SetBackStencilMask)(gcmContextData *context,u32 mask) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_FRONT_MASK,1); @@ -449,7 +449,7 @@ void RSX_FUNC(SetBackStencilMask)(gcmContextData *context,const u32 mask) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetBackStencilOp)(gcmContextData *context,const u32 fail,const u32 depthFail,const u32 depthPass) +void RSX_FUNC(SetBackStencilOp)(gcmContextData *context,u32 fail,u32 depthFail,u32 depthPass) { RSX_CONTEXT_CURRENT_BEGIN(4); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_FRONT_OP_FAIL,3); @@ -459,7 +459,7 @@ void RSX_FUNC(SetBackStencilOp)(gcmContextData *context,const u32 fail,const u32 RSX_CONTEXT_CURRENT_END(4); } -void RSX_FUNC(SetTwoSidedStencilTestEnable)(gcmContextData *context,const u32 enable) +void RSX_FUNC(SetTwoSidedStencilTestEnable)(gcmContextData *context,u32 enable) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_STENCIL_FRONT_ENABLE,1); @@ -467,7 +467,7 @@ void RSX_FUNC(SetTwoSidedStencilTestEnable)(gcmContextData *context,const u32 en RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetRenderEnable)(gcmContextData *context,const u8 mode,const u32 index) +void RSX_FUNC(SetRenderEnable)(gcmContextData *context,u8 mode,u32 index) { u32 offset = 0x10*index; @@ -486,7 +486,7 @@ void RSX_FUNC(SetRenderEnable)(gcmContextData *context,const u8 mode,const u32 i } } -void RSX_FUNC(SetReport)(gcmContextData *context,const u32 type,const u32 index) +void RSX_FUNC(SetReport)(gcmContextData *context,u32 type,u32 index) { u32 offset = 0x10*index; @@ -496,7 +496,7 @@ void RSX_FUNC(SetReport)(gcmContextData *context,const u32 type,const u32 index) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetClearReport)(gcmContextData *context,const u32 type) +void RSX_FUNC(SetClearReport)(gcmContextData *context,u32 type) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_QUERY_RESET,1); @@ -504,7 +504,7 @@ void RSX_FUNC(SetClearReport)(gcmContextData *context,const u32 type) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetZPixelCountEnable)(gcmContextData *context,const u32 enable) +void RSX_FUNC(SetZPixelCountEnable)(gcmContextData *context,u32 enable) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_QUERY_ENABLE,1); @@ -512,7 +512,7 @@ void RSX_FUNC(SetZPixelCountEnable)(gcmContextData *context,const u32 enable) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetSCullControl)(gcmContextData *context,const u8 sFunc,const u8 sRef,const u8 sMask) +void RSX_FUNC(SetSCullControl)(gcmContextData *context,u8 sFunc,u8 sRef,u8 sMask) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_SCULL_CONTROL,1); @@ -520,7 +520,7 @@ void RSX_FUNC(SetSCullControl)(gcmContextData *context,const u8 sFunc,const u8 s RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetZCullLimit)(gcmContextData *context,const u16 moveforwardlimit,const u16 pushbacklimit) +void RSX_FUNC(SetZCullLimit)(gcmContextData *context,u16 moveforwardlimit,u16 pushbacklimit) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_ZCULL_CONTROL1,1); @@ -528,7 +528,7 @@ void RSX_FUNC(SetZCullLimit)(gcmContextData *context,const u16 moveforwardlimit, RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetZCullStatsEnable)(gcmContextData *context,const u32 enable) +void RSX_FUNC(SetZCullStatsEnable)(gcmContextData *context,u32 enable) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_ZCULL_STATS_ENABLE,1); @@ -536,7 +536,7 @@ void RSX_FUNC(SetZCullStatsEnable)(gcmContextData *context,const u32 enable) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetZCullControl)(gcmContextData *context,const u8 zculldir,const u8 zcullformat) +void RSX_FUNC(SetZCullControl)(gcmContextData *context,u8 zculldir,u8 zcullformat) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_ZCULL_CONTROL0,1); @@ -544,7 +544,7 @@ void RSX_FUNC(SetZCullControl)(gcmContextData *context,const u8 zculldir,const u RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetClearZCullSurface)(gcmContextData *context,const u32 depth,const u32 stencil) +void RSX_FUNC(SetClearZCullSurface)(gcmContextData *context,u32 depth,u32 stencil) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_CLEAR_ZCULL_SURFACE,1); @@ -552,7 +552,7 @@ void RSX_FUNC(SetClearZCullSurface)(gcmContextData *context,const u32 depth,cons RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetZCullEnable)(gcmContextData *context,const u32 depth,const u32 stencil) +void RSX_FUNC(SetZCullEnable)(gcmContextData *context,u32 depth,u32 stencil) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_ZCULL_ENABLE,1); @@ -560,7 +560,7 @@ void RSX_FUNC(SetZCullEnable)(gcmContextData *context,const u32 depth,const u32 RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetPolygonSmoothEnable)(gcmContextData *context,const u32 enable) +void RSX_FUNC(SetPolygonSmoothEnable)(gcmContextData *context,u32 enable) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_POLYGON_SMOOTH_ENABLE,1); @@ -568,7 +568,7 @@ void RSX_FUNC(SetPolygonSmoothEnable)(gcmContextData *context,const u32 enable) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(LoadVertexProgramBlock)(gcmContextData *context,rsxVertexProgram *program,const void *ucode) +void RSX_FUNC(LoadVertexProgramBlock)(gcmContextData *context,const rsxVertexProgram *program,const void *ucode) { u32 pos = 0; u32 loop, rest; @@ -614,7 +614,7 @@ void RSX_FUNC(LoadVertexProgramBlock)(gcmContextData *context,rsxVertexProgram * RSX_CONTEXT_CURRENT_END(9 + loop*33 + (rest!=0 ? rest + 1 : 0)); } -void RSX_FUNC(LoadFragmentProgramLocation)(gcmContextData *context,rsxFragmentProgram *program,u32 offset,u32 location) +void RSX_FUNC(LoadFragmentProgramLocation)(gcmContextData *context,const rsxFragmentProgram *program,u32 offset,u32 location) { u32 i; u32 texcoords,texcoord2D,texcoord3D; @@ -650,7 +650,7 @@ void RSX_FUNC(LoadFragmentProgramLocation)(gcmContextData *context,rsxFragmentPr } } -void RSX_FUNC(UpdateFragmentProgramLocation)(gcmContextData *context,const u32 offset,const u32 location) +void RSX_FUNC(UpdateFragmentProgramLocation)(gcmContextData *context,u32 offset,u32 location) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_FP_ADDRESS,1); @@ -676,7 +676,7 @@ void RSX_FUNC(LoadVertexProgramParameterBlock)(gcmContextData *context,u32 base_ RSX_CONTEXT_CURRENT_END(const_cnt*6); } -void RSX_FUNC(LoadVertexProgram)(gcmContextData *context,rsxVertexProgram *program,const void *ucode) +void RSX_FUNC(LoadVertexProgram)(gcmContextData *context,const rsxVertexProgram *program,const void *ucode) { u32 i; u32 base_const = program->const_start; @@ -690,33 +690,32 @@ void RSX_FUNC(LoadVertexProgram)(gcmContextData *context,rsxVertexProgram *progr } } -void RSX_FUNC(SetVertexProgramParameter)(gcmContextData *context,rsxVertexProgram *program,s32 index,const f32 *value) +static inline __attribute__((always_inline)) void RSX_FUNC_INTERNAL(SetVertexProgramParameter)(gcmContextData *context,const rsxVertexProgram *program,const rsxProgramConst *param,const f32 *value) { u32 base_const = program->const_start; f32 params[4] = {0.0f,0.0f,0.0f,0.0f}; - rsxProgramConst *consts = rsxVertexProgramGetConsts(program); - switch(consts[index].type) { + switch(param->type) { case PARAM_FLOAT3x4: case PARAM_FLOAT4x4: - RSX_FUNC(LoadVertexProgramParameterBlock)(context,consts[index].index + base_const,consts[index].count,value); + RSX_FUNC(LoadVertexProgramParameterBlock)(context,param->index + base_const,param->count,value); return; case PARAM_FLOAT3x3: case PARAM_FLOAT4x3: { u32 i; - for(i=0;icount;i++,value+=3) { params[0] = value[0]; params[1] = value[1]; params[2] = value[2]; - RSX_FUNC(LoadVertexProgramParameterBlock)(context,consts[index].index + base_const + i,1,params); + RSX_FUNC(LoadVertexProgramParameterBlock)(context,param->index + base_const + i,1,params); } return; } case PARAM_FLOAT4: - RSX_FUNC(LoadVertexProgramParameterBlock)(context,consts[index].index + base_const,1,value); + RSX_FUNC(LoadVertexProgramParameterBlock)(context,param->index + base_const,1,value); return; case PARAM_FLOAT3: params[2] = value[2]; @@ -726,10 +725,21 @@ void RSX_FUNC(SetVertexProgramParameter)(gcmContextData *context,rsxVertexProgra case PARAM_FLOAT1: params[0] = value[0]; } - RSX_FUNC(LoadVertexProgramParameterBlock)(context,consts[index].index + base_const,1,params); + RSX_FUNC(LoadVertexProgramParameterBlock)(context,param->index + base_const,1,params); +} + +void RSX_FUNC(SetVertexProgramParameter)(gcmContextData *context,const rsxVertexProgram *program,const rsxProgramConst *param,const f32 *value) +{ + RSX_FUNC_INTERNAL(SetVertexProgramParameter)(context, program, param, value); +} + +void RSX_FUNC(SetVertexProgramParameterByIndex)(gcmContextData *context,const rsxVertexProgram *program,s32 index,const f32 *value) +{ + rsxProgramConst *consts = rsxVertexProgramGetConsts(program); + RSX_FUNC_INTERNAL(SetVertexProgramParameter)(context, program, &consts[index], value); } -void RSX_FUNC(SetVertexAttribOutputMask)(gcmContextData *context,const u32 mask) +void RSX_FUNC(SetVertexAttribOutputMask)(gcmContextData *context,u32 mask) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VP_RESULT_EN,1); @@ -737,20 +747,19 @@ void RSX_FUNC(SetVertexAttribOutputMask)(gcmContextData *context,const u32 mask) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetFragmentProgramParameter)(gcmContextData *context,rsxFragmentProgram *program,s32 index,const f32 *value,u32 offset,u32 location) +static inline __attribute__((always_inline)) void RSX_FUNC_INTERNAL(SetFragmentProgramParameter)(gcmContextData *context,const rsxFragmentProgram *program,const rsxProgramConst *param,const f32 *value,u32 offset,u32 location) { s32 i; f32 params[4] = {0.0f,0.0f,0.0f,0.0f}; - rsxProgramConst *consts = rsxFragmentProgramGetConsts(program); - switch(consts[index].type) { + switch(param->type) { case PARAM_FLOAT3x4: case PARAM_FLOAT4x4: { - s32 j,cnt = consts[index].count; + s32 j,cnt = param->count; for(j=0;jcount; for(j=0;jindex!=0xffffffff) { + rsxConstOffsetTable *co_table = rsxFragmentProgramGetConstOffsetTable(program,param->index); for(i=0;inum;i++) RSX_FUNC(InlineTransfer)(context,offset + co_table->offset[i],params,4,location); } } +void RSX_FUNC(SetFragmentProgramParameter)(gcmContextData *context,const rsxFragmentProgram *program,const rsxProgramConst *param,const f32 *value,u32 offset,u32 location) +{ + RSX_FUNC_INTERNAL(SetFragmentProgramParameter)(context, program, param, value, offset, location); +} + +void RSX_FUNC(SetFragmentProgramParameterByIndex)(gcmContextData *context,const rsxFragmentProgram *program,s32 index,const f32 *value,u32 offset,u32 location) +{ + rsxProgramConst *consts = rsxFragmentProgramGetConsts(program); + RSX_FUNC_INTERNAL(SetFragmentProgramParameter)(context, program, &consts[index], value, offset, location); +} + void RSX_FUNC(DrawVertexBegin)(gcmContextData *context,u32 type) { RSX_CONTEXT_CURRENT_BEGIN(2); @@ -818,7 +838,7 @@ void RSX_FUNC(DrawVertexEnd)(gcmContextData *context) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(DrawVertex1f)(gcmContextData *context,const u8 idx,const f32 v) +void RSX_FUNC(DrawVertex1f)(gcmContextData *context,u8 idx,f32 v) { ieee32 d; d.f = v; @@ -837,7 +857,7 @@ void RSX_FUNC(DrawVertex2f)(gcmContextData *context,u8 idx,const f32 v[2]) RSX_CONTEXT_CURRENT_END(3); } -void RSX_FUNC(DrawVertex3f)(gcmContextData *context,const u8 idx,const f32 v[3]) +void RSX_FUNC(DrawVertex3f)(gcmContextData *context,u8 idx,const f32 v[3]) { RSX_CONTEXT_CURRENT_BEGIN(4); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_3F_X(idx),3); @@ -845,7 +865,7 @@ void RSX_FUNC(DrawVertex3f)(gcmContextData *context,const u8 idx,const f32 v[3]) RSX_CONTEXT_CURRENT_END(4); } -void RSX_FUNC(DrawVertex4f)(gcmContextData *context,const u8 idx,const f32 v[4]) +void RSX_FUNC(DrawVertex4f)(gcmContextData *context,u8 idx,const f32 v[4]) { RSX_CONTEXT_CURRENT_BEGIN(5); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_4F_X(idx),4); @@ -853,7 +873,7 @@ void RSX_FUNC(DrawVertex4f)(gcmContextData *context,const u8 idx,const f32 v[4]) RSX_CONTEXT_CURRENT_END(5); } -void RSX_FUNC(DrawVertex4s)(gcmContextData *context,const u8 idx,const s16 v[4]) +void RSX_FUNC(DrawVertex4s)(gcmContextData *context,u8 idx,const s16 v[4]) { RSX_CONTEXT_CURRENT_BEGIN(3); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_4I_0(idx),2); @@ -862,7 +882,7 @@ void RSX_FUNC(DrawVertex4s)(gcmContextData *context,const u8 idx,const s16 v[4]) RSX_CONTEXT_CURRENT_END(3); } -void RSX_FUNC(DrawVertexScaled4s)(gcmContextData *context,const u8 idx,const s16 v[4]) +void RSX_FUNC(DrawVertexScaled4s)(gcmContextData *context,u8 idx,const s16 v[4]) { RSX_CONTEXT_CURRENT_BEGIN(3); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_4I_SCALED_0(idx),2); @@ -871,7 +891,7 @@ void RSX_FUNC(DrawVertexScaled4s)(gcmContextData *context,const u8 idx,const s16 RSX_CONTEXT_CURRENT_END(3); } -void RSX_FUNC(DrawVertex2s)(gcmContextData *context,const u8 idx,const s16 v[2]) +void RSX_FUNC(DrawVertex2s)(gcmContextData *context,u8 idx,const s16 v[2]) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_2I(idx),1); @@ -879,7 +899,7 @@ void RSX_FUNC(DrawVertex2s)(gcmContextData *context,const u8 idx,const s16 v[2]) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(DrawVertex4ub)(gcmContextData *context,const u8 idx,const u8 v[4]) +void RSX_FUNC(DrawVertex4ub)(gcmContextData *context,u8 idx,const u8 v[4]) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTX_ATTR_4UB(idx),1); @@ -941,7 +961,7 @@ void RSX_FUNC(LoadTexture)(gcmContextData *context,u8 index,const gcmTexture *te RSX_CONTEXT_CURRENT_END(9); } -void RSX_FUNC(LoadVertexTexture)(gcmContextData *context,const u8 index,const gcmTexture *texture) +void RSX_FUNC(LoadVertexTexture)(gcmContextData *context,u8 index,const gcmTexture *texture) { u32 format,offset,control,imagerect; @@ -974,7 +994,7 @@ void RSX_FUNC(TextureControl)(gcmContextData *context,u8 index,u32 enable,u16 mi RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(VertexTextureControl)(gcmContextData *context,const u8 index,const u32 enable,const u16 minlod,const u16 maxlod) +void RSX_FUNC(VertexTextureControl)(gcmContextData *context,u8 index,u32 enable,u16 minlod,u16 maxlod) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VP_TEXTURE_CONTROL0(index),1); @@ -982,15 +1002,15 @@ void RSX_FUNC(VertexTextureControl)(gcmContextData *context,const u8 index,const RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(TextureFilter)(gcmContextData *context,u8 index,u8 min,u8 mag,u8 conv) +void RSX_FUNC(TextureFilter)(gcmContextData *context,u8 index,u16 bias,u8 min,u8 mag,u8 conv) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_FILTER(index),1); - RSX_CONTEXT_CURRENTP[1] = ((mag << NV40TCL_TEX_FILTER_MAG_SHIFT) | (min << NV40TCL_TEX_FILTER_MIN_SHIFT) | (conv << NV40TCL_TEX_FILTER_CONV_SHIFT)); + RSX_CONTEXT_CURRENTP[1] = ((mag << NV40TCL_TEX_FILTER_MAG_SHIFT) | (min << NV40TCL_TEX_FILTER_MIN_SHIFT) | (conv << NV40TCL_TEX_FILTER_CONV_SHIFT) | (bias&0x1fff)); RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(VertexTextureFilter)(gcmContextData *context,const u8 index,const u16 bias) +void RSX_FUNC(VertexTextureFilter)(gcmContextData *context,u8 index,u16 bias) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VP_TEXTURE_FILTER(index),1); @@ -1011,7 +1031,7 @@ void RSX_FUNC(TextureWrapMode)(gcmContextData *context,u8 index,u8 wraps,u8 wrap RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(VertexTextureWrapMode)(gcmContextData *context,const u8 index,const u8 wraps,const u8 wrapt) +void RSX_FUNC(VertexTextureWrapMode)(gcmContextData *context,u8 index,u8 wraps,u8 wrapt) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VP_TEXTURE_ADDRESS(index),1); @@ -1019,7 +1039,7 @@ void RSX_FUNC(VertexTextureWrapMode)(gcmContextData *context,const u8 index,cons RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(TextureBorderColor)(gcmContextData *context,const u8 index,const u32 color) +void RSX_FUNC(TextureBorderColor)(gcmContextData *context,u8 index,u32 color) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_BORDER_COLOR(index),1); @@ -1027,7 +1047,7 @@ void RSX_FUNC(TextureBorderColor)(gcmContextData *context,const u8 index,const u RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(VertexTextureBorderColor)(gcmContextData *context,const u8 index,const u32 color) +void RSX_FUNC(VertexTextureBorderColor)(gcmContextData *context,u8 index,u32 color) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VP_TEXTURE_BORDER_COLOR(index),1); @@ -1035,7 +1055,7 @@ void RSX_FUNC(VertexTextureBorderColor)(gcmContextData *context,const u8 index,c RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(TextureOptimization)(gcmContextData *context,const u8 index,const u8 slope,const u8 iso,const u8 aniso) +void RSX_FUNC(TextureOptimization)(gcmContextData *context,u8 index,u8 slope,u8 iso,u8 aniso) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_TEX_CONTROL2(index),1); @@ -1043,7 +1063,7 @@ void RSX_FUNC(TextureOptimization)(gcmContextData *context,const u8 index,const RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(TextureAnisoSpread)(gcmContextData *context,const u8 index,const u8 reduceSamplesEnable,const u8 hReduceSamplesEnable,const u8 vReduceSamplesEnable,const u8 spacingSelect,const u8 hSpacingSelect,const u8 vSpacingSelect) +void RSX_FUNC(TextureAnisoSpread)(gcmContextData *context,u8 index,u8 reduceSamplesEnable,u8 hReduceSamplesEnable,u8 vReduceSamplesEnable,u8 spacingSelect,u8 hSpacingSelect,u8 vSpacingSelect) { u32 val = ((spacingSelect&0x7)<<0) | (( reduceSamplesEnable&0x1)<<4) | ((hSpacingSelect&0x7)<<8) | ((hReduceSamplesEnable&0x1)<<12) | @@ -1063,16 +1083,13 @@ void RSX_FUNC(SetZControl)(gcmContextData *context,u8 cullNearFar,u8 zClampEnabl RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(BindVertexArrayAttrib)(gcmContextData *context,u8 attr,u32 offset,u8 stride,u8 elems,u8 dtype,u8 location) +void RSX_FUNC(BindVertexArrayAttrib)(gcmContextData *context,u8 attr,u16 frequency,u32 offset,u8 stride,u8 elems,u8 dtype,u8 location) { RSX_CONTEXT_CURRENT_BEGIN(4); - - RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTXBUF_ADDRESS(attr),1); - RSX_CONTEXT_CURRENTP[1] = ((location << 31) | offset); - - RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_VTXFMT(attr),1); - RSX_CONTEXT_CURRENTP[3] = ((stride << NV40TCL_VTXFMT_STRIDE_SHIFT) | (elems << NV40TCL_VTXFMT_SIZE_SHIFT) | dtype); - + RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_VTXFMT(attr),1); + RSX_CONTEXT_CURRENTP[1] = ((frequency << 16) | (stride << NV40TCL_VTXFMT_STRIDE_SHIFT) | (elems << NV40TCL_VTXFMT_SIZE_SHIFT) | dtype); + RSX_CONTEXT_CURRENTP[2] = RSX_METHOD(NV40TCL_VTXBUF_ADDRESS(attr),1); + RSX_CONTEXT_CURRENTP[3] = ((location << 31) | offset); RSX_CONTEXT_CURRENT_END(4); } @@ -1143,7 +1160,7 @@ void RSX_FUNC(DrawVertexArray)(gcmContextData *context,u32 type,u32 start,u32 co RSX_FUNC_INTERNAL(DrawVertexArray)(context,type,start,count); } -void RSX_FUNC(DrawInlineVertexArray)(gcmContextData *context,const u8 type,const u32 count,const void *data) +void RSX_FUNC(DrawInlineVertexArray)(gcmContextData *context,u8 type,u32 count,const void *data) { u32 i,j,loop,rest; u32 *value = (u32*)data; @@ -1282,7 +1299,7 @@ void RSX_FUNC(DrawIndexArray)(gcmContextData *context,u8 type,u32 offset,u32 cou RSX_FUNC_INTERNAL(DrawIndexArray)(context,type,offset,count,data_type,location); } -void RSX_FUNC(DrawInlineIndexArray16)(gcmContextData *context,u8 type,u32 start,u32 count,u16 *data) +void RSX_FUNC(DrawInlineIndexArray16)(gcmContextData *context,u8 type,u32 start,u32 count,const u16 *data) { u32 odd,lcount; u32 loop,rest,i,j; @@ -1351,7 +1368,7 @@ void RSX_FUNC(DrawInlineIndexArray16)(gcmContextData *context,u8 type,u32 start, RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(DrawInlineIndexArray32)(gcmContextData *context,u8 type,u32 start,u32 count,u32 *data) +void RSX_FUNC(DrawInlineIndexArray32)(gcmContextData *context,u8 type,u32 start,u32 count,const u32 *data) { u32 i,j; u32 loop,rest; @@ -1417,7 +1434,7 @@ void RSX_FUNC(SetScissor)(gcmContextData *context,u16 x,u16 y,u16 w,u16 h) RSX_CONTEXT_CURRENT_END(3); } -void RSX_FUNC(SetAntialiasingControl)(gcmContextData *context,const u32 enable,const u32 alphaToCoverage,const u32 alphaToOne,const u32 sampleMask) +void RSX_FUNC(SetAntialiasingControl)(gcmContextData *context,u32 enable,u32 alphaToCoverage,u32 alphaToOne,u32 sampleMask) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_ANTI_ALIASING_CONTROL,1); @@ -1425,7 +1442,7 @@ void RSX_FUNC(SetAntialiasingControl)(gcmContextData *context,const u32 enable,c RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(InlineTransfer)(gcmContextData *context,const u32 dstOffset,const void *srcAddress,const u32 sizeInWords,const u8 location) +void RSX_FUNC(InlineTransfer)(gcmContextData *context,u32 dstOffset,const void *srcAddress,u32 sizeInWords,u8 location) { u32 *src; u32 pixelShift; @@ -1469,7 +1486,7 @@ void RSX_FUNC(InlineTransfer)(gcmContextData *context,const u32 dstOffset,const RSX_CONTEXT_CURRENT_END(12 + padSizeInWords); } -void RSX_FUNC(SetAlphaFunc)(gcmContextData *context,const u32 alphaFunc,const u32 ref) +void RSX_FUNC(SetAlphaFunc)(gcmContextData *context,u32 alphaFunc,u32 ref) { RSX_CONTEXT_CURRENT_BEGIN(3); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_ALPHA_TEST_FUNC,2); @@ -1478,7 +1495,7 @@ void RSX_FUNC(SetAlphaFunc)(gcmContextData *context,const u32 alphaFunc,const u3 RSX_CONTEXT_CURRENT_END(3); } -void RSX_FUNC(SetAlphaTestEnable)(gcmContextData *context,const u32 enable) +void RSX_FUNC(SetAlphaTestEnable)(gcmContextData *context,u32 enable) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_ALPHA_TEST_ENABLE,1); @@ -1530,7 +1547,7 @@ void RSX_FUNC(SetBlendEnable)(gcmContextData *context,u32 enable) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetBlendOptimization)(gcmContextData *context,const u32 enable) +void RSX_FUNC(SetBlendOptimization)(gcmContextData *context,u32 enable) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_DST_COL_REDUCE,1); @@ -1538,7 +1555,7 @@ void RSX_FUNC(SetBlendOptimization)(gcmContextData *context,const u32 enable) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetBlendEnableMrt)(gcmContextData *context, const u32 mrt1, const u32 mrt2, const u32 mrt3) +void RSX_FUNC(SetBlendEnableMrt)(gcmContextData *context, u32 mrt1, u32 mrt2, u32 mrt3) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_BLEND_ENABLE_MRT,1); @@ -1546,7 +1563,7 @@ void RSX_FUNC(SetBlendEnableMrt)(gcmContextData *context, const u32 mrt1, const RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetLogicOp)(gcmContextData *context,const u32 op) +void RSX_FUNC(SetLogicOp)(gcmContextData *context,u32 op) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_COLOR_LOGIC_OP,1); @@ -1554,7 +1571,7 @@ void RSX_FUNC(SetLogicOp)(gcmContextData *context,const u32 op) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetLogicOpEnable)(gcmContextData *context,const u32 enable) +void RSX_FUNC(SetLogicOpEnable)(gcmContextData *context,u32 enable) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_COLOR_LOGIC_OP_ENABLE,1); @@ -1562,7 +1579,7 @@ void RSX_FUNC(SetLogicOpEnable)(gcmContextData *context,const u32 enable) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetFogMode)(gcmContextData *context,const u32 mode) +void RSX_FUNC(SetFogMode)(gcmContextData *context,u32 mode) { RSX_CONTEXT_CURRENT_BEGIN(2); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_FOG_MODE,1); @@ -1570,7 +1587,7 @@ void RSX_FUNC(SetFogMode)(gcmContextData *context,const u32 mode) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetFogParams)(gcmContextData *context,const f32 p0,const f32 p1) +void RSX_FUNC(SetFogParams)(gcmContextData *context,f32 p0,f32 p1) { ieee32 d0,d1; d0.f = p0; @@ -1593,7 +1610,7 @@ void RSX_FUNC(SetTransformBranchBits)(gcmContextData *context,u32 branchBits) RSX_CONTEXT_CURRENT_END(2); } -void RSX_FUNC(SetPointSpriteControl)(gcmContextData *context,const u32 enable,const u32 rmode,const u32 texcoordMask) +void RSX_FUNC(SetPointSpriteControl)(gcmContextData *context,u32 enable,u32 rmode,u32 texcoordMask) { RSX_CONTEXT_CURRENT_BEGIN(4); RSX_CONTEXT_CURRENTP[0] = RSX_METHOD(NV40TCL_POINT_PARAMS_ENABLE,1); @@ -1603,7 +1620,7 @@ void RSX_FUNC(SetPointSpriteControl)(gcmContextData *context,const u32 enable,co RSX_CONTEXT_CURRENT_END(4); } -void RSX_FUNC(SetPointSize)(gcmContextData *context,const f32 size) +void RSX_FUNC(SetPointSize)(gcmContextData *context,f32 size) { ieee32 d; @@ -1675,7 +1692,7 @@ void RSX_FUNC(SetTransferDataFormat)(gcmContextData *context,s32 inpitch,s32 out RSX_CONTEXT_CURRENT_END(6); } -void RSX_FUNC(SetTransferImage)(gcmContextData *context,const u8 mode,const u32 dstOffset,const u32 dstPitch,const u32 dstX,const u32 dstY,const u32 srcOffset,const u32 srcPitch,const u32 srcX,const u32 srcY,const u32 width,const u32 height,const u32 bytesPerPixel) +void RSX_FUNC(SetTransferImage)(gcmContextData *context,u8 mode,u32 dstOffset,u32 dstPitch,u32 dstX,u32 dstY,u32 srcOffset,u32 srcPitch,u32 srcX,u32 srcY,u32 width,u32 height,u32 bytesPerPixel) { RSX_CONTEXT_CURRENT_BEGIN(26); @@ -1715,7 +1732,7 @@ void RSX_FUNC(SetTransferImage)(gcmContextData *context,const u8 mode,const u32 RSX_CONTEXT_CURRENT_END(26); } -void RSX_FUNC(SetTransferScaleMode)(gcmContextData *context,const u8 mode,const u8 surface) +void RSX_FUNC(SetTransferScaleMode)(gcmContextData *context,u8 mode,u8 surface) { RSX_CONTEXT_CURRENT_BEGIN(6); @@ -1969,7 +1986,7 @@ static inline __attribute__((always_inline)) void RSX_FUNC_INTERNAL(SetConvertSw } } -void RSX_FUNC(SetConvertSwizzleFormat)(gcmContextData *context,const u32 dstOffset,const u32 dstWidth,const u32 dstHeight,const u32 dstX,const u32 dstY,const u32 srcOffset,const u32 srcPitch,const u32 srcX,const u32 srcY,const u32 width,const u32 height,const u32 bytesPerPixel,const u32 mode) +void RSX_FUNC(SetConvertSwizzleFormat)(gcmContextData *context,u32 dstOffset,u32 dstWidth,u32 dstHeight,u32 dstX,u32 dstY,u32 srcOffset,u32 srcPitch,u32 srcX,u32 srcY,u32 width,u32 height,u32 bytesPerPixel,u32 mode) { RSX_FUNC_INTERNAL(SetConvertSwizzleFormat)(context,dstOffset,dstWidth,dstHeight,dstX,dstY,srcOffset,srcPitch,srcX,srcY,width,height,bytesPerPixel,mode); } diff --git a/ppu/librsx/fragment_program.c b/ppu/librsx/fragment_program.c index fb36784f..d32cc8fd 100644 --- a/ppu/librsx/fragment_program.c +++ b/ppu/librsx/fragment_program.c @@ -4,57 +4,63 @@ #include "rsx_internal.h" -void rsxFragmentProgramGetUCode(rsxFragmentProgram *fp,void **ucode,u32 *size) +void rsxFragmentProgramGetUCode(const rsxFragmentProgram *fp,void **ucode,u32 *size) { *size = fp->num_insn*sizeof(u32)*4; *ucode = (void*)(((u8*)fp) + fp->ucode_off); } -rsxProgramConst* rsxFragmentProgramGetConsts(rsxFragmentProgram *fp) +u16 rsxFragmentProgramGetNumConst(const rsxFragmentProgram *fp) { - return (rsxProgramConst*)(((u8*)fp) + fp->const_off); + return fp->num_const; } -s32 rsxFragmentProgramGetConst(rsxFragmentProgram *fp,const char *name) +rsxProgramConst* rsxFragmentProgramGetConsts(const rsxFragmentProgram *fp) { - u32 i; - rsxProgramConst *fpc = rsxFragmentProgramGetConsts(fp); + return __rsxGetConsts((rsxProgram*) fp); +} + +s32 rsxFragmentProgramGetConstIndex(const rsxFragmentProgram *fp,const char *name) +{ + return __rsxGetConstIndex((rsxProgram*) fp, name); +} - for(i=0;inum_const;i++) { - char *namePtr; +rsxProgramConst* rsxFragmentProgramGetConst(const rsxFragmentProgram *fp,const char *name) +{ + rsxProgramConst *fpc = __rsxGetConsts((rsxProgram*) fp); + s32 index = __rsxGetConstIndex((rsxProgram*) fp, name); - if(!fpc[i].name_off) continue; + if (index == -1) return NULL; - namePtr = ((char*)fp) + fpc[i].name_off; - if(strcasecmp(name,namePtr)==0) - return i; - } - return -1; + return &fpc[index]; } -rsxProgramAttrib* rsxFragmentProgramGetAttribs(rsxFragmentProgram *fp) +u16 rsxFragmentProgramGetNumAttrib(const rsxFragmentProgram *fp) { - return (rsxProgramAttrib*)(((u8*)fp) + fp->attr_off); + return fp->num_attr; } -s32 rsxFragmentProgramGetAttrib(rsxFragmentProgram *fp,const char *name) +rsxProgramAttrib* rsxFragmentProgramGetAttribs(const rsxFragmentProgram *fp) { - u32 i; - rsxProgramAttrib *attribs = rsxFragmentProgramGetAttribs(fp); - - for(i=0;inum_attr;i++) { - char *namePtr; + return __rsxGetAttrs((rsxProgram*) fp); +} - if(!attribs[i].name_off) continue; +s32 rsxFragmentProgramGetAttribIndex(const rsxFragmentProgram *fp,const char *name) +{ + rsxProgramAttrib *attr = __rsxGetAttr((rsxProgram*) fp, name); + + if (attr != NULL) + return attr->index; - namePtr = ((char*)fp) + attribs[i].name_off; - if(strcasecmp(name,namePtr)==0) - return attribs[i].index; - } return -1; } -rsxConstOffsetTable* rsxFragmentProgramGetConstOffsetTable(rsxFragmentProgram *fp,u32 table_off) +rsxProgramAttrib* rsxFragmentProgramGetAttrib(const rsxFragmentProgram *fp,const char *name) +{ + return __rsxGetAttr((rsxProgram*) fp, name); +} + +rsxConstOffsetTable* rsxFragmentProgramGetConstOffsetTable(const rsxFragmentProgram *fp,u32 table_off) { return (rsxConstOffsetTable*)(((u8*)fp) + table_off); } diff --git a/ppu/librsx/init.c b/ppu/librsx/init.c index ab9c4440..24cc4137 100644 --- a/ppu/librsx/init.c +++ b/ppu/librsx/init.c @@ -13,7 +13,7 @@ static gcmContextData sUserContext = extern s32 gcmInitBodyEx(gcmContextData* ATTRIBUTE_PRXPTR *ctx,const u32 cmdSize,const u32 ioSize,const void *ioAddress); -s32 rsxInit(gcmContextData **context,const u32 cmdSize,const u32 ioSize,const void *ioAddress) +s32 rsxInit(gcmContextData **context,u32 cmdSize,u32 ioSize,const void *ioAddress) { s32 ret = -1; @@ -27,7 +27,7 @@ s32 rsxInit(gcmContextData **context,const u32 cmdSize,const u32 ioSize,const vo return ret; } -void rsxSetupContextData(gcmContextData *context,const u32 *addr,const u32 size,gcmContextCallback cb) +void rsxSetupContextData(gcmContextData *context,const u32 *addr,u32 size,gcmContextCallback cb) { u32 alignedSize = size&~0x3; @@ -37,7 +37,7 @@ void rsxSetupContextData(gcmContextData *context,const u32 *addr,const u32 size, context->callback = (gcmContextCallback)__get_opd32(cb); } -void rsxSetCurrentBuffer(gcmContextData **context,const u32 *addr,const u32 size) +void rsxSetCurrentBuffer(gcmContextData **context,const u32 *addr,u32 size) { u32 alignedSize = size&~0x3; diff --git a/ppu/librsx/rsx_internal.c b/ppu/librsx/rsx_internal.c new file mode 100644 index 00000000..49c8cb09 --- /dev/null +++ b/ppu/librsx/rsx_internal.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include +#include + +#include "rsx_internal.h" + +rsxProgramAttrib* __rsxGetAttrs(const rsxProgram *prg) +{ + return (rsxProgramAttrib*)(((u8*)prg) + prg->attr_off);; +} + +rsxProgramAttrib* __rsxGetAttr(const rsxProgram *prg, const char *name) +{ + rsxProgramAttrib *attribs = __rsxGetAttrs(prg); + for(u32 i=0;inum_attr;i++) { + char *namePtr; + + if(!attribs[i].name_off) continue; + + namePtr = ((char*)prg) + attribs[i].name_off; + if(strcasecmp(name,namePtr)==0) + return &attribs[i]; + } + return NULL; +} + +rsxProgramConst* __rsxGetConsts(const rsxProgram *prg) +{ + return (rsxProgramConst*)(((u8*)prg) + prg->const_off); +} + +s32 __rsxGetConstIndex(const rsxProgram *prg, const char *name) +{ + rsxProgramConst *consts = __rsxGetConsts(prg); + for(u32 i=0;inum_const;i++) { + char *namePtr; + + if(!consts[i].name_off) continue; + + namePtr = ((char*)prg) + consts[i].name_off; + if(strcasecmp(name,namePtr)==0) + return i; + } + return -1; +} diff --git a/ppu/librsx/rsx_internal.h b/ppu/librsx/rsx_internal.h index a8a8b4ef..95115e8a 100644 --- a/ppu/librsx/rsx_internal.h +++ b/ppu/librsx/rsx_internal.h @@ -21,4 +21,25 @@ #define RSX_SUBCHANNEL_SHIFT (13) #define RSX_SUBCHANNEL_METHOD(channel,method,count) (((count)<num_insn*sizeof(u32)*4; *ucode = (void*)(((u8*)vp) + vp->ucode_off); } -rsxProgramConst* rsxVertexProgramGetConsts(rsxVertexProgram *vp) +u16 rsxVertexProgramGetNumConst(const rsxVertexProgram *vp) { - return (rsxProgramConst*)(((u8*)vp) + vp->const_off); + return vp->num_const; } -s32 rsxVertexProgramGetConst(rsxVertexProgram *vp,const char *name) +u16 rsxVertexProgramGetNumAttrib(const rsxVertexProgram *vp) { - u32 i; - rsxProgramConst *vpc = rsxVertexProgramGetConsts(vp); + return vp->num_attr; +} - for(i=0;inum_const;i++) { - char *namePtr; +rsxProgramConst* rsxVertexProgramGetConsts(const rsxVertexProgram *vp) +{ + return __rsxGetConsts((rsxProgram*) vp); +} - if(!vpc[i].name_off) continue; +rsxProgramConst* rsxVertexProgramGetConst(const rsxVertexProgram *vp,const char *name) +{ + rsxProgramConst *vpc = __rsxGetConsts((rsxProgram*) vp); + s32 index = __rsxGetConstIndex((rsxProgram*) vp, name); - namePtr = ((char*)vp) + vpc[i].name_off; - if(strcasecmp(name,namePtr)==0) - return i; - } - return -1; + if (index == -1) return NULL; + + return &vpc[index]; } -rsxProgramAttrib* rsxVertexProgramGetAttribs(rsxVertexProgram *vp) +s32 rsxVertexProgramGetConstIndex(const rsxVertexProgram *vp,const char *name) { - return (rsxProgramAttrib*)(((u8*)vp) + vp->attr_off); + return __rsxGetConstIndex((rsxProgram*) vp, name); } -s32 rsxVertexProgramGetAttrib(rsxVertexProgram *vp,const char *name) +rsxProgramAttrib* rsxVertexProgramGetAttribs(const rsxVertexProgram *vp) { - u32 i; - rsxProgramAttrib *attribs = rsxVertexProgramGetAttribs(vp); + return __rsxGetAttrs((rsxProgram*) vp); +} - for(i=0;inum_attr;i++) { - char *namePtr; +rsxProgramAttrib* rsxVertexProgramGetAttrib(const rsxVertexProgram *vp,const char *name) +{ + return __rsxGetAttr((rsxProgram*) vp, name); +} - if(!attribs[i].name_off) continue; +s32 rsxVertexProgramGetAttribIndex(const rsxVertexProgram *vp,const char *name) +{ + rsxProgramAttrib *attr = __rsxGetAttr((rsxProgram*) vp, name); + + if (attr != NULL) + return attr->index; - namePtr = ((char*)vp) + attribs[i].name_off; - if(strcasecmp(name,namePtr)==0) - return attribs[i].index; - } return -1; } From 5cbcdbf88a03f93c7ad6d536ade4601a63885864 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Thu, 16 Jul 2020 16:14:37 +0200 Subject: [PATCH 21/56] - add additional define --- ppu/include/sys/event_queue.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ppu/include/sys/event_queue.h b/ppu/include/sys/event_queue.h index 00c5f780..ec39e690 100644 --- a/ppu/include/sys/event_queue.h +++ b/ppu/include/sys/event_queue.h @@ -28,6 +28,9 @@ /*! \brief Used to auto create a event queue key. */ #define SYS_EVENT_QUEUE_KEY_LOCAL 0x00 +/*! \brief Force destruction of event queue. */ +#define SYS_EVENT_QUEUE_FORCE_DESTROY 0x01 + #ifdef __cplusplus extern "C" { #endif From f0ec5e9aac24294a65d01befc0490147545fce6f Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Thu, 16 Jul 2020 16:15:25 +0200 Subject: [PATCH 22/56] - changes according to the RSX/shader program API changes --- samples/graphics/rsxtest/source/main.cpp | 76 ++++++++++---------- samples/graphics/rsxtest_spu/source/main.cpp | 76 ++++++++++---------- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/samples/graphics/rsxtest/source/main.cpp b/samples/graphics/rsxtest/source/main.cpp index bf9d36ef..beba78a2 100644 --- a/samples/graphics/rsxtest/source/main.cpp +++ b/samples/graphics/rsxtest/source/main.cpp @@ -335,7 +335,7 @@ static void setTexture() texture.offset = texture_offset; rsxLoadTexture(context,textureUnit_id,&texture); rsxTextureControl(context,textureUnit_id,GCM_TRUE,0<<8,12<<8,GCM_TEXTURE_MAX_ANISO_1); - rsxTextureFilter(context,textureUnit_id,GCM_TEXTURE_LINEAR,GCM_TEXTURE_LINEAR,GCM_TEXTURE_CONVOLUTION_QUINCUNX); + rsxTextureFilter(context,textureUnit_id,0,GCM_TEXTURE_LINEAR,GCM_TEXTURE_LINEAR,GCM_TEXTURE_CONVOLUTION_QUINCUNX); rsxTextureWrapMode(context,textureUnit_id,GCM_TEXTURE_CLAMP_TO_EDGE,GCM_TEXTURE_CLAMP_TO_EDGE,GCM_TEXTURE_CLAMP_TO_EDGE,0,GCM_TEXTURE_ZFUNC_LESS,0); } @@ -385,11 +385,11 @@ void init_shader() rsxVertexProgramGetUCode(vpo, &vp_ucode, &vpsize); printf("vpsize: %d\n", vpsize); - projMatrix_id = rsxVertexProgramGetConst(vpo,"projMatrix"); - modelViewMatrix_id = rsxVertexProgramGetConst(vpo,"modelViewMatrix"); - vertexPosition_id = rsxVertexProgramGetAttrib(vpo,"vertexPosition"); - vertexNormal_id = rsxVertexProgramGetAttrib(vpo,"vertexNormal"); - vertexTexcoord_id = rsxVertexProgramGetAttrib(vpo,"vertexTexcoord"); + projMatrix_id = rsxVertexProgramGetConstIndex(vpo,"projMatrix"); + modelViewMatrix_id = rsxVertexProgramGetConstIndex(vpo,"modelViewMatrix"); + vertexPosition_id = rsxVertexProgramGetAttribIndex(vpo,"vertexPosition"); + vertexNormal_id = rsxVertexProgramGetAttribIndex(vpo,"vertexNormal"); + vertexTexcoord_id = rsxVertexProgramGetAttribIndex(vpo,"vertexTexcoord"); rsxFragmentProgramGetUCode(fpo, &fp_ucode, &fpsize); printf("fpsize: %d\n", fpsize); @@ -398,14 +398,14 @@ void init_shader() memcpy(fp_buffer,fp_ucode,fpsize); rsxAddressToOffset(fp_buffer,&fp_offset); - textureUnit_id = rsxFragmentProgramGetAttrib(fpo,"texture"); - eyePosition_id = rsxFragmentProgramGetConst(fpo,"eyePosition"); - globalAmbient_id = rsxFragmentProgramGetConst(fpo,"globalAmbient"); - lightPosition_id = rsxFragmentProgramGetConst(fpo,"lightPosition"); - lightColor_id = rsxFragmentProgramGetConst(fpo,"lightColor"); - shininess_id = rsxFragmentProgramGetConst(fpo,"shininess"); - Ks_id = rsxFragmentProgramGetConst(fpo,"Ks"); - Kd_id = rsxFragmentProgramGetConst(fpo,"Kd"); + textureUnit_id = rsxFragmentProgramGetAttribIndex(fpo,"texture"); + eyePosition_id = rsxFragmentProgramGetConstIndex(fpo,"eyePosition"); + globalAmbient_id = rsxFragmentProgramGetConstIndex(fpo,"globalAmbient"); + lightPosition_id = rsxFragmentProgramGetConstIndex(fpo,"lightPosition"); + lightColor_id = rsxFragmentProgramGetConstIndex(fpo,"lightColor"); + shininess_id = rsxFragmentProgramGetConstIndex(fpo,"shininess"); + Ks_id = rsxFragmentProgramGetConstIndex(fpo,"Ks"); + Kd_id = rsxFragmentProgramGetConstIndex(fpo,"Kd"); } void program_exit_callback() @@ -473,26 +473,26 @@ void drawFrame() objLightPos = modelMatrixIT*lightPos; rsxAddressToOffset(&mesh->vertices[0].pos,&offset); - rsxBindVertexArrayAttrib(context,vertexPosition_id,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context,vertexPosition_id,0,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); rsxAddressToOffset(&mesh->vertices[0].nrm,&offset); - rsxBindVertexArrayAttrib(context,vertexNormal_id,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context,vertexNormal_id,0,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); rsxAddressToOffset(&mesh->vertices[0].u,&offset); - rsxBindVertexArrayAttrib(context,vertexTexcoord_id,offset,sizeof(S3DVertex),2,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context,vertexTexcoord_id,0,offset,sizeof(S3DVertex),2,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); rsxLoadVertexProgram(context,vpo,vp_ucode); - rsxSetVertexProgramParameter(context,vpo,projMatrix_id,(float*)&P); - rsxSetVertexProgramParameter(context,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); + rsxSetVertexProgramParameterByIndex(context,vpo,projMatrix_id,(float*)&P); + rsxSetVertexProgramParameterByIndex(context,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); - rsxSetFragmentProgramParameter(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); rsxLoadFragmentProgramLocation(context,fpo,fp_offset,GCM_LOCATION_RSX); @@ -519,26 +519,26 @@ void drawFrame() objLightPos = modelMatrixIT*lightPos; rsxAddressToOffset(&mesh->vertices[0].pos,&offset); - rsxBindVertexArrayAttrib(context,vertexPosition_id,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context,vertexPosition_id,0,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); rsxAddressToOffset(&mesh->vertices[0].nrm,&offset); - rsxBindVertexArrayAttrib(context,vertexNormal_id,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context,vertexNormal_id,0,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); rsxAddressToOffset(&mesh->vertices[0].u,&offset); - rsxBindVertexArrayAttrib(context,vertexTexcoord_id,offset,sizeof(S3DVertex),2,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context,vertexTexcoord_id,0,offset,sizeof(S3DVertex),2,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); rsxLoadVertexProgram(context,vpo,vp_ucode); - rsxSetVertexProgramParameter(context,vpo,projMatrix_id,(float*)&P); - rsxSetVertexProgramParameter(context,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); + rsxSetVertexProgramParameterByIndex(context,vpo,projMatrix_id,(float*)&P); + rsxSetVertexProgramParameterByIndex(context,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); - rsxSetFragmentProgramParameter(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); rsxLoadFragmentProgramLocation(context,fpo,fp_offset,GCM_LOCATION_RSX); diff --git a/samples/graphics/rsxtest_spu/source/main.cpp b/samples/graphics/rsxtest_spu/source/main.cpp index 18d144b6..324f8d64 100644 --- a/samples/graphics/rsxtest_spu/source/main.cpp +++ b/samples/graphics/rsxtest_spu/source/main.cpp @@ -114,7 +114,7 @@ static void setTexture() texture.offset = texture_offset; rsxLoadTexture(context,textureUnit_id,&texture); rsxTextureControl(context,textureUnit_id,GCM_TRUE,0<<8,12<<8,GCM_TEXTURE_MAX_ANISO_1); - rsxTextureFilter(context,textureUnit_id,GCM_TEXTURE_LINEAR,GCM_TEXTURE_LINEAR,GCM_TEXTURE_CONVOLUTION_QUINCUNX); + rsxTextureFilter(context,textureUnit_id,0,GCM_TEXTURE_LINEAR,GCM_TEXTURE_LINEAR,GCM_TEXTURE_CONVOLUTION_QUINCUNX); rsxTextureWrapMode(context,textureUnit_id,GCM_TEXTURE_CLAMP_TO_EDGE,GCM_TEXTURE_CLAMP_TO_EDGE,GCM_TEXTURE_CLAMP_TO_EDGE,0,GCM_TEXTURE_ZFUNC_LESS,0); } @@ -165,11 +165,11 @@ void init_shader() rsxVertexProgramGetUCode(vpo, &vp_ucode, &vpsize); printf("vpsize: %d\n", vpsize); - projMatrix_id = rsxVertexProgramGetConst(vpo,"projMatrix"); - modelViewMatrix_id = rsxVertexProgramGetConst(vpo,"modelViewMatrix"); - vertexPosition_id = rsxVertexProgramGetAttrib(vpo,"vertexPosition"); - vertexNormal_id = rsxVertexProgramGetAttrib(vpo,"vertexNormal"); - vertexTexcoord_id = rsxVertexProgramGetAttrib(vpo,"vertexTexcoord"); + projMatrix_id = rsxVertexProgramGetConstIndex(vpo,"projMatrix"); + modelViewMatrix_id = rsxVertexProgramGetConstIndex(vpo,"modelViewMatrix"); + vertexPosition_id = rsxVertexProgramGetAttribIndex(vpo,"vertexPosition"); + vertexNormal_id = rsxVertexProgramGetAttribIndex(vpo,"vertexNormal"); + vertexTexcoord_id = rsxVertexProgramGetAttribIndex(vpo,"vertexTexcoord"); rsxFragmentProgramGetUCode(fpo,&fp_ucode,&fpsize); printf("fpsize: %d\n", fpsize); @@ -178,14 +178,14 @@ void init_shader() memcpy(fp_buffer,fp_ucode,fpsize); rsxAddressToOffset(fp_buffer,&fp_offset); - textureUnit_id = rsxFragmentProgramGetAttrib(fpo,"texture"); - eyePosition_id = rsxFragmentProgramGetConst(fpo,"eyePosition"); - globalAmbient_id = rsxFragmentProgramGetConst(fpo,"globalAmbient"); - lightPosition_id = rsxFragmentProgramGetConst(fpo,"lightPosition"); - lightColor_id = rsxFragmentProgramGetConst(fpo,"lightColor"); - shininess_id = rsxFragmentProgramGetConst(fpo,"shininess"); - Ks_id = rsxFragmentProgramGetConst(fpo,"Ks"); - Kd_id = rsxFragmentProgramGetConst(fpo,"Kd"); + textureUnit_id = rsxFragmentProgramGetAttribIndex(fpo,"texture"); + eyePosition_id = rsxFragmentProgramGetConstIndex(fpo,"eyePosition"); + globalAmbient_id = rsxFragmentProgramGetConstIndex(fpo,"globalAmbient"); + lightPosition_id = rsxFragmentProgramGetConstIndex(fpo,"lightPosition"); + lightColor_id = rsxFragmentProgramGetConstIndex(fpo,"lightColor"); + shininess_id = rsxFragmentProgramGetConstIndex(fpo,"shininess"); + Ks_id = rsxFragmentProgramGetConstIndex(fpo,"Ks"); + Kd_id = rsxFragmentProgramGetConstIndex(fpo,"Kd"); } void program_exit_callback() @@ -268,22 +268,22 @@ void drawFrame() wait_signal_spu(); - rsxBindVertexArrayAttrib(context,vertexPosition_id,mesh->pos_off,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); - rsxBindVertexArrayAttrib(context,vertexNormal_id,mesh->nrm_off,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); - rsxBindVertexArrayAttrib(context,vertexTexcoord_id,mesh->uv_off,sizeof(S3DVertex),2,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context,vertexPosition_id,0,mesh->pos_off,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context,vertexNormal_id,0,mesh->nrm_off,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context,vertexTexcoord_id,0,mesh->uv_off,sizeof(S3DVertex),2,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); rsxLoadVertexProgram(context,vpo,vp_ucode); - rsxSetVertexProgramParameter(context,vpo,projMatrix_id,(float*)&P); - rsxSetVertexProgramParameter(context,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); + rsxSetVertexProgramParameterByIndex(context,vpo,projMatrix_id,(float*)&P); + rsxSetVertexProgramParameterByIndex(context,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); - rsxSetFragmentProgramParameter(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); rsxLoadFragmentProgramLocation(context,fpo,fp_offset,GCM_LOCATION_RSX); @@ -308,22 +308,22 @@ void drawFrame() objEyePos = modelMatrixIT*eye_pos; objLightPos = modelMatrixIT*lightPos; - rsxBindVertexArrayAttrib(context,vertexPosition_id,mesh->pos_off,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); - rsxBindVertexArrayAttrib(context,vertexNormal_id,mesh->nrm_off,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); - rsxBindVertexArrayAttrib(context,vertexTexcoord_id,mesh->uv_off,sizeof(S3DVertex),2,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context,vertexPosition_id,0,mesh->pos_off,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context,vertexNormal_id,0,mesh->nrm_off,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context,vertexTexcoord_id,0,mesh->uv_off,sizeof(S3DVertex),2,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); rsxLoadVertexProgram(context,vpo,vp_ucode); - rsxSetVertexProgramParameter(context,vpo,projMatrix_id,(float*)&P); - rsxSetVertexProgramParameter(context,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); + rsxSetVertexProgramParameterByIndex(context,vpo,projMatrix_id,(float*)&P); + rsxSetVertexProgramParameterByIndex(context,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); - rsxSetFragmentProgramParameter(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); - rsxSetFragmentProgramParameter(context,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(context,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); rsxLoadFragmentProgramLocation(context,fpo,fp_offset,GCM_LOCATION_RSX); From 99cd685c82df55fc2d9205b09373d582c3ff9c1a Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Fri, 17 Jul 2020 08:04:37 +0200 Subject: [PATCH 23/56] - add more advanced rsx test samples --- samples/graphics/rsxtest_flip/Makefile | 164 + samples/graphics/rsxtest_flip/include/acid.h | 2939 +++++++++++++++++ samples/graphics/rsxtest_flip/include/mesh.h | 62 + .../graphics/rsxtest_flip/include/rsxutil.h | 44 + .../shaders/diffuse_specular_shader.fcg | 35 + .../shaders/diffuse_specular_shader.vcg | 21 + samples/graphics/rsxtest_flip/source/main.cpp | 603 ++++ .../graphics/rsxtest_flip/source/rsxutil.cpp | 298 ++ samples/graphics/rsxtest_tile/Makefile | 164 + samples/graphics/rsxtest_tile/include/acid.h | 2939 +++++++++++++++++ samples/graphics/rsxtest_tile/include/mesh.h | 62 + .../graphics/rsxtest_tile/include/rsxutil.h | 44 + .../shaders/diffuse_specular_shader.fcg | 35 + .../shaders/diffuse_specular_shader.vcg | 21 + samples/graphics/rsxtest_tile/source/main.cpp | 603 ++++ .../graphics/rsxtest_tile/source/rsxutil.cpp | 322 ++ 16 files changed, 8356 insertions(+) create mode 100644 samples/graphics/rsxtest_flip/Makefile create mode 100644 samples/graphics/rsxtest_flip/include/acid.h create mode 100644 samples/graphics/rsxtest_flip/include/mesh.h create mode 100644 samples/graphics/rsxtest_flip/include/rsxutil.h create mode 100644 samples/graphics/rsxtest_flip/shaders/diffuse_specular_shader.fcg create mode 100644 samples/graphics/rsxtest_flip/shaders/diffuse_specular_shader.vcg create mode 100644 samples/graphics/rsxtest_flip/source/main.cpp create mode 100644 samples/graphics/rsxtest_flip/source/rsxutil.cpp create mode 100644 samples/graphics/rsxtest_tile/Makefile create mode 100644 samples/graphics/rsxtest_tile/include/acid.h create mode 100644 samples/graphics/rsxtest_tile/include/mesh.h create mode 100644 samples/graphics/rsxtest_tile/include/rsxutil.h create mode 100644 samples/graphics/rsxtest_tile/shaders/diffuse_specular_shader.fcg create mode 100644 samples/graphics/rsxtest_tile/shaders/diffuse_specular_shader.vcg create mode 100644 samples/graphics/rsxtest_tile/source/main.cpp create mode 100644 samples/graphics/rsxtest_tile/source/rsxutil.cpp diff --git a/samples/graphics/rsxtest_flip/Makefile b/samples/graphics/rsxtest_flip/Makefile new file mode 100644 index 00000000..aae67e84 --- /dev/null +++ b/samples/graphics/rsxtest_flip/Makefile @@ -0,0 +1,164 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PSL1GHT)),) +$(error "Please set PSL1GHT in your environment. export PSL1GHT=") +endif + +include $(PSL1GHT)/ppu_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +SHADERS := shaders +INCLUDES := include + +TITLE := RSX Test - PSL1GHT +APPID := RSX00003 +CONTENTID := UP0001-$(APPID)_00-0000000000000000 + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +CFLAGS = -Wall -mcpu=cell $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lsimdmath -lrsx -lgcm_sys -lio -lsysutil -lrt -llv2 -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ + $(foreach dir,$(SHADERS),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export BUILDDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) +VCGFILES := $(foreach dir,$(SHADERS),$(notdir $(wildcard $(dir)/*.vcg))) +FCGFILES := $(foreach dir,$(SHADERS),$(notdir $(wildcard $(dir)/*.fcg))) + +VPOFILES := $(VCGFILES:.vcg=.vpo) +FPOFILES := $(FCGFILES:.fcg=.fpo) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(addsuffix .o,$(VPOFILES)) \ + $(addsuffix .o,$(FPOFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES), -I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(LIBPSL1GHT_INC) \ + -I$(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + $(LIBPSL1GHT_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).self + +#--------------------------------------------------------------------------------- +run: + ps3load $(OUTPUT).self + +#--------------------------------------------------------------------------------- +pkg: $(BUILD) $(OUTPUT).pkg + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).self: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .bin extension +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.vpo.o : %.vpo +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.fpo.o : %.fpo +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/samples/graphics/rsxtest_flip/include/acid.h b/samples/graphics/rsxtest_flip/include/acid.h new file mode 100644 index 00000000..d1643518 --- /dev/null +++ b/samples/graphics/rsxtest_flip/include/acid.h @@ -0,0 +1,2939 @@ +/* GIMP RGBA C-Source image dump (acid.c) */ + +static const struct { + unsigned int width; + unsigned int height; + unsigned int bytes_per_pixel; /* 3:RGB, 4:RGBA */ + unsigned char pixel_data[128 * 128 * 4 + 1]; +} acid = { + 128, 128, 4, + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\335\335\335\0\265\265\265\0\221\221\221\0qqq\0UUU\0===\0)))\0\31\31" + "\31\0\15\15\15\0\5\5\5\0\1\1\1\0\1\1\1\0\5\5\5\0\15\15\15\0\31\31\31\0))" + ")\0===\0UUU\0qqq\0\221\221\221\0\265\265\265\0\335\335\335\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\345\345\345\0\251\251\251\0qqq\0===\0\15\15\15\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\15\15\15\0===\0qqq\0\251\251\251\0\345\345\345\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0qqq\0---\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\7\0\0\0\15\0\0\0" + "\23\0\0\0\30\0\0\0\35\0\0\0!\0\0\0$\0\0\0&\0\0\0(\0\0\0(\0\0\0'\0\0\0$\0" + "\0\0!\0\0\0\35\0\0\0\31\0\0\0\23\0\0\0\15\0\0\0\7\0\0\0\2\0\0\0\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0---\0qqq\0\271\271\271\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\265\265\265\0aaa\0\21\21\21\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0\17\0\0\0\30\0\0\0\40\0\0\0+\0\0\0B\0" + "\0\0\\\0\0\0t\0\0\0\211\0\0\0\234\0\0\0\254\0\0\0\271\0\0\0\304\0\0\0\313" + "\0\0\0\313\0\0\0\304\0\0\0\271\0\0\0\254\0\0\0\234\0\0\0\211\0\0\0t\0\0\0" + "\\\0\0\0B\0\0\0+\0\0\0!\0\0\0\30\0\0\0\17\0\0\0\6\0\0\0\1\0\0\0\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\21\21\21\0aaa\0\265\265\265\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\331\331\331\0yyy\0\35\35\35\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\2\0\0\0\14\0\0\0\30\0\0\0&\0\0\0?\0\0\0d\0\0\0\211\0\0\0\254\0\0\0\311" + "\0\0\0\331\0\0\0\341\0\0\0\347\0\0\0\354\0\0\0\361\0\0\0\365\0\0\0\370\0" + "\0\0\373\0\0\0\375\0\0\0\375\0\0\0\373\0\0\0\371\0\0\0\365\0\0\0\361\0\0" + "\0\355\0\0\0\347\0\0\0\341\0\0\0\331\0\0\0\312\0\0\0\254\0\0\0\211\0\0\0" + "d\0\0\0?\0\0\0&\0\0\0\30\0\0\0\15\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0" + "\35\35\35\0yyy\0\331\331\331\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0QQQ\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\16\0\0\0\33\0\0" + "\0.\0\0\0Y\0\0\0\211\0\0\0\265\0\0\0\325\0\0\0\343\0\0\0\354\0\0\0\365\0" + "\0\0\374\40!\0\377FH\0\377hk\0\377\206\213\0\377\240\246\0\377\267\275\0" + "\377\311\321\0\377\330\341\0\377\344\354\0\377\344\354\0\377\330\341\0\377" + "\311\321\0\377\267\275\0\377\240\246\0\377\206\213\0\377hk\0\377FH\0\377" + "\40!\0\377\0\0\0\375\0\0\0\365\0\0\0\355\0\0\0\343\0\0\0\326\0\0\0\266\0" + "\0\0\211\0\0\0Z\0\0\0/\0\0\0\33\0\0\0\16\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0" + "\0\0\0\0QQQ\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\255\255\255\0===\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\11\0\0\0" + "\27\0\0\0-\0\0\0]\0\0\0\224\0\0\0\304\0\0\0\336\0\0\0\354\0\0\0\367\30\31" + "\0\376QT\0\377\206\213\0\377\267\275\0\377\344\354\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\344\354\0\377\267\275\0\377\206\213\0\377QT\0\377\30\31\0" + "\376\0\0\0\367\0\0\0\354\0\0\0\337\0\0\0\305\0\0\0\224\0\0\0]\0\0\0-\0\0" + "\0\30\0\0\0\11\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0===\0\255\255\255\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265" + "\0===\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\15\0\0\0!\0\0\0H\0\0\0\204" + "\0\0\0\274\0\0\0\336\0\0\0\357\0\0\0\373BD\0\377\206\213\0\377\306\315\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\306\315\0\377\206\213\0\377BD\0\377\0\0\0\373" + "\0\0\0\357\0\0\0\337\0\0\0\275\0\0\0\204\0\0\0I\0\0\0!\0\0\0\15\0\0\0\3\0" + "\0\0\1\0\0\0\0\0\0\0\0===\0\265\265\265\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321\321\0QQ" + "Q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\17\0\0\0'\0\0\0[\0\0\0\236\0\0" + "\0\324\0\0\0\353\0\0\0\371FH\0\377\225\232\0\377\340\350\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\340\350\0\377\225\232\0\377FH\0\377\0\0\0\371\0" + "\0\0\353\0\0\0\324\0\0\0\236\0\0\0\\\0\0\0'\0\0\0\17\0\0\0\3\0\0\0\1\0\0" + "\0\0\0\0\0\0QQQ\0\321\321\321\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0yyy\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\16\0\0\0(\0\0\0c\0\0\0\250" + "\0\0\0\332\0\0\0\361$%\0\375~\203\0\377\325\335\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\325\335\0\377~\203\0\377$%\0\375\0\0\0\361\0\0\0" + "\333\0\0\0\251\0\0\0c\0\0\0(\0\0\0\17\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0yyy" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\265\265\265\0)))\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\13\0\0\0$\0\0\0_" + "\0\0\0\250\0\0\0\334\0\0\0\364BD\0\376\244\252\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\244\252\0\377BD\0\376\0\0\0\364\0\0\0" + "\335\0\0\0\250\0\0\0`\0\0\0%\0\0\0\13\0\0\0\2\0\0\0\1\0\0\0\0)))\0\265\265" + "\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\4\0\0\0\34\0\0\0O\0\0\0\234\0\0\0\332\0\0\0\364FH\0\376\267\275\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377FH\0\376\0\0\0\364\0" + "\0\0\332\0\0\0\234\0\0\0P\0\0\0\34\0\0\0\4\0\0\0\1\0\0\0\0\0\0\0\0qqq\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\315\315\315" + "\0""555\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\17\0\0\0""0\0\0\0\203\0\0\0\321\0" + "\0\0\360+-\0\375\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377+-\0\375" + "\0\0\0\360\0\0\0\322\0\0\0\203\0\0\0""1\0\0\0\17\0\0\0\3\0\0\0\1\0\0\0\0" + """555\0\315\315\315\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\235\235\235\0\1\1\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\4\0\0\0\40\0\0\0`\0\0\0\262\0\0\0\346\0\0\0\374~\203\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377~\203\0\377\0\0\0\374\0\0\0\347\0\0\0\263\0\0\0a\0\0\0\40\0\0\0\5\0\0" + "\0\1\0\0\0\0\1\1\1\0\235\235\235\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\14\0\0" + "\0/\0\0\0\207\0\0\0\326\0\0\0\366FH\0\376\311\321\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\311\321\0\377FH\0\376\0\0\0\366\0\0\0\327\0\0\0\207\0\0\0" + """0\0\0\0\14\0\0\0\2\0\0\0\1\0\0\0\0uuu\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\375" + "\375\375\0UUU\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\30\0\0\0S\0\0\0\252\0\0\0\345" + "\0\0\0\374\206\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\374\0\0\0\346" + "\0\0\0\253\0\0\0T\0\0\0\31\0\0\0\4\0\0\0\1\0\0\0\0UUU\0\375\375\375\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\351\351\351\0==" + "=\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\"\0\0\0q\0\0\0\316\0\0\0\363/1\0\376\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377/1\0\376" + "\0\0\0\364\0\0\0\316\0\0\0q\0\0\0\"\0\0\0\5\0\0\0\1\0\0\0\0===\0\351\351" + "\351\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0---\0\0\0\0\0\0\0\0\1" + "\0\0\0\6\0\0\0*\0\0\0\205\0\0\0\332\0\0\0\373\\`\0\377\357\370\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357\370" + "\0\377\\`\0\377\0\0\0\373\0\0\0\332\0\0\0\206\0\0\0+\0\0\0\6\0\0\0\1\0\0" + "\0\1---\0\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\331\331\331\0%%%\0\0\0\0\0\0\0\0\1\0\0\0\11" + "\0\0\0""2\0\0\0\225\0\0\0\342\0\0\0\374\202\207\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\374\0\0\0\342\0\0\0" + "\226\0\0\0""2\0\0\0\11\0\0\0\1\0\0\0\1%%%\0\331\331\331\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\335\335\335\0%%%\0\0\0\0\0\0\0\0\1\0\0\0\15" + "\0\0\0?\0\0\0\243\0\0\0\350\2\2\0\375\240\246\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377" + "\2\2\0\375\0\0\0\350\0\0\0\244\0\0\0@\0\0\0\15\0\0\0\2\0\0\0\1%%%\0\335\335" + "\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\351\351\351\0---\0\0\0\0\0\0\0\0\1\0\0\0\16" + "\0\0\0H\0\0\0\257\0\0\0\355\25\25\0\376\267\275\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\25\25\0\376\0\0\0\355\0\0\0\257\0\0\0H\0\0\0" + "\17\0\0\0\2\0\0\0\1---\0\351\351\351\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\375\375\375\0===\0\0\0\0\0\0\0\0\1\0\0\0\15" + "\0\0\0H\0\0\0\264\0\0\0\360\40!\0\376\306\315\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\40!\0\376\0\0\0" + "\360\0\0\0\265\0\0\0I\0\0\0\15\0\0\0\1\0\0\0\1===\0\375\375\375\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0UUU\0\0\0\0\0\0\0\0\1\0\0\0\11" + "\0\0\0?\0\0\0\257\0\0\0\360$%\0\376\315\325\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\315\325" + "\0\377$%\0\377\0\0\0\360\0\0\0\257\0\0\0@\0\0\0\11\0\0\0\1\0\0\0\1UUU\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0""2" + "\0\0\0\244\0\0\0\355\40!\0\376\315\325\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377\40!\0\376\0\0\0\355\0\0\0\244\0\0\0""2\0" + "\0\0\6\0\0\0\1\0\0\0\0uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\235\235\235\0\0\0\0\0\0\0\0\0\0\0" + "\0\4\0\0\0*\0\0\0\225\0\0\0\350\25\25\0\376\306\315\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\25" + "\25\0\376\0\0\0\350\0\0\0\226\0\0\0*\0\0\0\5\0\0\0\1\0\0\0\0\235\235\235" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\315\315\315\0" + "\1\1\1\0\0\0\0\0\0\0\0\3\0\0\0\"\0\0\0\205\0\0\0\342\2\2\0\375\267\275\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\267\275\0\377\2\2\0\375\0\0\0\342\0\0\0" + "\206\0\0\0#\0\0\0\4\0\0\0\1\1\1\1\0\315\315\315\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0""555\0\0\0\0\0\0\0\0\2\0\0\0\30\0\0\0q\0\0\0\332\0\0\0\374" + "\240\246\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\350\361\0\377\254\263\0\377.0\0\377\13\14\0\377UX\0\377" + "\317\327\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377\0" + "\0\0\374\0\0\0\333\0\0\0r\0\0\0\31\0\0\0\2\0\0\0\1""555\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\0\0\0\0\14\0\0\0S\0\0\0\316\0\0\0\373\202\207" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\310\317\0\3779<\0\377\0\0\0\3779<\0\377\310\317\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\306\315\0\377!\"\0\377\0\0\0\377\0\0\0\377\4\4\0\377PT\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\373" + "\0\0\0\316\0\0\0S\0\0\0\14\0\0\0\1\0\0\0\0qqq\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0" + "\0\0\0\0\0\0\4\0\0\0/\0\0\0\253\0\0\0\364\\`\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377/2\0\377\0\0\0\377\0\0\0\377\0\0\0\377/2\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\237\245\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\20\20\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\\`\0\377\0\0\0\364\0\0\0" + "\253\0\0\0""0\0\0\0\5\0\0\0\1\0\0\0\0\265\265\265\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0)))\0\0\0\0\0\0\0\0\3\0\0\0\40\0\0" + "\0\207\0\0\0\345/1\0\376\357\370\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\207\215\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\357\370\0\377/1\0\376\0\0\0\346" + "\0\0\0\207\0\0\0\40\0\0\0\3\0\0\0\1)))\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0yyy\0\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0`\0\0\0\326\0\0\0\374\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\5\5\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377\0\0\0\374\0\0\0" + "\327\0\0\0a\0\0\0\17\0\0\0\1\0\0\0\0yyy\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321\321\0" + "\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0""1\0\0\0\263\0\0\0\365\206\213\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0" + "\366\0\0\0\263\0\0\0""1\0\0\0\5\0\0\0\1\0\0\0\0\321\321\321\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0QQQ\0\0" + "\0\0\0\0\0\0\2\0\0\0\34\0\0\0\203\0\0\0\346FH\0\376\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\376" + "\0\0\0\347\0\0\0\204\0\0\0\34\0\0\0\2\0\0\0\1QQQ\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0\0\0\0" + "\0\0\0\12\0\0\0O\0\0\0\321\0\0\0\374\311\321\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\311\321" + "\0\377\0\0\0\374\0\0\0\322\0\0\0P\0\0\0\13\0\0\0\1\0\0\0\0\265\265\265\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0" + "\0%\0\0\0\234\0\0\0\360~\203\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377~\203\0\377\0\0\0\361\0\0\0\234\0\0\0%\0\0\0\3\0\0\0\1===\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\255\255\255\0\0\0\0\0\0\0\0\0\0\0\0\16\0\0\0_\0" + "\0\0\332+-\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377+-\0\375\0\0\0\332\0\0\0`\0\0\0\17\0\0\0\1\0\0\0\0\255\255" + "\255\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0(\0\0\0\250\0\0\0" + "\363\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\244\252\0\377\0\0\0\364\0\0\0\250\0\0\0)\0\0\0\3\0\0\0\1=" + "==\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\271\271\271\0\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0c\0\0\0\334FH" + "\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377FH\0\376\0\0\0\335\0\0\0d\0\0\0\17\0\0\0" + "\1\0\0\0\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0'\0\0\0\251\0\0\0\363\267\275" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\364\0\0\0\251\0\0\0" + "(\0\0\0\3\0\0\0\1QQQ\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\331\331\331\0\0\0\0\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\333BD\0\375\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377BD\0\376\0\0\0\333\0\0\0\\" + "\0\0\0\16\0\0\0\1\0\0\0\0\331\331\331\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0yyy\0\0\0\0\0\0\0\0\2\0\0\0!\0\0\0\235\0\0\0\361\244\252\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377\0\0\0\361\0" + "\0\0\236\0\0\0\"\0\0\0\2\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\35\35\35\0\0\0\0\0\0\0\0\10\0\0\0H\0\0\0\324$%\0\375\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377$%\0\375\0" + "\0\0\325\0\0\0I\0\0\0\11\0\0\0\1\35\35\35\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265" + "\265\0\0\0\0\0\0\0\0\0\0\0\0\26\0\0\0\203\0\0\0\353~\203\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377~\203" + "\0\377\0\0\0\353\0\0\0\204\0\0\0\27\0\0\0\1\0\0\0\0\265\265\265\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0aaa\0\0\0\0\0\0\0\0\3\0\0\0-\0\0\0\275\0\0\0\370\325\335\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\325\335\0\377\0\0\0\371\0\0\0\276\0\0\0-\0\0\0\3\0\0\0\1aaa\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\21\21\21\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\336FH\0\376\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377FH\0\377\0\0\0\337\0\0\0]\0\0\0\16\0\0\0\1\21\21\21\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0\0\0\0\0\0\0\0\0\0\0\0\32\0\0\0\223\0\0\0\357\225\232\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\225\232\0\377\0\0\0\357\0\0\0\224\0\0\0\33\0" + "\0\0\1\0\0\0\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\2\0\0\0.\0\0\0\305\0\0\0\372\340\350\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\340\350\0\377\0\0\0\373\0\0\0\305\0\0\0" + "/\0\0\0\3\0\0\0\1qqq\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0---\0\0\0\0\0\0\0\0\14\0\0\0Y\0\0\0\336BD\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377BD\0\377\0\0\0\337\0\0\0Z\0\0\0" + "\15\0\0\0\1---\0\377\377\377\0\377\377\377\0\377\377\377\0\345\345\345\0" + "\0\0\0\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\355\0" + "\0\0\211\0\0\0\30\0\0\0\1\0\0\0\0\345\345\345\0\377\377\377\0\377\377\377" + "\0\251\251\251\0\0\0\0\0\0\0\0\1\0\0\0&\0\0\0\265\0\0\0\367\306\315\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377" + "\0\0\0\367\0\0\0\266\0\0\0&\0\0\0\2\0\0\0\1\251\251\251\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\6\0\0\0?\0\0\0\325\30\31\0\376\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\30\31\0\376\0\0\0\326\0\0\0?\0\0\0\6\0\0\0\1qqq\0\377\377\377\0\377\377" + "\377\0===\0\0\0\0\0\0\0\0\17\0\0\0d\0\0\0\343QT\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377QT" + "\0\377\0\0\0\344\0\0\0e\0\0\0\17\0\0\0\1===\0\377\377\377\0\377\377\377\0" + "\15\15\15\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\15\15\15\0\377\377\377" + "\0\335\335\335\0\0\0\0\0\0\0\0\0\0\0\0\40\0\0\0\254\0\0\0\365\267\275\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\0\0\0\365\0\0\0\255\0\0\0!\0\0\0\1\0\0\0\0\335" + "\335\335\0\265\265\265\0\0\0\0\0\0\0\0\1\0\0\0+\0\0\0\311\0\0\0\374\344\354" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\210\215\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\7\7\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\344\354\0\377\0\0\0\374\0\0\0\312\0\0\0,\0\0\0\2" + "\0\0\0\1\265\265\265\0\221\221\221\0\0\0\0\0\0\0\0\6\0\0\0B\0\0\0\331\40" + "!\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\237\245\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\20\21\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\40!\0\377\0\0\0" + "\332\0\0\0B\0\0\0\7\0\0\0\1\221\221\221\0qqq\0\0\0\0\0\0\0\0\15\0\0\0\\\0" + "\0\0\341FH\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\37702\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\37702\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377!\"\0\377\0\0" + "\0\377\0\0\0\377\4\4\0\377PS\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\377\0\0\0\342\0" + "\0\0]\0\0\0\15\0\0\0\1qqq\0UUU\0\0\0\0\0\0\0\0\23\0\0\0t\0\0\0\347hk\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\310\317\0\3779<\0\377\0" + "\0\0\3779<\0\377\310\317\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\350\361\0\377\254\263\0\377.0\0\377" + "\13\14\0\377UX\0\377\317\327\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377hk\0\377\0\0\0\350\0" + "\0\0u\0\0\0\23\0\0\0\1UUU\0===\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206" + "\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1===" + "\0)))\0\0\0\0\0\0\0\0\35\0\0\0\234\0\0\0\361\240\246\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246" + "\0\377\0\0\0\361\0\0\0\235\0\0\0\35\0\0\0\1)))\0\31\31\31\0\0\0\0\0\0\0\0" + "\40\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0" + "\0\0\255\0\0\0!\0\0\0\1\31\31\31\0\15\15\15\0\0\0\0\0\0\0\0$\0\0\0\271\0" + "\0\0\370\311\321\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\311\321\0\377\0\0\0\371\0\0\0\272\0\0\0" + "$\0\0\0\1\15\15\15\0\5\5\5\0\0\0\0\0\0\0\0&\0\0\0\304\0\0\0\373\330\341\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\330\341\0\377\0\0\0\373\0\0\0\305\0\0\0'\0\0\0\1\5\5\5\0\1\1\1" + "\0\0\0\0\0\0\0\0(\0\0\0\313\0\0\0\374\344\354\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377" + "\0\0\0\375\0\0\0\313\0\0\0)\0\0\0\1\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0(\0\0\0" + "\313\0\0\0\374\344\354\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\344\354\0\377\0\0\0\375\0\0\0\313\0\0" + "\0)\0\0\0\1\1\1\1\0\5\5\5\0\0\0\0\0\0\0\0&\0\0\0\304\0\0\0\373\330\341\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\330\341\0\377\0\0\0\373\0\0\0\305\0\0\0'\0\0\0\1\5\5\5\0\15\15" + "\15\0\0\0\0\0\0\0\0$\0\0\0\271\0\0\0\370\311\321\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\311\321\0" + "\377\0\0\0\371\0\0\0\272\0\0\0$\0\0\0\1\15\15\15\0\31\31\31\0\0\0\0\0\0\0" + "\0\40\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0" + "\0\0\255\0\0\0!\0\0\0\1\31\31\31\0)))\0\0\0\0\0\0\0\0\35\0\0\0\234\0\0\0" + "\361\240\246\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\240\246\0\377\0\0\0\361\0\0\0\235\0\0\0\35\0\0\0" + "\1)))\0===\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\206" + "\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1===\0UUU\0\0\0\0\0\0\0\0\23" + "\0\0\0t\0\0\0\347hk\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377hk\0\377\0\0\0\350\0\0\0u\0\0\0\23\0\0\0" + "\1UUU\0qqq\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\341FH\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\377\0" + "\0\0\342\0\0\0]\0\0\0\15\0\0\0\1qqq\0\221\221\221\0\0\0\0\0\0\0\0\6\0\0\0" + "B\0\0\0\331\40!\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\40!\0\377\0\0\0\332\0\0\0C\0\0\0\7\0\0\0\1\221" + "\221\221\0\265\265\265\0\0\0\0\0\0\0\0\1\0\0\0,\0\0\0\312\0\0\0\374\344\354" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377" + "\0\0\0\375\0\0\0\312\0\0\0,\0\0\0\2\0\0\0\1\265\265\265\0\335\335\335\0\0" + "\0\0\0\0\0\0\1\0\0\0!\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0\0\0\255\0\0" + "\0\"\0\0\0\1\0\0\0\1\335\335\335\0\377\377\377\0\15\15\15\0\0\0\0\0\0\0\0" + "\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\15" + "\15\15\0\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\17\0\0\0d\0\0\0\343" + "QT\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377QT\0\377\0" + "\0\0\344\0\0\0e\0\0\0\17\0\0\0\1===\0\377\377\377\0\377\377\377\0qqq\0\0" + "\0\0\0\0\0\0\6\0\0\0?\0\0\0\325\30\31\0\376\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\30\31\0\376\0\0\0\326\0\0\0?\0\0\0\6\0\0\0\1qqq\0\377" + "\377\377\0\377\377\377\0\251\251\251\0\0\0\0\0\0\0\0\1\0\0\0&\0\0\0\266\0" + "\0\0\367\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\335\345\0\377\261\270\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\0\0\0\370" + "\0\0\0\266\0\0\0'\0\0\0\2\0\0\0\1\251\251\251\0\377\377\377\0\377\377\377" + "\0\345\345\345\0\0\0\0\0\0\0\0\1\0\0\0\31\0\0\0\212\0\0\0\354\206\213\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\322\332\0\377\36" + "\37\0\377\10\10\0\377\265\273\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\316\326\0\377\230\235\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\0" + "\0\0\1\345\345\345\0\377\377\377\0\377\377\377\0\377\377\377\0---\0\0\0\0" + "\0\0\0\0\14\0\0\0Z\0\0\0\337BD\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\322\332\0\377\36\37\0\377\0\0\0\377\0\0\0\377\1\1\0\377\201\206\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\265\273\0\377\17" + "\20\0\377\2\2\0\377\224\231\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377BD\0\377\0\0\0\340\0\0\0Z\0\0\0" + "\15\0\0\0\1---\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "qqq\0\0\0\0\0\0\0\0\2\0\0\0.\0\0\0\305\0\0\0\372\340\350\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\322\332\0\377\36\37\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377SV\0\377\353\364\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377w{\0\377\1\1\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377;>\0\377<>\0\377<>\0\377\303\312\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340" + "\350\0\377\0\0\0\373\0\0\0\305\0\0\0/\0\0\0\3\0\0\0\1qqq\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0\0\0\0\0\0\0\0\1" + "\0\0\0\33\0\0\0\224\0\0\0\357\225\232\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377\26\26\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377.0\0\377" + "\330\340\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\345\355\0\377?A" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\1\1\0\377\310\320\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\225\232\0\377\0\0\0\360\0\0\0" + "\225\0\0\0\34\0\0\0\1\0\0\0\1\271\271\271\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\21\21\21\0\0\0\0\0\0\0\0\15\0\0\0" + "]\0\0\0\337FH\0\376\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\257\266\0\377\4\4\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\16\17\0\377\265\273" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\277\306\0\377\23\24\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\2\2\0\377\226\233\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377FH\0\377\0\0\0\337\0\0\0^\0\0\0\16\0\0\0\1\21\21" + "\21\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0aaa\0\0\0\0\0\0\0\0\3\0\0\0-\0\0\0\275\0\0\0\370\325\335" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\300\307\0\377\30\31\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377z\177\0\377\355\366" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377pt\0\377\2\2\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\2\2" + "\0\377\235\243\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\325\335\0\377\0\0\0\371" + "\0\0\0\276\0\0\0-\0\0\0\3\0\0\0\1aaa\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0" + "\0\0\1\0\0\0\27\0\0\0\204\0\0\0\353~\203\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\330\341\0\377&'\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\25\25\0\377\277\306\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\310\317\0\377" + "-/\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\25\25\0\377\267\275\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377~\203\0\377\0\0\0\354\0\0\0\205\0\0\0\30\0\0\0\1\0\0\0\1\265\265" + "\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\35\35\35\0\0\0\0\0\0\0\0\11\0\0\0I\0\0\0\324" + "$%\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\350\360\0\377EG\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\2\2\0\377mq\0\377\352\363\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\342\352\0" + "\377fi\0\377\2\2\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377*,\0\377\333\344\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377$%\0\375\0\0\0\325\0\0\0J\0\0\0\11\0\0" + "\0\1\35\35\35\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0\0\2" + "\0\0\0!\0\0\0\236\0\0\0\361\244\252\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\357\370\0\377tx\0\377\1\1\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\35\36\0\377\235\243\0" + "\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377\220" + "\225\0\377\15\16\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377gj\0\377\353\364\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377\0\0\0\362\0" + "\0\0\237\0\0\0\"\0\0\0\2\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\331\331\331\0\0\0\0\0\0\0\0\1\0\0\0\16\0\0\0\\\0\0\0\333BD\0\375\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\265\273\0" + "\377\13\14\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377-/\0\377\273\302\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\355\366\0\377\177\204" + "\0\377\15\16\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\11\12\0\377\246\254\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "BD\0\376\0\0\0\333\0\0\0]\0\0\0\17\0\0\0\1\0\0\0\1\331\331\331\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0'" + "\0\0\0\251\0\0\0\363\267\275\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\346\356\0\377HK\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377=?\0\377\247\255\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377\206\213\0\377" + "\34\35\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377/1\0\377\325\335\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\267\275\0\377\0\0\0\364\0\0\0\251\0\0\0(\0\0\0\3\0\0\0\1QQQ\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0\0" + "\0\0\0\0\0\0\1\0\0\0\20\0\0\0d\0\0\0\335FH\0\375\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\211\217\0\377\10\11\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\14\15\0\377qu\0\377" + "\343\353\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\356\367\0\377\254\263\0\377hl\0\377\5\5\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\16\17\0\377\231\237\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377FH\0\376\0\0\0\335\0\0\0d\0\0\0\20\0\0\0\1\0\0\0\1\271\271\271" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0)\0\0\0\251\0\0\0\363\244\252\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\346\356\0\377fi\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\4\4\0\377BD\0\377\206\213\0\377\301\310\0\377\354\365\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340\350\0\377\230\235\0" + "\377^b\0\377\25\25\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377Z^" + "\0\377\335\345\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377" + "\0\0\0\364\0\0\0\251\0\0\0*\0\0\0\3\0\0\0\1===\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\255" + "\255\255\0\0\0\0\0\0\0\0\1\0\0\0\17\0\0\0`\0\0\0\332+-\0\375\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\254\263\0\377\25\25\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\12\13\0\377;>\0\377UX\0" + "\377lp\0\377x|\0\377x|\0\377x|\0\377bf\0\377HK\0\377\6\6\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\37713\0\377\275\304" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "+-\0\375\0\0\0\332\0\0\0a\0\0\0\17\0\0\0\1\0\0\0\1\255\255\255\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0%\0\0\0\234\0\0\0" + "\360~\203\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357\370" + "\0\377\216\223\0\377\16\17\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + ",.\0\377\247\255\0\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377~\203\0\377\0\0\0\361\0\0\0\235\0\0\0%\0" + "\0\0\3\0\0\0\1===\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265" + "\265\265\0\0\0\0\0\0\0\0\1\0\0\0\13\0\0\0P\0\0\0\322\0\0\0\374\311\321\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\355" + "\366\0\377\213\221\0\377\17\20\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\37724\0\377\246\254\0\377\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\311\321\0\377\0\0\0\374\0\0\0\323\0\0\0Q\0\0\0\14\0\0\0\1\0\0\0\1" + "\265\265\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0\34\0\0\0\204\0\0\0\347FH\0\376\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\356\367\0\377\235\243\0\377(*\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\5\5\0" + "\377hl\0\377\270\276\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377FH\0\377\0\0\0\347\0\0\0\204\0\0\0\35\0\0\0\2\0\0\0\1QQQ\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321" + "\321\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0""1\0\0\0\263\0\0\0\365\206\213\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\320\330\0\377\\`\0\377\7" + "\7\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\3778:\0\377\226\233\0" + "\377\344\354\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\206\213\0\377\0\0\0\366\0\0\0\263\0\0\0""1\0\0\0\5\0\0\0\1\0\0\0\1" + "\321\321\321\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0\0\1\0\0\0\17\0\0\0a\0\0\0" + "\327\0\0\0\374\276\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\357\370\0\377\245\253\0\377w{\0\377:=\0\377\3\3\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377*,\0\377dh\0\377\217\224\0\377\350" + "\361\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\276\305\0\377\0\0\0\375\0\0\0\330\0\0\0b\0\0" + "\0\20\0\0\0\1\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + ")))\0\0\0\0\0\0\0\0\3\0\0\0\40\0\0\0\207\0\0\0\346/1\0\376\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\357\370\0\377\305\314\0\377\247\255\0\377" + "\212\220\0\377x}\0\377x}\0\377x}\0\377x}\0\377{\200\0\377\240\246\0\377\270" + "\276\0\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\357\370\0\377/1\0\376\0\0\0\347\0\0\0\210\0\0\0!\0\0\0\3\0\0\0" + "\1)))\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265" + "\265\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0""0\0\0\0\253\0\0\0\364\\`\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\\`\0\377\0\0\0\364\0\0\0\254\0\0\0""1" + "\0\0\0\6\0\0\0\1\0\0\0\1\265\265\265\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0\0\0\1\0\0\0\14\0\0" + "\0T\0\0\0\317\0\0\0\373\202\207\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\373" + "\0\0\0\317\0\0\0T\0\0\0\15\0\0\0\1\0\0\0\1qqq\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0""555\0\0\0\0\0\0\0\0\2\0\0\0\30\0\0\0q\0\0\0\332\0\0\0\374\240\246\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377\0" + "\0\0\374\0\0\0\333\0\0\0r\0\0\0\31\0\0\0\2\0\0\0\1""555\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\315\315\315\0\1\1\1\0\0\0\0\1\0\0\0\4\0\0\0\"\0" + "\0\0\205\0\0\0\342\2\2\0\375\267\275\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\267" + "\275\0\377\2\2\0\375\0\0\0\342\0\0\0\206\0\0\0#\0\0\0\4\0\0\0\1\1\1\1\1\315" + "\315\315\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\235\235" + "\235\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0+\0\0\0\226\0\0\0\350\25\25\0\376\306" + "\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\306\315\0\377\25\25\0\376\0\0\0\351\0\0\0\226\0\0\0+\0\0\0\6\0" + "\0\0\1\0\0\0\1\235\235\235\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0" + """2\0\0\0\244\0\0\0\355\40!\0\376\315\325\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377\40!\0\376\0\0\0\356\0\0\0\245\0\0\0""3\0" + "\0\0\7\0\0\0\1\0\0\0\1uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0UUU\0\0" + "\0\0\0\0\0\0\1\0\0\0\11\0\0\0@\0\0\0\260\0\0\0\360$%\0\376\315\325\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377$%\0\377\0\0\0\361\0\0\0\260\0\0\0A\0\0\0" + "\12\0\0\0\1\0\0\0\1UUU\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\375\375\375\0===\0\0\0\0\1\0\0\0\2\0\0\0\16\0\0\0H\0\0\0\264\0\0\0\360" + "\40!\0\376\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\306\315\0\377\40!\0\376\0\0\0\361\0\0\0\265\0\0\0I\0\0\0\16" + "\0\0\0\2\0\0\0\1===\1\375\375\375\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\351\351\351\0---\0\0\0\0\1\0\0\0\2\0\0\0\17" + "\0\0\0I\0\0\0\260\0\0\0\355\25\25\0\376\267\275\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\25\25\0\376\0\0\0\356\0\0\0\260\0\0\0I\0\0\0" + "\20\0\0\0\2\0\0\0\1---\1\351\351\351\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0" + "%%%\0\0\0\0\1\0\0\0\2\0\0\0\16\0\0\0@\0\0\0\244\0\0\0\350\2\2\0\375\240\246" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\240\246\0\377\2\2\0\375\0\0\0\351\0\0\0\245\0\0\0@\0\0\0\16" + "\0\0\0\2\0\0\0\1%%%\1\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\331\331\331\0%%%\0\0\0\0\1\0\0\0\2\0\0\0\12\0\0\0""2\0\0\0\226" + "\0\0\0\342\0\0\0\374\202\207\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\202\207\0\377\0\0\0\374\0\0\0\343\0\0\0\226\0\0\0""3\0\0\0" + "\12\0\0\0\2\0\0\0\1%%%\1\331\331\331\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0---\0\0\0\0\1\0\0" + "\0\1\0\0\0\6\0\0\0*\0\0\0\205\0\0\0\332\0\0\0\373\\`\0\377\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357" + "\370\0\377\\`\0\377\0\0\0\373\0\0\0\333\0\0\0\206\0\0\0+\0\0\0\6\0\0\0\1" + "\0\0\0\1---\1\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\351\351\351\0" + "===\0\0\0\0\1\0\0\0\1\0\0\0\5\0\0\0#\0\0\0r\0\0\0\317\0\0\0\364/1\0\376\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377/1\0\376" + "\0\0\0\365\0\0\0\317\0\0\0r\0\0\0#\0\0\0\6\0\0\0\1\0\0\0\1===\1\351\351\351" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\375\375\375" + "\0UUU\0\0\0\0\0\0\0\0\1\0\0\0\4\0\0\0\31\0\0\0S\0\0\0\253\0\0\0\346\0\0\0" + "\374\206\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\375\0\0\0\347\0\0\0" + "\254\0\0\0T\0\0\0\31\0\0\0\4\0\0\0\1\0\0\0\1UUU\0\375\375\375\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\1\0\0\0\3\0\0\0\14\0\0\0/\0\0\0\210\0" + "\0\0\327\0\0\0\366FH\0\376\311\321\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\311\321\0\377FH\0\377\0\0\0\367\0\0\0\330\0\0\0\210\0\0\0""0\0\0\0\15\0" + "\0\0\3\0\0\0\1\0\0\0\1uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\235\235\235\0\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\5\0" + "\0\0!\0\0\0a\0\0\0\263\0\0\0\347\0\0\0\374~\203\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377~\203\0\377\0\0\0" + "\374\0\0\0\347\0\0\0\264\0\0\0b\0\0\0!\0\0\0\6\0\0\0\1\0\0\0\1\1\1\1\1\235" + "\235\235\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\315\315\315\0""555\0\0\0\0\0\0\0\0\1\0" + "\0\0\3\0\0\0\17\0\0\0""1\0\0\0\204\0\0\0\322\0\0\0\361+-\0\375\244\252\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\244\252\0\377+-\0\375\0\0\0\361\0\0\0\323\0\0\0" + "\204\0\0\0""1\0\0\0\20\0\0\0\3\0\0\0\1\0\0\0\1""555\0\315\315\315\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0" + "\0\0\1\0\0\0\1\0\0\0\5\0\0\0\34\0\0\0P\0\0\0\234\0\0\0\332\0\0\0\364FH\0" + "\376\267\275\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377FH" + "\0\376\0\0\0\364\0\0\0\333\0\0\0\235\0\0\0Q\0\0\0\35\0\0\0\5\0\0\0\1\0\0" + "\0\1\0\0\0\1qqq\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0)))\0\0" + "\0\0\0\0\0\0\1\0\0\0\3\0\0\0\14\0\0\0%\0\0\0`\0\0\0\251\0\0\0\335\0\0\0\364" + "BD\0\376\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\244\252\0\377BD\0\376\0\0\0\364\0\0\0\336\0\0\0\251\0\0\0a\0\0\0&" + "\0\0\0\14\0\0\0\3\0\0\0\1\0\0\0\1)))\0\265\265\265\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0" + "\0\1\0\0\0\1\0\0\0\3\0\0\0\17\0\0\0)\0\0\0d\0\0\0\251\0\0\0\333\0\0\0\362" + "$%\0\375~\203\0\377\325\335\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\325\335\0\377~\203\0\377$%\0\375\0\0\0\362\0\0\0\334\0\0\0\252\0\0\0" + "d\0\0\0)\0\0\0\17\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1yyy\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\321\321\321\0QQQ\0\0\0\0\0\0\0\0\1\0\0\0\1\0" + "\0\0\4\0\0\0\20\0\0\0(\0\0\0\\\0\0\0\236\0\0\0\325\0\0\0\354\0\0\0\372FH" + "\0\377\225\232\0\377\340\350\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340" + "\350\0\377\225\232\0\377FH\0\377\0\0\0\372\0\0\0\354\0\0\0\325\0\0\0\236" + "\0\0\0]\0\0\0(\0\0\0\20\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1QQQ\0\321\321\321" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\265\265\265\0===\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\3\0\0\0" + "\16\0\0\0\"\0\0\0I\0\0\0\205\0\0\0\275\0\0\0\337\0\0\0\360\0\0\0\374BD\0" + "\377\206\213\0\377\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\206" + "\213\0\377BD\0\377\0\0\0\374\0\0\0\360\0\0\0\340\0\0\0\276\0\0\0\205\0\0" + "\0J\0\0\0\"\0\0\0\16\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1===\0\265\265\265\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\255\255" + "\255\0===\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\11\0\0\0\27\0\0\0-\0\0" + "\0]\0\0\0\225\0\0\0\305\0\0\0\337\0\0\0\355\0\0\0\370\30\31\0\376QT\0\377" + "\206\213\0\377\267\275\0\377\344\354\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\344\354\0\377\267\275\0\377\206\213\0\377QT\0\377\30\31\0\376\0\0\0\370" + "\0\0\0\355\0\0\0\340\0\0\0\306\0\0\0\225\0\0\0^\0\0\0-\0\0\0\30\0\0\0\11" + "\0\0\0\3\0\0\0\1\0\0\0\1\0\0\0\1===\0\255\255\255\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\271\271\271\0QQQ\0\0\0\0\0\0\0\0\1\0\0\0\1" + "\0\0\0\1\0\0\0\3\0\0\0\16\0\0\0\33\0\0\0/\0\0\0Z\0\0\0\212\0\0\0\266\0\0" + "\0\326\0\0\0\344\0\0\0\355\0\0\0\365\0\0\0\374\40!\0\377FH\0\377hk\0\377" + "\206\213\0\377\240\246\0\377\267\275\0\377\311\321\0\377\330\341\0\377\344" + "\354\0\377\344\354\0\377\330\341\0\377\311\321\0\377\267\275\0\377\240\246" + "\0\377\206\213\0\377hk\0\377FH\0\377\40!\0\377\0\0\0\375\0\0\0\366\0\0\0" + "\355\0\0\0\344\0\0\0\326\0\0\0\266\0\0\0\212\0\0\0Z\0\0\0/\0\0\0\33\0\0\0" + "\16\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1QQQ\0\271\271\271\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\331\331\331\0yyy\0\35\35\35\0\0\0\0\0\0\0\0\1\0" + "\0\0\1\0\0\0\1\0\0\0\3\0\0\0\15\0\0\0\31\0\0\0'\0\0\0@\0\0\0e\0\0\0\212\0" + "\0\0\255\0\0\0\312\0\0\0\332\0\0\0\342\0\0\0\350\0\0\0\355\0\0\0\361\0\0" + "\0\365\0\0\0\371\0\0\0\373\0\0\0\375\0\0\0\375\0\0\0\374\0\0\0\371\0\0\0" + "\366\0\0\0\362\0\0\0\355\0\0\0\350\0\0\0\342\0\0\0\332\0\0\0\313\0\0\0\255" + "\0\0\0\212\0\0\0e\0\0\0@\0\0\0'\0\0\0\31\0\0\0\16\0\0\0\4\0\0\0\2\0\0\0\1" + "\0\0\0\1\0\0\0\1\35\35\35\0yyy\0\331\331\331\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\265\265\265\0aaa\0\21\21\21\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0" + "\0\2\0\0\0\6\0\0\0\17\0\0\0\31\0\0\0!\0\0\0,\0\0\0C\0\0\0]\0\0\0u\0\0\0\212" + "\0\0\0\235\0\0\0\255\0\0\0\272\0\0\0\305\0\0\0\314\0\0\0\314\0\0\0\305\0" + "\0\0\272\0\0\0\255\0\0\0\235\0\0\0\212\0\0\0u\0\0\0]\0\0\0C\0\0\0,\0\0\0" + "\"\0\0\0\31\0\0\0\20\0\0\0\7\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\21\21" + "\21\0aaa\0\265\265\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0qqq\0---\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\2" + "\0\0\0\7\0\0\0\15\0\0\0\23\0\0\0\31\0\0\0\35\0\0\0!\0\0\0$\0\0\0'\0\0\0)" + "\0\0\0)\0\0\0'\0\0\0%\0\0\0!\0\0\0\36\0\0\0\31\0\0\0\24\0\0\0\16\0\0\0\10" + "\0\0\0\3\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1---\0qqq\0\271\271" + "\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\345\345\345\0" + "\251\251\251\0qqq\0===\0\15\15\15\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" + "\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" + "\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\15\15" + "\15\0===\0qqq\0\251\251\251\0\345\345\345\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0" + "\265\265\265\0\221\221\221\0qqq\0UUU\0===\0)))\0\31\31\31\0\15\15\15\0\5" + "\5\5\0\1\1\1\0\1\1\1\0\5\5\5\0\15\15\15\0\31\31\31\0)))\0===\0UUU\0qqq\0" + "\221\221\221\0\265\265\265\0\335\335\335\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0", +}; + diff --git a/samples/graphics/rsxtest_flip/include/mesh.h b/samples/graphics/rsxtest_flip/include/mesh.h new file mode 100644 index 00000000..63665494 --- /dev/null +++ b/samples/graphics/rsxtest_flip/include/mesh.h @@ -0,0 +1,62 @@ +#ifndef __MESH_H__ +#define __MESH_H__ + +#include + +using namespace Vectormath::Aos; + +template< class T > +inline const T& min_(const T& a,const T& b) +{ + return a +inline const T& max_(const T& a,const T& b) +{ + return a +inline const T clamp(const T& val,const T& low,const T& high) +{ + return min_(max_(val,low),high); +} + +struct S3DVertex +{ + S3DVertex() {}; + S3DVertex(f32 x,f32 y,f32 z,f32 nx,f32 ny,f32 nz,f32 tu,f32 tv) + : pos(x,y,z),nrm(nx,ny,nz),u(tu),v(tv) {}; + + inline S3DVertex& operator=(const S3DVertex& other) + { + pos = other.pos; + nrm = other.nrm; + u = other.u; + v = other.v; + return *this; + } + + Vector3 pos; + Vector3 nrm; + + f32 u,v; +}; + +template< class T > +class CMeshBuffer +{ +public: + CMeshBuffer() : indices(NULL),cnt_indices(0),vertices(NULL),cnt_vertices(0) {}; + + u16 *indices; + u32 cnt_indices; + + S3DVertex *vertices; + u32 cnt_vertices; +}; + +typedef CMeshBuffer SMeshBuffer; + +#endif diff --git a/samples/graphics/rsxtest_flip/include/rsxutil.h b/samples/graphics/rsxtest_flip/include/rsxutil.h new file mode 100644 index 00000000..fc5d132a --- /dev/null +++ b/samples/graphics/rsxtest_flip/include/rsxutil.h @@ -0,0 +1,44 @@ +#ifndef __RSXUTIL_H__ +#define __RSXUTIL_H__ + +#include + +#define DEFAULT_CB_SIZE 0x80000 // 512Kb default command buffer size +#define HOST_ADDR_ALIGNMENT (1024*1024) +#define HOST_SIZE (32*1024*1024) + +#define GCM_PREPARED_BUFFER_INDEX 65 +#define GCM_BUFFER_STATUS_INDEX 66 +#define GCM_WAIT_LABEL_INDEX 255 + +#define MAX_BUFFER_QUEUE_SIZE 1 + +#define BUFFER_IDLE 0 +#define BUFFER_BUSY 1 + +#define FRAME_BUFFER_COUNT 4 + +extern gcmContextData *gGcmContext; + +extern u32 curr_fb; + +extern u32 display_width; +extern u32 display_height; + +extern u32 depth_pitch; +extern u32 depth_offset; +extern u32 *depth_buffer; + +extern u32 color_pitch; +extern u32 color_offset[FRAME_BUFFER_COUNT]; +extern u32 *color_buffer[FRAME_BUFFER_COUNT]; + +extern f32 aspect_ratio; + +void initScreen(); +void flip(); +void finish(); + +void setRenderTarget(u32 index); + +#endif // __RSXUTIL_H__ diff --git a/samples/graphics/rsxtest_flip/shaders/diffuse_specular_shader.fcg b/samples/graphics/rsxtest_flip/shaders/diffuse_specular_shader.fcg new file mode 100644 index 00000000..6ebb917e --- /dev/null +++ b/samples/graphics/rsxtest_flip/shaders/diffuse_specular_shader.fcg @@ -0,0 +1,35 @@ +void main +( + float4 position : TEXCOORD0, + float3 normal : TEXCOORD1, + float2 texcoord : TEXCOORD2, + + uniform float3 globalAmbient, + uniform float3 lightPosition, + uniform float3 lightColor, + uniform float3 eyePosition, + uniform float3 Kd, + uniform float3 Ks, + uniform float shininess, + + uniform sampler2D texture, + + out float4 oColor +) +{ + float3 N = normalize(normal); + + float3 L = normalize(lightPosition - position.xyz); + float diffuseLight = max(dot(N,L),0.0f); + float3 diffuse = Kd*lightColor*diffuseLight; + + float3 V = normalize(eyePosition - position.xyz); + float3 H = normalize(L + V); + float specularLight = pow(max(dot(H,N),0.0f),shininess); + if(diffuseLight<=0) specularLight = 0; + float3 specular = Ks*specularLight; + + float3 color = tex2D(texture,texcoord).xyz*(diffuse + globalAmbient) + specular; + + oColor = float4(color,1.0f); +} diff --git a/samples/graphics/rsxtest_flip/shaders/diffuse_specular_shader.vcg b/samples/graphics/rsxtest_flip/shaders/diffuse_specular_shader.vcg new file mode 100644 index 00000000..c11e6228 --- /dev/null +++ b/samples/graphics/rsxtest_flip/shaders/diffuse_specular_shader.vcg @@ -0,0 +1,21 @@ +void main +( + float3 vertexPosition : POSITION, + float3 vertexNormal : NORMAL, + float2 vertexTexcoord : TEXCOORD0, + + uniform float4x4 projMatrix, + uniform float4x4 modelViewMatrix, + + out float4 ePosition : POSITION, + out float4 oPosition : TEXCOORD0, + out float3 oNormal : TEXCOORD1, + out float2 oTexcoord : TEXCOORD2 +) +{ + ePosition = mul(mul(projMatrix,modelViewMatrix),float4(vertexPosition,1.0f)); + + oPosition = float4(vertexPosition,1.0f); + oNormal = vertexNormal; + oTexcoord = vertexTexcoord; +} diff --git a/samples/graphics/rsxtest_flip/source/main.cpp b/samples/graphics/rsxtest_flip/source/main.cpp new file mode 100644 index 00000000..9f0c2394 --- /dev/null +++ b/samples/graphics/rsxtest_flip/source/main.cpp @@ -0,0 +1,603 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "acid.h" +#include "mesh.h" +#include "rsxutil.h" + +#include "diffuse_specular_shader_vpo.h" +#include "diffuse_specular_shader_fpo.h" + +#define DEGTORAD(a) ( (a) * 0.01745329252f ) +#define RADTODEG(a) ( (a) * 57.29577951f ) + +SYS_PROCESS_PARAM(1001, 0x100000); + +u32 running = 0; + +u32 fp_offset; +u32 *fp_buffer; + +u32 *texture_buffer; +u32 texture_offset; + +s32 projMatrix_id = -1; +s32 modelViewMatrix_id = -1; +s32 vertexPosition_id = -1; +s32 vertexNormal_id = -1; +s32 vertexTexcoord_id = -1; +s32 textureUnit_id = -1; +s32 eyePosition_id = -1; +s32 globalAmbient_id = -1; +s32 lightPosition_id = -1; +s32 lightColor_id = -1; +s32 Kd_id = -1; +s32 Ks_id = -1; +s32 shininess_id = -1; + +Point3 eye_pos = Point3(0.0f,0.0f,20.0f); +Point3 eye_dir = Point3(0.0f,0.0f,0.0f); +Vector3 up_vec = Vector3(0.0f,1.0f,0.0f); + +void *vp_ucode = NULL; +rsxVertexProgram *vpo = (rsxVertexProgram*)diffuse_specular_shader_vpo; + +void *fp_ucode = NULL; +rsxFragmentProgram *fpo = (rsxFragmentProgram*)diffuse_specular_shader_fpo; + +static Matrix4 P; +static SMeshBuffer *sphere = NULL; +static SMeshBuffer *donut = NULL; +static SMeshBuffer *cube = NULL; + +extern "C" { +static void program_exit_callback() +{ + finish(); +} + +static void sysutil_exit_callback(u64 status,u64 param,void *usrdata) +{ + switch(status) { + case SYSUTIL_EXIT_GAME: + running = 0; + break; + case SYSUTIL_DRAW_BEGIN: + case SYSUTIL_DRAW_END: + break; + default: + break; + } +} +} + +static void init_texture() +{ + u32 i; + u8 *buffer; + const u8 *data = acid.pixel_data; + + texture_buffer = (u32*)rsxMemalign(128,(acid.width*acid.height*4)); + if(!texture_buffer) return; + + rsxAddressToOffset(texture_buffer,&texture_offset); + + buffer = (u8*)texture_buffer; + for(i=0;icnt_indices = 36; + buffer->indices = (u16*)rsxMemalign(128,buffer->cnt_indices*sizeof(u16)); + + for(i=0;i<36;i++) buffer->indices[i] = u[i]; + + buffer->cnt_vertices = 12; + buffer->vertices = (S3DVertex*)rsxMemalign(128,buffer->cnt_vertices*sizeof(S3DVertex)); + + buffer->vertices[0] = S3DVertex(0,0,0, -1,-1,-1, 1, 0); + buffer->vertices[1] = S3DVertex(1,0,0, 1,-1,-1, 1, 1); + buffer->vertices[2] = S3DVertex(1,1,0, 1, 1,-1, 0, 1); + buffer->vertices[3] = S3DVertex(0,1,0, -1, 1,-1, 0, 0); + buffer->vertices[4] = S3DVertex(1,0,1, 1,-1, 1, 1, 0); + buffer->vertices[5] = S3DVertex(1,1,1, 1, 1, 1, 0, 0); + buffer->vertices[6] = S3DVertex(0,1,1, -1, 1, 1, 0, 1); + buffer->vertices[7] = S3DVertex(0,0,1, -1,-1, 1, 1, 1); + buffer->vertices[8] = S3DVertex(0,1,1, -1, 1, 1, 1, 0); + buffer->vertices[9] = S3DVertex(0,1,0, -1, 1,-1, 1, 1); + buffer->vertices[10] = S3DVertex(1,0,1, 1,-1, 1, 0, 1); + buffer->vertices[11] = S3DVertex(1,0,0, 1,-1,-1, 0, 0); + + for(i=0;i<12;i++) { + buffer->vertices[i].pos -= Vector3(0.5f,0.5f,0.5f); + buffer->vertices[i].pos *= size; + } + + return buffer; +} + +static SMeshBuffer* createDonut(f32 outerRadius,f32 innerRadius,u32 polyCntX,u32 polyCntY) +{ + u32 i,x,y,level; + SMeshBuffer *buffer = new SMeshBuffer(); + + if(polyCntX<2) polyCntX = 2; + if(polyCntY<2) polyCntY = 2; + while(polyCntX*polyCntY>32767) { + polyCntX /= 2; + polyCntY /= 2; + } + + f32 ay = 0; + const f32 angleX = 2*M_PI/polyCntX; + const f32 angleY = 2*M_PI/polyCntY; + const u32 polyCntXpitch = polyCntX +1; + const u32 polyCntYpitch = polyCntY + 1; + + buffer->cnt_vertices = polyCntYpitch*polyCntXpitch; + buffer->vertices = (S3DVertex*)rsxMemalign(128,buffer->cnt_vertices*sizeof(S3DVertex)); + + buffer->cnt_indices = polyCntY*polyCntX*6; + buffer->indices = (u16*)rsxMemalign(128,buffer->cnt_indices*sizeof(u16)); + + i = 0; + for(y=0;y<=polyCntY;y++) { + f32 axz = 0; + + const f32 sinay = sinf(ay); + const f32 cosay = cosf(ay); + const f32 tu = (f32)y/(f32)polyCntY; + for(x=0;x<=polyCntX;x++) { + const Vector3 pos(static_cast((outerRadius - (innerRadius*cosf(axz)))*cosay), + static_cast((outerRadius - (innerRadius*cosf(axz)))*sinay), + static_cast(innerRadius*sinf(axz))); + + const Vector3 nrm(static_cast(-cosf(axz)*cosay), + static_cast(-cosf(axz)*sinay), + static_cast(sinf(axz))); + + buffer->vertices[i] = S3DVertex(pos.getX(),pos.getY(),pos.getZ(),nrm.getX(),nrm.getY(),nrm.getZ(),tu,(f32)x/(f32)polyCntX); + + axz += angleX; + i++; + } + ay += angleY; + } + + i = 0; + level = 0; + for(y=0;yindices[i++] = curr; + buffer->indices[i++] = curr + polyCntXpitch; + buffer->indices[i++] = curr + 1 + polyCntXpitch; + + buffer->indices[i++] = curr; + buffer->indices[i++] = curr + 1 + polyCntXpitch; + buffer->indices[i++] = curr + 1; + } + + buffer->indices[i++] = level + polyCntX; + buffer->indices[i++] = level + polyCntX - 1; + buffer->indices[i++] = level + polyCntX - 1 + polyCntXpitch; + + buffer->indices[i++] = level + polyCntX; + buffer->indices[i++] = level + polyCntX - 1 + polyCntXpitch; + buffer->indices[i++] = level + polyCntX + polyCntXpitch; + + level += polyCntXpitch; + } + + return buffer; +} + +static SMeshBuffer* createSphere(f32 radius,u32 polyCntX,u32 polyCntY) +{ + u32 i,p1,p2,level; + u32 x,y,polyCntXpitch; + const f32 RECIPROCAL_PI = 1.0f/M_PI; + SMeshBuffer *buffer = new SMeshBuffer(); + + if(polyCntX<2) polyCntX = 2; + if(polyCntY<2) polyCntY = 2; + if(polyCntX*polyCntY>32767) { + if(polyCntX>polyCntY) + polyCntX = 32767/polyCntY-1; + else + polyCntY = 32767/(polyCntX+1); + } + polyCntXpitch = polyCntX+1; + + buffer->cnt_vertices = (polyCntXpitch*polyCntY)+2; + buffer->vertices = (S3DVertex*)rsxMemalign(128,buffer->cnt_vertices*sizeof(S3DVertex)); + + buffer->cnt_indices = (polyCntX*polyCntY)*6; + buffer->indices = (u16*)rsxMemalign(128,buffer->cnt_indices*sizeof(u16)); + + i = 0; + level = 0; + for(p1=0;p1indices[i++] = curr; + buffer->indices[i++] = curr + polyCntXpitch; + buffer->indices[i++] = curr + 1 + polyCntXpitch; + + buffer->indices[i++] = curr; + buffer->indices[i++] = curr + 1 + polyCntXpitch; + buffer->indices[i++] = curr + 1; + } + + buffer->indices[i++] = level + polyCntX; + buffer->indices[i++] = level + polyCntX - 1; + buffer->indices[i++] = level + polyCntX - 1 + polyCntXpitch; + + buffer->indices[i++] = level + polyCntX; + buffer->indices[i++] = level + polyCntX - 1 + polyCntXpitch; + buffer->indices[i++] = level + polyCntX + polyCntXpitch; + + level += polyCntXpitch; + } + + const u32 polyCntSq = polyCntXpitch*polyCntY; + const u32 polyCntSq1 = polyCntSq+1; + const u32 polyCntSqM1 = (polyCntY-1)*polyCntXpitch; + + for(p2=0;p2indices[i++] = polyCntSq; + buffer->indices[i++] = p2; + buffer->indices[i++] = p2+1; + + buffer->indices[i++] = polyCntSq1; + buffer->indices[i++] = polyCntSqM1+p2; + buffer->indices[i++] = polyCntSqM1+p2+1; + } + + buffer->indices[i++] = polyCntSq; + buffer->indices[i++] = polyCntX-1; + buffer->indices[i++] = polyCntX; + + buffer->indices[i++] = polyCntSq1; + buffer->indices[i++] = polyCntSqM1; + buffer->indices[i++] = polyCntSqM1+polyCntX-1; + + f32 axz; + f32 ay = 0; + const f32 angelX = 2*M_PI/polyCntX; + const f32 angelY = M_PI/polyCntY; + + i = 0; + for(y=0;y(radius*cosf(axz)*sinay), static_cast(radius*cosf(ay)), static_cast(radius*sinf(axz)*sinay)); + + Vector3 normal = normalize(pos); + + f32 tu = 0.5F; + if(y==0) { + if(normal.getY()!=-1.0F && normal.getY()!=1.0F) + tu = static_cast(acosf(clamp(normal.getX()/sinay,-1.0f,1.0f))*0.5F*RECIPROCAL_PI); + if(normal.getZ()<0.0F) + tu = 1-tu; + } else + tu = buffer->vertices[i - polyCntXpitch].u; + + buffer->vertices[i] = S3DVertex(pos.getX(),pos.getY(),pos.getZ(),normal.getX(),normal.getY(),normal.getZ(),tu,static_cast(ay*RECIPROCAL_PI)); + axz += angelX; + i++; + } + buffer->vertices[i] = S3DVertex(buffer->vertices[i-polyCntX]); + buffer->vertices[i].u = 1.0F; + i++; + } + + buffer->vertices[i++] = S3DVertex(0.0F,radius,0.0F,0.0F,1.0F,0.0F,0.5F,0.0F); + buffer->vertices[i] = S3DVertex(0.0F,-radius,0.0F,0.0F,-1.0F,0.0F,0.5F,1.0F); + + return buffer; +} + +static void setTexture() +{ + u32 width = 128; + u32 height = 128; + u32 pitch = (width*4); + gcmTexture texture; + + if(!texture_buffer) return; + + rsxInvalidateTextureCache(gGcmContext,GCM_INVALIDATE_TEXTURE); + + texture.format = (GCM_TEXTURE_FORMAT_A8R8G8B8 | GCM_TEXTURE_FORMAT_LIN); + texture.mipmap = 1; + texture.dimension = GCM_TEXTURE_DIMS_2D; + texture.cubemap = GCM_FALSE; + texture.remap = ((GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_B_SHIFT) | + (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_G_SHIFT) | + (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_R_SHIFT) | + (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_A_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_B << GCM_TEXTURE_REMAP_COLOR_B_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_G << GCM_TEXTURE_REMAP_COLOR_G_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_R << GCM_TEXTURE_REMAP_COLOR_R_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_A << GCM_TEXTURE_REMAP_COLOR_A_SHIFT)); + texture.width = width; + texture.height = height; + texture.depth = 1; + texture.location = GCM_LOCATION_RSX; + texture.pitch = pitch; + texture.offset = texture_offset; + rsxLoadTexture(gGcmContext,textureUnit_id,&texture); + rsxTextureControl(gGcmContext,textureUnit_id,GCM_TRUE,0<<8,12<<8,GCM_TEXTURE_MAX_ANISO_1); + rsxTextureFilter(gGcmContext,textureUnit_id,0,GCM_TEXTURE_LINEAR,GCM_TEXTURE_LINEAR,GCM_TEXTURE_CONVOLUTION_QUINCUNX); + rsxTextureWrapMode(gGcmContext,textureUnit_id,GCM_TEXTURE_CLAMP_TO_EDGE,GCM_TEXTURE_CLAMP_TO_EDGE,GCM_TEXTURE_CLAMP_TO_EDGE,0,GCM_TEXTURE_ZFUNC_LESS,0); +} + +static void setDrawEnv() +{ + rsxSetColorMask(gGcmContext,GCM_COLOR_MASK_B | + GCM_COLOR_MASK_G | + GCM_COLOR_MASK_R | + GCM_COLOR_MASK_A); + + rsxSetColorMaskMRT(gGcmContext,0); + + u16 x,y,w,h; + f32 min, max; + f32 scale[4],offset[4]; + + x = 0; + y = 0; + w = display_width; + h = display_height; + min = 0.0f; + max = 1.0f; + scale[0] = w*0.5f; + scale[1] = h*-0.5f; + scale[2] = (max - min)*0.5f; + scale[3] = 0.0f; + offset[0] = x + w*0.5f; + offset[1] = y + h*0.5f; + offset[2] = (max + min)*0.5f; + offset[3] = 0.0f; + + rsxSetViewport(gGcmContext,x, y, w, h, min, max, scale, offset); + rsxSetScissor(gGcmContext,x,y,w,h); + + rsxSetDepthTestEnable(gGcmContext,GCM_TRUE); + rsxSetDepthFunc(gGcmContext,GCM_LESS); + rsxSetShadeModel(gGcmContext,GCM_SHADE_MODEL_SMOOTH); + rsxSetDepthWriteEnable(gGcmContext,1); + rsxSetFrontFace(gGcmContext,GCM_FRONTFACE_CCW); +} + +void init_shader() +{ + u32 fpsize = 0; + u32 vpsize = 0; + + rsxVertexProgramGetUCode(vpo, &vp_ucode, &vpsize); + printf("vpsize: %d\n", vpsize); + + projMatrix_id = rsxVertexProgramGetConstIndex(vpo,"projMatrix"); + modelViewMatrix_id = rsxVertexProgramGetConstIndex(vpo,"modelViewMatrix"); + vertexPosition_id = rsxVertexProgramGetAttribIndex(vpo,"vertexPosition"); + vertexNormal_id = rsxVertexProgramGetAttribIndex(vpo,"vertexNormal"); + vertexTexcoord_id = rsxVertexProgramGetAttribIndex(vpo,"vertexTexcoord"); + + rsxFragmentProgramGetUCode(fpo, &fp_ucode, &fpsize); + printf("fpsize: %d\n", fpsize); + + fp_buffer = (u32*)rsxMemalign(64,fpsize); + memcpy(fp_buffer,fp_ucode,fpsize); + rsxAddressToOffset(fp_buffer,&fp_offset); + + textureUnit_id = rsxFragmentProgramGetAttribIndex(fpo,"texture"); + eyePosition_id = rsxFragmentProgramGetConstIndex(fpo,"eyePosition"); + globalAmbient_id = rsxFragmentProgramGetConstIndex(fpo,"globalAmbient"); + lightPosition_id = rsxFragmentProgramGetConstIndex(fpo,"lightPosition"); + lightColor_id = rsxFragmentProgramGetConstIndex(fpo,"lightColor"); + shininess_id = rsxFragmentProgramGetConstIndex(fpo,"shininess"); + Ks_id = rsxFragmentProgramGetConstIndex(fpo,"Ks"); + Kd_id = rsxFragmentProgramGetConstIndex(fpo,"Kd"); +} + +void drawFrame() +{ + u32 i,offset,color = 0; + Matrix4 rotX,rotY; + Vector4 objEyePos,objLightPos; + Matrix4 viewMatrix,modelMatrix,modelMatrixIT,modelViewMatrix; + Point3 lightPos = Point3(250.0f,150.0f,150.0f); + f32 globalAmbientColor[3] = {0.1f,0.1f,0.1f}; + f32 lightColor[3] = {0.95f,0.95f,0.95f}; + f32 materialColorDiffuse[3] = {0.5f,0.0f,0.0f}; + f32 materialColorSpecular[3] = {0.7f,0.6f,0.6f}; + f32 shininess = 17.8954f; + static f32 rot = 0.0f; + SMeshBuffer *mesh = NULL; + + setTexture(); + setDrawEnv(); + + rsxSetClearColor(gGcmContext,color); + rsxSetClearDepthStencil(gGcmContext,0xffff); + rsxClearSurface(gGcmContext,GCM_CLEAR_R | + GCM_CLEAR_G | + GCM_CLEAR_B | + GCM_CLEAR_A | + GCM_CLEAR_S | + GCM_CLEAR_Z); + + rsxSetZControl(gGcmContext,0,1,1); + + for(i=0;i<8;i++) + rsxSetViewportClip(gGcmContext,i,display_width,display_height); + + viewMatrix = Matrix4::lookAt(eye_pos,eye_dir,up_vec); + + mesh = sphere; + rotX = Matrix4::rotationX(DEGTORAD(30.0f)); + rotY = Matrix4::rotationY(DEGTORAD(rot)); + modelMatrix = rotX*rotY; + modelMatrixIT = inverse(modelMatrix); + modelViewMatrix = transpose(viewMatrix*modelMatrix); + + objEyePos = modelMatrixIT*eye_pos; + objLightPos = modelMatrixIT*lightPos; + + rsxAddressToOffset(&mesh->vertices[0].pos,&offset); + rsxBindVertexArrayAttrib(gGcmContext,vertexPosition_id,0,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxAddressToOffset(&mesh->vertices[0].nrm,&offset); + rsxBindVertexArrayAttrib(gGcmContext,vertexNormal_id,0,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxAddressToOffset(&mesh->vertices[0].u,&offset); + rsxBindVertexArrayAttrib(gGcmContext,vertexTexcoord_id,0,offset,sizeof(S3DVertex),2,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxLoadVertexProgram(gGcmContext,vpo,vp_ucode); + rsxSetVertexProgramParameterByIndex(gGcmContext,vpo,projMatrix_id,(float*)&P); + rsxSetVertexProgramParameterByIndex(gGcmContext,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); + + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); + + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); + + rsxLoadFragmentProgramLocation(gGcmContext,fpo,fp_offset,GCM_LOCATION_RSX); + + rsxSetUserClipPlaneControl(gGcmContext,GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE); + + rsxAddressToOffset(&mesh->indices[0],&offset); + rsxDrawIndexArray(gGcmContext,GCM_TYPE_TRIANGLES,offset,mesh->cnt_indices,GCM_INDEX_TYPE_16B,GCM_LOCATION_RSX); + + mesh = donut; + rotX = Matrix4::rotationX(DEGTORAD(rot)); + rotY = Matrix4::rotationY(DEGTORAD(30.0f)); + modelMatrix = rotX*rotY; + modelMatrix.setTranslation(Vector3(3.0f,5.0f,-8.0f)); + + modelMatrixIT = inverse(modelMatrix); + modelViewMatrix = transpose(viewMatrix*modelMatrix); + + objEyePos = modelMatrixIT*eye_pos; + objLightPos = modelMatrixIT*lightPos; + + rsxAddressToOffset(&mesh->vertices[0].pos,&offset); + rsxBindVertexArrayAttrib(gGcmContext,vertexPosition_id,0,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxAddressToOffset(&mesh->vertices[0].nrm,&offset); + rsxBindVertexArrayAttrib(gGcmContext,vertexNormal_id,0,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxAddressToOffset(&mesh->vertices[0].u,&offset); + rsxBindVertexArrayAttrib(gGcmContext,vertexTexcoord_id,0,offset,sizeof(S3DVertex),2,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxLoadVertexProgram(gGcmContext,vpo,vp_ucode); + rsxSetVertexProgramParameterByIndex(gGcmContext,vpo,projMatrix_id,(float*)&P); + rsxSetVertexProgramParameterByIndex(gGcmContext,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); + + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); + + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); + + rsxLoadFragmentProgramLocation(gGcmContext,fpo,fp_offset,GCM_LOCATION_RSX); + + rsxSetUserClipPlaneControl(gGcmContext,GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE); + + rsxAddressToOffset(&mesh->indices[0],&offset); + rsxDrawIndexArray(gGcmContext,GCM_TYPE_TRIANGLES,offset,mesh->cnt_indices,GCM_INDEX_TYPE_16B,GCM_LOCATION_RSX); + + rot += 4.0f; + if(rot>=360.0f) rot = 0.0f; +} + +int main(int argc,const char *argv[]) +{ + padInfo padinfo; + padData paddata; + + printf("rsxtest_flip started...\n"); + + ioPadInit(7); + initScreen(); + + atexit(program_exit_callback); + sysUtilRegisterCallback(0,sysutil_exit_callback,NULL); + + init_shader(); + init_texture(); + + sphere = createSphere(3.0f,32,32); + donut = createDonut(3.0f,1.5f,32,32); + cube = createCube(5.0f); + + P = transpose(Matrix4::perspective(DEGTORAD(45.0f),aspect_ratio,1.0f,3000.0f)); + + setTexture(); + setDrawEnv(); + + running = 1; + while(running) { + sysUtilCheckCallback(); + + ioPadGetInfo(&padinfo); + for(int i=0; i < MAX_PADS; i++){ + if(padinfo.status[i]){ + ioPadGetData(i, &paddata); + + if(paddata.BTN_CROSS) + goto done; + } + } + + drawFrame(); + flip(); + } + +done: + printf("rsxtest_flip done...\n"); + finish(); + return 0; +} diff --git a/samples/graphics/rsxtest_flip/source/rsxutil.cpp b/samples/graphics/rsxtest_flip/source/rsxutil.cpp new file mode 100644 index 00000000..53b5be20 --- /dev/null +++ b/samples/graphics/rsxtest_flip/source/rsxutil.cpp @@ -0,0 +1,298 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "rsxutil.h" + +videoResolution vResolution; +gcmContextData *gGcmContext = NULL; + +u32 curr_fb = 0; + +u32 display_width; +u32 display_height; + +u32 depth_pitch; +u32 depth_offset; +u32 *depth_buffer; + +u32 color_pitch; +u32 color_offset[FRAME_BUFFER_COUNT]; +u32 *color_buffer[FRAME_BUFFER_COUNT]; + +f32 aspect_ratio; + +u32 fbOnDisplay = 0; +u32 fbFlipped = 0; +bool fbOnFlip = false; +sys_event_queue_t flipEventQueue; +sys_event_port_t flipEventPort; + +gcmSurface surface; + +static u32 sLabelVal = 1; + +static u32 sResolutionIds[] = { + VIDEO_RESOLUTION_960x1080, + VIDEO_RESOLUTION_720, + VIDEO_RESOLUTION_480, + VIDEO_RESOLUTION_576 +}; +static size_t RESOLUTION_ID_COUNT = sizeof(sResolutionIds)/sizeof(u32); + +extern "C" { +static void flipHandler(const u32 head) +{ + (void)head; + u32 v = fbFlipped; + + for (u32 i = fbOnDisplay; i != v; i=(i + 1)%FRAME_BUFFER_COUNT) { + *((vu32*) gcmGetLabelAddress(GCM_BUFFER_STATUS_INDEX + i)) = BUFFER_IDLE; + } + fbOnDisplay = v; + fbOnFlip = false; + + sysEventPortSend(flipEventPort, 0, 0, 0); +} + +static void vblankHandler(const u32 head) +{ + (void)head; + u32 data; + u32 bufferToFlip; + u32 indexToFlip; + + data = *((vu32*) gcmGetLabelAddress(GCM_PREPARED_BUFFER_INDEX)); + bufferToFlip = (data >> 8); + indexToFlip = (data & 0x07); + + if (!fbOnFlip) { + if (bufferToFlip != fbOnDisplay) { + s32 ret = gcmSetFlipImmediate(indexToFlip); + if (ret != 0) { + printf("flip immediate failed\n"); + return; + } + fbFlipped = bufferToFlip; + fbOnFlip = true; + } + } +} +} + +static void syncPPUGPU() +{ + vu32 *label = (vu32*) gcmGetLabelAddress(GCM_PREPARED_BUFFER_INDEX); + while(((curr_fb + FRAME_BUFFER_COUNT - ((*label)>>8))%FRAME_BUFFER_COUNT) > MAX_BUFFER_QUEUE_SIZE) { + sys_event_t event; + + sysEventQueueReceive(flipEventQueue, &event, 0); + sysEventQueueDrain(flipEventQueue); + } +} + +static void waitRSXFinish() +{ + rsxSetWriteBackendLabel(gGcmContext,GCM_WAIT_LABEL_INDEX,sLabelVal); + + rsxFlushBuffer(gGcmContext); + + while(*(vu32*)gcmGetLabelAddress(GCM_WAIT_LABEL_INDEX)!=sLabelVal) + usleep(30); + + ++sLabelVal; +} + +static void waitRSXIdle() +{ + rsxSetWriteBackendLabel(gGcmContext,GCM_WAIT_LABEL_INDEX,sLabelVal); + rsxSetWaitLabel(gGcmContext,GCM_WAIT_LABEL_INDEX,sLabelVal); + + ++sLabelVal; + + waitRSXFinish(); +} + +void initVideoConfiguration() +{ + s32 rval = 0; + s32 resId = 0; + + for (size_t i=0;i < RESOLUTION_ID_COUNT;i++) { + rval = videoGetResolutionAvailability(VIDEO_PRIMARY, sResolutionIds[i], VIDEO_ASPECT_AUTO, 0); + if (rval != 1) continue; + + resId = sResolutionIds[i]; + rval = videoGetResolution(resId, &vResolution); + if(!rval) break; + } + + if(rval) { + printf("Error: videoGetResolutionAvailability failed. No usable resolution.\n"); + exit(1); + } + + videoConfiguration config = { + (u8)resId, + VIDEO_BUFFER_FORMAT_XRGB, + VIDEO_ASPECT_AUTO, + {0,0,0,0,0,0,0,0,0}, + (u32)vResolution.width*4 + }; + + rval = videoConfigure(VIDEO_PRIMARY, &config, NULL, 0); + if(rval) { + printf("Error: videoConfigure failed.\n"); + exit(1); + } + + videoState state; + + rval = videoGetState(VIDEO_PRIMARY, 0, &state); + switch(state.displayMode.aspect) { + case VIDEO_ASPECT_4_3: + aspect_ratio = 4.0f/3.0f; + break; + case VIDEO_ASPECT_16_9: + aspect_ratio = 16.0f/9.0f; + break; + default: + printf("unknown aspect ratio %x\n", state.displayMode.aspect); + aspect_ratio = 16.0f/9.0f; + break; + } + + display_height = vResolution.height; + display_width = vResolution.width; +} + +void initFlipEvent() +{ + sys_event_queue_attr_t queueAttr = { SYS_EVENT_QUEUE_PRIO, SYS_EVENT_QUEUE_PPU, "\0" }; + + sysEventQueueCreate(&flipEventQueue, &queueAttr, SYS_EVENT_QUEUE_KEY_LOCAL, 32); + sysEventPortCreate(&flipEventPort, SYS_EVENT_PORT_LOCAL, SYS_EVENT_PORT_NO_NAME); + sysEventPortConnectLocal(flipEventPort, flipEventQueue); + + gcmSetFlipHandler(flipHandler); + gcmSetVBlankHandler(vblankHandler); +} + +void initRenderTarget() +{ + memset(&surface, 0, sizeof(gcmSurface)); + + surface.colorFormat = GCM_SURFACE_X8R8G8B8; + surface.colorTarget = GCM_SURFACE_TARGET_0; + surface.colorLocation[0] = GCM_LOCATION_RSX; + surface.colorOffset[0] = color_offset[curr_fb]; + surface.colorPitch[0] = color_pitch; + + for(u32 i=1; i< GCM_MAX_MRT_COUNT;i++) { + surface.colorLocation[i] = GCM_LOCATION_RSX; + surface.colorOffset[i] = color_offset[curr_fb]; + surface.colorPitch[i] = 64; + } + + surface.depthFormat = GCM_SURFACE_ZETA_Z16; + surface.depthLocation = GCM_LOCATION_RSX; + surface.depthOffset = depth_offset; + surface.depthPitch = depth_pitch; + + surface.type = GCM_SURFACE_TYPE_LINEAR; + surface.antiAlias = GCM_SURFACE_CENTER_1; + + surface.width = display_width; + surface.height = display_height; + surface.x = 0; + surface.y = 0; + +} + +void setRenderTarget(u32 index) +{ + surface.colorOffset[0] = color_offset[index]; + rsxSetSurface(gGcmContext,&surface); +} + +void initScreen() +{ + u32 zs_depth = 4; + u32 color_depth = 4; + u32 bufferSize = rsxAlign(HOST_ADDR_ALIGNMENT, (DEFAULT_CB_SIZE + HOST_SIZE)); + + gcmInitDefaultFifoMode(GCM_DEFAULT_FIFO_MODE_CONDITIONAL); + + void *hostAddr = memalign(HOST_ADDR_ALIGNMENT, bufferSize); + rsxInit(&gGcmContext, DEFAULT_CB_SIZE, bufferSize, hostAddr); + + initVideoConfiguration(); + + color_pitch = display_width*color_depth; + depth_pitch = display_width*zs_depth; + + waitRSXIdle(); + + gcmSetFlipMode(GCM_FLIP_HSYNC); + + void *buffer; + for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { + buffer = rsxMemalign(64,(display_height*color_pitch)); + rsxAddressToOffset(buffer,&color_offset[i]); + printf("fb[%d]: %p (%08x) [%dx%d] %d\n", i, buffer, color_offset[i], display_width, display_height, color_pitch); + gcmSetDisplayBuffer(i,color_offset[i],color_pitch,display_width,display_height); + } + + buffer = rsxMemalign(64,(display_height*depth_pitch)*2); + rsxAddressToOffset(buffer, &depth_offset); + + for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { + *((vu32*) gcmGetLabelAddress(GCM_BUFFER_STATUS_INDEX + i)) = BUFFER_IDLE; + } + *((vu32*) gcmGetLabelAddress(GCM_PREPARED_BUFFER_INDEX)) = (fbOnDisplay << 8); + *((vu32*) gcmGetLabelAddress(GCM_BUFFER_STATUS_INDEX + fbOnDisplay)) = BUFFER_BUSY; + + curr_fb = (fbOnDisplay + 1)%FRAME_BUFFER_COUNT; + + initFlipEvent(); + initRenderTarget(); + + rsxSetWriteCommandLabel(gGcmContext, GCM_BUFFER_STATUS_INDEX + curr_fb, BUFFER_BUSY); +} + +void flip() +{ + s32 qid = gcmSetPrepareFlip(gGcmContext, curr_fb); + while (qid < 0) { + usleep(100); + qid = gcmSetPrepareFlip(gGcmContext, curr_fb); + } + + rsxSetWriteBackendLabel(gGcmContext, GCM_PREPARED_BUFFER_INDEX, ((curr_fb << 8) | qid)); + rsxFlushBuffer(gGcmContext); + + syncPPUGPU(); + + curr_fb = (curr_fb + 1)%FRAME_BUFFER_COUNT; + + rsxSetWaitLabel(gGcmContext, GCM_BUFFER_STATUS_INDEX + curr_fb, BUFFER_IDLE); + rsxSetWriteCommandLabel(gGcmContext, GCM_BUFFER_STATUS_INDEX + curr_fb, BUFFER_BUSY); + + setRenderTarget(curr_fb); +} + +void finish() +{ + rsxFinish(gGcmContext,1); + + u32 data = *((vu32*) gcmGetLabelAddress(GCM_PREPARED_BUFFER_INDEX)); + u32 lastBuffer = (data >> 8); + while (lastBuffer != fbOnDisplay) + usleep(100); +} diff --git a/samples/graphics/rsxtest_tile/Makefile b/samples/graphics/rsxtest_tile/Makefile new file mode 100644 index 00000000..aae67e84 --- /dev/null +++ b/samples/graphics/rsxtest_tile/Makefile @@ -0,0 +1,164 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PSL1GHT)),) +$(error "Please set PSL1GHT in your environment. export PSL1GHT=") +endif + +include $(PSL1GHT)/ppu_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +SHADERS := shaders +INCLUDES := include + +TITLE := RSX Test - PSL1GHT +APPID := RSX00003 +CONTENTID := UP0001-$(APPID)_00-0000000000000000 + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +CFLAGS = -Wall -mcpu=cell $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lsimdmath -lrsx -lgcm_sys -lio -lsysutil -lrt -llv2 -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ + $(foreach dir,$(SHADERS),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export BUILDDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) +VCGFILES := $(foreach dir,$(SHADERS),$(notdir $(wildcard $(dir)/*.vcg))) +FCGFILES := $(foreach dir,$(SHADERS),$(notdir $(wildcard $(dir)/*.fcg))) + +VPOFILES := $(VCGFILES:.vcg=.vpo) +FPOFILES := $(FCGFILES:.fcg=.fpo) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(addsuffix .o,$(VPOFILES)) \ + $(addsuffix .o,$(FPOFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES), -I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(LIBPSL1GHT_INC) \ + -I$(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + $(LIBPSL1GHT_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).self + +#--------------------------------------------------------------------------------- +run: + ps3load $(OUTPUT).self + +#--------------------------------------------------------------------------------- +pkg: $(BUILD) $(OUTPUT).pkg + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).self: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .bin extension +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.vpo.o : %.vpo +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.fpo.o : %.fpo +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/samples/graphics/rsxtest_tile/include/acid.h b/samples/graphics/rsxtest_tile/include/acid.h new file mode 100644 index 00000000..d1643518 --- /dev/null +++ b/samples/graphics/rsxtest_tile/include/acid.h @@ -0,0 +1,2939 @@ +/* GIMP RGBA C-Source image dump (acid.c) */ + +static const struct { + unsigned int width; + unsigned int height; + unsigned int bytes_per_pixel; /* 3:RGB, 4:RGBA */ + unsigned char pixel_data[128 * 128 * 4 + 1]; +} acid = { + 128, 128, 4, + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\335\335\335\0\265\265\265\0\221\221\221\0qqq\0UUU\0===\0)))\0\31\31" + "\31\0\15\15\15\0\5\5\5\0\1\1\1\0\1\1\1\0\5\5\5\0\15\15\15\0\31\31\31\0))" + ")\0===\0UUU\0qqq\0\221\221\221\0\265\265\265\0\335\335\335\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\345\345\345\0\251\251\251\0qqq\0===\0\15\15\15\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\15\15\15\0===\0qqq\0\251\251\251\0\345\345\345\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0qqq\0---\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\7\0\0\0\15\0\0\0" + "\23\0\0\0\30\0\0\0\35\0\0\0!\0\0\0$\0\0\0&\0\0\0(\0\0\0(\0\0\0'\0\0\0$\0" + "\0\0!\0\0\0\35\0\0\0\31\0\0\0\23\0\0\0\15\0\0\0\7\0\0\0\2\0\0\0\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0---\0qqq\0\271\271\271\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\265\265\265\0aaa\0\21\21\21\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0\17\0\0\0\30\0\0\0\40\0\0\0+\0\0\0B\0" + "\0\0\\\0\0\0t\0\0\0\211\0\0\0\234\0\0\0\254\0\0\0\271\0\0\0\304\0\0\0\313" + "\0\0\0\313\0\0\0\304\0\0\0\271\0\0\0\254\0\0\0\234\0\0\0\211\0\0\0t\0\0\0" + "\\\0\0\0B\0\0\0+\0\0\0!\0\0\0\30\0\0\0\17\0\0\0\6\0\0\0\1\0\0\0\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\21\21\21\0aaa\0\265\265\265\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\331\331\331\0yyy\0\35\35\35\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\2\0\0\0\14\0\0\0\30\0\0\0&\0\0\0?\0\0\0d\0\0\0\211\0\0\0\254\0\0\0\311" + "\0\0\0\331\0\0\0\341\0\0\0\347\0\0\0\354\0\0\0\361\0\0\0\365\0\0\0\370\0" + "\0\0\373\0\0\0\375\0\0\0\375\0\0\0\373\0\0\0\371\0\0\0\365\0\0\0\361\0\0" + "\0\355\0\0\0\347\0\0\0\341\0\0\0\331\0\0\0\312\0\0\0\254\0\0\0\211\0\0\0" + "d\0\0\0?\0\0\0&\0\0\0\30\0\0\0\15\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0" + "\35\35\35\0yyy\0\331\331\331\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0QQQ\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\16\0\0\0\33\0\0" + "\0.\0\0\0Y\0\0\0\211\0\0\0\265\0\0\0\325\0\0\0\343\0\0\0\354\0\0\0\365\0" + "\0\0\374\40!\0\377FH\0\377hk\0\377\206\213\0\377\240\246\0\377\267\275\0" + "\377\311\321\0\377\330\341\0\377\344\354\0\377\344\354\0\377\330\341\0\377" + "\311\321\0\377\267\275\0\377\240\246\0\377\206\213\0\377hk\0\377FH\0\377" + "\40!\0\377\0\0\0\375\0\0\0\365\0\0\0\355\0\0\0\343\0\0\0\326\0\0\0\266\0" + "\0\0\211\0\0\0Z\0\0\0/\0\0\0\33\0\0\0\16\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0" + "\0\0\0\0QQQ\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\255\255\255\0===\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\11\0\0\0" + "\27\0\0\0-\0\0\0]\0\0\0\224\0\0\0\304\0\0\0\336\0\0\0\354\0\0\0\367\30\31" + "\0\376QT\0\377\206\213\0\377\267\275\0\377\344\354\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\344\354\0\377\267\275\0\377\206\213\0\377QT\0\377\30\31\0" + "\376\0\0\0\367\0\0\0\354\0\0\0\337\0\0\0\305\0\0\0\224\0\0\0]\0\0\0-\0\0" + "\0\30\0\0\0\11\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0===\0\255\255\255\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265" + "\0===\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\15\0\0\0!\0\0\0H\0\0\0\204" + "\0\0\0\274\0\0\0\336\0\0\0\357\0\0\0\373BD\0\377\206\213\0\377\306\315\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\306\315\0\377\206\213\0\377BD\0\377\0\0\0\373" + "\0\0\0\357\0\0\0\337\0\0\0\275\0\0\0\204\0\0\0I\0\0\0!\0\0\0\15\0\0\0\3\0" + "\0\0\1\0\0\0\0\0\0\0\0===\0\265\265\265\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321\321\0QQ" + "Q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\17\0\0\0'\0\0\0[\0\0\0\236\0\0" + "\0\324\0\0\0\353\0\0\0\371FH\0\377\225\232\0\377\340\350\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\340\350\0\377\225\232\0\377FH\0\377\0\0\0\371\0" + "\0\0\353\0\0\0\324\0\0\0\236\0\0\0\\\0\0\0'\0\0\0\17\0\0\0\3\0\0\0\1\0\0" + "\0\0\0\0\0\0QQQ\0\321\321\321\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0yyy\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\16\0\0\0(\0\0\0c\0\0\0\250" + "\0\0\0\332\0\0\0\361$%\0\375~\203\0\377\325\335\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\325\335\0\377~\203\0\377$%\0\375\0\0\0\361\0\0\0" + "\333\0\0\0\251\0\0\0c\0\0\0(\0\0\0\17\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0yyy" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\265\265\265\0)))\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\13\0\0\0$\0\0\0_" + "\0\0\0\250\0\0\0\334\0\0\0\364BD\0\376\244\252\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\244\252\0\377BD\0\376\0\0\0\364\0\0\0" + "\335\0\0\0\250\0\0\0`\0\0\0%\0\0\0\13\0\0\0\2\0\0\0\1\0\0\0\0)))\0\265\265" + "\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\4\0\0\0\34\0\0\0O\0\0\0\234\0\0\0\332\0\0\0\364FH\0\376\267\275\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377FH\0\376\0\0\0\364\0" + "\0\0\332\0\0\0\234\0\0\0P\0\0\0\34\0\0\0\4\0\0\0\1\0\0\0\0\0\0\0\0qqq\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\315\315\315" + "\0""555\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\17\0\0\0""0\0\0\0\203\0\0\0\321\0" + "\0\0\360+-\0\375\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377+-\0\375" + "\0\0\0\360\0\0\0\322\0\0\0\203\0\0\0""1\0\0\0\17\0\0\0\3\0\0\0\1\0\0\0\0" + """555\0\315\315\315\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\235\235\235\0\1\1\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\4\0\0\0\40\0\0\0`\0\0\0\262\0\0\0\346\0\0\0\374~\203\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377~\203\0\377\0\0\0\374\0\0\0\347\0\0\0\263\0\0\0a\0\0\0\40\0\0\0\5\0\0" + "\0\1\0\0\0\0\1\1\1\0\235\235\235\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\14\0\0" + "\0/\0\0\0\207\0\0\0\326\0\0\0\366FH\0\376\311\321\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\311\321\0\377FH\0\376\0\0\0\366\0\0\0\327\0\0\0\207\0\0\0" + """0\0\0\0\14\0\0\0\2\0\0\0\1\0\0\0\0uuu\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\375" + "\375\375\0UUU\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\30\0\0\0S\0\0\0\252\0\0\0\345" + "\0\0\0\374\206\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\374\0\0\0\346" + "\0\0\0\253\0\0\0T\0\0\0\31\0\0\0\4\0\0\0\1\0\0\0\0UUU\0\375\375\375\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\351\351\351\0==" + "=\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\"\0\0\0q\0\0\0\316\0\0\0\363/1\0\376\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377/1\0\376" + "\0\0\0\364\0\0\0\316\0\0\0q\0\0\0\"\0\0\0\5\0\0\0\1\0\0\0\0===\0\351\351" + "\351\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0---\0\0\0\0\0\0\0\0\1" + "\0\0\0\6\0\0\0*\0\0\0\205\0\0\0\332\0\0\0\373\\`\0\377\357\370\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357\370" + "\0\377\\`\0\377\0\0\0\373\0\0\0\332\0\0\0\206\0\0\0+\0\0\0\6\0\0\0\1\0\0" + "\0\1---\0\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\331\331\331\0%%%\0\0\0\0\0\0\0\0\1\0\0\0\11" + "\0\0\0""2\0\0\0\225\0\0\0\342\0\0\0\374\202\207\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\374\0\0\0\342\0\0\0" + "\226\0\0\0""2\0\0\0\11\0\0\0\1\0\0\0\1%%%\0\331\331\331\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\335\335\335\0%%%\0\0\0\0\0\0\0\0\1\0\0\0\15" + "\0\0\0?\0\0\0\243\0\0\0\350\2\2\0\375\240\246\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377" + "\2\2\0\375\0\0\0\350\0\0\0\244\0\0\0@\0\0\0\15\0\0\0\2\0\0\0\1%%%\0\335\335" + "\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\351\351\351\0---\0\0\0\0\0\0\0\0\1\0\0\0\16" + "\0\0\0H\0\0\0\257\0\0\0\355\25\25\0\376\267\275\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\25\25\0\376\0\0\0\355\0\0\0\257\0\0\0H\0\0\0" + "\17\0\0\0\2\0\0\0\1---\0\351\351\351\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\375\375\375\0===\0\0\0\0\0\0\0\0\1\0\0\0\15" + "\0\0\0H\0\0\0\264\0\0\0\360\40!\0\376\306\315\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\40!\0\376\0\0\0" + "\360\0\0\0\265\0\0\0I\0\0\0\15\0\0\0\1\0\0\0\1===\0\375\375\375\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0UUU\0\0\0\0\0\0\0\0\1\0\0\0\11" + "\0\0\0?\0\0\0\257\0\0\0\360$%\0\376\315\325\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\315\325" + "\0\377$%\0\377\0\0\0\360\0\0\0\257\0\0\0@\0\0\0\11\0\0\0\1\0\0\0\1UUU\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0""2" + "\0\0\0\244\0\0\0\355\40!\0\376\315\325\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377\40!\0\376\0\0\0\355\0\0\0\244\0\0\0""2\0" + "\0\0\6\0\0\0\1\0\0\0\0uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\235\235\235\0\0\0\0\0\0\0\0\0\0\0" + "\0\4\0\0\0*\0\0\0\225\0\0\0\350\25\25\0\376\306\315\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\25" + "\25\0\376\0\0\0\350\0\0\0\226\0\0\0*\0\0\0\5\0\0\0\1\0\0\0\0\235\235\235" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\315\315\315\0" + "\1\1\1\0\0\0\0\0\0\0\0\3\0\0\0\"\0\0\0\205\0\0\0\342\2\2\0\375\267\275\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\267\275\0\377\2\2\0\375\0\0\0\342\0\0\0" + "\206\0\0\0#\0\0\0\4\0\0\0\1\1\1\1\0\315\315\315\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0""555\0\0\0\0\0\0\0\0\2\0\0\0\30\0\0\0q\0\0\0\332\0\0\0\374" + "\240\246\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\350\361\0\377\254\263\0\377.0\0\377\13\14\0\377UX\0\377" + "\317\327\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377\0" + "\0\0\374\0\0\0\333\0\0\0r\0\0\0\31\0\0\0\2\0\0\0\1""555\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\0\0\0\0\14\0\0\0S\0\0\0\316\0\0\0\373\202\207" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\310\317\0\3779<\0\377\0\0\0\3779<\0\377\310\317\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\306\315\0\377!\"\0\377\0\0\0\377\0\0\0\377\4\4\0\377PT\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\373" + "\0\0\0\316\0\0\0S\0\0\0\14\0\0\0\1\0\0\0\0qqq\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0" + "\0\0\0\0\0\0\4\0\0\0/\0\0\0\253\0\0\0\364\\`\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377/2\0\377\0\0\0\377\0\0\0\377\0\0\0\377/2\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\237\245\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\20\20\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\\`\0\377\0\0\0\364\0\0\0" + "\253\0\0\0""0\0\0\0\5\0\0\0\1\0\0\0\0\265\265\265\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0)))\0\0\0\0\0\0\0\0\3\0\0\0\40\0\0" + "\0\207\0\0\0\345/1\0\376\357\370\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\207\215\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\357\370\0\377/1\0\376\0\0\0\346" + "\0\0\0\207\0\0\0\40\0\0\0\3\0\0\0\1)))\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0yyy\0\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0`\0\0\0\326\0\0\0\374\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\5\5\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377\0\0\0\374\0\0\0" + "\327\0\0\0a\0\0\0\17\0\0\0\1\0\0\0\0yyy\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321\321\0" + "\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0""1\0\0\0\263\0\0\0\365\206\213\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0" + "\366\0\0\0\263\0\0\0""1\0\0\0\5\0\0\0\1\0\0\0\0\321\321\321\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0QQQ\0\0" + "\0\0\0\0\0\0\2\0\0\0\34\0\0\0\203\0\0\0\346FH\0\376\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\376" + "\0\0\0\347\0\0\0\204\0\0\0\34\0\0\0\2\0\0\0\1QQQ\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0\0\0\0" + "\0\0\0\12\0\0\0O\0\0\0\321\0\0\0\374\311\321\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\311\321" + "\0\377\0\0\0\374\0\0\0\322\0\0\0P\0\0\0\13\0\0\0\1\0\0\0\0\265\265\265\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0" + "\0%\0\0\0\234\0\0\0\360~\203\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377~\203\0\377\0\0\0\361\0\0\0\234\0\0\0%\0\0\0\3\0\0\0\1===\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\255\255\255\0\0\0\0\0\0\0\0\0\0\0\0\16\0\0\0_\0" + "\0\0\332+-\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377+-\0\375\0\0\0\332\0\0\0`\0\0\0\17\0\0\0\1\0\0\0\0\255\255" + "\255\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0(\0\0\0\250\0\0\0" + "\363\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\244\252\0\377\0\0\0\364\0\0\0\250\0\0\0)\0\0\0\3\0\0\0\1=" + "==\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\271\271\271\0\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0c\0\0\0\334FH" + "\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377FH\0\376\0\0\0\335\0\0\0d\0\0\0\17\0\0\0" + "\1\0\0\0\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0'\0\0\0\251\0\0\0\363\267\275" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\364\0\0\0\251\0\0\0" + "(\0\0\0\3\0\0\0\1QQQ\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\331\331\331\0\0\0\0\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\333BD\0\375\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377BD\0\376\0\0\0\333\0\0\0\\" + "\0\0\0\16\0\0\0\1\0\0\0\0\331\331\331\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0yyy\0\0\0\0\0\0\0\0\2\0\0\0!\0\0\0\235\0\0\0\361\244\252\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377\0\0\0\361\0" + "\0\0\236\0\0\0\"\0\0\0\2\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\35\35\35\0\0\0\0\0\0\0\0\10\0\0\0H\0\0\0\324$%\0\375\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377$%\0\375\0" + "\0\0\325\0\0\0I\0\0\0\11\0\0\0\1\35\35\35\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265" + "\265\0\0\0\0\0\0\0\0\0\0\0\0\26\0\0\0\203\0\0\0\353~\203\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377~\203" + "\0\377\0\0\0\353\0\0\0\204\0\0\0\27\0\0\0\1\0\0\0\0\265\265\265\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0aaa\0\0\0\0\0\0\0\0\3\0\0\0-\0\0\0\275\0\0\0\370\325\335\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\325\335\0\377\0\0\0\371\0\0\0\276\0\0\0-\0\0\0\3\0\0\0\1aaa\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\21\21\21\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\336FH\0\376\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377FH\0\377\0\0\0\337\0\0\0]\0\0\0\16\0\0\0\1\21\21\21\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0\0\0\0\0\0\0\0\0\0\0\0\32\0\0\0\223\0\0\0\357\225\232\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\225\232\0\377\0\0\0\357\0\0\0\224\0\0\0\33\0" + "\0\0\1\0\0\0\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\2\0\0\0.\0\0\0\305\0\0\0\372\340\350\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\340\350\0\377\0\0\0\373\0\0\0\305\0\0\0" + "/\0\0\0\3\0\0\0\1qqq\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0---\0\0\0\0\0\0\0\0\14\0\0\0Y\0\0\0\336BD\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377BD\0\377\0\0\0\337\0\0\0Z\0\0\0" + "\15\0\0\0\1---\0\377\377\377\0\377\377\377\0\377\377\377\0\345\345\345\0" + "\0\0\0\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\355\0" + "\0\0\211\0\0\0\30\0\0\0\1\0\0\0\0\345\345\345\0\377\377\377\0\377\377\377" + "\0\251\251\251\0\0\0\0\0\0\0\0\1\0\0\0&\0\0\0\265\0\0\0\367\306\315\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377" + "\0\0\0\367\0\0\0\266\0\0\0&\0\0\0\2\0\0\0\1\251\251\251\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\6\0\0\0?\0\0\0\325\30\31\0\376\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\30\31\0\376\0\0\0\326\0\0\0?\0\0\0\6\0\0\0\1qqq\0\377\377\377\0\377\377" + "\377\0===\0\0\0\0\0\0\0\0\17\0\0\0d\0\0\0\343QT\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377QT" + "\0\377\0\0\0\344\0\0\0e\0\0\0\17\0\0\0\1===\0\377\377\377\0\377\377\377\0" + "\15\15\15\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\15\15\15\0\377\377\377" + "\0\335\335\335\0\0\0\0\0\0\0\0\0\0\0\0\40\0\0\0\254\0\0\0\365\267\275\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\0\0\0\365\0\0\0\255\0\0\0!\0\0\0\1\0\0\0\0\335" + "\335\335\0\265\265\265\0\0\0\0\0\0\0\0\1\0\0\0+\0\0\0\311\0\0\0\374\344\354" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\210\215\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\7\7\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\344\354\0\377\0\0\0\374\0\0\0\312\0\0\0,\0\0\0\2" + "\0\0\0\1\265\265\265\0\221\221\221\0\0\0\0\0\0\0\0\6\0\0\0B\0\0\0\331\40" + "!\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\237\245\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\20\21\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\40!\0\377\0\0\0" + "\332\0\0\0B\0\0\0\7\0\0\0\1\221\221\221\0qqq\0\0\0\0\0\0\0\0\15\0\0\0\\\0" + "\0\0\341FH\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\37702\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\37702\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377!\"\0\377\0\0" + "\0\377\0\0\0\377\4\4\0\377PS\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\377\0\0\0\342\0" + "\0\0]\0\0\0\15\0\0\0\1qqq\0UUU\0\0\0\0\0\0\0\0\23\0\0\0t\0\0\0\347hk\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\310\317\0\3779<\0\377\0" + "\0\0\3779<\0\377\310\317\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\350\361\0\377\254\263\0\377.0\0\377" + "\13\14\0\377UX\0\377\317\327\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377hk\0\377\0\0\0\350\0" + "\0\0u\0\0\0\23\0\0\0\1UUU\0===\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206" + "\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1===" + "\0)))\0\0\0\0\0\0\0\0\35\0\0\0\234\0\0\0\361\240\246\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246" + "\0\377\0\0\0\361\0\0\0\235\0\0\0\35\0\0\0\1)))\0\31\31\31\0\0\0\0\0\0\0\0" + "\40\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0" + "\0\0\255\0\0\0!\0\0\0\1\31\31\31\0\15\15\15\0\0\0\0\0\0\0\0$\0\0\0\271\0" + "\0\0\370\311\321\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\311\321\0\377\0\0\0\371\0\0\0\272\0\0\0" + "$\0\0\0\1\15\15\15\0\5\5\5\0\0\0\0\0\0\0\0&\0\0\0\304\0\0\0\373\330\341\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\330\341\0\377\0\0\0\373\0\0\0\305\0\0\0'\0\0\0\1\5\5\5\0\1\1\1" + "\0\0\0\0\0\0\0\0(\0\0\0\313\0\0\0\374\344\354\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377" + "\0\0\0\375\0\0\0\313\0\0\0)\0\0\0\1\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0(\0\0\0" + "\313\0\0\0\374\344\354\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\344\354\0\377\0\0\0\375\0\0\0\313\0\0" + "\0)\0\0\0\1\1\1\1\0\5\5\5\0\0\0\0\0\0\0\0&\0\0\0\304\0\0\0\373\330\341\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\330\341\0\377\0\0\0\373\0\0\0\305\0\0\0'\0\0\0\1\5\5\5\0\15\15" + "\15\0\0\0\0\0\0\0\0$\0\0\0\271\0\0\0\370\311\321\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\311\321\0" + "\377\0\0\0\371\0\0\0\272\0\0\0$\0\0\0\1\15\15\15\0\31\31\31\0\0\0\0\0\0\0" + "\0\40\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0" + "\0\0\255\0\0\0!\0\0\0\1\31\31\31\0)))\0\0\0\0\0\0\0\0\35\0\0\0\234\0\0\0" + "\361\240\246\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\240\246\0\377\0\0\0\361\0\0\0\235\0\0\0\35\0\0\0" + "\1)))\0===\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\206" + "\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1===\0UUU\0\0\0\0\0\0\0\0\23" + "\0\0\0t\0\0\0\347hk\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377hk\0\377\0\0\0\350\0\0\0u\0\0\0\23\0\0\0" + "\1UUU\0qqq\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\341FH\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\377\0" + "\0\0\342\0\0\0]\0\0\0\15\0\0\0\1qqq\0\221\221\221\0\0\0\0\0\0\0\0\6\0\0\0" + "B\0\0\0\331\40!\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\40!\0\377\0\0\0\332\0\0\0C\0\0\0\7\0\0\0\1\221" + "\221\221\0\265\265\265\0\0\0\0\0\0\0\0\1\0\0\0,\0\0\0\312\0\0\0\374\344\354" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377" + "\0\0\0\375\0\0\0\312\0\0\0,\0\0\0\2\0\0\0\1\265\265\265\0\335\335\335\0\0" + "\0\0\0\0\0\0\1\0\0\0!\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0\0\0\255\0\0" + "\0\"\0\0\0\1\0\0\0\1\335\335\335\0\377\377\377\0\15\15\15\0\0\0\0\0\0\0\0" + "\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\15" + "\15\15\0\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\17\0\0\0d\0\0\0\343" + "QT\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377QT\0\377\0" + "\0\0\344\0\0\0e\0\0\0\17\0\0\0\1===\0\377\377\377\0\377\377\377\0qqq\0\0" + "\0\0\0\0\0\0\6\0\0\0?\0\0\0\325\30\31\0\376\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\30\31\0\376\0\0\0\326\0\0\0?\0\0\0\6\0\0\0\1qqq\0\377" + "\377\377\0\377\377\377\0\251\251\251\0\0\0\0\0\0\0\0\1\0\0\0&\0\0\0\266\0" + "\0\0\367\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\335\345\0\377\261\270\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\0\0\0\370" + "\0\0\0\266\0\0\0'\0\0\0\2\0\0\0\1\251\251\251\0\377\377\377\0\377\377\377" + "\0\345\345\345\0\0\0\0\0\0\0\0\1\0\0\0\31\0\0\0\212\0\0\0\354\206\213\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\322\332\0\377\36" + "\37\0\377\10\10\0\377\265\273\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\316\326\0\377\230\235\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\0" + "\0\0\1\345\345\345\0\377\377\377\0\377\377\377\0\377\377\377\0---\0\0\0\0" + "\0\0\0\0\14\0\0\0Z\0\0\0\337BD\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\322\332\0\377\36\37\0\377\0\0\0\377\0\0\0\377\1\1\0\377\201\206\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\265\273\0\377\17" + "\20\0\377\2\2\0\377\224\231\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377BD\0\377\0\0\0\340\0\0\0Z\0\0\0" + "\15\0\0\0\1---\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "qqq\0\0\0\0\0\0\0\0\2\0\0\0.\0\0\0\305\0\0\0\372\340\350\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\322\332\0\377\36\37\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377SV\0\377\353\364\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377w{\0\377\1\1\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377;>\0\377<>\0\377<>\0\377\303\312\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340" + "\350\0\377\0\0\0\373\0\0\0\305\0\0\0/\0\0\0\3\0\0\0\1qqq\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0\0\0\0\0\0\0\0\1" + "\0\0\0\33\0\0\0\224\0\0\0\357\225\232\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377\26\26\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377.0\0\377" + "\330\340\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\345\355\0\377?A" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\1\1\0\377\310\320\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\225\232\0\377\0\0\0\360\0\0\0" + "\225\0\0\0\34\0\0\0\1\0\0\0\1\271\271\271\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\21\21\21\0\0\0\0\0\0\0\0\15\0\0\0" + "]\0\0\0\337FH\0\376\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\257\266\0\377\4\4\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\16\17\0\377\265\273" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\277\306\0\377\23\24\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\2\2\0\377\226\233\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377FH\0\377\0\0\0\337\0\0\0^\0\0\0\16\0\0\0\1\21\21" + "\21\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0aaa\0\0\0\0\0\0\0\0\3\0\0\0-\0\0\0\275\0\0\0\370\325\335" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\300\307\0\377\30\31\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377z\177\0\377\355\366" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377pt\0\377\2\2\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\2\2" + "\0\377\235\243\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\325\335\0\377\0\0\0\371" + "\0\0\0\276\0\0\0-\0\0\0\3\0\0\0\1aaa\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0" + "\0\0\1\0\0\0\27\0\0\0\204\0\0\0\353~\203\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\330\341\0\377&'\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\25\25\0\377\277\306\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\310\317\0\377" + "-/\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\25\25\0\377\267\275\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377~\203\0\377\0\0\0\354\0\0\0\205\0\0\0\30\0\0\0\1\0\0\0\1\265\265" + "\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\35\35\35\0\0\0\0\0\0\0\0\11\0\0\0I\0\0\0\324" + "$%\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\350\360\0\377EG\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\2\2\0\377mq\0\377\352\363\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\342\352\0" + "\377fi\0\377\2\2\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377*,\0\377\333\344\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377$%\0\375\0\0\0\325\0\0\0J\0\0\0\11\0\0" + "\0\1\35\35\35\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0\0\2" + "\0\0\0!\0\0\0\236\0\0\0\361\244\252\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\357\370\0\377tx\0\377\1\1\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\35\36\0\377\235\243\0" + "\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377\220" + "\225\0\377\15\16\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377gj\0\377\353\364\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377\0\0\0\362\0" + "\0\0\237\0\0\0\"\0\0\0\2\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\331\331\331\0\0\0\0\0\0\0\0\1\0\0\0\16\0\0\0\\\0\0\0\333BD\0\375\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\265\273\0" + "\377\13\14\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377-/\0\377\273\302\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\355\366\0\377\177\204" + "\0\377\15\16\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\11\12\0\377\246\254\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "BD\0\376\0\0\0\333\0\0\0]\0\0\0\17\0\0\0\1\0\0\0\1\331\331\331\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0'" + "\0\0\0\251\0\0\0\363\267\275\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\346\356\0\377HK\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377=?\0\377\247\255\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377\206\213\0\377" + "\34\35\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377/1\0\377\325\335\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\267\275\0\377\0\0\0\364\0\0\0\251\0\0\0(\0\0\0\3\0\0\0\1QQQ\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0\0" + "\0\0\0\0\0\0\1\0\0\0\20\0\0\0d\0\0\0\335FH\0\375\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\211\217\0\377\10\11\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\14\15\0\377qu\0\377" + "\343\353\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\356\367\0\377\254\263\0\377hl\0\377\5\5\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\16\17\0\377\231\237\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377FH\0\376\0\0\0\335\0\0\0d\0\0\0\20\0\0\0\1\0\0\0\1\271\271\271" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0)\0\0\0\251\0\0\0\363\244\252\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\346\356\0\377fi\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\4\4\0\377BD\0\377\206\213\0\377\301\310\0\377\354\365\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340\350\0\377\230\235\0" + "\377^b\0\377\25\25\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377Z^" + "\0\377\335\345\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377" + "\0\0\0\364\0\0\0\251\0\0\0*\0\0\0\3\0\0\0\1===\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\255" + "\255\255\0\0\0\0\0\0\0\0\1\0\0\0\17\0\0\0`\0\0\0\332+-\0\375\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\254\263\0\377\25\25\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\12\13\0\377;>\0\377UX\0" + "\377lp\0\377x|\0\377x|\0\377x|\0\377bf\0\377HK\0\377\6\6\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\37713\0\377\275\304" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "+-\0\375\0\0\0\332\0\0\0a\0\0\0\17\0\0\0\1\0\0\0\1\255\255\255\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0%\0\0\0\234\0\0\0" + "\360~\203\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357\370" + "\0\377\216\223\0\377\16\17\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + ",.\0\377\247\255\0\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377~\203\0\377\0\0\0\361\0\0\0\235\0\0\0%\0" + "\0\0\3\0\0\0\1===\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265" + "\265\265\0\0\0\0\0\0\0\0\1\0\0\0\13\0\0\0P\0\0\0\322\0\0\0\374\311\321\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\355" + "\366\0\377\213\221\0\377\17\20\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\37724\0\377\246\254\0\377\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\311\321\0\377\0\0\0\374\0\0\0\323\0\0\0Q\0\0\0\14\0\0\0\1\0\0\0\1" + "\265\265\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0\34\0\0\0\204\0\0\0\347FH\0\376\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\356\367\0\377\235\243\0\377(*\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\5\5\0" + "\377hl\0\377\270\276\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377FH\0\377\0\0\0\347\0\0\0\204\0\0\0\35\0\0\0\2\0\0\0\1QQQ\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321" + "\321\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0""1\0\0\0\263\0\0\0\365\206\213\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\320\330\0\377\\`\0\377\7" + "\7\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\3778:\0\377\226\233\0" + "\377\344\354\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\206\213\0\377\0\0\0\366\0\0\0\263\0\0\0""1\0\0\0\5\0\0\0\1\0\0\0\1" + "\321\321\321\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0\0\1\0\0\0\17\0\0\0a\0\0\0" + "\327\0\0\0\374\276\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\357\370\0\377\245\253\0\377w{\0\377:=\0\377\3\3\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377*,\0\377dh\0\377\217\224\0\377\350" + "\361\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\276\305\0\377\0\0\0\375\0\0\0\330\0\0\0b\0\0" + "\0\20\0\0\0\1\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + ")))\0\0\0\0\0\0\0\0\3\0\0\0\40\0\0\0\207\0\0\0\346/1\0\376\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\357\370\0\377\305\314\0\377\247\255\0\377" + "\212\220\0\377x}\0\377x}\0\377x}\0\377x}\0\377{\200\0\377\240\246\0\377\270" + "\276\0\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\357\370\0\377/1\0\376\0\0\0\347\0\0\0\210\0\0\0!\0\0\0\3\0\0\0" + "\1)))\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265" + "\265\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0""0\0\0\0\253\0\0\0\364\\`\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\\`\0\377\0\0\0\364\0\0\0\254\0\0\0""1" + "\0\0\0\6\0\0\0\1\0\0\0\1\265\265\265\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0\0\0\1\0\0\0\14\0\0" + "\0T\0\0\0\317\0\0\0\373\202\207\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\373" + "\0\0\0\317\0\0\0T\0\0\0\15\0\0\0\1\0\0\0\1qqq\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0""555\0\0\0\0\0\0\0\0\2\0\0\0\30\0\0\0q\0\0\0\332\0\0\0\374\240\246\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377\0" + "\0\0\374\0\0\0\333\0\0\0r\0\0\0\31\0\0\0\2\0\0\0\1""555\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\315\315\315\0\1\1\1\0\0\0\0\1\0\0\0\4\0\0\0\"\0" + "\0\0\205\0\0\0\342\2\2\0\375\267\275\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\267" + "\275\0\377\2\2\0\375\0\0\0\342\0\0\0\206\0\0\0#\0\0\0\4\0\0\0\1\1\1\1\1\315" + "\315\315\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\235\235" + "\235\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0+\0\0\0\226\0\0\0\350\25\25\0\376\306" + "\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\306\315\0\377\25\25\0\376\0\0\0\351\0\0\0\226\0\0\0+\0\0\0\6\0" + "\0\0\1\0\0\0\1\235\235\235\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0" + """2\0\0\0\244\0\0\0\355\40!\0\376\315\325\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377\40!\0\376\0\0\0\356\0\0\0\245\0\0\0""3\0" + "\0\0\7\0\0\0\1\0\0\0\1uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0UUU\0\0" + "\0\0\0\0\0\0\1\0\0\0\11\0\0\0@\0\0\0\260\0\0\0\360$%\0\376\315\325\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377$%\0\377\0\0\0\361\0\0\0\260\0\0\0A\0\0\0" + "\12\0\0\0\1\0\0\0\1UUU\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\375\375\375\0===\0\0\0\0\1\0\0\0\2\0\0\0\16\0\0\0H\0\0\0\264\0\0\0\360" + "\40!\0\376\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\306\315\0\377\40!\0\376\0\0\0\361\0\0\0\265\0\0\0I\0\0\0\16" + "\0\0\0\2\0\0\0\1===\1\375\375\375\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\351\351\351\0---\0\0\0\0\1\0\0\0\2\0\0\0\17" + "\0\0\0I\0\0\0\260\0\0\0\355\25\25\0\376\267\275\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\25\25\0\376\0\0\0\356\0\0\0\260\0\0\0I\0\0\0" + "\20\0\0\0\2\0\0\0\1---\1\351\351\351\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0" + "%%%\0\0\0\0\1\0\0\0\2\0\0\0\16\0\0\0@\0\0\0\244\0\0\0\350\2\2\0\375\240\246" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\240\246\0\377\2\2\0\375\0\0\0\351\0\0\0\245\0\0\0@\0\0\0\16" + "\0\0\0\2\0\0\0\1%%%\1\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\331\331\331\0%%%\0\0\0\0\1\0\0\0\2\0\0\0\12\0\0\0""2\0\0\0\226" + "\0\0\0\342\0\0\0\374\202\207\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\202\207\0\377\0\0\0\374\0\0\0\343\0\0\0\226\0\0\0""3\0\0\0" + "\12\0\0\0\2\0\0\0\1%%%\1\331\331\331\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0---\0\0\0\0\1\0\0" + "\0\1\0\0\0\6\0\0\0*\0\0\0\205\0\0\0\332\0\0\0\373\\`\0\377\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357" + "\370\0\377\\`\0\377\0\0\0\373\0\0\0\333\0\0\0\206\0\0\0+\0\0\0\6\0\0\0\1" + "\0\0\0\1---\1\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\351\351\351\0" + "===\0\0\0\0\1\0\0\0\1\0\0\0\5\0\0\0#\0\0\0r\0\0\0\317\0\0\0\364/1\0\376\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377/1\0\376" + "\0\0\0\365\0\0\0\317\0\0\0r\0\0\0#\0\0\0\6\0\0\0\1\0\0\0\1===\1\351\351\351" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\375\375\375" + "\0UUU\0\0\0\0\0\0\0\0\1\0\0\0\4\0\0\0\31\0\0\0S\0\0\0\253\0\0\0\346\0\0\0" + "\374\206\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\375\0\0\0\347\0\0\0" + "\254\0\0\0T\0\0\0\31\0\0\0\4\0\0\0\1\0\0\0\1UUU\0\375\375\375\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\1\0\0\0\3\0\0\0\14\0\0\0/\0\0\0\210\0" + "\0\0\327\0\0\0\366FH\0\376\311\321\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\311\321\0\377FH\0\377\0\0\0\367\0\0\0\330\0\0\0\210\0\0\0""0\0\0\0\15\0" + "\0\0\3\0\0\0\1\0\0\0\1uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\235\235\235\0\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\5\0" + "\0\0!\0\0\0a\0\0\0\263\0\0\0\347\0\0\0\374~\203\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377~\203\0\377\0\0\0" + "\374\0\0\0\347\0\0\0\264\0\0\0b\0\0\0!\0\0\0\6\0\0\0\1\0\0\0\1\1\1\1\1\235" + "\235\235\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\315\315\315\0""555\0\0\0\0\0\0\0\0\1\0" + "\0\0\3\0\0\0\17\0\0\0""1\0\0\0\204\0\0\0\322\0\0\0\361+-\0\375\244\252\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\244\252\0\377+-\0\375\0\0\0\361\0\0\0\323\0\0\0" + "\204\0\0\0""1\0\0\0\20\0\0\0\3\0\0\0\1\0\0\0\1""555\0\315\315\315\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0" + "\0\0\1\0\0\0\1\0\0\0\5\0\0\0\34\0\0\0P\0\0\0\234\0\0\0\332\0\0\0\364FH\0" + "\376\267\275\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377FH" + "\0\376\0\0\0\364\0\0\0\333\0\0\0\235\0\0\0Q\0\0\0\35\0\0\0\5\0\0\0\1\0\0" + "\0\1\0\0\0\1qqq\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0)))\0\0" + "\0\0\0\0\0\0\1\0\0\0\3\0\0\0\14\0\0\0%\0\0\0`\0\0\0\251\0\0\0\335\0\0\0\364" + "BD\0\376\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\244\252\0\377BD\0\376\0\0\0\364\0\0\0\336\0\0\0\251\0\0\0a\0\0\0&" + "\0\0\0\14\0\0\0\3\0\0\0\1\0\0\0\1)))\0\265\265\265\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0" + "\0\1\0\0\0\1\0\0\0\3\0\0\0\17\0\0\0)\0\0\0d\0\0\0\251\0\0\0\333\0\0\0\362" + "$%\0\375~\203\0\377\325\335\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\325\335\0\377~\203\0\377$%\0\375\0\0\0\362\0\0\0\334\0\0\0\252\0\0\0" + "d\0\0\0)\0\0\0\17\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1yyy\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\321\321\321\0QQQ\0\0\0\0\0\0\0\0\1\0\0\0\1\0" + "\0\0\4\0\0\0\20\0\0\0(\0\0\0\\\0\0\0\236\0\0\0\325\0\0\0\354\0\0\0\372FH" + "\0\377\225\232\0\377\340\350\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340" + "\350\0\377\225\232\0\377FH\0\377\0\0\0\372\0\0\0\354\0\0\0\325\0\0\0\236" + "\0\0\0]\0\0\0(\0\0\0\20\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1QQQ\0\321\321\321" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\265\265\265\0===\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\3\0\0\0" + "\16\0\0\0\"\0\0\0I\0\0\0\205\0\0\0\275\0\0\0\337\0\0\0\360\0\0\0\374BD\0" + "\377\206\213\0\377\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\206" + "\213\0\377BD\0\377\0\0\0\374\0\0\0\360\0\0\0\340\0\0\0\276\0\0\0\205\0\0" + "\0J\0\0\0\"\0\0\0\16\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1===\0\265\265\265\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\255\255" + "\255\0===\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\11\0\0\0\27\0\0\0-\0\0" + "\0]\0\0\0\225\0\0\0\305\0\0\0\337\0\0\0\355\0\0\0\370\30\31\0\376QT\0\377" + "\206\213\0\377\267\275\0\377\344\354\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\344\354\0\377\267\275\0\377\206\213\0\377QT\0\377\30\31\0\376\0\0\0\370" + "\0\0\0\355\0\0\0\340\0\0\0\306\0\0\0\225\0\0\0^\0\0\0-\0\0\0\30\0\0\0\11" + "\0\0\0\3\0\0\0\1\0\0\0\1\0\0\0\1===\0\255\255\255\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\271\271\271\0QQQ\0\0\0\0\0\0\0\0\1\0\0\0\1" + "\0\0\0\1\0\0\0\3\0\0\0\16\0\0\0\33\0\0\0/\0\0\0Z\0\0\0\212\0\0\0\266\0\0" + "\0\326\0\0\0\344\0\0\0\355\0\0\0\365\0\0\0\374\40!\0\377FH\0\377hk\0\377" + "\206\213\0\377\240\246\0\377\267\275\0\377\311\321\0\377\330\341\0\377\344" + "\354\0\377\344\354\0\377\330\341\0\377\311\321\0\377\267\275\0\377\240\246" + "\0\377\206\213\0\377hk\0\377FH\0\377\40!\0\377\0\0\0\375\0\0\0\366\0\0\0" + "\355\0\0\0\344\0\0\0\326\0\0\0\266\0\0\0\212\0\0\0Z\0\0\0/\0\0\0\33\0\0\0" + "\16\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1QQQ\0\271\271\271\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\331\331\331\0yyy\0\35\35\35\0\0\0\0\0\0\0\0\1\0" + "\0\0\1\0\0\0\1\0\0\0\3\0\0\0\15\0\0\0\31\0\0\0'\0\0\0@\0\0\0e\0\0\0\212\0" + "\0\0\255\0\0\0\312\0\0\0\332\0\0\0\342\0\0\0\350\0\0\0\355\0\0\0\361\0\0" + "\0\365\0\0\0\371\0\0\0\373\0\0\0\375\0\0\0\375\0\0\0\374\0\0\0\371\0\0\0" + "\366\0\0\0\362\0\0\0\355\0\0\0\350\0\0\0\342\0\0\0\332\0\0\0\313\0\0\0\255" + "\0\0\0\212\0\0\0e\0\0\0@\0\0\0'\0\0\0\31\0\0\0\16\0\0\0\4\0\0\0\2\0\0\0\1" + "\0\0\0\1\0\0\0\1\35\35\35\0yyy\0\331\331\331\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\265\265\265\0aaa\0\21\21\21\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0" + "\0\2\0\0\0\6\0\0\0\17\0\0\0\31\0\0\0!\0\0\0,\0\0\0C\0\0\0]\0\0\0u\0\0\0\212" + "\0\0\0\235\0\0\0\255\0\0\0\272\0\0\0\305\0\0\0\314\0\0\0\314\0\0\0\305\0" + "\0\0\272\0\0\0\255\0\0\0\235\0\0\0\212\0\0\0u\0\0\0]\0\0\0C\0\0\0,\0\0\0" + "\"\0\0\0\31\0\0\0\20\0\0\0\7\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\21\21" + "\21\0aaa\0\265\265\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0qqq\0---\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\2" + "\0\0\0\7\0\0\0\15\0\0\0\23\0\0\0\31\0\0\0\35\0\0\0!\0\0\0$\0\0\0'\0\0\0)" + "\0\0\0)\0\0\0'\0\0\0%\0\0\0!\0\0\0\36\0\0\0\31\0\0\0\24\0\0\0\16\0\0\0\10" + "\0\0\0\3\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1---\0qqq\0\271\271" + "\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\345\345\345\0" + "\251\251\251\0qqq\0===\0\15\15\15\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" + "\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" + "\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\15\15" + "\15\0===\0qqq\0\251\251\251\0\345\345\345\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0" + "\265\265\265\0\221\221\221\0qqq\0UUU\0===\0)))\0\31\31\31\0\15\15\15\0\5" + "\5\5\0\1\1\1\0\1\1\1\0\5\5\5\0\15\15\15\0\31\31\31\0)))\0===\0UUU\0qqq\0" + "\221\221\221\0\265\265\265\0\335\335\335\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0", +}; + diff --git a/samples/graphics/rsxtest_tile/include/mesh.h b/samples/graphics/rsxtest_tile/include/mesh.h new file mode 100644 index 00000000..63665494 --- /dev/null +++ b/samples/graphics/rsxtest_tile/include/mesh.h @@ -0,0 +1,62 @@ +#ifndef __MESH_H__ +#define __MESH_H__ + +#include + +using namespace Vectormath::Aos; + +template< class T > +inline const T& min_(const T& a,const T& b) +{ + return a +inline const T& max_(const T& a,const T& b) +{ + return a +inline const T clamp(const T& val,const T& low,const T& high) +{ + return min_(max_(val,low),high); +} + +struct S3DVertex +{ + S3DVertex() {}; + S3DVertex(f32 x,f32 y,f32 z,f32 nx,f32 ny,f32 nz,f32 tu,f32 tv) + : pos(x,y,z),nrm(nx,ny,nz),u(tu),v(tv) {}; + + inline S3DVertex& operator=(const S3DVertex& other) + { + pos = other.pos; + nrm = other.nrm; + u = other.u; + v = other.v; + return *this; + } + + Vector3 pos; + Vector3 nrm; + + f32 u,v; +}; + +template< class T > +class CMeshBuffer +{ +public: + CMeshBuffer() : indices(NULL),cnt_indices(0),vertices(NULL),cnt_vertices(0) {}; + + u16 *indices; + u32 cnt_indices; + + S3DVertex *vertices; + u32 cnt_vertices; +}; + +typedef CMeshBuffer SMeshBuffer; + +#endif diff --git a/samples/graphics/rsxtest_tile/include/rsxutil.h b/samples/graphics/rsxtest_tile/include/rsxutil.h new file mode 100644 index 00000000..fc5d132a --- /dev/null +++ b/samples/graphics/rsxtest_tile/include/rsxutil.h @@ -0,0 +1,44 @@ +#ifndef __RSXUTIL_H__ +#define __RSXUTIL_H__ + +#include + +#define DEFAULT_CB_SIZE 0x80000 // 512Kb default command buffer size +#define HOST_ADDR_ALIGNMENT (1024*1024) +#define HOST_SIZE (32*1024*1024) + +#define GCM_PREPARED_BUFFER_INDEX 65 +#define GCM_BUFFER_STATUS_INDEX 66 +#define GCM_WAIT_LABEL_INDEX 255 + +#define MAX_BUFFER_QUEUE_SIZE 1 + +#define BUFFER_IDLE 0 +#define BUFFER_BUSY 1 + +#define FRAME_BUFFER_COUNT 4 + +extern gcmContextData *gGcmContext; + +extern u32 curr_fb; + +extern u32 display_width; +extern u32 display_height; + +extern u32 depth_pitch; +extern u32 depth_offset; +extern u32 *depth_buffer; + +extern u32 color_pitch; +extern u32 color_offset[FRAME_BUFFER_COUNT]; +extern u32 *color_buffer[FRAME_BUFFER_COUNT]; + +extern f32 aspect_ratio; + +void initScreen(); +void flip(); +void finish(); + +void setRenderTarget(u32 index); + +#endif // __RSXUTIL_H__ diff --git a/samples/graphics/rsxtest_tile/shaders/diffuse_specular_shader.fcg b/samples/graphics/rsxtest_tile/shaders/diffuse_specular_shader.fcg new file mode 100644 index 00000000..6ebb917e --- /dev/null +++ b/samples/graphics/rsxtest_tile/shaders/diffuse_specular_shader.fcg @@ -0,0 +1,35 @@ +void main +( + float4 position : TEXCOORD0, + float3 normal : TEXCOORD1, + float2 texcoord : TEXCOORD2, + + uniform float3 globalAmbient, + uniform float3 lightPosition, + uniform float3 lightColor, + uniform float3 eyePosition, + uniform float3 Kd, + uniform float3 Ks, + uniform float shininess, + + uniform sampler2D texture, + + out float4 oColor +) +{ + float3 N = normalize(normal); + + float3 L = normalize(lightPosition - position.xyz); + float diffuseLight = max(dot(N,L),0.0f); + float3 diffuse = Kd*lightColor*diffuseLight; + + float3 V = normalize(eyePosition - position.xyz); + float3 H = normalize(L + V); + float specularLight = pow(max(dot(H,N),0.0f),shininess); + if(diffuseLight<=0) specularLight = 0; + float3 specular = Ks*specularLight; + + float3 color = tex2D(texture,texcoord).xyz*(diffuse + globalAmbient) + specular; + + oColor = float4(color,1.0f); +} diff --git a/samples/graphics/rsxtest_tile/shaders/diffuse_specular_shader.vcg b/samples/graphics/rsxtest_tile/shaders/diffuse_specular_shader.vcg new file mode 100644 index 00000000..c11e6228 --- /dev/null +++ b/samples/graphics/rsxtest_tile/shaders/diffuse_specular_shader.vcg @@ -0,0 +1,21 @@ +void main +( + float3 vertexPosition : POSITION, + float3 vertexNormal : NORMAL, + float2 vertexTexcoord : TEXCOORD0, + + uniform float4x4 projMatrix, + uniform float4x4 modelViewMatrix, + + out float4 ePosition : POSITION, + out float4 oPosition : TEXCOORD0, + out float3 oNormal : TEXCOORD1, + out float2 oTexcoord : TEXCOORD2 +) +{ + ePosition = mul(mul(projMatrix,modelViewMatrix),float4(vertexPosition,1.0f)); + + oPosition = float4(vertexPosition,1.0f); + oNormal = vertexNormal; + oTexcoord = vertexTexcoord; +} diff --git a/samples/graphics/rsxtest_tile/source/main.cpp b/samples/graphics/rsxtest_tile/source/main.cpp new file mode 100644 index 00000000..ad19210e --- /dev/null +++ b/samples/graphics/rsxtest_tile/source/main.cpp @@ -0,0 +1,603 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "acid.h" +#include "mesh.h" +#include "rsxutil.h" + +#include "diffuse_specular_shader_vpo.h" +#include "diffuse_specular_shader_fpo.h" + +#define DEGTORAD(a) ( (a) * 0.01745329252f ) +#define RADTODEG(a) ( (a) * 57.29577951f ) + +SYS_PROCESS_PARAM(1001, 0x100000); + +u32 running = 0; + +u32 fp_offset; +u32 *fp_buffer; + +u32 *texture_buffer; +u32 texture_offset; + +s32 projMatrix_id = -1; +s32 modelViewMatrix_id = -1; +s32 vertexPosition_id = -1; +s32 vertexNormal_id = -1; +s32 vertexTexcoord_id = -1; +s32 textureUnit_id = -1; +s32 eyePosition_id = -1; +s32 globalAmbient_id = -1; +s32 lightPosition_id = -1; +s32 lightColor_id = -1; +s32 Kd_id = -1; +s32 Ks_id = -1; +s32 shininess_id = -1; + +Point3 eye_pos = Point3(0.0f,0.0f,20.0f); +Point3 eye_dir = Point3(0.0f,0.0f,0.0f); +Vector3 up_vec = Vector3(0.0f,1.0f,0.0f); + +void *vp_ucode = NULL; +rsxVertexProgram *vpo = (rsxVertexProgram*)diffuse_specular_shader_vpo; + +void *fp_ucode = NULL; +rsxFragmentProgram *fpo = (rsxFragmentProgram*)diffuse_specular_shader_fpo; + +static Matrix4 P; +static SMeshBuffer *sphere = NULL; +static SMeshBuffer *donut = NULL; +static SMeshBuffer *cube = NULL; + +extern "C" { +static void program_exit_callback() +{ + finish(); +} + +static void sysutil_exit_callback(u64 status,u64 param,void *usrdata) +{ + switch(status) { + case SYSUTIL_EXIT_GAME: + running = 0; + break; + case SYSUTIL_DRAW_BEGIN: + case SYSUTIL_DRAW_END: + break; + default: + break; + } +} +} + +static void init_texture() +{ + u32 i; + u8 *buffer; + const u8 *data = acid.pixel_data; + + texture_buffer = (u32*)rsxMemalign(128,(acid.width*acid.height*4)); + if(!texture_buffer) return; + + rsxAddressToOffset(texture_buffer,&texture_offset); + + buffer = (u8*)texture_buffer; + for(i=0;icnt_indices = 36; + buffer->indices = (u16*)rsxMemalign(128,buffer->cnt_indices*sizeof(u16)); + + for(i=0;i<36;i++) buffer->indices[i] = u[i]; + + buffer->cnt_vertices = 12; + buffer->vertices = (S3DVertex*)rsxMemalign(128,buffer->cnt_vertices*sizeof(S3DVertex)); + + buffer->vertices[0] = S3DVertex(0,0,0, -1,-1,-1, 1, 0); + buffer->vertices[1] = S3DVertex(1,0,0, 1,-1,-1, 1, 1); + buffer->vertices[2] = S3DVertex(1,1,0, 1, 1,-1, 0, 1); + buffer->vertices[3] = S3DVertex(0,1,0, -1, 1,-1, 0, 0); + buffer->vertices[4] = S3DVertex(1,0,1, 1,-1, 1, 1, 0); + buffer->vertices[5] = S3DVertex(1,1,1, 1, 1, 1, 0, 0); + buffer->vertices[6] = S3DVertex(0,1,1, -1, 1, 1, 0, 1); + buffer->vertices[7] = S3DVertex(0,0,1, -1,-1, 1, 1, 1); + buffer->vertices[8] = S3DVertex(0,1,1, -1, 1, 1, 1, 0); + buffer->vertices[9] = S3DVertex(0,1,0, -1, 1,-1, 1, 1); + buffer->vertices[10] = S3DVertex(1,0,1, 1,-1, 1, 0, 1); + buffer->vertices[11] = S3DVertex(1,0,0, 1,-1,-1, 0, 0); + + for(i=0;i<12;i++) { + buffer->vertices[i].pos -= Vector3(0.5f,0.5f,0.5f); + buffer->vertices[i].pos *= size; + } + + return buffer; +} + +static SMeshBuffer* createDonut(f32 outerRadius,f32 innerRadius,u32 polyCntX,u32 polyCntY) +{ + u32 i,x,y,level; + SMeshBuffer *buffer = new SMeshBuffer(); + + if(polyCntX<2) polyCntX = 2; + if(polyCntY<2) polyCntY = 2; + while(polyCntX*polyCntY>32767) { + polyCntX /= 2; + polyCntY /= 2; + } + + f32 ay = 0; + const f32 angleX = 2*M_PI/polyCntX; + const f32 angleY = 2*M_PI/polyCntY; + const u32 polyCntXpitch = polyCntX +1; + const u32 polyCntYpitch = polyCntY + 1; + + buffer->cnt_vertices = polyCntYpitch*polyCntXpitch; + buffer->vertices = (S3DVertex*)rsxMemalign(128,buffer->cnt_vertices*sizeof(S3DVertex)); + + buffer->cnt_indices = polyCntY*polyCntX*6; + buffer->indices = (u16*)rsxMemalign(128,buffer->cnt_indices*sizeof(u16)); + + i = 0; + for(y=0;y<=polyCntY;y++) { + f32 axz = 0; + + const f32 sinay = sinf(ay); + const f32 cosay = cosf(ay); + const f32 tu = (f32)y/(f32)polyCntY; + for(x=0;x<=polyCntX;x++) { + const Vector3 pos(static_cast((outerRadius - (innerRadius*cosf(axz)))*cosay), + static_cast((outerRadius - (innerRadius*cosf(axz)))*sinay), + static_cast(innerRadius*sinf(axz))); + + const Vector3 nrm(static_cast(-cosf(axz)*cosay), + static_cast(-cosf(axz)*sinay), + static_cast(sinf(axz))); + + buffer->vertices[i] = S3DVertex(pos.getX(),pos.getY(),pos.getZ(),nrm.getX(),nrm.getY(),nrm.getZ(),tu,(f32)x/(f32)polyCntX); + + axz += angleX; + i++; + } + ay += angleY; + } + + i = 0; + level = 0; + for(y=0;yindices[i++] = curr; + buffer->indices[i++] = curr + polyCntXpitch; + buffer->indices[i++] = curr + 1 + polyCntXpitch; + + buffer->indices[i++] = curr; + buffer->indices[i++] = curr + 1 + polyCntXpitch; + buffer->indices[i++] = curr + 1; + } + + buffer->indices[i++] = level + polyCntX; + buffer->indices[i++] = level + polyCntX - 1; + buffer->indices[i++] = level + polyCntX - 1 + polyCntXpitch; + + buffer->indices[i++] = level + polyCntX; + buffer->indices[i++] = level + polyCntX - 1 + polyCntXpitch; + buffer->indices[i++] = level + polyCntX + polyCntXpitch; + + level += polyCntXpitch; + } + + return buffer; +} + +static SMeshBuffer* createSphere(f32 radius,u32 polyCntX,u32 polyCntY) +{ + u32 i,p1,p2,level; + u32 x,y,polyCntXpitch; + const f32 RECIPROCAL_PI = 1.0f/M_PI; + SMeshBuffer *buffer = new SMeshBuffer(); + + if(polyCntX<2) polyCntX = 2; + if(polyCntY<2) polyCntY = 2; + if(polyCntX*polyCntY>32767) { + if(polyCntX>polyCntY) + polyCntX = 32767/polyCntY-1; + else + polyCntY = 32767/(polyCntX+1); + } + polyCntXpitch = polyCntX+1; + + buffer->cnt_vertices = (polyCntXpitch*polyCntY)+2; + buffer->vertices = (S3DVertex*)rsxMemalign(128,buffer->cnt_vertices*sizeof(S3DVertex)); + + buffer->cnt_indices = (polyCntX*polyCntY)*6; + buffer->indices = (u16*)rsxMemalign(128,buffer->cnt_indices*sizeof(u16)); + + i = 0; + level = 0; + for(p1=0;p1indices[i++] = curr; + buffer->indices[i++] = curr + polyCntXpitch; + buffer->indices[i++] = curr + 1 + polyCntXpitch; + + buffer->indices[i++] = curr; + buffer->indices[i++] = curr + 1 + polyCntXpitch; + buffer->indices[i++] = curr + 1; + } + + buffer->indices[i++] = level + polyCntX; + buffer->indices[i++] = level + polyCntX - 1; + buffer->indices[i++] = level + polyCntX - 1 + polyCntXpitch; + + buffer->indices[i++] = level + polyCntX; + buffer->indices[i++] = level + polyCntX - 1 + polyCntXpitch; + buffer->indices[i++] = level + polyCntX + polyCntXpitch; + + level += polyCntXpitch; + } + + const u32 polyCntSq = polyCntXpitch*polyCntY; + const u32 polyCntSq1 = polyCntSq+1; + const u32 polyCntSqM1 = (polyCntY-1)*polyCntXpitch; + + for(p2=0;p2indices[i++] = polyCntSq; + buffer->indices[i++] = p2; + buffer->indices[i++] = p2+1; + + buffer->indices[i++] = polyCntSq1; + buffer->indices[i++] = polyCntSqM1+p2; + buffer->indices[i++] = polyCntSqM1+p2+1; + } + + buffer->indices[i++] = polyCntSq; + buffer->indices[i++] = polyCntX-1; + buffer->indices[i++] = polyCntX; + + buffer->indices[i++] = polyCntSq1; + buffer->indices[i++] = polyCntSqM1; + buffer->indices[i++] = polyCntSqM1+polyCntX-1; + + f32 axz; + f32 ay = 0; + const f32 angelX = 2*M_PI/polyCntX; + const f32 angelY = M_PI/polyCntY; + + i = 0; + for(y=0;y(radius*cosf(axz)*sinay), static_cast(radius*cosf(ay)), static_cast(radius*sinf(axz)*sinay)); + + Vector3 normal = normalize(pos); + + f32 tu = 0.5F; + if(y==0) { + if(normal.getY()!=-1.0F && normal.getY()!=1.0F) + tu = static_cast(acosf(clamp(normal.getX()/sinay,-1.0f,1.0f))*0.5F*RECIPROCAL_PI); + if(normal.getZ()<0.0F) + tu = 1-tu; + } else + tu = buffer->vertices[i - polyCntXpitch].u; + + buffer->vertices[i] = S3DVertex(pos.getX(),pos.getY(),pos.getZ(),normal.getX(),normal.getY(),normal.getZ(),tu,static_cast(ay*RECIPROCAL_PI)); + axz += angelX; + i++; + } + buffer->vertices[i] = S3DVertex(buffer->vertices[i-polyCntX]); + buffer->vertices[i].u = 1.0F; + i++; + } + + buffer->vertices[i++] = S3DVertex(0.0F,radius,0.0F,0.0F,1.0F,0.0F,0.5F,0.0F); + buffer->vertices[i] = S3DVertex(0.0F,-radius,0.0F,0.0F,-1.0F,0.0F,0.5F,1.0F); + + return buffer; +} + +static void setTexture() +{ + u32 width = 128; + u32 height = 128; + u32 pitch = (width*4); + gcmTexture texture; + + if(!texture_buffer) return; + + rsxInvalidateTextureCache(gGcmContext,GCM_INVALIDATE_TEXTURE); + + texture.format = (GCM_TEXTURE_FORMAT_A8R8G8B8 | GCM_TEXTURE_FORMAT_LIN); + texture.mipmap = 1; + texture.dimension = GCM_TEXTURE_DIMS_2D; + texture.cubemap = GCM_FALSE; + texture.remap = ((GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_B_SHIFT) | + (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_G_SHIFT) | + (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_R_SHIFT) | + (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_A_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_B << GCM_TEXTURE_REMAP_COLOR_B_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_G << GCM_TEXTURE_REMAP_COLOR_G_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_R << GCM_TEXTURE_REMAP_COLOR_R_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_A << GCM_TEXTURE_REMAP_COLOR_A_SHIFT)); + texture.width = width; + texture.height = height; + texture.depth = 1; + texture.location = GCM_LOCATION_RSX; + texture.pitch = pitch; + texture.offset = texture_offset; + rsxLoadTexture(gGcmContext,textureUnit_id,&texture); + rsxTextureControl(gGcmContext,textureUnit_id,GCM_TRUE,0<<8,12<<8,GCM_TEXTURE_MAX_ANISO_1); + rsxTextureFilter(gGcmContext,textureUnit_id,0,GCM_TEXTURE_LINEAR,GCM_TEXTURE_LINEAR,GCM_TEXTURE_CONVOLUTION_QUINCUNX); + rsxTextureWrapMode(gGcmContext,textureUnit_id,GCM_TEXTURE_CLAMP_TO_EDGE,GCM_TEXTURE_CLAMP_TO_EDGE,GCM_TEXTURE_CLAMP_TO_EDGE,0,GCM_TEXTURE_ZFUNC_LESS,0); +} + +static void setDrawEnv() +{ + rsxSetColorMask(gGcmContext,GCM_COLOR_MASK_B | + GCM_COLOR_MASK_G | + GCM_COLOR_MASK_R | + GCM_COLOR_MASK_A); + + rsxSetColorMaskMRT(gGcmContext,0); + + u16 x,y,w,h; + f32 min, max; + f32 scale[4],offset[4]; + + x = 0; + y = 0; + w = display_width; + h = display_height; + min = 0.0f; + max = 1.0f; + scale[0] = w*0.5f; + scale[1] = h*-0.5f; + scale[2] = (max - min)*0.5f; + scale[3] = 0.0f; + offset[0] = x + w*0.5f; + offset[1] = y + h*0.5f; + offset[2] = (max + min)*0.5f; + offset[3] = 0.0f; + + rsxSetViewport(gGcmContext,x, y, w, h, min, max, scale, offset); + rsxSetScissor(gGcmContext,x,y,w,h); + + rsxSetDepthTestEnable(gGcmContext,GCM_TRUE); + rsxSetDepthFunc(gGcmContext,GCM_LESS); + rsxSetShadeModel(gGcmContext,GCM_SHADE_MODEL_SMOOTH); + rsxSetDepthWriteEnable(gGcmContext,1); + rsxSetFrontFace(gGcmContext,GCM_FRONTFACE_CCW); +} + +void init_shader() +{ + u32 fpsize = 0; + u32 vpsize = 0; + + rsxVertexProgramGetUCode(vpo, &vp_ucode, &vpsize); + printf("vpsize: %d\n", vpsize); + + projMatrix_id = rsxVertexProgramGetConstIndex(vpo,"projMatrix"); + modelViewMatrix_id = rsxVertexProgramGetConstIndex(vpo,"modelViewMatrix"); + vertexPosition_id = rsxVertexProgramGetAttribIndex(vpo,"vertexPosition"); + vertexNormal_id = rsxVertexProgramGetAttribIndex(vpo,"vertexNormal"); + vertexTexcoord_id = rsxVertexProgramGetAttribIndex(vpo,"vertexTexcoord"); + + rsxFragmentProgramGetUCode(fpo, &fp_ucode, &fpsize); + printf("fpsize: %d\n", fpsize); + + fp_buffer = (u32*)rsxMemalign(64,fpsize); + memcpy(fp_buffer,fp_ucode,fpsize); + rsxAddressToOffset(fp_buffer,&fp_offset); + + textureUnit_id = rsxFragmentProgramGetAttribIndex(fpo,"texture"); + eyePosition_id = rsxFragmentProgramGetConstIndex(fpo,"eyePosition"); + globalAmbient_id = rsxFragmentProgramGetConstIndex(fpo,"globalAmbient"); + lightPosition_id = rsxFragmentProgramGetConstIndex(fpo,"lightPosition"); + lightColor_id = rsxFragmentProgramGetConstIndex(fpo,"lightColor"); + shininess_id = rsxFragmentProgramGetConstIndex(fpo,"shininess"); + Ks_id = rsxFragmentProgramGetConstIndex(fpo,"Ks"); + Kd_id = rsxFragmentProgramGetConstIndex(fpo,"Kd"); +} + +void drawFrame() +{ + u32 i,offset,color = 0; + Matrix4 rotX,rotY; + Vector4 objEyePos,objLightPos; + Matrix4 viewMatrix,modelMatrix,modelMatrixIT,modelViewMatrix; + Point3 lightPos = Point3(250.0f,150.0f,150.0f); + f32 globalAmbientColor[3] = {0.1f,0.1f,0.1f}; + f32 lightColor[3] = {0.95f,0.95f,0.95f}; + f32 materialColorDiffuse[3] = {0.5f,0.0f,0.0f}; + f32 materialColorSpecular[3] = {0.7f,0.6f,0.6f}; + f32 shininess = 17.8954f; + static f32 rot = 0.0f; + SMeshBuffer *mesh = NULL; + + setTexture(); + setDrawEnv(); + + rsxSetClearColor(gGcmContext,color); + rsxSetClearDepthStencil(gGcmContext,0xffffff00); + rsxClearSurface(gGcmContext,GCM_CLEAR_R | + GCM_CLEAR_G | + GCM_CLEAR_B | + GCM_CLEAR_A | + GCM_CLEAR_S | + GCM_CLEAR_Z); + + rsxSetZControl(gGcmContext,0,1,1); + + for(i=0;i<8;i++) + rsxSetViewportClip(gGcmContext,i,display_width,display_height); + + viewMatrix = Matrix4::lookAt(eye_pos,eye_dir,up_vec); + + mesh = sphere; + rotX = Matrix4::rotationX(DEGTORAD(30.0f)); + rotY = Matrix4::rotationY(DEGTORAD(rot)); + modelMatrix = rotX*rotY; + modelMatrixIT = inverse(modelMatrix); + modelViewMatrix = transpose(viewMatrix*modelMatrix); + + objEyePos = modelMatrixIT*eye_pos; + objLightPos = modelMatrixIT*lightPos; + + rsxAddressToOffset(&mesh->vertices[0].pos,&offset); + rsxBindVertexArrayAttrib(gGcmContext,vertexPosition_id,0,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxAddressToOffset(&mesh->vertices[0].nrm,&offset); + rsxBindVertexArrayAttrib(gGcmContext,vertexNormal_id,0,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxAddressToOffset(&mesh->vertices[0].u,&offset); + rsxBindVertexArrayAttrib(gGcmContext,vertexTexcoord_id,0,offset,sizeof(S3DVertex),2,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxLoadVertexProgram(gGcmContext,vpo,vp_ucode); + rsxSetVertexProgramParameterByIndex(gGcmContext,vpo,projMatrix_id,(float*)&P); + rsxSetVertexProgramParameterByIndex(gGcmContext,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); + + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); + + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); + + rsxLoadFragmentProgramLocation(gGcmContext,fpo,fp_offset,GCM_LOCATION_RSX); + + rsxSetUserClipPlaneControl(gGcmContext,GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE); + + rsxAddressToOffset(&mesh->indices[0],&offset); + rsxDrawIndexArray(gGcmContext,GCM_TYPE_TRIANGLES,offset,mesh->cnt_indices,GCM_INDEX_TYPE_16B,GCM_LOCATION_RSX); + + mesh = donut; + rotX = Matrix4::rotationX(DEGTORAD(rot)); + rotY = Matrix4::rotationY(DEGTORAD(30.0f)); + modelMatrix = rotX*rotY; + modelMatrix.setTranslation(Vector3(3.0f,5.0f,-8.0f)); + + modelMatrixIT = inverse(modelMatrix); + modelViewMatrix = transpose(viewMatrix*modelMatrix); + + objEyePos = modelMatrixIT*eye_pos; + objLightPos = modelMatrixIT*lightPos; + + rsxAddressToOffset(&mesh->vertices[0].pos,&offset); + rsxBindVertexArrayAttrib(gGcmContext,vertexPosition_id,0,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxAddressToOffset(&mesh->vertices[0].nrm,&offset); + rsxBindVertexArrayAttrib(gGcmContext,vertexNormal_id,0,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxAddressToOffset(&mesh->vertices[0].u,&offset); + rsxBindVertexArrayAttrib(gGcmContext,vertexTexcoord_id,0,offset,sizeof(S3DVertex),2,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxLoadVertexProgram(gGcmContext,vpo,vp_ucode); + rsxSetVertexProgramParameterByIndex(gGcmContext,vpo,projMatrix_id,(float*)&P); + rsxSetVertexProgramParameterByIndex(gGcmContext,vpo,modelViewMatrix_id,(float*)&modelViewMatrix); + + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,eyePosition_id,(float*)&objEyePos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,globalAmbient_id,globalAmbientColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,lightPosition_id,(float*)&objLightPos,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,lightColor_id,lightColor,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,shininess_id,&shininess,fp_offset,GCM_LOCATION_RSX); + + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,Kd_id,materialColorDiffuse,fp_offset,GCM_LOCATION_RSX); + rsxSetFragmentProgramParameterByIndex(gGcmContext,fpo,Ks_id,materialColorSpecular,fp_offset,GCM_LOCATION_RSX); + + rsxLoadFragmentProgramLocation(gGcmContext,fpo,fp_offset,GCM_LOCATION_RSX); + + rsxSetUserClipPlaneControl(gGcmContext,GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE); + + rsxAddressToOffset(&mesh->indices[0],&offset); + rsxDrawIndexArray(gGcmContext,GCM_TYPE_TRIANGLES,offset,mesh->cnt_indices,GCM_INDEX_TYPE_16B,GCM_LOCATION_RSX); + + rot += 4.0f; + if(rot>=360.0f) rot = 0.0f; +} + +int main(int argc,const char *argv[]) +{ + padInfo padinfo; + padData paddata; + + printf("rsxtest_flip started...\n"); + + ioPadInit(7); + initScreen(); + + atexit(program_exit_callback); + sysUtilRegisterCallback(0,sysutil_exit_callback,NULL); + + init_shader(); + init_texture(); + + sphere = createSphere(3.0f,32,32); + donut = createDonut(3.0f,1.5f,32,32); + cube = createCube(5.0f); + + P = transpose(Matrix4::perspective(DEGTORAD(45.0f),aspect_ratio,1.0f,3000.0f)); + + setTexture(); + setDrawEnv(); + + running = 1; + while(running) { + sysUtilCheckCallback(); + + ioPadGetInfo(&padinfo); + for(int i=0; i < MAX_PADS; i++){ + if(padinfo.status[i]){ + ioPadGetData(i, &paddata); + + if(paddata.BTN_CROSS) + goto done; + } + } + + drawFrame(); + flip(); + } + +done: + printf("rsxtest_flip done...\n"); + finish(); + return 0; +} diff --git a/samples/graphics/rsxtest_tile/source/rsxutil.cpp b/samples/graphics/rsxtest_tile/source/rsxutil.cpp new file mode 100644 index 00000000..27afec2b --- /dev/null +++ b/samples/graphics/rsxtest_tile/source/rsxutil.cpp @@ -0,0 +1,322 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "rsxutil.h" + +videoResolution vResolution; +gcmContextData *gGcmContext = NULL; + +u32 curr_fb = 0; + +u32 display_width; +u32 display_height; + +u32 depth_pitch; +u32 depth_offset; +u32 *depth_buffer; + +u32 color_pitch; +u32 color_offset[FRAME_BUFFER_COUNT]; +u32 *color_buffer[FRAME_BUFFER_COUNT]; + +f32 aspect_ratio; + +u32 fbOnDisplay = 0; +u32 fbFlipped = 0; +bool fbOnFlip = false; +sys_event_queue_t flipEventQueue; +sys_event_port_t flipEventPort; + +gcmSurface surface; + +static u32 sLabelVal = 1; + +static u32 sResolutionIds[] = { + VIDEO_RESOLUTION_960x1080, + VIDEO_RESOLUTION_720, + VIDEO_RESOLUTION_480, + VIDEO_RESOLUTION_576 +}; +static size_t RESOLUTION_ID_COUNT = sizeof(sResolutionIds)/sizeof(u32); + +extern "C" { +static void flipHandler(const u32 head) +{ + (void)head; + u32 v = fbFlipped; + + for (u32 i = fbOnDisplay; i != v; i=(i + 1)%FRAME_BUFFER_COUNT) { + *((vu32*) gcmGetLabelAddress(GCM_BUFFER_STATUS_INDEX + i)) = BUFFER_IDLE; + } + fbOnDisplay = v; + fbOnFlip = false; + + sysEventPortSend(flipEventPort, 0, 0, 0); +} + +static void vblankHandler(const u32 head) +{ + (void)head; + u32 data; + u32 bufferToFlip; + u32 indexToFlip; + + data = *((vu32*) gcmGetLabelAddress(GCM_PREPARED_BUFFER_INDEX)); + bufferToFlip = (data >> 8); + indexToFlip = (data & 0x07); + + if (!fbOnFlip) { + if (bufferToFlip != fbOnDisplay) { + s32 ret = gcmSetFlipImmediate(indexToFlip); + if (ret != 0) { + printf("flip immediate failed\n"); + return; + } + fbFlipped = bufferToFlip; + fbOnFlip = true; + } + } +} +} + +static void syncPPUGPU() +{ + vu32 *label = (vu32*) gcmGetLabelAddress(GCM_PREPARED_BUFFER_INDEX); + while(((curr_fb + FRAME_BUFFER_COUNT - ((*label)>>8))%FRAME_BUFFER_COUNT) > MAX_BUFFER_QUEUE_SIZE) { + sys_event_t event; + + sysEventQueueReceive(flipEventQueue, &event, 0); + sysEventQueueDrain(flipEventQueue); + } +} + +static void waitRSXFinish() +{ + rsxSetWriteBackendLabel(gGcmContext,GCM_WAIT_LABEL_INDEX,sLabelVal); + + rsxFlushBuffer(gGcmContext); + + while(*(vu32*)gcmGetLabelAddress(GCM_WAIT_LABEL_INDEX)!=sLabelVal) + usleep(30); + + ++sLabelVal; +} + +static void waitRSXIdle() +{ + rsxSetWriteBackendLabel(gGcmContext,GCM_WAIT_LABEL_INDEX,sLabelVal); + rsxSetWaitLabel(gGcmContext,GCM_WAIT_LABEL_INDEX,sLabelVal); + + ++sLabelVal; + + waitRSXFinish(); +} + +void initVideoConfiguration() +{ + s32 rval = 0; + s32 resId = 0; + + for (size_t i=0;i < RESOLUTION_ID_COUNT;i++) { + rval = videoGetResolutionAvailability(VIDEO_PRIMARY, sResolutionIds[i], VIDEO_ASPECT_AUTO, 0); + if (rval != 1) continue; + + resId = sResolutionIds[i]; + rval = videoGetResolution(resId, &vResolution); + if(!rval) break; + } + + if(rval) { + printf("Error: videoGetResolutionAvailability failed. No usable resolution.\n"); + exit(1); + } + + videoConfiguration config = { + (u8)resId, + VIDEO_BUFFER_FORMAT_XRGB, + VIDEO_ASPECT_AUTO, + {0,0,0,0,0,0,0,0,0}, + gcmGetTiledPitchSize(vResolution.width*4) + }; + + rval = videoConfigure(VIDEO_PRIMARY, &config, NULL, 0); + if(rval) { + printf("Error: videoConfigure failed.\n"); + exit(1); + } + + videoState state; + + rval = videoGetState(VIDEO_PRIMARY, 0, &state); + switch(state.displayMode.aspect) { + case VIDEO_ASPECT_4_3: + aspect_ratio = 4.0f/3.0f; + break; + case VIDEO_ASPECT_16_9: + aspect_ratio = 16.0f/9.0f; + break; + default: + printf("unknown aspect ratio %x\n", state.displayMode.aspect); + aspect_ratio = 16.0f/9.0f; + break; + } + + display_height = vResolution.height; + display_width = vResolution.width; +} + +void initFlipEvent() +{ + sys_event_queue_attr_t queueAttr = { SYS_EVENT_QUEUE_PRIO, SYS_EVENT_QUEUE_PPU, "\0" }; + + sysEventQueueCreate(&flipEventQueue, &queueAttr, SYS_EVENT_QUEUE_KEY_LOCAL, 32); + sysEventPortCreate(&flipEventPort, SYS_EVENT_PORT_LOCAL, SYS_EVENT_PORT_NO_NAME); + sysEventPortConnectLocal(flipEventPort, flipEventQueue); + + gcmSetFlipHandler(flipHandler); + gcmSetVBlankHandler(vblankHandler); +} + +void initRenderTarget() +{ + memset(&surface, 0, sizeof(gcmSurface)); + + surface.colorFormat = GCM_SURFACE_X8R8G8B8; + surface.colorTarget = GCM_SURFACE_TARGET_0; + surface.colorLocation[0] = GCM_LOCATION_RSX; + surface.colorOffset[0] = color_offset[curr_fb]; + surface.colorPitch[0] = color_pitch; + + for(u32 i=1; i< GCM_MAX_MRT_COUNT;i++) { + surface.colorLocation[i] = GCM_LOCATION_RSX; + surface.colorOffset[i] = color_offset[curr_fb]; + surface.colorPitch[i] = 64; + } + + surface.depthFormat = GCM_SURFACE_ZETA_Z24S8; + surface.depthLocation = GCM_LOCATION_RSX; + surface.depthOffset = depth_offset; + surface.depthPitch = depth_pitch; + + surface.type = GCM_SURFACE_TYPE_LINEAR; + surface.antiAlias = GCM_SURFACE_CENTER_1; + + surface.width = display_width; + surface.height = display_height; + surface.x = 0; + surface.y = 0; +} + +void setRenderTarget(u32 index) +{ + surface.colorOffset[0] = color_offset[index]; + rsxSetSurface(gGcmContext,&surface); +} + +void initScreen() +{ + u32 zs_depth = 4; + u32 color_depth = 4; + u32 bufferSize = rsxAlign(HOST_ADDR_ALIGNMENT, (DEFAULT_CB_SIZE + HOST_SIZE)); + + gcmInitDefaultFifoMode(GCM_DEFAULT_FIFO_MODE_CONDITIONAL); + + void *hostAddr = memalign(HOST_ADDR_ALIGNMENT, bufferSize); + rsxInit(&gGcmContext, DEFAULT_CB_SIZE, bufferSize, hostAddr); + + initVideoConfiguration(); + + color_pitch = gcmGetTiledPitchSize(display_width*color_depth); + depth_pitch = gcmGetTiledPitchSize(display_width*zs_depth); + + u32 bufferHeight = rsxAlign(GCM_ZCULL_ALIGN_HEIGHT, display_height); + u32 colorBufferSize = bufferHeight*color_pitch; + u32 depthBufferSize = bufferHeight*depth_pitch; + + waitRSXIdle(); + + gcmSetFlipMode(GCM_FLIP_HSYNC); + + void *buffer; + u32 regionSize, offset = 0; + u32 gcmBufferOffset, tileIndex = 0, tagMemOffset = 0; + for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { + color_offset[i] = offset; + offset += colorBufferSize; + offset = rsxAlign(8*color_pitch, offset); + } + regionSize = offset + colorBufferSize; + regionSize = rsxAlign(32*color_pitch, regionSize); + regionSize = rsxAlign(GCM_TILE_ALIGN_OFFSET, regionSize); + + buffer = rsxMemalign(GCM_TILE_ALIGN_SIZE, regionSize); + rsxAddressToOffset(buffer, &gcmBufferOffset); + gcmSetTile(tileIndex++, GCM_LOCATION_RSX, gcmBufferOffset, regionSize, color_pitch, GCM_COMPMODE_DISABLED, tagMemOffset, 0); + tagMemOffset += rsxAlign(GCM_TILE_ALIGN_OFFSET, regionSize); + + for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { + color_offset[i] += gcmBufferOffset; + gcmSetDisplayBuffer(i, color_offset[i], color_pitch, display_width, display_height); + printf("fb[%d]: %p (%08x) [%dx%d] %d\n", i, (void*)((intptr_t)buffer + (color_offset[i] - gcmBufferOffset)), color_offset[i], display_width, display_height, color_pitch); + } + + regionSize = depthBufferSize; + regionSize = rsxAlign(32*depth_pitch, regionSize); + regionSize = rsxAlign(GCM_TILE_ALIGN_OFFSET, regionSize); + buffer = rsxMemalign(GCM_TILE_ALIGN_SIZE, regionSize); + + rsxAddressToOffset(buffer, &depth_offset); + gcmSetTile(tileIndex++, GCM_LOCATION_RSX, depth_offset, regionSize, depth_pitch, GCM_COMPMODE_Z32_SEPSTENCIL_REGULAR, tagMemOffset, 1); + tagMemOffset += rsxAlign(GCM_TILE_ALIGN_OFFSET, regionSize); + + for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { + *((vu32*) gcmGetLabelAddress(GCM_BUFFER_STATUS_INDEX + i)) = BUFFER_IDLE; + } + *((vu32*) gcmGetLabelAddress(GCM_PREPARED_BUFFER_INDEX)) = (fbOnDisplay << 8); + *((vu32*) gcmGetLabelAddress(GCM_BUFFER_STATUS_INDEX + fbOnDisplay)) = BUFFER_BUSY; + + curr_fb = (fbOnDisplay + 1)%FRAME_BUFFER_COUNT; + + initFlipEvent(); + initRenderTarget(); + + rsxSetWriteCommandLabel(gGcmContext, GCM_BUFFER_STATUS_INDEX + curr_fb, BUFFER_BUSY); +} + +void flip() +{ + s32 qid = gcmSetPrepareFlip(gGcmContext, curr_fb); + while (qid < 0) { + usleep(100); + qid = gcmSetPrepareFlip(gGcmContext, curr_fb); + } + + rsxSetWriteBackendLabel(gGcmContext, GCM_PREPARED_BUFFER_INDEX, ((curr_fb << 8) | qid)); + rsxFlushBuffer(gGcmContext); + + syncPPUGPU(); + + curr_fb = (curr_fb + 1)%FRAME_BUFFER_COUNT; + + rsxSetWaitLabel(gGcmContext, GCM_BUFFER_STATUS_INDEX + curr_fb, BUFFER_IDLE); + rsxSetWriteCommandLabel(gGcmContext, GCM_BUFFER_STATUS_INDEX + curr_fb, BUFFER_BUSY); + + setRenderTarget(curr_fb); +} + +void finish() +{ + rsxFinish(gGcmContext,1); + + u32 data = *((vu32*) gcmGetLabelAddress(GCM_PREPARED_BUFFER_INDEX)); + u32 lastBuffer = (data >> 8); + while (lastBuffer != fbOnDisplay) + usleep(100); +} From 288f8c7e7a5017fb51e042b135ad674b85376cdc Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Fri, 17 Jul 2020 08:15:32 +0200 Subject: [PATCH 24/56] - stay c99 compliant --- ppu/librsx/rsx_internal.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ppu/librsx/rsx_internal.c b/ppu/librsx/rsx_internal.c index 49c8cb09..d562190b 100644 --- a/ppu/librsx/rsx_internal.c +++ b/ppu/librsx/rsx_internal.c @@ -14,8 +14,9 @@ rsxProgramAttrib* __rsxGetAttrs(const rsxProgram *prg) rsxProgramAttrib* __rsxGetAttr(const rsxProgram *prg, const char *name) { + u32 i; rsxProgramAttrib *attribs = __rsxGetAttrs(prg); - for(u32 i=0;inum_attr;i++) { + for(i=0;inum_attr;i++) { char *namePtr; if(!attribs[i].name_off) continue; @@ -34,8 +35,9 @@ rsxProgramConst* __rsxGetConsts(const rsxProgram *prg) s32 __rsxGetConstIndex(const rsxProgram *prg, const char *name) { + u32 i; rsxProgramConst *consts = __rsxGetConsts(prg); - for(u32 i=0;inum_const;i++) { + for(i=0;inum_const;i++) { char *namePtr; if(!consts[i].name_off) continue; From 37da6d650ef98ebc5eb4427db2f843464a130ebd Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Fri, 17 Jul 2020 09:22:45 +0200 Subject: [PATCH 25/56] - improve samples/fix aspect ratio --- samples/graphics/rsxtest/include/rsxutil.h | 17 ++- samples/graphics/rsxtest/source/main.cpp | 53 ++++----- samples/graphics/rsxtest/source/rsxutil.cpp | 107 ++++++++++++----- .../graphics/rsxtest_spu/include/rsxutil.h | 17 ++- samples/graphics/rsxtest_spu/source/main.cpp | 61 +++++----- .../graphics/rsxtest_spu/source/rsxutil.cpp | 109 ++++++++++++------ 6 files changed, 244 insertions(+), 120 deletions(-) diff --git a/samples/graphics/rsxtest/include/rsxutil.h b/samples/graphics/rsxtest/include/rsxutil.h index f2fe1d1c..a9f2a683 100644 --- a/samples/graphics/rsxtest/include/rsxutil.h +++ b/samples/graphics/rsxtest/include/rsxutil.h @@ -2,14 +2,29 @@ #define __RSXUTIL_H__ #include +#include #define CB_SIZE 0x100000 #define HOST_SIZE (32*1024*1024) +#define FRAME_BUFFER_COUNT 2 + extern gcmContextData *context; + +extern u32 curr_fb; + extern u32 display_width; extern u32 display_height; -extern u32 curr_fb; + +extern u32 depth_pitch; +extern u32 depth_offset; +extern u32 *depth_buffer; + +extern u32 color_pitch; +extern u32 color_offset[FRAME_BUFFER_COUNT]; +extern u32 *color_buffer[FRAME_BUFFER_COUNT]; + +extern f32 aspect_ratio; void setRenderTarget(u32 index); void init_screen(void *host_addr,u32 size); diff --git a/samples/graphics/rsxtest/source/main.cpp b/samples/graphics/rsxtest/source/main.cpp index beba78a2..91578c64 100644 --- a/samples/graphics/rsxtest/source/main.cpp +++ b/samples/graphics/rsxtest/source/main.cpp @@ -48,8 +48,6 @@ Point3 eye_pos = Point3(0.0f,0.0f,20.0f); Point3 eye_dir = Point3(0.0f,0.0f,0.0f); Vector3 up_vec = Vector3(0.0f,1.0f,0.0f); -f32 aspect_ratio = 4.0f/3.0f; - void *vp_ucode = NULL; rsxVertexProgram *vpo = (rsxVertexProgram*)diffuse_specular_shader_vpo; @@ -63,6 +61,27 @@ static SMeshBuffer *cube = NULL; SYS_PROCESS_PARAM(1001, 0x100000); +extern "C" { +static void program_exit_callback() +{ + gcmSetWaitFlip(context); + rsxFinish(context,1); +} + +static void sysutil_exit_callback(u64 status,u64 param,void *usrdata) +{ + switch(status) { + case SYSUTIL_EXIT_GAME: + running = 0; + break; + case SYSUTIL_DRAW_BEGIN: + case SYSUTIL_DRAW_END: + break; + default: + break; + } +} +} static void init_texture() { u32 i; @@ -408,26 +427,6 @@ void init_shader() Kd_id = rsxFragmentProgramGetConstIndex(fpo,"Kd"); } -void program_exit_callback() -{ - gcmSetWaitFlip(context); - rsxFinish(context,1); -} - -void sysutil_exit_callback(u64 status,u64 param,void *usrdata) -{ - switch(status) { - case SYSUTIL_EXIT_GAME: - running = 0; - break; - case SYSUTIL_DRAW_BEGIN: - case SYSUTIL_DRAW_END: - break; - default: - break; - } -} - void drawFrame() { u32 i,offset,color = 0; @@ -591,9 +590,8 @@ int main(int argc,const char *argv[]) if(padinfo.status[i]){ ioPadGetData(i, &paddata); - if(paddata.BTN_CROSS){ - return 0; - } + if(paddata.BTN_CROSS) + goto done; } } @@ -602,5 +600,8 @@ int main(int argc,const char *argv[]) flip(); } - return 0; +done: + printf("rsxtest done...\n"); + program_exit_callback(); + return 0; } diff --git a/samples/graphics/rsxtest/source/rsxutil.cpp b/samples/graphics/rsxtest/source/rsxutil.cpp index 01e6a667..883c8fc2 100644 --- a/samples/graphics/rsxtest/source/rsxutil.cpp +++ b/samples/graphics/rsxtest/source/rsxutil.cpp @@ -5,14 +5,13 @@ #include #include -#include #include #include "rsxutil.h" #define GCM_LABEL_INDEX 255 -videoResolution res; +videoResolution vResolution; gcmContextData *context = NULL; u32 curr_fb = 0; @@ -26,8 +25,18 @@ u32 depth_offset; u32 *depth_buffer; u32 color_pitch; -u32 color_offset[2]; -u32 *color_buffer[2]; +u32 color_offset[FRAME_BUFFER_COUNT]; +u32 *color_buffer[FRAME_BUFFER_COUNT]; + +f32 aspect_ratio; + +static u32 sResolutionIds[] = { + VIDEO_RESOLUTION_960x1080, + VIDEO_RESOLUTION_720, + VIDEO_RESOLUTION_480, + VIDEO_RESOLUTION_576 +}; +static size_t RESOLUTION_ID_COUNT = sizeof(sResolutionIds)/sizeof(u32); static u32 sLabelVal = 1; @@ -53,6 +62,59 @@ static void waitRSXIdle() waitFinish(); } +void initVideoConfiguration() +{ + s32 rval = 0; + s32 resId = 0; + + for (size_t i=0;i < RESOLUTION_ID_COUNT;i++) { + rval = videoGetResolutionAvailability(VIDEO_PRIMARY, sResolutionIds[i], VIDEO_ASPECT_AUTO, 0); + if (rval != 1) continue; + + resId = sResolutionIds[i]; + rval = videoGetResolution(resId, &vResolution); + if(!rval) break; + } + + if(rval) { + printf("Error: videoGetResolutionAvailability failed. No usable resolution.\n"); + exit(1); + } + + videoConfiguration config = { + (u8)resId, + VIDEO_BUFFER_FORMAT_XRGB, + VIDEO_ASPECT_AUTO, + {0,0,0,0,0,0,0,0,0}, + (u32)vResolution.width*4 + }; + + rval = videoConfigure(VIDEO_PRIMARY, &config, NULL, 0); + if(rval) { + printf("Error: videoConfigure failed.\n"); + exit(1); + } + + videoState state; + + rval = videoGetState(VIDEO_PRIMARY, 0, &state); + switch(state.displayMode.aspect) { + case VIDEO_ASPECT_4_3: + aspect_ratio = 4.0f/3.0f; + break; + case VIDEO_ASPECT_16_9: + aspect_ratio = 16.0f/9.0f; + break; + default: + printf("unknown aspect ratio %x\n", state.displayMode.aspect); + aspect_ratio = 16.0f/9.0f; + break; + } + + display_height = vResolution.height; + display_width = vResolution.width; +} + void setRenderTarget(u32 index) { gcmSurface sf; @@ -91,41 +153,26 @@ void setRenderTarget(u32 index) void init_screen(void *host_addr,u32 size) { - rsxInit(&context,CB_SIZE,size,host_addr); + u32 zs_depth = 4; + u32 color_depth = 4; - videoState state; - videoGetState(0,0,&state); - - videoGetResolution(state.displayMode.resolution,&res); - - videoConfiguration vconfig; - memset(&vconfig,0,sizeof(videoConfiguration)); + rsxInit(&context,CB_SIZE,size,host_addr); - vconfig.resolution = state.displayMode.resolution; - vconfig.format = VIDEO_BUFFER_FORMAT_XRGB; - vconfig.pitch = res.width*sizeof(u32); + initVideoConfiguration(); waitRSXIdle(); - videoConfigure(0,&vconfig,NULL,0); - videoGetState(0,0,&state); - gcmSetFlipMode(GCM_FLIP_VSYNC); - display_width = res.width; - display_height = res.height; - - color_pitch = display_width*sizeof(u32); - color_buffer[0] = (u32*)rsxMemalign(64,(display_height*color_pitch)); - color_buffer[1] = (u32*)rsxMemalign(64,(display_height*color_pitch)); - - rsxAddressToOffset(color_buffer[0],&color_offset[0]); - rsxAddressToOffset(color_buffer[1],&color_offset[1]); + color_pitch = display_width*color_depth; + depth_pitch = display_width*zs_depth; - gcmSetDisplayBuffer(0,color_offset[0],color_pitch,display_width,display_height); - gcmSetDisplayBuffer(1,color_offset[1],color_pitch,display_width,display_height); + for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { + color_buffer[i] = (u32*)rsxMemalign(64,(display_height*color_pitch)); + rsxAddressToOffset(color_buffer[i],&color_offset[i]); + gcmSetDisplayBuffer(i,color_offset[i],color_pitch,display_width,display_height); + } - depth_pitch = display_width*sizeof(u32); depth_buffer = (u32*)rsxMemalign(64,(display_height*depth_pitch)*2); rsxAddressToOffset(depth_buffer,&depth_offset); } diff --git a/samples/graphics/rsxtest_spu/include/rsxutil.h b/samples/graphics/rsxtest_spu/include/rsxutil.h index f2fe1d1c..a9f2a683 100644 --- a/samples/graphics/rsxtest_spu/include/rsxutil.h +++ b/samples/graphics/rsxtest_spu/include/rsxutil.h @@ -2,14 +2,29 @@ #define __RSXUTIL_H__ #include +#include #define CB_SIZE 0x100000 #define HOST_SIZE (32*1024*1024) +#define FRAME_BUFFER_COUNT 2 + extern gcmContextData *context; + +extern u32 curr_fb; + extern u32 display_width; extern u32 display_height; -extern u32 curr_fb; + +extern u32 depth_pitch; +extern u32 depth_offset; +extern u32 *depth_buffer; + +extern u32 color_pitch; +extern u32 color_offset[FRAME_BUFFER_COUNT]; +extern u32 *color_buffer[FRAME_BUFFER_COUNT]; + +extern f32 aspect_ratio; void setRenderTarget(u32 index); void init_screen(void *host_addr,u32 size); diff --git a/samples/graphics/rsxtest_spu/source/main.cpp b/samples/graphics/rsxtest_spu/source/main.cpp index 324f8d64..6dd7e7f0 100644 --- a/samples/graphics/rsxtest_spu/source/main.cpp +++ b/samples/graphics/rsxtest_spu/source/main.cpp @@ -50,8 +50,6 @@ Point3 eye_pos = Point3(0.0f,0.0f,20.0f); Point3 eye_dir = Point3(0.0f,0.0f,0.0f); Vector3 up_vec = Vector3(0.0f,1.0f,0.0f); -f32 aspect_ratio = 4.0f/3.0f; - void *vp_ucode = NULL; rsxVertexProgram *vpo = (rsxVertexProgram*)diffuse_specular_shader_vpo; @@ -63,6 +61,29 @@ static SMeshBuffer *sphere = NULL; static SMeshBuffer *donut = NULL; static SMeshBuffer *cube = NULL; +extern "C" { +static void program_exit_callback() +{ + gcmSetWaitFlip(context); + rsxFinish(context,1); + + shutdown_spu(); +} + +static void sysutil_exit_callback(u64 status,u64 param,void *usrdata) +{ + switch(status) { + case SYSUTIL_EXIT_GAME: + running = 0; + break; + case SYSUTIL_DRAW_BEGIN: + case SYSUTIL_DRAW_END: + break; + default: + break; + } +} +} static void init_texture() { u32 i; @@ -188,28 +209,6 @@ void init_shader() Kd_id = rsxFragmentProgramGetConstIndex(fpo,"Kd"); } -void program_exit_callback() -{ - gcmSetWaitFlip(context); - rsxFinish(context,1); - - shutdown_spu(); -} - -void sysutil_exit_callback(u64 status,u64 param,void *usrdata) -{ - switch(status) { - case SYSUTIL_EXIT_GAME: - running = 0; - break; - case SYSUTIL_DRAW_BEGIN: - case SYSUTIL_DRAW_END: - break; - default: - break; - } -} - void initialize() { void *host_addr = memalign(1024*1024,HOST_SIZE); @@ -349,6 +348,8 @@ int main(int argc,const char *argv[]) padData paddata; rsxProgramConst *consts = rsxFragmentProgramGetConsts(fpo); + printf("rsxtest_spu started...\n"); + initialize(); ioPadInit(7); @@ -381,9 +382,8 @@ int main(int argc,const char *argv[]) if(padinfo.status[i]){ ioPadGetData(i, &paddata); - if(paddata.BTN_CROSS){ - return 0; - } + if(paddata.BTN_CROSS) + goto done; } } @@ -391,6 +391,9 @@ int main(int argc,const char *argv[]) drawFrame(); flip(); } - - return 0; + +done: + printf("rsxtest_spu done...\n"); + program_exit_callback(); + return 0; } diff --git a/samples/graphics/rsxtest_spu/source/rsxutil.cpp b/samples/graphics/rsxtest_spu/source/rsxutil.cpp index 87692ab7..89448912 100644 --- a/samples/graphics/rsxtest_spu/source/rsxutil.cpp +++ b/samples/graphics/rsxtest_spu/source/rsxutil.cpp @@ -5,14 +5,13 @@ #include #include -#include #include #include "rsxutil.h" #define GCM_LABEL_INDEX 255 -videoResolution res; +videoResolution vResolution; gcmContextData *context = NULL; u32 curr_fb = 0; @@ -26,8 +25,18 @@ u32 depth_offset; u32 *depth_buffer; u32 color_pitch; -u32 color_offset[2]; -u32 *color_buffer[2]; +u32 color_offset[FRAME_BUFFER_COUNT]; +u32 *color_buffer[FRAME_BUFFER_COUNT]; + +f32 aspect_ratio; + +static u32 sResolutionIds[] = { + VIDEO_RESOLUTION_960x1080, + VIDEO_RESOLUTION_720, + VIDEO_RESOLUTION_480, + VIDEO_RESOLUTION_576 +}; +static size_t RESOLUTION_ID_COUNT = sizeof(sResolutionIds)/sizeof(u32); static u32 sLabelVal = 1; @@ -55,6 +64,59 @@ static void waitRSXIdle() waitFinish(); } +void initVideoConfiguration() +{ + s32 rval = 0; + s32 resId = 0; + + for (size_t i=0;i < RESOLUTION_ID_COUNT;i++) { + rval = videoGetResolutionAvailability(VIDEO_PRIMARY, sResolutionIds[i], VIDEO_ASPECT_AUTO, 0); + if (rval != 1) continue; + + resId = sResolutionIds[i]; + rval = videoGetResolution(resId, &vResolution); + if(!rval) break; + } + + if(rval) { + printf("Error: videoGetResolutionAvailability failed. No usable resolution.\n"); + exit(1); + } + + videoConfiguration config = { + (u8)resId, + VIDEO_BUFFER_FORMAT_XRGB, + VIDEO_ASPECT_AUTO, + {0,0,0,0,0,0,0,0,0}, + (u32)vResolution.width*4 + }; + + rval = videoConfigure(VIDEO_PRIMARY, &config, NULL, 0); + if(rval) { + printf("Error: videoConfigure failed.\n"); + exit(1); + } + + videoState state; + + rval = videoGetState(VIDEO_PRIMARY, 0, &state); + switch(state.displayMode.aspect) { + case VIDEO_ASPECT_4_3: + aspect_ratio = 4.0f/3.0f; + break; + case VIDEO_ASPECT_16_9: + aspect_ratio = 16.0f/9.0f; + break; + default: + printf("unknown aspect ratio %x\n", state.displayMode.aspect); + aspect_ratio = 16.0f/9.0f; + break; + } + + display_height = vResolution.height; + display_width = vResolution.width; +} + void setRenderTarget(u32 index) { gcmSurface sf; @@ -93,47 +155,28 @@ void setRenderTarget(u32 index) void init_screen(void *host_addr,u32 size) { - printf("initializing screen....\n"); + u32 zs_depth = 4; + u32 color_depth = 4; rsxInit(&context,CB_SIZE,size,host_addr); - videoState state; - videoGetState(0,0,&state); - - videoGetResolution(state.displayMode.resolution,&res); - - videoConfiguration vconfig; - memset(&vconfig,0,sizeof(videoConfiguration)); - - vconfig.resolution = state.displayMode.resolution; - vconfig.format = VIDEO_BUFFER_FORMAT_XRGB; - vconfig.pitch = res.width*sizeof(u32); + initVideoConfiguration(); waitRSXIdle(); - videoConfigure(0,&vconfig,NULL,0); - videoGetState(0,0,&state); - gcmSetFlipMode(GCM_FLIP_VSYNC); - display_width = res.width; - display_height = res.height; + color_pitch = display_width*color_depth; + depth_pitch = display_width*zs_depth; - color_pitch = display_width*sizeof(u32); - color_buffer[0] = (u32*)rsxMemalign(64,(display_height*color_pitch)); - color_buffer[1] = (u32*)rsxMemalign(64,(display_height*color_pitch)); + for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { + color_buffer[i] = (u32*)rsxMemalign(64,(display_height*color_pitch)); + rsxAddressToOffset(color_buffer[i],&color_offset[i]); + gcmSetDisplayBuffer(i,color_offset[i],color_pitch,display_width,display_height); + } - rsxAddressToOffset(color_buffer[0],&color_offset[0]); - rsxAddressToOffset(color_buffer[1],&color_offset[1]); - - gcmSetDisplayBuffer(0,color_offset[0],color_pitch,display_width,display_height); - gcmSetDisplayBuffer(1,color_offset[1],color_pitch,display_width,display_height); - - depth_pitch = display_width*sizeof(u32); depth_buffer = (u32*)rsxMemalign(64,(display_height*depth_pitch)*2); rsxAddressToOffset(depth_buffer,&depth_offset); - - printf("screen initialized....\n"); } void waitflip() From 12c99781d0ced9caef3ffc963f553370c1b06310 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Fri, 17 Jul 2020 11:34:50 +0200 Subject: [PATCH 26/56] - start support FP16 (half float precision) --- tools/cgcomp/include/compilerfp.h | 5 +++++ tools/cgcomp/source/compilerfp.cpp | 28 +++++++++++++++++----------- tools/cgcomp/source/fpparser.cpp | 5 ++--- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/tools/cgcomp/include/compilerfp.h b/tools/cgcomp/include/compilerfp.h index 4aad129a..68c59a32 100644 --- a/tools/cgcomp/include/compilerfp.h +++ b/tools/cgcomp/include/compilerfp.h @@ -1,6 +1,8 @@ #ifndef __COMPILERFP_H__ #define __COMPILERFP_H__ +#define NUM_HW_REGS 48 + class CParser; struct fragment_program_exec @@ -57,6 +59,7 @@ class CCompilerFP int grow_insns(int count); + void reserveReg(const struct nvfx_reg& reg); struct nvfx_reg temp(); void release_temps(); @@ -105,6 +108,8 @@ class CCompilerFP int m_rTemps; int m_rTempsDiscard; + u8 m_HWRegs[NUM_HW_REGS]; + std::list m_lParameters; std::list m_lConstData; std::stack m_repStack; diff --git a/tools/cgcomp/source/compilerfp.cpp b/tools/cgcomp/source/compilerfp.cpp index cf4184c4..64c78fcb 100644 --- a/tools/cgcomp/source/compilerfp.cpp +++ b/tools/cgcomp/source/compilerfp.cpp @@ -33,10 +33,10 @@ CCompilerFP::~CCompilerFP() void CCompilerFP::Prepare(CParser *pParser) { - int high_temp = -1; int i,j,nCount = pParser->GetInstructionCount(); struct nvfx_insn *insns = pParser->GetInstructions(); + memset(m_HWRegs, 0, NUM_HW_REGS); m_lParameters = pParser->GetParameters(); for(i=0;ireg.index>high_temp) high_temp = src->reg.index; + reserveReg(src->reg); break; } } switch(insn->dst.type) { case NVFXSR_TEMP: - if((s32)insn->dst.index>high_temp) high_temp = insn->dst.index; + reserveReg(insn->dst); break; } } - - if(++high_temp) { - for(i=0;iindex; switch(dst->type) { case NVFXSR_TEMP: - if(m_nNumRegs<(s32)(index + 1)) - m_nNumRegs = (index + 1); - break; + { + u32 hwReg = dst->is_fp16 ? (index >> 1) : index; + if(m_nNumRegs<(s32)(hwReg + 1)) + m_nNumRegs = (hwReg + 1); + } + break; + case NVFXSR_OUTPUT: if(dst->index==0 && !dst->is_fp16) m_nFPControl |= 0x40; @@ -676,6 +675,13 @@ struct nvfx_reg CCompilerFP::imm(f32 x, f32 y, f32 z, f32 w) return nvfx_reg(NVFXSR_IMM, idx); } +void CCompilerFP::reserveReg(const struct nvfx_reg& reg) +{ + u32 index = reg.is_fp16 ? (reg.index >> 1) : reg.index; + m_HWRegs[index] |= reg.is_fp16 ? (1 << (reg.index&0x01)) : 0x03; + m_rTemps |= (1 << index); +} + struct nvfx_reg CCompilerFP::temp() { s32 idx = __builtin_ctzll(~m_rTemps); diff --git a/tools/cgcomp/source/fpparser.cpp b/tools/cgcomp/source/fpparser.cpp index 75296a0d..9657172f 100644 --- a/tools/cgcomp/source/fpparser.cpp +++ b/tools/cgcomp/source/fpparser.cpp @@ -551,10 +551,9 @@ void CFPParser::ParseVectorSrc(const char *token,struct nvfx_src *reg) } if(token[0]=='R' || token[0]=='H') { - token = ParseTempReg(token,&idx); - reg->reg.type = NVFXSR_TEMP; - reg->reg.index = idx; + reg->reg.is_fp16 = (token[0]=='H'); + token = ParseTempReg(token,®->reg.index); } else if(token[0]=='f') { if(token[1]=='[') { token = ParseInputReg(&token[2],&idx); From d76e05864573e7f1b0aff54c8663617b667ce6cf Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Fri, 17 Jul 2020 15:24:00 +0200 Subject: [PATCH 27/56] - use correct macro --- samples/graphics/rsxtest_tile/source/rsxutil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/graphics/rsxtest_tile/source/rsxutil.cpp b/samples/graphics/rsxtest_tile/source/rsxutil.cpp index 27afec2b..92fb40b6 100644 --- a/samples/graphics/rsxtest_tile/source/rsxutil.cpp +++ b/samples/graphics/rsxtest_tile/source/rsxutil.cpp @@ -273,7 +273,7 @@ void initScreen() buffer = rsxMemalign(GCM_TILE_ALIGN_SIZE, regionSize); rsxAddressToOffset(buffer, &depth_offset); - gcmSetTile(tileIndex++, GCM_LOCATION_RSX, depth_offset, regionSize, depth_pitch, GCM_COMPMODE_Z32_SEPSTENCIL_REGULAR, tagMemOffset, 1); + gcmSetTile(tileIndex++, GCM_LOCATION_RSX, depth_offset, regionSize, depth_pitch, GCM_COMPMODE_Z32_SEPSTENCIL, tagMemOffset, 1); tagMemOffset += rsxAlign(GCM_TILE_ALIGN_OFFSET, regionSize); for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { From ace7f47d838a5d176b3d2f098aa8a80d6b6c674d Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Fri, 17 Jul 2020 15:25:07 +0200 Subject: [PATCH 28/56] - use correct macro --- samples/graphics/rsxtest_tile/source/rsxutil.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/graphics/rsxtest_tile/source/rsxutil.cpp b/samples/graphics/rsxtest_tile/source/rsxutil.cpp index 92fb40b6..a8108b9e 100644 --- a/samples/graphics/rsxtest_tile/source/rsxutil.cpp +++ b/samples/graphics/rsxtest_tile/source/rsxutil.cpp @@ -236,7 +236,7 @@ void initScreen() color_pitch = gcmGetTiledPitchSize(display_width*color_depth); depth_pitch = gcmGetTiledPitchSize(display_width*zs_depth); - u32 bufferHeight = rsxAlign(GCM_ZCULL_ALIGN_HEIGHT, display_height); + u32 bufferHeight = rsxAlign(GCM_TILE_LOCAL_ALIGN_HEIGHT, display_height); u32 colorBufferSize = bufferHeight*color_pitch; u32 depthBufferSize = bufferHeight*depth_pitch; @@ -273,7 +273,7 @@ void initScreen() buffer = rsxMemalign(GCM_TILE_ALIGN_SIZE, regionSize); rsxAddressToOffset(buffer, &depth_offset); - gcmSetTile(tileIndex++, GCM_LOCATION_RSX, depth_offset, regionSize, depth_pitch, GCM_COMPMODE_Z32_SEPSTENCIL, tagMemOffset, 1); + gcmSetTile(tileIndex++, GCM_LOCATION_RSX, depth_offset, regionSize, depth_pitch, GCM_COMPMODE_Z32_SEPSTENCIL_REGULAR, tagMemOffset, 1); tagMemOffset += rsxAlign(GCM_TILE_ALIGN_OFFSET, regionSize); for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { From 80cbc2a781fca769b77b0f88322fc052724acc7d Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Fri, 17 Jul 2020 22:22:10 +0200 Subject: [PATCH 29/56] - do not clobber a volatile register here (might be passed as argument) --- ppu/sprx/common/exports.S | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ppu/sprx/common/exports.S b/ppu/sprx/common/exports.S index bb492b8f..644cb838 100644 --- a/ppu/sprx/common/exports.S +++ b/ppu/sprx/common/exports.S @@ -11,19 +11,21 @@ LIBRARY_SYMBOL: .section ".sceStub.text","ax"; \ .globl __##name; \ __##name: \ + std r31,-8(r1); \ mflr r0; \ std r0,16(r1); \ stdu r1,-128(r1); \ std r2,112(r1); \ - lis r12,name##_stub@ha; \ - lwz r12,name##_stub@l(r12); \ - lwz r0,0(r12); \ - lwz r2,4(r12); \ + lis r31,name##_stub@ha; \ + lwz r31,name##_stub@l(r31); \ + lwz r0,0(r31); \ + lwz r2,4(r31); \ mtctr r0; \ bctrl; \ ld r2,112(r1); \ addi r1,r1,128; \ ld r0,16(r1); \ + ld r31,-8(r1); \ mtlr r0; \ blr; \ .align 3; \ From ee9156b4f5acd72846e7f8d675576780fe92827e Mon Sep 17 00:00:00 2001 From: Marcus Comstedt Date: Fri, 17 Jul 2020 23:39:35 +0200 Subject: [PATCH 30/56] Remove symbol dot from asm call to sbrk_deinit The symbol with the dot is no longer available after the latest toolchain update, but binutils is able to sort things out if using the symbol without the dot. --- ppu/librt/sbrk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ppu/librt/sbrk.c b/ppu/librt/sbrk.c index db63dd78..cc953039 100644 --- a/ppu/librt/sbrk.c +++ b/ppu/librt/sbrk.c @@ -81,7 +81,7 @@ static void sbrk_init() appear after crtbegin.o in the link order, this will place the call to sbrk_deinit() after the call to __do_global_dtors_aux(), which is what we want. */ -asm ("\t.section\t.fini\n\tbl .sbrk_deinit\n\tnop\n\t.previous"); +asm ("\t.section\t.fini\n\tbl sbrk_deinit\n\tnop\n\t.previous"); static void sbrk_deinit() __attribute__((used)); static void sbrk_deinit() { From 08d3524401bfa936ba3f6f5021807c3a3c485997 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Sat, 18 Jul 2020 08:14:42 +0200 Subject: [PATCH 31/56] - revert 42952d76 --- ppu/sprx/common/exports.S | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ppu/sprx/common/exports.S b/ppu/sprx/common/exports.S index 644cb838..bb492b8f 100644 --- a/ppu/sprx/common/exports.S +++ b/ppu/sprx/common/exports.S @@ -11,21 +11,19 @@ LIBRARY_SYMBOL: .section ".sceStub.text","ax"; \ .globl __##name; \ __##name: \ - std r31,-8(r1); \ mflr r0; \ std r0,16(r1); \ stdu r1,-128(r1); \ std r2,112(r1); \ - lis r31,name##_stub@ha; \ - lwz r31,name##_stub@l(r31); \ - lwz r0,0(r31); \ - lwz r2,4(r31); \ + lis r12,name##_stub@ha; \ + lwz r12,name##_stub@l(r12); \ + lwz r0,0(r12); \ + lwz r2,4(r12); \ mtctr r0; \ bctrl; \ ld r2,112(r1); \ addi r1,r1,128; \ ld r0,16(r1); \ - ld r31,-8(r1); \ mtlr r0; \ blr; \ .align 3; \ From 1ffb2780913acf62173d803d48f0fd17d4f98373 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Sun, 19 Jul 2020 12:56:41 +0200 Subject: [PATCH 32/56] - fix issue with functions having more than 8 arguments (as per spec only the first 8 arguments a passed by register, the rest, up to va_len, is passed on stack) --- ppu/sprx/common/exports.S | 49 +++++++++++++++++++++++++++++++++-- ppu/sprx/common/libexport.c | 6 +++++ ppu/sprx/libgcm_sys/exports.h | 4 +-- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/ppu/sprx/common/exports.S b/ppu/sprx/common/exports.S index bb492b8f..fccad6c5 100644 --- a/ppu/sprx/common/exports.S +++ b/ppu/sprx/common/exports.S @@ -1,6 +1,10 @@ #include "config.h" #include +#define GP_ARG_MIN_REG 3 +#define GP_ARG_MAX_REG 10 +#define GP_ARG_NUM_REG (GP_ARG_MAX_REG - GP_ARG_MIN_REG + 1) + .align 2 .section ".rodata.sceFNID","a" .globl LIBRARY_SYMBOL @@ -13,16 +17,16 @@ LIBRARY_SYMBOL: __##name: \ mflr r0; \ std r0,16(r1); \ + std r2,40(r1); \ stdu r1,-128(r1); \ - std r2,112(r1); \ lis r12,name##_stub@ha; \ lwz r12,name##_stub@l(r12); \ lwz r0,0(r12); \ lwz r2,4(r12); \ mtctr r0; \ bctrl; \ - ld r2,112(r1); \ addi r1,r1,128; \ + ld r2,40(r1); \ ld r0,16(r1); \ mtlr r0; \ blr; \ @@ -32,4 +36,45 @@ __##name: \ name: \ .quad __##name,.TOC.@tocbase,0 +#define EXPORT_VA(name,fnid,argc) \ + .align 2; \ + .section ".sceStub.text","ax"; \ + .globl __##name; \ +__##name: \ + mflr r0; \ + std r0,16(r1); \ + std r2,40(r1); \ + std r30,-16(r1); \ + std r31,-8(r1); \ + stdu r1,-160(r1); \ + li r12,(argc - GP_ARG_NUM_REG); \ + li r31,276; \ + li r30,112; \ + mtctr r12; \ +1: \ + lwzx r12,r31,r1; \ + clrldi r11,r12,32; \ + stdx r11,r30,r1; \ + addi r31,r31,8; \ + addi r30,r30,8; \ + bdnz 1b; \ + lis r12,name##_stub@ha; \ + lwz r12,name##_stub@l(r12); \ + lwz r0,0(r12); \ + lwz r2,4(r12); \ + mtctr r0; \ + bctrl; \ + addi r1,r1,160; \ + ld r30,-16(r1); \ + ld r31,-8(r1); \ + ld r2,40(r1); \ + ld r0,16(r1); \ + mtlr r0; \ + blr; \ + .align 3; \ + .section ".opd","aw"; \ + .globl name; \ +name: \ + .quad __##name,.TOC.@tocbase,0 + #include "exports.h" diff --git a/ppu/sprx/common/libexport.c b/ppu/sprx/common/libexport.c index 613d5b10..4781b544 100644 --- a/ppu/sprx/common/libexport.c +++ b/ppu/sprx/common/libexport.c @@ -42,4 +42,10 @@ static prx_header header __attribute__((section(".lib.stub"))) = { const void* name##_stub __attribute__((section(".data.sceFStub." LIBRARY_NAME))) = &__##name; \ const uint32_t name##_fnid __attribute__((section(".rodata.sceFNID"))) = fnid +// duplicate the macro impl, as i don't know whether macro substitution works properly, with strigify, or not. +#define EXPORT_VA(name, fnid, argc) \ + extern void* __##name; \ + const void* name##_stub __attribute__((section(".data.sceFStub." LIBRARY_NAME))) = &__##name; \ + const uint32_t name##_fnid __attribute__((section(".rodata.sceFNID"))) = fnid + #include "exports.h" diff --git a/ppu/sprx/libgcm_sys/exports.h b/ppu/sprx/libgcm_sys/exports.h index 48dc6c82..100c69b8 100644 --- a/ppu/sprx/libgcm_sys/exports.h +++ b/ppu/sprx/libgcm_sys/exports.h @@ -27,8 +27,8 @@ EXPORT(gcmGetReport, 0x99d397ac); EXPORT(gcmSetTile, 0xd0b1d189); EXPORT(gcmBindTile, 0x4524cccd); EXPORT(gcmUnbindTile, 0xd9b7653e); -EXPORT(gcmSetZcull, 0xd34a420d); -EXPORT(gcmBindZcull, 0x9dc04436); +EXPORT_VA(gcmSetZcull, 0xd34a420d, 12); +EXPORT_VA(gcmBindZcull, 0x9dc04436, 12); EXPORT(gcmUnbindZcull, 0xa75640e8); EXPORT(gcmGetTimeStamp, 0x5a41c10f); EXPORT(gcmSetVBlankFrequency, 0xffe0160e); From 14eb0022f389913cb585fac6d097f8df1f936079 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Sun, 19 Jul 2020 12:58:28 +0200 Subject: [PATCH 33/56] - simplify tile example - enable zcull binding --- .../graphics/rsxtest_tile/include/rsxutil.h | 4 +- .../graphics/rsxtest_tile/source/rsxutil.cpp | 58 +++++++------------ 2 files changed, 24 insertions(+), 38 deletions(-) diff --git a/samples/graphics/rsxtest_tile/include/rsxutil.h b/samples/graphics/rsxtest_tile/include/rsxutil.h index fc5d132a..1b520d6b 100644 --- a/samples/graphics/rsxtest_tile/include/rsxutil.h +++ b/samples/graphics/rsxtest_tile/include/rsxutil.h @@ -27,11 +27,11 @@ extern u32 display_height; extern u32 depth_pitch; extern u32 depth_offset; -extern u32 *depth_buffer; +extern void *depth_buffer; extern u32 color_pitch; extern u32 color_offset[FRAME_BUFFER_COUNT]; -extern u32 *color_buffer[FRAME_BUFFER_COUNT]; +extern void *color_buffer[FRAME_BUFFER_COUNT]; extern f32 aspect_ratio; diff --git a/samples/graphics/rsxtest_tile/source/rsxutil.cpp b/samples/graphics/rsxtest_tile/source/rsxutil.cpp index a8108b9e..7dcb1507 100644 --- a/samples/graphics/rsxtest_tile/source/rsxutil.cpp +++ b/samples/graphics/rsxtest_tile/source/rsxutil.cpp @@ -20,11 +20,11 @@ u32 display_height; u32 depth_pitch; u32 depth_offset; -u32 *depth_buffer; +void *depth_buffer; u32 color_pitch; u32 color_offset[FRAME_BUFFER_COUNT]; -u32 *color_buffer[FRAME_BUFFER_COUNT]; +void *color_buffer[FRAME_BUFFER_COUNT]; f32 aspect_ratio; @@ -233,48 +233,34 @@ void initScreen() initVideoConfiguration(); + waitRSXIdle(); + + gcmSetFlipMode(GCM_FLIP_HSYNC); + color_pitch = gcmGetTiledPitchSize(display_width*color_depth); depth_pitch = gcmGetTiledPitchSize(display_width*zs_depth); + u32 tileIndex = 0; u32 bufferHeight = rsxAlign(GCM_TILE_LOCAL_ALIGN_HEIGHT, display_height); u32 colorBufferSize = bufferHeight*color_pitch; u32 depthBufferSize = bufferHeight*depth_pitch; - - waitRSXIdle(); - - gcmSetFlipMode(GCM_FLIP_HSYNC); - - void *buffer; - u32 regionSize, offset = 0; - u32 gcmBufferOffset, tileIndex = 0, tagMemOffset = 0; - for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { - color_offset[i] = offset; - offset += colorBufferSize; - offset = rsxAlign(8*color_pitch, offset); + for (u32 i=0; i < FRAME_BUFFER_COUNT;i++, tileIndex++) { + bufferSize = rsxAlign(GCM_TILE_ALIGN_OFFSET, colorBufferSize); + color_buffer[i] = rsxMemalign(GCM_TILE_ALIGN_SIZE, bufferSize); + rsxAddressToOffset(color_buffer[i], &color_offset[i]); + gcmSetDisplayBuffer(i, color_offset[i], color_pitch, display_width, display_height); + gcmSetTileInfo(tileIndex, GCM_LOCATION_RSX, color_offset[i], bufferSize, color_pitch, GCM_COMPMODE_DISABLED, 0, 0); + gcmBindTile(tileIndex); + printf("fb[%d]: %p (%08x) [%dx%d] %d\n", i, color_buffer[i], color_offset[i], display_width, display_height, color_pitch); } - regionSize = offset + colorBufferSize; - regionSize = rsxAlign(32*color_pitch, regionSize); - regionSize = rsxAlign(GCM_TILE_ALIGN_OFFSET, regionSize); - - buffer = rsxMemalign(GCM_TILE_ALIGN_SIZE, regionSize); - rsxAddressToOffset(buffer, &gcmBufferOffset); - gcmSetTile(tileIndex++, GCM_LOCATION_RSX, gcmBufferOffset, regionSize, color_pitch, GCM_COMPMODE_DISABLED, tagMemOffset, 0); - tagMemOffset += rsxAlign(GCM_TILE_ALIGN_OFFSET, regionSize); - for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { - color_offset[i] += gcmBufferOffset; - gcmSetDisplayBuffer(i, color_offset[i], color_pitch, display_width, display_height); - printf("fb[%d]: %p (%08x) [%dx%d] %d\n", i, (void*)((intptr_t)buffer + (color_offset[i] - gcmBufferOffset)), color_offset[i], display_width, display_height, color_pitch); - } - - regionSize = depthBufferSize; - regionSize = rsxAlign(32*depth_pitch, regionSize); - regionSize = rsxAlign(GCM_TILE_ALIGN_OFFSET, regionSize); - buffer = rsxMemalign(GCM_TILE_ALIGN_SIZE, regionSize); - - rsxAddressToOffset(buffer, &depth_offset); - gcmSetTile(tileIndex++, GCM_LOCATION_RSX, depth_offset, regionSize, depth_pitch, GCM_COMPMODE_Z32_SEPSTENCIL_REGULAR, tagMemOffset, 1); - tagMemOffset += rsxAlign(GCM_TILE_ALIGN_OFFSET, regionSize); + bufferSize = rsxAlign(GCM_TILE_ALIGN_OFFSET, depthBufferSize); + depth_buffer = rsxMemalign(GCM_TILE_ALIGN_SIZE, bufferSize); + rsxAddressToOffset(depth_buffer, &depth_offset); + gcmSetTileInfo(tileIndex, GCM_LOCATION_RSX, depth_offset, bufferSize, depth_pitch, GCM_COMPMODE_Z32_SEPSTENCIL, 0, 2); + gcmBindTile(tileIndex); + + gcmSetZcull(0, depth_offset, rsxAlign(64, display_width), rsxAlign(64, display_height), 0, GCM_ZCULL_Z24S8, GCM_SURFACE_CENTER_1, GCM_ZCULL_LESS, GCM_ZCULL_LONES, GCM_SCULL_SFUNC_LESS, 1, 0xff); for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { *((vu32*) gcmGetLabelAddress(GCM_BUFFER_STATUS_INDEX + i)) = BUFFER_IDLE; From eaf6a3be0b3fa64a2f3fbfae8dd919cfb13a6d6b Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Sun, 19 Jul 2020 14:07:19 +0200 Subject: [PATCH 34/56] - add methods --- ppu/include/sysutil/video.h | 6 +++--- ppu/sprx/libsysutil/sysutil_wrapper.c | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ppu/include/sysutil/video.h b/ppu/include/sysutil/video.h index 36534d90..6b22cab7 100644 --- a/ppu/include/sysutil/video.h +++ b/ppu/include/sysutil/video.h @@ -211,6 +211,7 @@ typedef struct _videoDeviceInfo videoKSVList ksvList; } videoDeviceInfo; +typedef s32 (*videoCallback)(u32 slot, u32 videoOut, u32 deviceIndex, u32 event, videoDeviceInfo *info, void *userData); /*! \brief Get video state @@ -251,9 +252,8 @@ s32 videoGetResolutionAvailability(u32 videoOut, u32 resolutionId, u32 aspect, u s32 videoDebugSetMonitorType(u32 videoOut, u32 monitorType); s32 videoGetConvertCursorColorInfo(u8 *rgbOutputRange); -/* TODO: typedef int (*videoCallback)(u32 slot, u32 videoOut, u32 deviceIndex, u32 event, videoDeviceInfo *info, void *userData ); */ -/* TODO: s32 videoRegisterCallback(u32 slot, videoCallback function, void *userData); */ -/* TODO: s32 videoUnregisterCallback(u32 slot); */ +s32 videoRegisterCallback(u32 slot, videoCallback cbVideo, void *userData); +s32 videoUnregisterCallback(u32 slot); #ifdef __cplusplus } diff --git a/ppu/sprx/libsysutil/sysutil_wrapper.c b/ppu/sprx/libsysutil/sysutil_wrapper.c index 55e37d06..bdc78c48 100644 --- a/ppu/sprx/libsysutil/sysutil_wrapper.c +++ b/ppu/sprx/libsysutil/sysutil_wrapper.c @@ -7,6 +7,7 @@ #include #include #include +#include /* sysUtil functions */ extern s32 sysUtilRegisterCallbackEx(s32 slot,opd32 *opd,void *usrdata); @@ -53,6 +54,9 @@ extern s32 sysGameDiscRegisterDiscChangeCallbackEx(sysGameDiscEjectCallback cbEj /* Disc utility support */ extern s32 sysDiscRegisterDiscChangeCallbackEx(opd32 *cbEject,opd32 *cbInsert); +/* video system function */ +extern s32 videoRegisterCallbackEx(u32 slot, opd32 *cbVideo, void *userData); + /* sysUtil wrapper functions */ s32 sysUtilRegisterCallback(s32 slot,sysutilCallback cb,void *usrdata) { @@ -229,3 +233,8 @@ s32 sysDiscRegisterDiscChangeCallback(sysDiscEjectCallback cbEject,sysDiscInsert return sysDiscRegisterDiscChangeCallbackEx((opd32*)__get_opd32(cbEject),(opd32*)__get_opd32(cbInsert)); } +/* video system functions */ +s32 videoRegisterCallback(u32 slot, videoCallback cbVideo, void *userData) +{ + return videoRegisterCallbackEx(slot, (opd32*)__get_opd32(cbVideo), userData); +} From 0e320ab80afc7f4250f27206d81d1a502e65f6a3 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Mon, 20 Jul 2020 12:36:47 +0200 Subject: [PATCH 35/56] - prepare macro to be able to handle argc <= 8 (preparation for later macro collapsing) --- ppu/sprx/common/exports.S | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ppu/sprx/common/exports.S b/ppu/sprx/common/exports.S index fccad6c5..0f5fdd8d 100644 --- a/ppu/sprx/common/exports.S +++ b/ppu/sprx/common/exports.S @@ -48,16 +48,19 @@ __##name: \ std r31,-8(r1); \ stdu r1,-160(r1); \ li r12,(argc - GP_ARG_NUM_REG); \ + extsh. r12,r12; \ + ble 1f; \ li r31,276; \ li r30,112; \ mtctr r12; \ -1: \ +2: \ lwzx r12,r31,r1; \ clrldi r11,r12,32; \ stdx r11,r30,r1; \ addi r31,r31,8; \ addi r30,r30,8; \ - bdnz 1b; \ + bdnz 2b; \ +1: \ lis r12,name##_stub@ha; \ lwz r12,name##_stub@l(r12); \ lwz r0,0(r12); \ From cf74d816333441cd686e4960aee955967a3c3958 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Mon, 20 Jul 2020 12:41:12 +0200 Subject: [PATCH 36/56] - no need to use an additional register here. --- ppu/sprx/common/exports.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ppu/sprx/common/exports.S b/ppu/sprx/common/exports.S index 0f5fdd8d..efa42fa7 100644 --- a/ppu/sprx/common/exports.S +++ b/ppu/sprx/common/exports.S @@ -55,8 +55,8 @@ __##name: \ mtctr r12; \ 2: \ lwzx r12,r31,r1; \ - clrldi r11,r12,32; \ - stdx r11,r30,r1; \ + clrldi r12,r12,32; \ + stdx r12,r30,r1; \ addi r31,r31,8; \ addi r30,r30,8; \ bdnz 2b; \ From 1ec4a178c1922ac10f95d31ef586ad58570fbb20 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Wed, 22 Jul 2020 19:03:42 +0200 Subject: [PATCH 37/56] - correct atomic.h - pass NULL if NULL (do not deref) --- ppu/include/sys/atomic.h | 24 ++++++++++----------- ppu/sprx/libgcm_sys/gcm_wrapper.c | 14 ++++++------- ppu/sprx/libhttp/http_wrapper.c | 30 +++++++++++++-------------- ppu/sprx/libhttp/https_wrapper.c | 6 +++--- ppu/sprx/libresc/resc_wrapper.c | 4 ++-- ppu/sprx/libsysutil/sysutil_wrapper.c | 4 ++-- 6 files changed, 41 insertions(+), 41 deletions(-) diff --git a/ppu/include/sys/atomic.h b/ppu/include/sys/atomic.h index ce7ec789..6ec725fc 100644 --- a/ppu/include/sys/atomic.h +++ b/ppu/include/sys/atomic.h @@ -19,7 +19,7 @@ #include typedef struct { volatile u32 counter; } atomic_t; -typedef struct { u64 counter; } atomic64_t; +typedef struct { volatile u64 counter; } atomic64_t; static inline u32 sysAtomicRead(const atomic_t *v) { @@ -232,7 +232,7 @@ static inline u32 __xchg(volatile void *ptr, u32 x, unsigned int size) * and return the old value of *p. */ static inline u64 -__cmpxchg_u32(volatile unsigned int *p, u64 old, u64 new) +__cmpxchg_u32(volatile unsigned int *p, u64 oldv, u64 newv) { unsigned int prev; @@ -245,14 +245,14 @@ __cmpxchg_u32(volatile unsigned int *p, u64 old, u64 new) "\n\ 2:" : "=&r" (prev), "+m" (*p) - : "r" (p), "r" (old), "r" (new) + : "r" (p), "r" (oldv), "r" (newv) : "cc", "memory"); return prev; } static inline u64 -__cmpxchg_u64(volatile u64 *p, u64 old, u64 new) +__cmpxchg_u64(volatile u64 *p, u64 oldv, u64 newv) { u64 prev; @@ -265,7 +265,7 @@ __cmpxchg_u64(volatile u64 *p, u64 old, u64 new) "\n\ 2:" : "=&r" (prev), "+m" (*p) - : "r" (p), "r" (old), "r" (new) + : "r" (p), "r" (oldv), "r" (newv) : "cc", "memory"); return prev; @@ -276,17 +276,17 @@ __cmpxchg_u64(volatile u64 *p, u64 old, u64 new) extern void __cmpxchg_called_with_bad_pointer(void); static inline u64 -__cmpxchg(volatile void *ptr, u64 old, u64 new, +__cmpxchg(volatile void *ptr, u64 oldv, u64 newv, unsigned int size) { switch (size) { case 4: - return __cmpxchg_u32(ptr, old, new); + return __cmpxchg_u32((volatile u32*)ptr, oldv, newv); case 8: - return __cmpxchg_u64(ptr, old, new); + return __cmpxchg_u64((volatile u64*)ptr, oldv, newv); } __cmpxchg_called_with_bad_pointer(); - return old; + return oldv; } #define cmpxchg(ptr, o, n) \ @@ -298,7 +298,7 @@ __cmpxchg(volatile void *ptr, u64 old, u64 new, }) #define sysAtomicCompareAndSwap(v, o, n) (cmpxchg(&((v)->counter), (o), (n))) -#define sysAtomicSwap(v, new) (xchg(&((v)->counter), new)) +#define sysAtomicSwap(v, new) (xchg(&((v)->counter), newv)) /** * atomic_add_unless - add unless the number is a given value @@ -309,7 +309,7 @@ __cmpxchg(volatile void *ptr, u64 old, u64 new, * Atomically adds @a to @v, so long as it was not @u. * Returns non-zero if @v was not @u, and zero otherwise. */ -static inline u32 sysAtomicAddUnless(atomic_t *v, u32 a, int u) +static inline u32 sysAtomicAddUnless(atomic_t *v, u32 a, u32 u) { u32 t; @@ -530,7 +530,7 @@ static inline u64 sysAtomic64DecIfPositive(atomic64_t *v) } #define sysAtomic64CompareAndSwap(v, o, n) (cmpxchg(&((v)->counter), (o), (n))) -#define sysAtomic64Swap(v, new) (xchg(&((v)->counter), new)) +#define sysAtomic64Swap(v, new) (xchg(&((v)->counter), newv)) /** * atomic64_add_unless - add unless the number is a given value diff --git a/ppu/sprx/libgcm_sys/gcm_wrapper.c b/ppu/sprx/libgcm_sys/gcm_wrapper.c index 2902db71..ef4ed3fc 100644 --- a/ppu/sprx/libgcm_sys/gcm_wrapper.c +++ b/ppu/sprx/libgcm_sys/gcm_wrapper.c @@ -39,35 +39,35 @@ s32 gcmIoOffsetToAddress(u32 offset,void **address) void gcmSetVBlankHandler(void (*handler)(const u32 head)) { - gcmSetVBlankHandlerEx((opd32*)__get_opd32(handler)); + gcmSetVBlankHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); } void gcmSetFlipHandler(void (*handler)(const u32 head)) { - gcmSetFlipHandlerEx((opd32*)__get_opd32(handler)); + gcmSetFlipHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); } void gcmSetGraphicsHandler(void (*handler)(const u32 val)) { - gcmSetGraphicsHandlerEx((opd32*)__get_opd32(handler)); + gcmSetGraphicsHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); } void gcmSetSecondVHandler(void (*handler)(const u32 head)) { - gcmSetSecondVHandlerEx((opd32*)__get_opd32(handler)); + gcmSetSecondVHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); } void gcmSetUserHandler(void (*handler)(const u32 cause)) { - gcmSetUserHandlerEx((opd32*)__get_opd32(handler)); + gcmSetUserHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); } void gcmSetQueueHandler(void (*handler)(const u32 head)) { - gcmSetQueueHandlerEx((opd32*)__get_opd32(handler)); + gcmSetQueueHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); } void gcmSetUserCommand(void (*handler)(const u32 cause)) { - gcmSetUserCommandEx((opd32*)__get_opd32(handler)); + gcmSetUserCommandEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); } diff --git a/ppu/sprx/libhttp/http_wrapper.c b/ppu/sprx/libhttp/http_wrapper.c index 1b81c0ef..7bb9d3f0 100644 --- a/ppu/sprx/libhttp/http_wrapper.c +++ b/ppu/sprx/libhttp/http_wrapper.c @@ -13,36 +13,36 @@ extern s32 httpClientSetCookieRecvCallbackEx(httpClientId cid, opd32 *opd, void s32 httpClientSetAuthenticationCallback(httpClientId cid,httpAuthenticationCallback cb,void *arg) { - printf ( "IN: httpClientSetAuthenticationStateCallback(%p, %p, %p)\n", cid, cb, arg ) ; - printf ( "OUT: httpClientSetAuthenticationStateCallbackEx(%p, %p, %p)\n", cid, (opd32*)__get_opd32(cb), arg ) ; - return httpClientSetAuthenticationCallbackEx(cid,(opd32*)__get_opd32(cb),arg); + printf ( "IN: httpClientSetAuthenticationStateCallback(%d, %p, %p)\n", cid, cb, arg) ; + printf ( "OUT: httpClientSetAuthenticationStateCallbackEx(%d, %p, %p)\n", cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg) ; + return httpClientSetAuthenticationCallbackEx(cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg); } s32 httpClientSetTransactionStateCallback(httpClientId cid,httpTransactionStateCallback cb,void *arg) { - printf ( "IN: httpClientSetTransactionStateCallback(%p, %p, %p)\n", cid, cb, arg ) ; - printf ( "OUT: httpClientSetTransactionStateCallbackEx(%p, %p, %p)\n", cid, (opd32*)__get_opd32(cb), arg ) ; - return httpClientSetTransactionStateCallbackEx(cid,(opd32*)__get_opd32(cb),arg); + printf ( "IN: httpClientSetTransactionStateCallback(%d, %p, %p)\n", cid, cb, arg) ; + printf ( "OUT: httpClientSetTransactionStateCallbackEx(%d, %p, %p)\n", cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg) ; + return httpClientSetTransactionStateCallbackEx(cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg); } s32 httpClientSetRedirectCallback(httpClientId cid,httpRedirectCallback cb,void *arg) { - printf ( "IN: httpClientSetRedirectCallback(%p, %p, %p)\n", cid, cb, arg ) ; - printf ( "OUT: httpClientSetRedirectCallbackEx(%p, %p, %p)\n", cid, (opd32*)__get_opd32(cb), arg ) ; - return httpClientSetRedirectCallbackEx(cid,(opd32*)__get_opd32(cb),arg); + printf ( "IN: httpClientSetRedirectCallback(%d, %p, %p)\n", cid, cb, arg) ; + printf ( "OUT: httpClientSetRedirectCallbackEx(%d, %p, %p)\n", cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg) ; + return httpClientSetRedirectCallbackEx(cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg); } s32 httpClientSetCookieSendCallback(httpClientId cid, httpCookieSendCallback cb, void *arg) { - printf ( "IN: httpClientSetCookieSendCallback(%p, %p, %p)\n", cid, cb, arg ) ; - printf ( "OUT: httpClientSetCookieSendCallbackEx(%p, %p, %p)\n", cid, (opd32*)__get_opd32(cb), arg ) ; - return httpClientSetCookieSendCallbackEx(cid, (opd32*)__get_opd32(cb),arg); + printf ( "IN: httpClientSetCookieSendCallback(%d, %p, %p)\n", cid, cb, arg ) ; + printf ( "OUT: httpClientSetCookieSendCallbackEx(%d, %p, %p)\n", cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg) ; + return httpClientSetCookieSendCallbackEx(cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg); } s32 httpClientSetCookieRecvCallback(httpClientId cid, httpCookieRecvCallback cb, void *arg) { - printf ( "IN: httpClientSetCookieRecvCallback(%p, %p, %p)\n", cid, cb, arg ) ; - printf ( "OUT: httpClientSetCookieRecvCallbackEx(%p, %p, %p)\n", cid, (opd32*)__get_opd32(cb), arg ) ; - return httpClientSetCookieRecvCallbackEx(cid, (opd32*)__get_opd32(cb),arg); + printf ( "IN: httpClientSetCookieRecvCallback(%d, %p, %p)\n", cid, cb, arg ) ; + printf ( "OUT: httpClientSetCookieRecvCallbackEx(%d, %p, %p)\n", cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg) ; + return httpClientSetCookieRecvCallbackEx(cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg); } diff --git a/ppu/sprx/libhttp/https_wrapper.c b/ppu/sprx/libhttp/https_wrapper.c index d073d3c1..bac2709b 100644 --- a/ppu/sprx/libhttp/https_wrapper.c +++ b/ppu/sprx/libhttp/https_wrapper.c @@ -9,8 +9,8 @@ extern s32 httpClientSetSslCallbackEx(httpClientId cid, opd32 *opd, void *userAr s32 httpClientSetSslCallback(httpClientId cid, httpsSslCallback cb, void *arg) { - printf ( "IN: httpClientSetSslCallback(%p, %p, %p)\n", cid, cb, arg ) ; - printf ( "OUT: httpClientSetSslCallbackEx(%p, %p, %p)\n", cid, (opd32*)__get_opd32(cb), arg ) ; - return httpClientSetSslCallbackEx(cid, (opd32*)__get_opd32(cb),arg); + printf ( "IN: httpClientSetSslCallback(%d, %p, %p)\n", cid, cb, arg) ; + printf ( "OUT: httpClientSetSslCallbackEx(%d, %p, %p)\n", cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg) ; + return httpClientSetSslCallbackEx(cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg); } diff --git a/ppu/sprx/libresc/resc_wrapper.c b/ppu/sprx/libresc/resc_wrapper.c index f2b5b811..e9931498 100644 --- a/ppu/sprx/libresc/resc_wrapper.c +++ b/ppu/sprx/libresc/resc_wrapper.c @@ -9,11 +9,11 @@ extern void rescSetFlipHandlerEx(opd32 *opd); void rescSetVBlankHandler(void (*handler)(const u32 head)) { - rescSetVBlankHandlerEx((opd32*)__get_opd32(handler)); + rescSetVBlankHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); } void rescSetFlipHandler(void (*handler)(const u32 head)) { - rescSetFlipHandlerEx((opd32*)__get_opd32(handler)); + rescSetFlipHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); } diff --git a/ppu/sprx/libsysutil/sysutil_wrapper.c b/ppu/sprx/libsysutil/sysutil_wrapper.c index bdc78c48..c72a2960 100644 --- a/ppu/sprx/libsysutil/sysutil_wrapper.c +++ b/ppu/sprx/libsysutil/sysutil_wrapper.c @@ -60,7 +60,7 @@ extern s32 videoRegisterCallbackEx(u32 slot, opd32 *cbVideo, void *userData); /* sysUtil wrapper functions */ s32 sysUtilRegisterCallback(s32 slot,sysutilCallback cb,void *usrdata) { - return sysUtilRegisterCallbackEx(slot,(opd32*)__get_opd32(cb),usrdata); + return sysUtilRegisterCallbackEx(slot, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL),usrdata); } /* msgDialog wraper functions */ @@ -236,5 +236,5 @@ s32 sysDiscRegisterDiscChangeCallback(sysDiscEjectCallback cbEject,sysDiscInsert /* video system functions */ s32 videoRegisterCallback(u32 slot, videoCallback cbVideo, void *userData) { - return videoRegisterCallbackEx(slot, (opd32*)__get_opd32(cbVideo), userData); + return videoRegisterCallbackEx(slot, (cbVideo != NULL ? (opd32*)__get_opd32(cbVideo) : NULL), userData); } From 8c41cd064e6954a73182a7e35a567cf75537aec6 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Wed, 22 Jul 2020 19:13:52 +0200 Subject: [PATCH 38/56] - revert most changes and fix it a the right place --- ppu/include/ppu-asm.h | 2 +- ppu/sprx/libgcm_sys/gcm_wrapper.c | 14 +++++++------- ppu/sprx/libhttp/http_wrapper.c | 20 ++++++++++---------- ppu/sprx/libhttp/https_wrapper.c | 4 ++-- ppu/sprx/libresc/resc_wrapper.c | 4 ++-- ppu/sprx/libsysutil/sysutil_wrapper.c | 4 ++-- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/ppu/include/ppu-asm.h b/ppu/include/ppu-asm.h index f8b41c1e..eb34a1e0 100644 --- a/ppu/include/ppu-asm.h +++ b/ppu/include/ppu-asm.h @@ -5,7 +5,7 @@ #define PPU_ALIGNMENT 8 -#define __get_opd32(opd64) ((unsigned long long)(((intptr_t)(opd64)) + 16)) +#define __get_opd32(opd64) ((unsigned long long)((intptr_t)(opd64) ? (((intptr_t)(opd64)) + 16) : 0L)) #define __get_addr32(addr) (unsigned int)((unsigned long long)(addr)) diff --git a/ppu/sprx/libgcm_sys/gcm_wrapper.c b/ppu/sprx/libgcm_sys/gcm_wrapper.c index ef4ed3fc..2902db71 100644 --- a/ppu/sprx/libgcm_sys/gcm_wrapper.c +++ b/ppu/sprx/libgcm_sys/gcm_wrapper.c @@ -39,35 +39,35 @@ s32 gcmIoOffsetToAddress(u32 offset,void **address) void gcmSetVBlankHandler(void (*handler)(const u32 head)) { - gcmSetVBlankHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); + gcmSetVBlankHandlerEx((opd32*)__get_opd32(handler)); } void gcmSetFlipHandler(void (*handler)(const u32 head)) { - gcmSetFlipHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); + gcmSetFlipHandlerEx((opd32*)__get_opd32(handler)); } void gcmSetGraphicsHandler(void (*handler)(const u32 val)) { - gcmSetGraphicsHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); + gcmSetGraphicsHandlerEx((opd32*)__get_opd32(handler)); } void gcmSetSecondVHandler(void (*handler)(const u32 head)) { - gcmSetSecondVHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); + gcmSetSecondVHandlerEx((opd32*)__get_opd32(handler)); } void gcmSetUserHandler(void (*handler)(const u32 cause)) { - gcmSetUserHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); + gcmSetUserHandlerEx((opd32*)__get_opd32(handler)); } void gcmSetQueueHandler(void (*handler)(const u32 head)) { - gcmSetQueueHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); + gcmSetQueueHandlerEx((opd32*)__get_opd32(handler)); } void gcmSetUserCommand(void (*handler)(const u32 cause)) { - gcmSetUserCommandEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); + gcmSetUserCommandEx((opd32*)__get_opd32(handler)); } diff --git a/ppu/sprx/libhttp/http_wrapper.c b/ppu/sprx/libhttp/http_wrapper.c index 7bb9d3f0..5008a921 100644 --- a/ppu/sprx/libhttp/http_wrapper.c +++ b/ppu/sprx/libhttp/http_wrapper.c @@ -14,35 +14,35 @@ extern s32 httpClientSetCookieRecvCallbackEx(httpClientId cid, opd32 *opd, void s32 httpClientSetAuthenticationCallback(httpClientId cid,httpAuthenticationCallback cb,void *arg) { printf ( "IN: httpClientSetAuthenticationStateCallback(%d, %p, %p)\n", cid, cb, arg) ; - printf ( "OUT: httpClientSetAuthenticationStateCallbackEx(%d, %p, %p)\n", cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg) ; - return httpClientSetAuthenticationCallbackEx(cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg); + printf ( "OUT: httpClientSetAuthenticationStateCallbackEx(%d, %p, %p)\n", cid, (opd32*)__get_opd32(cb), arg) ; + return httpClientSetAuthenticationCallbackEx(cid, (opd32*)__get_opd32(cb), arg); } s32 httpClientSetTransactionStateCallback(httpClientId cid,httpTransactionStateCallback cb,void *arg) { printf ( "IN: httpClientSetTransactionStateCallback(%d, %p, %p)\n", cid, cb, arg) ; - printf ( "OUT: httpClientSetTransactionStateCallbackEx(%d, %p, %p)\n", cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg) ; - return httpClientSetTransactionStateCallbackEx(cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg); + printf ( "OUT: httpClientSetTransactionStateCallbackEx(%d, %p, %p)\n", cid, (opd32*)__get_opd32(cb), arg) ; + return httpClientSetTransactionStateCallbackEx(cid, (opd32*)__get_opd32(cb), arg); } s32 httpClientSetRedirectCallback(httpClientId cid,httpRedirectCallback cb,void *arg) { printf ( "IN: httpClientSetRedirectCallback(%d, %p, %p)\n", cid, cb, arg) ; - printf ( "OUT: httpClientSetRedirectCallbackEx(%d, %p, %p)\n", cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg) ; - return httpClientSetRedirectCallbackEx(cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg); + printf ( "OUT: httpClientSetRedirectCallbackEx(%d, %p, %p)\n", cid, (opd32*)__get_opd32(cb), arg) ; + return httpClientSetRedirectCallbackEx(cid, (opd32*)__get_opd32(cb), arg); } s32 httpClientSetCookieSendCallback(httpClientId cid, httpCookieSendCallback cb, void *arg) { printf ( "IN: httpClientSetCookieSendCallback(%d, %p, %p)\n", cid, cb, arg ) ; - printf ( "OUT: httpClientSetCookieSendCallbackEx(%d, %p, %p)\n", cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg) ; - return httpClientSetCookieSendCallbackEx(cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg); + printf ( "OUT: httpClientSetCookieSendCallbackEx(%d, %p, %p)\n", cid, (opd32*)__get_opd32(cb), arg) ; + return httpClientSetCookieSendCallbackEx(cid, (opd32*)__get_opd32(cb), arg); } s32 httpClientSetCookieRecvCallback(httpClientId cid, httpCookieRecvCallback cb, void *arg) { printf ( "IN: httpClientSetCookieRecvCallback(%d, %p, %p)\n", cid, cb, arg ) ; - printf ( "OUT: httpClientSetCookieRecvCallbackEx(%d, %p, %p)\n", cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg) ; - return httpClientSetCookieRecvCallbackEx(cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg); + printf ( "OUT: httpClientSetCookieRecvCallbackEx(%d, %p, %p)\n", cid, (opd32*)__get_opd32(cb), arg) ; + return httpClientSetCookieRecvCallbackEx(cid, (opd32*)__get_opd32(cb), arg); } diff --git a/ppu/sprx/libhttp/https_wrapper.c b/ppu/sprx/libhttp/https_wrapper.c index bac2709b..761e3812 100644 --- a/ppu/sprx/libhttp/https_wrapper.c +++ b/ppu/sprx/libhttp/https_wrapper.c @@ -10,7 +10,7 @@ extern s32 httpClientSetSslCallbackEx(httpClientId cid, opd32 *opd, void *userAr s32 httpClientSetSslCallback(httpClientId cid, httpsSslCallback cb, void *arg) { printf ( "IN: httpClientSetSslCallback(%d, %p, %p)\n", cid, cb, arg) ; - printf ( "OUT: httpClientSetSslCallbackEx(%d, %p, %p)\n", cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg) ; - return httpClientSetSslCallbackEx(cid, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL), arg); + printf ( "OUT: httpClientSetSslCallbackEx(%d, %p, %p)\n", cid, (opd32*)__get_opd32(cb), arg) ; + return httpClientSetSslCallbackEx(cid, (opd32*)__get_opd32(cb), arg); } diff --git a/ppu/sprx/libresc/resc_wrapper.c b/ppu/sprx/libresc/resc_wrapper.c index e9931498..f2b5b811 100644 --- a/ppu/sprx/libresc/resc_wrapper.c +++ b/ppu/sprx/libresc/resc_wrapper.c @@ -9,11 +9,11 @@ extern void rescSetFlipHandlerEx(opd32 *opd); void rescSetVBlankHandler(void (*handler)(const u32 head)) { - rescSetVBlankHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); + rescSetVBlankHandlerEx((opd32*)__get_opd32(handler)); } void rescSetFlipHandler(void (*handler)(const u32 head)) { - rescSetFlipHandlerEx(handler != NULL ? (opd32*)__get_opd32(handler) : NULL); + rescSetFlipHandlerEx((opd32*)__get_opd32(handler)); } diff --git a/ppu/sprx/libsysutil/sysutil_wrapper.c b/ppu/sprx/libsysutil/sysutil_wrapper.c index c72a2960..2e36a764 100644 --- a/ppu/sprx/libsysutil/sysutil_wrapper.c +++ b/ppu/sprx/libsysutil/sysutil_wrapper.c @@ -60,7 +60,7 @@ extern s32 videoRegisterCallbackEx(u32 slot, opd32 *cbVideo, void *userData); /* sysUtil wrapper functions */ s32 sysUtilRegisterCallback(s32 slot,sysutilCallback cb,void *usrdata) { - return sysUtilRegisterCallbackEx(slot, (cb != NULL ? (opd32*)__get_opd32(cb) : NULL),usrdata); + return sysUtilRegisterCallbackEx(slot, (opd32*)__get_opd32(cb),usrdata); } /* msgDialog wraper functions */ @@ -236,5 +236,5 @@ s32 sysDiscRegisterDiscChangeCallback(sysDiscEjectCallback cbEject,sysDiscInsert /* video system functions */ s32 videoRegisterCallback(u32 slot, videoCallback cbVideo, void *userData) { - return videoRegisterCallbackEx(slot, (cbVideo != NULL ? (opd32*)__get_opd32(cbVideo) : NULL), userData); + return videoRegisterCallbackEx(slot, (opd32*)__get_opd32(cbVideo), userData); } From 2a7b39f719fc06e7857b412a1a2bf13a0343c936 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Wed, 22 Jul 2020 19:25:08 +0200 Subject: [PATCH 39/56] - fix compile warnings --- ppu/sprx/libjpgdec/jpgdec.c | 6 ++++-- ppu/sprx/libpngdec/pngdec.c | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ppu/sprx/libjpgdec/jpgdec.c b/ppu/sprx/libjpgdec/jpgdec.c index 8314ddbb..d44ae3ee 100644 --- a/ppu/sprx/libjpgdec/jpgdec.c +++ b/ppu/sprx/libjpgdec/jpgdec.c @@ -53,13 +53,15 @@ static s32 decodeJPEG(jpgDecSource *src,jpgData *out) jpgDecThreadInParam InThdParam; jpgDecThreadOutParam OutThdParam; jpgDecDataCtrlParam dataCtrlParam; + jpgCbCtrlMalloc fnMalloc = jpg_malloc; + jpgCbCtrlFree fnFree = jpg_free; InThdParam.spu_enable = JPGDEC_SPU_THREAD_DISABLE; InThdParam.ppu_prio = 512; InThdParam.spu_prio = 200; - InThdParam.malloc_func = (jpgCbCtrlMalloc)__get_opd32(jpg_malloc); + InThdParam.malloc_func = (jpgCbCtrlMalloc)__get_opd32(fnMalloc); InThdParam.malloc_arg = NULL; - InThdParam.free_func = (jpgCbCtrlFree)__get_opd32(jpg_free); + InThdParam.free_func = (jpgCbCtrlFree)__get_opd32(fnFree); InThdParam.free_arg = NULL; ret = jpgDecCreate(&mHandle,&InThdParam,&OutThdParam); diff --git a/ppu/sprx/libpngdec/pngdec.c b/ppu/sprx/libpngdec/pngdec.c index 78cad20b..74ec5eb8 100644 --- a/ppu/sprx/libpngdec/pngdec.c +++ b/ppu/sprx/libpngdec/pngdec.c @@ -53,13 +53,15 @@ static s32 decodePNG(pngDecSource *src,pngData *out) pngDecThreadInParam InThdParam; pngDecThreadOutParam OutThdParam; pngDecDataCtrlParam dataCtrlParam; + pngCbCtrlMalloc fnMalloc = png_malloc; + pngCbCtrlFree fnFree = png_free; InThdParam.spu_enable = PNGDEC_SPU_THREAD_DISABLE; InThdParam.ppu_prio = 512; InThdParam.spu_prio = 200; - InThdParam.malloc_func = (pngCbCtrlMalloc)__get_opd32(png_malloc); + InThdParam.malloc_func = (pngCbCtrlMalloc)__get_opd32(fnMalloc); InThdParam.malloc_arg = NULL; - InThdParam.free_func = (pngCbCtrlFree)__get_opd32(png_free); + InThdParam.free_func = (pngCbCtrlFree)__get_opd32(fnFree); InThdParam.free_arg = NULL; ret= pngDecCreate(&mHandle, &InThdParam, &OutThdParam); From 4441cb9a0caed78fd150d9fec7fe834cc1a3c85a Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Sat, 25 Jul 2020 12:22:03 +0200 Subject: [PATCH 40/56] - also set BFC as output --- tools/cgcomp/source/compilervp.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tools/cgcomp/source/compilervp.cpp b/tools/cgcomp/source/compilervp.cpp index d6ae5eec..b8173cbb 100644 --- a/tools/cgcomp/source/compilervp.cpp +++ b/tools/cgcomp/source/compilervp.cpp @@ -348,14 +348,21 @@ void CCompilerVP::emit_dst(struct nvfx_insn *insn,u8 slot) insn->mask = NVFX_VP_MASK_W; m_nOutputMask |= (1 << 11); break; - case NV40_VP_INST_DEST_COL0 : m_nOutputMask |= (1 << 0); break; - case NV40_VP_INST_DEST_COL1 : m_nOutputMask |= (1 << 1); break; - case NV40_VP_INST_DEST_BFC0 : m_nOutputMask |= (1 << 2); break; - case NV40_VP_INST_DEST_BFC1 : m_nOutputMask |= (1 << 3); break; + case NV40_VP_INST_DEST_COL0 : + case NV40_VP_INST_DEST_COL1 : + m_nOutputMask |= (1 << (dst->index - NV40_VP_INST_DEST_COL0)); + m_nOutputMask |= (4 << (dst->index - NV40_VP_INST_DEST_COL0)); + break; + case NV40_VP_INST_DEST_BFC0 : + case NV40_VP_INST_DEST_BFC1 : + m_nOutputMask |= (1 << (dst->index - NV40_VP_INST_DEST_BFC0)); + m_nOutputMask |= (4 << (dst->index - NV40_VP_INST_DEST_BFC0)); + break; case NV40_VP_INST_DEST_FOGC : m_nOutputMask |= (1 << 4); break; case NV40_VP_INST_DEST_PSZ : m_nOutputMask |= (1 << 5); break; default: - if(dst->index>=NV40_VP_INST_DEST_TC(0) && dst->index<=NV40_VP_INST_DEST_TC(7)) m_nOutputMask |= (0x4000 << (dst->index - NV40_VP_INST_DEST_TC0)); + if(dst->index>=NV40_VP_INST_DEST_TC(0) && dst->index<=NV40_VP_INST_DEST_TC(7)) + m_nOutputMask |= (0x4000 << (dst->index - NV40_VP_INST_DEST_TC0)); break; } hw[3] |= (dst->index << NV40_VP_INST_DEST_SHIFT); From 869e2655899376331cec1848f340e3e2e98e1f65 Mon Sep 17 00:00:00 2001 From: crystalct Date: Mon, 27 Jul 2020 09:08:24 +0200 Subject: [PATCH 41/56] Cairo clock example --- samples/graphics/cairo_clock/Makefile | 145 ++++++++++++ samples/graphics/cairo_clock/Readme.md | 3 + samples/graphics/cairo_clock/cairo_clock.png | Bin 0 -> 34167 bytes .../graphics/cairo_clock/include/rsxutil.h | 42 ++++ samples/graphics/cairo_clock/source/main.c | 203 ++++++++++++++++ samples/graphics/cairo_clock/source/rsxutil.c | 224 ++++++++++++++++++ 6 files changed, 617 insertions(+) create mode 100644 samples/graphics/cairo_clock/Makefile create mode 100644 samples/graphics/cairo_clock/Readme.md create mode 100644 samples/graphics/cairo_clock/cairo_clock.png create mode 100644 samples/graphics/cairo_clock/include/rsxutil.h create mode 100644 samples/graphics/cairo_clock/source/main.c create mode 100644 samples/graphics/cairo_clock/source/rsxutil.c diff --git a/samples/graphics/cairo_clock/Makefile b/samples/graphics/cairo_clock/Makefile new file mode 100644 index 00000000..5e2d7756 --- /dev/null +++ b/samples/graphics/cairo_clock/Makefile @@ -0,0 +1,145 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PSL1GHT)),) +$(error "Please set PSL1GHT in your environment. export PSL1GHT=") +endif + +include $(PSL1GHT)/ppu_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include + +TITLE := Cairo clock sample - PSL1GHT +APPID := CAIRO0002 +CONTENTID := UP0001-$(APPID)_00-0000000000000000 + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +CFLAGS = -O2 -Wall -mcpu=cell $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lgcm_sys -lrsx -lsysutil -lio -lcairo -lfreetype -lz -lpixman-1 -lm -lrt -llv2 + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export BUILDDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES), -I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(LIBPSL1GHT_INC) \ + -I$(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + $(LIBPSL1GHT_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).self $(OUTPUT).fake.self $(OUTPUT).gnpdrm.pkg $(OUTPUT).pkg + + +#--------------------------------------------------------------------------------- +run: + ps3load $(OUTPUT).self + +#--------------------------------------------------------------------------------- +pkg: $(BUILD) $(OUTPUT).pkg + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).self: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .bin extension +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/samples/graphics/cairo_clock/Readme.md b/samples/graphics/cairo_clock/Readme.md new file mode 100644 index 00000000..df42ce88 --- /dev/null +++ b/samples/graphics/cairo_clock/Readme.md @@ -0,0 +1,3 @@ +A nice cairo example. + +![Cairo Clock](https://github.com/crystalct/PS3LibrariesUpdate/blob/master/samples/cairo_clock/cairo_clock.png) diff --git a/samples/graphics/cairo_clock/cairo_clock.png b/samples/graphics/cairo_clock/cairo_clock.png new file mode 100644 index 0000000000000000000000000000000000000000..62d5d45d453f702b1d9317e7c9c2e393331ab9a1 GIT binary patch literal 34167 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 +#include + +#define CB_SIZE 0x100000 +#define HOST_SIZE (32*1024*1024) + +typedef struct +{ + + int height; + int width; + int id; + uint32_t *ptr; + // Internal stuff + uint32_t offset; +} rsxBuffer; + + +/* Block the PPU thread untill the previous flip operation has finished. */ +void waitFlip (void); +/* Flip a buffer onto the screen. Returns TRUE on success */ +int flip (gcmContextData *context, s32 buffer); +/* Create a buffer to draw into and assign it to @id. Returns NULL on error */ +int makeBuffer (rsxBuffer * buffer, u16 width, u16 height, int id); +/* Get current screen resolution. returns TRUE on success */ +int getResolution (u16 *width, u16 *height); +/* Initilize the RSX properly. Returns NULL on error */ +gcmContextData *initScreen (void *host_addr, u32 size); +/* Sets the target buffer to render to */ +void setRenderTarget(gcmContextData *context, rsxBuffer *buffer); + +#endif /* __RSXUTIL_H__ */ diff --git a/samples/graphics/cairo_clock/source/main.c b/samples/graphics/cairo_clock/source/main.c new file mode 100644 index 00000000..a5d96d54 --- /dev/null +++ b/samples/graphics/cairo_clock/source/main.c @@ -0,0 +1,203 @@ +// Cairo clock sample by CrystalCT (crystal@unict.it) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rsxutil.h" + +#define MAX_BUFFERS 2 + +#define DEBUG(...) + +u16 width; +u16 height; + +// Draw a single frame, do all your drawing/animation from in here. +void +drawFrame (rsxBuffer *buffer, int frame) +{ + cairo_t *cr; + cairo_surface_t *surface = NULL; + + + + + + + surface = cairo_image_surface_create_for_data ((u8 *) buffer->ptr, + CAIRO_FORMAT_RGB24, buffer->width, buffer->height, buffer->width * 4); + + if (surface != NULL) { + cr = cairo_create (surface); + if (cr != NULL) { + // Lets start by clearing everything + cairo_scale(cr, height, height); + cairo_translate(cr, 0.5 * width/height, 0.5); + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_paint (cr); + + /* Draw what needs tobe drawn */ + { + // ARC + double xc = 0.0; + double yc = 0.0; + double m_radius = 0.42; + double m_line_width = 0.05; + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); // Black + + cairo_set_line_width (cr, m_line_width); + + cairo_arc (cr, xc, yc, m_radius, 0, 2 * M_PI); + cairo_stroke(cr); + + //clock ticks + int i; + for (i = 0; i < 12; i++) + { + double inset = 0.05; + + cairo_save(cr); + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + + if(i % 3 != 0) + { + inset *= 0.8; + cairo_set_line_width(cr, 0.03); + } + + cairo_move_to(cr, + (m_radius - inset) * cos (i * M_PI / 6), + (m_radius - inset) * sin (i * M_PI / 6)); + cairo_line_to (cr, + m_radius * cos (i * M_PI / 6), + m_radius * sin (i * M_PI / 6)); + cairo_stroke(cr); + cairo_restore(cr); /* stack-pen-size */ + } + + // store the current time + time_t rawtime; + time(&rawtime); + struct tm * timeinfo = localtime (&rawtime); + + // compute the angles of the indicators of our clock + double minutes = timeinfo->tm_min * M_PI / 30; + double hours = timeinfo->tm_hour * M_PI / 6; + double seconds= timeinfo->tm_sec * M_PI / 30; + + cairo_save(cr); + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + + // draw the seconds hand + cairo_save(cr); + cairo_set_line_width(cr, m_line_width / 3); + cairo_set_source_rgb(cr, 0.7, 0.7, 0.7); // gray + cairo_move_to(cr, 0, 0); + cairo_line_to(cr, sin(seconds) * (m_radius * 0.9), + -cos(seconds) * (m_radius * 0.9)); + cairo_stroke(cr); + cairo_restore(cr); + + // draw the minutes hand + cairo_set_source_rgb(cr, 0.117, 0.337, 0.612); // blue + cairo_move_to(cr, 0, 0); + cairo_line_to(cr, sin(minutes + seconds / 60) * (m_radius * 0.8), + -cos(minutes + seconds / 60) * (m_radius * 0.8)); + cairo_stroke(cr); + + // draw the hours hand + cairo_set_source_rgb(cr, 0.337, 0.612, 0.117); // green + cairo_move_to(cr, 0, 0); + cairo_line_to(cr, sin(hours + minutes / 12.0) * (m_radius * 0.5), + -cos(hours + minutes / 12.0) * (m_radius * 0.5)); + cairo_stroke(cr); + cairo_restore(cr); + + // draw a little dot in the middle + cairo_arc(cr, 0, 0, m_line_width / 3.0, 0, 2 * M_PI); + cairo_fill(cr); + + } + + cairo_destroy (cr); // Realease Surface + } + + cairo_surface_finish (surface); + cairo_surface_destroy (surface); // Flush and destroy the cairo surface + } + +} + + + +int +main (s32 argc, const char* argv[]) +{ + gcmContextData *context; + void *host_addr = NULL; + rsxBuffer buffers[MAX_BUFFERS]; + int currentBuffer = 0; + padInfo padinfo; + padData paddata; + + int frame = 0; + int i; + + /* Allocate a 1Mb buffer, alligned to a 1Mb boundary + * to be our shared IO memory with the RSX. */ + host_addr = memalign (1024*1024, HOST_SIZE); + context = initScreen (host_addr, HOST_SIZE); + ioPadInit (7); + + getResolution(&width, &height); + for (i = 0; i < MAX_BUFFERS; i++) + makeBuffer (&buffers[i], width, height, i); + + flip(context, MAX_BUFFERS - 1); + + DEBUG ("Starting Cairo test\n"); + + while (1) { + ioPadGetInfo (&padinfo); + for(i = 0; i < MAX_PADS; i++) { + if(padinfo.status[i]) { + ioPadGetData (i, &paddata); + if(paddata.BTN_START) { + goto end; + } + } + } + + setRenderTarget(context, &buffers[currentBuffer]); + + DEBUG ("Drawing frame %d\n", frame); + waitFlip (); + drawFrame (&buffers[currentBuffer], frame++); // Draw into the unused buffer + flip (context, buffers[currentBuffer].id); // Flip buffer onto screen + + currentBuffer++; + if (currentBuffer >= MAX_BUFFERS) + currentBuffer = 0; + } + + end: + + gcmSetWaitFlip(context); + for (i = 0; i < MAX_BUFFERS; i++) + rsxFree (buffers[i].ptr); + + rsxFinish (context, 1); + free (host_addr); + ioPadEnd(); + + return 0; +} + diff --git a/samples/graphics/cairo_clock/source/rsxutil.c b/samples/graphics/cairo_clock/source/rsxutil.c new file mode 100644 index 00000000..ec417867 --- /dev/null +++ b/samples/graphics/cairo_clock/source/rsxutil.c @@ -0,0 +1,224 @@ +/* + * This software is distributed under the terms of the GNU General Public + * License ("GPL") version 3, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rsxutil.h" + +#define GCM_LABEL_INDEX 255 + +static void waitRSXIdle(gcmContextData *context); + +static u32 depth_pitch; +static u32 depth_offset; +static u32 *depth_buffer; + +void +waitFlip () +{ + while (gcmGetFlipStatus () != 0) + usleep (200); /* Sleep, to not stress the cpu. */ + gcmResetFlipStatus (); +} + +int +flip (gcmContextData *context, s32 buffer) +{ + if (gcmSetFlip (context, buffer) == 0) { + rsxFlushBuffer (context); + // Prevent the RSX from continuing until the flip has finished. + gcmSetWaitFlip (context); + + return TRUE; + } + return FALSE; +} + + +int +makeBuffer (rsxBuffer * buffer, u16 width, u16 height, int id) +{ + int depth = sizeof(u32); + int pitch = depth * width; + int size = depth * width * height; + + buffer->ptr = (uint32_t*) rsxMemalign (64, size); + + if (buffer->ptr == NULL) + goto error; + + if (rsxAddressToOffset (buffer->ptr, &buffer->offset) != 0) + goto error; + + /* Register the display buffer with the RSX */ + if (gcmSetDisplayBuffer (id, buffer->offset, pitch, width, height) != 0) + goto error; + + buffer->width = width; + buffer->height = height; + buffer->id = id; + + return TRUE; + + error: + if (buffer->ptr != NULL) + rsxFree (buffer->ptr); + + return FALSE; +} + +int +getResolution (u16 *width, u16 *height) +{ + videoState state; + videoResolution resolution; + + /* Get the state of the display */ + if (videoGetState (0, 0, &state) == 0 && + videoGetResolution (state.displayMode.resolution, &resolution) == 0) { + if (width) + *width = resolution.width; + if (height) + *height = resolution.height; + + return TRUE; + } + return FALSE; +} + +gcmContextData * +initScreen (void *host_addr, u32 size) +{ + gcmContextData *context = NULL; /* Context to keep track of the RSX buffer. */ + videoState state; + videoConfiguration vconfig; + videoResolution res; /* Screen Resolution */ + + /* Initilise Reality, which sets up the command buffer and shared IO memory */ + rsxInit (&context, CB_SIZE, size, host_addr); + if (context == NULL) + goto error; + + /* Get the state of the display */ + if (videoGetState (0, 0, &state) != 0) + goto error; + + /* Make sure display is enabled */ + if (state.state != 0) + goto error; + + /* Get the current resolution */ + if (videoGetResolution (state.displayMode.resolution, &res) != 0) + goto error; + + /* Configure the buffer format to xRGB */ + memset (&vconfig, 0, sizeof(videoConfiguration)); + vconfig.resolution = state.displayMode.resolution; + vconfig.format = VIDEO_BUFFER_FORMAT_XRGB; + vconfig.pitch = res.width * sizeof(u32); + vconfig.aspect = state.displayMode.aspect; + + waitRSXIdle(context); + + if (videoConfigure (0, &vconfig, NULL, 0) != 0) + goto error; + + if (videoGetState (0, 0, &state) != 0) + goto error; + + gcmSetFlipMode (GCM_FLIP_VSYNC); // Wait for VSYNC to flip + + depth_pitch = res.width * sizeof(u32); + depth_buffer = (u32 *) rsxMemalign (64, (res.height * depth_pitch)* 2); + rsxAddressToOffset (depth_buffer, &depth_offset); + + gcmResetFlipStatus(); + + return context; + + error: + if (context) + rsxFinish (context, 0); + + if (host_addr) + free (host_addr); + + return NULL; +} + + +static void +waitFinish(gcmContextData *context, u32 sLabelVal) +{ + rsxSetWriteBackendLabel (context, GCM_LABEL_INDEX, sLabelVal); + + rsxFlushBuffer (context); + + while(*(vu32 *) gcmGetLabelAddress (GCM_LABEL_INDEX) != sLabelVal) + usleep(30); + + sLabelVal++; +} + +static void +waitRSXIdle(gcmContextData *context) +{ + u32 sLabelVal = 1; + + rsxSetWriteBackendLabel (context, GCM_LABEL_INDEX, sLabelVal); + rsxSetWaitLabel (context, GCM_LABEL_INDEX, sLabelVal); + + sLabelVal++; + + waitFinish(context, sLabelVal); +} + +void +setRenderTarget(gcmContextData *context, rsxBuffer *buffer) +{ + gcmSurface sf; + + sf.colorFormat = GCM_SURFACE_X8R8G8B8; + sf.colorTarget = GCM_SURFACE_TARGET_0; + sf.colorLocation[0] = GCM_LOCATION_RSX; + sf.colorOffset[0] = buffer->offset; + sf.colorPitch[0] = depth_pitch; + + sf.colorLocation[1] = GCM_LOCATION_RSX; + sf.colorLocation[2] = GCM_LOCATION_RSX; + sf.colorLocation[3] = GCM_LOCATION_RSX; + sf.colorOffset[1] = 0; + sf.colorOffset[2] = 0; + sf.colorOffset[3] = 0; + sf.colorPitch[1] = 64; + sf.colorPitch[2] = 64; + sf.colorPitch[3] = 64; + + sf.depthFormat = GCM_SURFACE_ZETA_Z16; + sf.depthLocation = GCM_LOCATION_RSX; + sf.depthOffset = depth_offset; + sf.depthPitch = depth_pitch; + + sf.type = GCM_TEXTURE_LINEAR; + sf.antiAlias = GCM_SURFACE_CENTER_1; + + sf.width = buffer->width; + sf.height = buffer->height; + sf.x = 0; + sf.y = 0; + + rsxSetSurface (context, &sf); +} From 9b7c7ce9037d24ad0926fef3a4b9affa3fba56ea Mon Sep 17 00:00:00 2001 From: Salvo Cristaldi Date: Fri, 22 Jan 2021 12:03:10 +0100 Subject: [PATCH 42/56] add RSX basic example --- samples/graphics/rsx_Basic/Makefile | 164 + samples/graphics/rsx_Basic/Readme.md | 4 + samples/graphics/rsx_Basic/include/acid.h | 2939 +++++++++++++++++ samples/graphics/rsx_Basic/include/mesh.h | 62 + samples/graphics/rsx_Basic/include/rsxutil.h | 36 + samples/graphics/rsx_Basic/rsx_basic.png | Bin 0 -> 277138 bytes .../shaders/diffuse_specular_shader.fcg | 9 + .../shaders/diffuse_specular_shader.vcg | 16 + samples/graphics/rsx_Basic/source/main.cpp | 251 ++ samples/graphics/rsx_Basic/source/rsxutil.cpp | 204 ++ 10 files changed, 3685 insertions(+) create mode 100644 samples/graphics/rsx_Basic/Makefile create mode 100644 samples/graphics/rsx_Basic/Readme.md create mode 100644 samples/graphics/rsx_Basic/include/acid.h create mode 100644 samples/graphics/rsx_Basic/include/mesh.h create mode 100644 samples/graphics/rsx_Basic/include/rsxutil.h create mode 100644 samples/graphics/rsx_Basic/rsx_basic.png create mode 100644 samples/graphics/rsx_Basic/shaders/diffuse_specular_shader.fcg create mode 100644 samples/graphics/rsx_Basic/shaders/diffuse_specular_shader.vcg create mode 100644 samples/graphics/rsx_Basic/source/main.cpp create mode 100644 samples/graphics/rsx_Basic/source/rsxutil.cpp diff --git a/samples/graphics/rsx_Basic/Makefile b/samples/graphics/rsx_Basic/Makefile new file mode 100644 index 00000000..f5b861d3 --- /dev/null +++ b/samples/graphics/rsx_Basic/Makefile @@ -0,0 +1,164 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PSL1GHT)),) +$(error "Please set PSL1GHT in your environment. export PSL1GHT=") +endif + +include $(PSL1GHT)/ppu_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +SHADERS := shaders +INCLUDES := include + +TITLE := RSX Test - PSL1GHT +APPID := RSX00003 +CONTENTID := UP0001-$(APPID)_00-0000000000000000 + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +CFLAGS = -Wall -mcpu=cell $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lsimdmath -lrsx -lgcm_sys -lio -lsysutil -lrt -llv2 -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ + $(foreach dir,$(SHADERS),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export BUILDDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) +VCGFILES := $(foreach dir,$(SHADERS),$(notdir $(wildcard $(dir)/*.vcg))) +FCGFILES := $(foreach dir,$(SHADERS),$(notdir $(wildcard $(dir)/*.fcg))) + +VPOFILES := $(VCGFILES:.vcg=.vpo) +FPOFILES := $(FCGFILES:.fcg=.fpo) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(addsuffix .o,$(VPOFILES)) \ + $(addsuffix .o,$(FPOFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES), -I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(LIBPSL1GHT_INC) \ + -I$(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + $(LIBPSL1GHT_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).self $(OUTPUT).fake.self + +#--------------------------------------------------------------------------------- +run: + ps3load $(OUTPUT).self + +#--------------------------------------------------------------------------------- +pkg: $(BUILD) $(OUTPUT).pkg + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).self: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .bin extension +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.vpo.o : %.vpo +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.fpo.o : %.fpo +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/samples/graphics/rsx_Basic/Readme.md b/samples/graphics/rsx_Basic/Readme.md new file mode 100644 index 00000000..42540369 --- /dev/null +++ b/samples/graphics/rsx_Basic/Readme.md @@ -0,0 +1,4 @@ +A basic example of to use RSX and Vertex/Fragment without camera position, eyes position, lights, 3D objects, textures, etc etc. +Just draw a tringle..... + +![RSX basic example](https://github.com/crystalct/PS3LibrariesUpdate/blob/master/samples/cairo_clock/rsx_basic.png) diff --git a/samples/graphics/rsx_Basic/include/acid.h b/samples/graphics/rsx_Basic/include/acid.h new file mode 100644 index 00000000..d1643518 --- /dev/null +++ b/samples/graphics/rsx_Basic/include/acid.h @@ -0,0 +1,2939 @@ +/* GIMP RGBA C-Source image dump (acid.c) */ + +static const struct { + unsigned int width; + unsigned int height; + unsigned int bytes_per_pixel; /* 3:RGB, 4:RGBA */ + unsigned char pixel_data[128 * 128 * 4 + 1]; +} acid = { + 128, 128, 4, + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\335\335\335\0\265\265\265\0\221\221\221\0qqq\0UUU\0===\0)))\0\31\31" + "\31\0\15\15\15\0\5\5\5\0\1\1\1\0\1\1\1\0\5\5\5\0\15\15\15\0\31\31\31\0))" + ")\0===\0UUU\0qqq\0\221\221\221\0\265\265\265\0\335\335\335\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\345\345\345\0\251\251\251\0qqq\0===\0\15\15\15\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\15\15\15\0===\0qqq\0\251\251\251\0\345\345\345\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0qqq\0---\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\7\0\0\0\15\0\0\0" + "\23\0\0\0\30\0\0\0\35\0\0\0!\0\0\0$\0\0\0&\0\0\0(\0\0\0(\0\0\0'\0\0\0$\0" + "\0\0!\0\0\0\35\0\0\0\31\0\0\0\23\0\0\0\15\0\0\0\7\0\0\0\2\0\0\0\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0---\0qqq\0\271\271\271\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\265\265\265\0aaa\0\21\21\21\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0\17\0\0\0\30\0\0\0\40\0\0\0+\0\0\0B\0" + "\0\0\\\0\0\0t\0\0\0\211\0\0\0\234\0\0\0\254\0\0\0\271\0\0\0\304\0\0\0\313" + "\0\0\0\313\0\0\0\304\0\0\0\271\0\0\0\254\0\0\0\234\0\0\0\211\0\0\0t\0\0\0" + "\\\0\0\0B\0\0\0+\0\0\0!\0\0\0\30\0\0\0\17\0\0\0\6\0\0\0\1\0\0\0\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\21\21\21\0aaa\0\265\265\265\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\331\331\331\0yyy\0\35\35\35\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\2\0\0\0\14\0\0\0\30\0\0\0&\0\0\0?\0\0\0d\0\0\0\211\0\0\0\254\0\0\0\311" + "\0\0\0\331\0\0\0\341\0\0\0\347\0\0\0\354\0\0\0\361\0\0\0\365\0\0\0\370\0" + "\0\0\373\0\0\0\375\0\0\0\375\0\0\0\373\0\0\0\371\0\0\0\365\0\0\0\361\0\0" + "\0\355\0\0\0\347\0\0\0\341\0\0\0\331\0\0\0\312\0\0\0\254\0\0\0\211\0\0\0" + "d\0\0\0?\0\0\0&\0\0\0\30\0\0\0\15\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0" + "\35\35\35\0yyy\0\331\331\331\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0QQQ\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\16\0\0\0\33\0\0" + "\0.\0\0\0Y\0\0\0\211\0\0\0\265\0\0\0\325\0\0\0\343\0\0\0\354\0\0\0\365\0" + "\0\0\374\40!\0\377FH\0\377hk\0\377\206\213\0\377\240\246\0\377\267\275\0" + "\377\311\321\0\377\330\341\0\377\344\354\0\377\344\354\0\377\330\341\0\377" + "\311\321\0\377\267\275\0\377\240\246\0\377\206\213\0\377hk\0\377FH\0\377" + "\40!\0\377\0\0\0\375\0\0\0\365\0\0\0\355\0\0\0\343\0\0\0\326\0\0\0\266\0" + "\0\0\211\0\0\0Z\0\0\0/\0\0\0\33\0\0\0\16\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0" + "\0\0\0\0QQQ\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\255\255\255\0===\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\11\0\0\0" + "\27\0\0\0-\0\0\0]\0\0\0\224\0\0\0\304\0\0\0\336\0\0\0\354\0\0\0\367\30\31" + "\0\376QT\0\377\206\213\0\377\267\275\0\377\344\354\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\344\354\0\377\267\275\0\377\206\213\0\377QT\0\377\30\31\0" + "\376\0\0\0\367\0\0\0\354\0\0\0\337\0\0\0\305\0\0\0\224\0\0\0]\0\0\0-\0\0" + "\0\30\0\0\0\11\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0===\0\255\255\255\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265" + "\0===\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\15\0\0\0!\0\0\0H\0\0\0\204" + "\0\0\0\274\0\0\0\336\0\0\0\357\0\0\0\373BD\0\377\206\213\0\377\306\315\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\306\315\0\377\206\213\0\377BD\0\377\0\0\0\373" + "\0\0\0\357\0\0\0\337\0\0\0\275\0\0\0\204\0\0\0I\0\0\0!\0\0\0\15\0\0\0\3\0" + "\0\0\1\0\0\0\0\0\0\0\0===\0\265\265\265\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321\321\0QQ" + "Q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\17\0\0\0'\0\0\0[\0\0\0\236\0\0" + "\0\324\0\0\0\353\0\0\0\371FH\0\377\225\232\0\377\340\350\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\340\350\0\377\225\232\0\377FH\0\377\0\0\0\371\0" + "\0\0\353\0\0\0\324\0\0\0\236\0\0\0\\\0\0\0'\0\0\0\17\0\0\0\3\0\0\0\1\0\0" + "\0\0\0\0\0\0QQQ\0\321\321\321\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0yyy\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\16\0\0\0(\0\0\0c\0\0\0\250" + "\0\0\0\332\0\0\0\361$%\0\375~\203\0\377\325\335\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\325\335\0\377~\203\0\377$%\0\375\0\0\0\361\0\0\0" + "\333\0\0\0\251\0\0\0c\0\0\0(\0\0\0\17\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0yyy" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\265\265\265\0)))\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\13\0\0\0$\0\0\0_" + "\0\0\0\250\0\0\0\334\0\0\0\364BD\0\376\244\252\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\244\252\0\377BD\0\376\0\0\0\364\0\0\0" + "\335\0\0\0\250\0\0\0`\0\0\0%\0\0\0\13\0\0\0\2\0\0\0\1\0\0\0\0)))\0\265\265" + "\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\4\0\0\0\34\0\0\0O\0\0\0\234\0\0\0\332\0\0\0\364FH\0\376\267\275\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377FH\0\376\0\0\0\364\0" + "\0\0\332\0\0\0\234\0\0\0P\0\0\0\34\0\0\0\4\0\0\0\1\0\0\0\0\0\0\0\0qqq\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\315\315\315" + "\0""555\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\17\0\0\0""0\0\0\0\203\0\0\0\321\0" + "\0\0\360+-\0\375\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377+-\0\375" + "\0\0\0\360\0\0\0\322\0\0\0\203\0\0\0""1\0\0\0\17\0\0\0\3\0\0\0\1\0\0\0\0" + """555\0\315\315\315\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\235\235\235\0\1\1\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\4\0\0\0\40\0\0\0`\0\0\0\262\0\0\0\346\0\0\0\374~\203\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377~\203\0\377\0\0\0\374\0\0\0\347\0\0\0\263\0\0\0a\0\0\0\40\0\0\0\5\0\0" + "\0\1\0\0\0\0\1\1\1\0\235\235\235\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\14\0\0" + "\0/\0\0\0\207\0\0\0\326\0\0\0\366FH\0\376\311\321\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\311\321\0\377FH\0\376\0\0\0\366\0\0\0\327\0\0\0\207\0\0\0" + """0\0\0\0\14\0\0\0\2\0\0\0\1\0\0\0\0uuu\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\375" + "\375\375\0UUU\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\30\0\0\0S\0\0\0\252\0\0\0\345" + "\0\0\0\374\206\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\374\0\0\0\346" + "\0\0\0\253\0\0\0T\0\0\0\31\0\0\0\4\0\0\0\1\0\0\0\0UUU\0\375\375\375\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\351\351\351\0==" + "=\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\"\0\0\0q\0\0\0\316\0\0\0\363/1\0\376\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377/1\0\376" + "\0\0\0\364\0\0\0\316\0\0\0q\0\0\0\"\0\0\0\5\0\0\0\1\0\0\0\0===\0\351\351" + "\351\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0---\0\0\0\0\0\0\0\0\1" + "\0\0\0\6\0\0\0*\0\0\0\205\0\0\0\332\0\0\0\373\\`\0\377\357\370\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357\370" + "\0\377\\`\0\377\0\0\0\373\0\0\0\332\0\0\0\206\0\0\0+\0\0\0\6\0\0\0\1\0\0" + "\0\1---\0\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\331\331\331\0%%%\0\0\0\0\0\0\0\0\1\0\0\0\11" + "\0\0\0""2\0\0\0\225\0\0\0\342\0\0\0\374\202\207\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\374\0\0\0\342\0\0\0" + "\226\0\0\0""2\0\0\0\11\0\0\0\1\0\0\0\1%%%\0\331\331\331\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\335\335\335\0%%%\0\0\0\0\0\0\0\0\1\0\0\0\15" + "\0\0\0?\0\0\0\243\0\0\0\350\2\2\0\375\240\246\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377" + "\2\2\0\375\0\0\0\350\0\0\0\244\0\0\0@\0\0\0\15\0\0\0\2\0\0\0\1%%%\0\335\335" + "\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\351\351\351\0---\0\0\0\0\0\0\0\0\1\0\0\0\16" + "\0\0\0H\0\0\0\257\0\0\0\355\25\25\0\376\267\275\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\25\25\0\376\0\0\0\355\0\0\0\257\0\0\0H\0\0\0" + "\17\0\0\0\2\0\0\0\1---\0\351\351\351\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\375\375\375\0===\0\0\0\0\0\0\0\0\1\0\0\0\15" + "\0\0\0H\0\0\0\264\0\0\0\360\40!\0\376\306\315\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\40!\0\376\0\0\0" + "\360\0\0\0\265\0\0\0I\0\0\0\15\0\0\0\1\0\0\0\1===\0\375\375\375\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0UUU\0\0\0\0\0\0\0\0\1\0\0\0\11" + "\0\0\0?\0\0\0\257\0\0\0\360$%\0\376\315\325\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\315\325" + "\0\377$%\0\377\0\0\0\360\0\0\0\257\0\0\0@\0\0\0\11\0\0\0\1\0\0\0\1UUU\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0""2" + "\0\0\0\244\0\0\0\355\40!\0\376\315\325\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377\40!\0\376\0\0\0\355\0\0\0\244\0\0\0""2\0" + "\0\0\6\0\0\0\1\0\0\0\0uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\235\235\235\0\0\0\0\0\0\0\0\0\0\0" + "\0\4\0\0\0*\0\0\0\225\0\0\0\350\25\25\0\376\306\315\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\25" + "\25\0\376\0\0\0\350\0\0\0\226\0\0\0*\0\0\0\5\0\0\0\1\0\0\0\0\235\235\235" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\315\315\315\0" + "\1\1\1\0\0\0\0\0\0\0\0\3\0\0\0\"\0\0\0\205\0\0\0\342\2\2\0\375\267\275\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\267\275\0\377\2\2\0\375\0\0\0\342\0\0\0" + "\206\0\0\0#\0\0\0\4\0\0\0\1\1\1\1\0\315\315\315\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0""555\0\0\0\0\0\0\0\0\2\0\0\0\30\0\0\0q\0\0\0\332\0\0\0\374" + "\240\246\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\350\361\0\377\254\263\0\377.0\0\377\13\14\0\377UX\0\377" + "\317\327\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377\0" + "\0\0\374\0\0\0\333\0\0\0r\0\0\0\31\0\0\0\2\0\0\0\1""555\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\0\0\0\0\14\0\0\0S\0\0\0\316\0\0\0\373\202\207" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\310\317\0\3779<\0\377\0\0\0\3779<\0\377\310\317\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\306\315\0\377!\"\0\377\0\0\0\377\0\0\0\377\4\4\0\377PT\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\373" + "\0\0\0\316\0\0\0S\0\0\0\14\0\0\0\1\0\0\0\0qqq\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0" + "\0\0\0\0\0\0\4\0\0\0/\0\0\0\253\0\0\0\364\\`\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377/2\0\377\0\0\0\377\0\0\0\377\0\0\0\377/2\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\237\245\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\20\20\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\\`\0\377\0\0\0\364\0\0\0" + "\253\0\0\0""0\0\0\0\5\0\0\0\1\0\0\0\0\265\265\265\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0)))\0\0\0\0\0\0\0\0\3\0\0\0\40\0\0" + "\0\207\0\0\0\345/1\0\376\357\370\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\207\215\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\357\370\0\377/1\0\376\0\0\0\346" + "\0\0\0\207\0\0\0\40\0\0\0\3\0\0\0\1)))\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0yyy\0\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0`\0\0\0\326\0\0\0\374\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\5\5\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377\0\0\0\374\0\0\0" + "\327\0\0\0a\0\0\0\17\0\0\0\1\0\0\0\0yyy\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321\321\0" + "\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0""1\0\0\0\263\0\0\0\365\206\213\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0" + "\366\0\0\0\263\0\0\0""1\0\0\0\5\0\0\0\1\0\0\0\0\321\321\321\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0QQQ\0\0" + "\0\0\0\0\0\0\2\0\0\0\34\0\0\0\203\0\0\0\346FH\0\376\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\376" + "\0\0\0\347\0\0\0\204\0\0\0\34\0\0\0\2\0\0\0\1QQQ\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0\0\0\0" + "\0\0\0\12\0\0\0O\0\0\0\321\0\0\0\374\311\321\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\311\321" + "\0\377\0\0\0\374\0\0\0\322\0\0\0P\0\0\0\13\0\0\0\1\0\0\0\0\265\265\265\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0" + "\0%\0\0\0\234\0\0\0\360~\203\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377~\203\0\377\0\0\0\361\0\0\0\234\0\0\0%\0\0\0\3\0\0\0\1===\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\255\255\255\0\0\0\0\0\0\0\0\0\0\0\0\16\0\0\0_\0" + "\0\0\332+-\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377+-\0\375\0\0\0\332\0\0\0`\0\0\0\17\0\0\0\1\0\0\0\0\255\255" + "\255\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0(\0\0\0\250\0\0\0" + "\363\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\244\252\0\377\0\0\0\364\0\0\0\250\0\0\0)\0\0\0\3\0\0\0\1=" + "==\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\271\271\271\0\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0c\0\0\0\334FH" + "\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377FH\0\376\0\0\0\335\0\0\0d\0\0\0\17\0\0\0" + "\1\0\0\0\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0'\0\0\0\251\0\0\0\363\267\275" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\364\0\0\0\251\0\0\0" + "(\0\0\0\3\0\0\0\1QQQ\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\331\331\331\0\0\0\0\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\333BD\0\375\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377BD\0\376\0\0\0\333\0\0\0\\" + "\0\0\0\16\0\0\0\1\0\0\0\0\331\331\331\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0yyy\0\0\0\0\0\0\0\0\2\0\0\0!\0\0\0\235\0\0\0\361\244\252\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377\0\0\0\361\0" + "\0\0\236\0\0\0\"\0\0\0\2\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\35\35\35\0\0\0\0\0\0\0\0\10\0\0\0H\0\0\0\324$%\0\375\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377$%\0\375\0" + "\0\0\325\0\0\0I\0\0\0\11\0\0\0\1\35\35\35\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265" + "\265\0\0\0\0\0\0\0\0\0\0\0\0\26\0\0\0\203\0\0\0\353~\203\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377~\203" + "\0\377\0\0\0\353\0\0\0\204\0\0\0\27\0\0\0\1\0\0\0\0\265\265\265\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0aaa\0\0\0\0\0\0\0\0\3\0\0\0-\0\0\0\275\0\0\0\370\325\335\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\325\335\0\377\0\0\0\371\0\0\0\276\0\0\0-\0\0\0\3\0\0\0\1aaa\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\21\21\21\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\336FH\0\376\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377FH\0\377\0\0\0\337\0\0\0]\0\0\0\16\0\0\0\1\21\21\21\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0\0\0\0\0\0\0\0\0\0\0\0\32\0\0\0\223\0\0\0\357\225\232\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\225\232\0\377\0\0\0\357\0\0\0\224\0\0\0\33\0" + "\0\0\1\0\0\0\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\2\0\0\0.\0\0\0\305\0\0\0\372\340\350\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\340\350\0\377\0\0\0\373\0\0\0\305\0\0\0" + "/\0\0\0\3\0\0\0\1qqq\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0---\0\0\0\0\0\0\0\0\14\0\0\0Y\0\0\0\336BD\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377BD\0\377\0\0\0\337\0\0\0Z\0\0\0" + "\15\0\0\0\1---\0\377\377\377\0\377\377\377\0\377\377\377\0\345\345\345\0" + "\0\0\0\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\355\0" + "\0\0\211\0\0\0\30\0\0\0\1\0\0\0\0\345\345\345\0\377\377\377\0\377\377\377" + "\0\251\251\251\0\0\0\0\0\0\0\0\1\0\0\0&\0\0\0\265\0\0\0\367\306\315\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377" + "\0\0\0\367\0\0\0\266\0\0\0&\0\0\0\2\0\0\0\1\251\251\251\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\6\0\0\0?\0\0\0\325\30\31\0\376\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\30\31\0\376\0\0\0\326\0\0\0?\0\0\0\6\0\0\0\1qqq\0\377\377\377\0\377\377" + "\377\0===\0\0\0\0\0\0\0\0\17\0\0\0d\0\0\0\343QT\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377QT" + "\0\377\0\0\0\344\0\0\0e\0\0\0\17\0\0\0\1===\0\377\377\377\0\377\377\377\0" + "\15\15\15\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\15\15\15\0\377\377\377" + "\0\335\335\335\0\0\0\0\0\0\0\0\0\0\0\0\40\0\0\0\254\0\0\0\365\267\275\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\0\0\0\365\0\0\0\255\0\0\0!\0\0\0\1\0\0\0\0\335" + "\335\335\0\265\265\265\0\0\0\0\0\0\0\0\1\0\0\0+\0\0\0\311\0\0\0\374\344\354" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\210\215\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\7\7\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\344\354\0\377\0\0\0\374\0\0\0\312\0\0\0,\0\0\0\2" + "\0\0\0\1\265\265\265\0\221\221\221\0\0\0\0\0\0\0\0\6\0\0\0B\0\0\0\331\40" + "!\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\237\245\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\20\21\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\40!\0\377\0\0\0" + "\332\0\0\0B\0\0\0\7\0\0\0\1\221\221\221\0qqq\0\0\0\0\0\0\0\0\15\0\0\0\\\0" + "\0\0\341FH\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\37702\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\37702\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377!\"\0\377\0\0" + "\0\377\0\0\0\377\4\4\0\377PS\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\377\0\0\0\342\0" + "\0\0]\0\0\0\15\0\0\0\1qqq\0UUU\0\0\0\0\0\0\0\0\23\0\0\0t\0\0\0\347hk\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\310\317\0\3779<\0\377\0" + "\0\0\3779<\0\377\310\317\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\350\361\0\377\254\263\0\377.0\0\377" + "\13\14\0\377UX\0\377\317\327\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377hk\0\377\0\0\0\350\0" + "\0\0u\0\0\0\23\0\0\0\1UUU\0===\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206" + "\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1===" + "\0)))\0\0\0\0\0\0\0\0\35\0\0\0\234\0\0\0\361\240\246\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246" + "\0\377\0\0\0\361\0\0\0\235\0\0\0\35\0\0\0\1)))\0\31\31\31\0\0\0\0\0\0\0\0" + "\40\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0" + "\0\0\255\0\0\0!\0\0\0\1\31\31\31\0\15\15\15\0\0\0\0\0\0\0\0$\0\0\0\271\0" + "\0\0\370\311\321\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\311\321\0\377\0\0\0\371\0\0\0\272\0\0\0" + "$\0\0\0\1\15\15\15\0\5\5\5\0\0\0\0\0\0\0\0&\0\0\0\304\0\0\0\373\330\341\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\330\341\0\377\0\0\0\373\0\0\0\305\0\0\0'\0\0\0\1\5\5\5\0\1\1\1" + "\0\0\0\0\0\0\0\0(\0\0\0\313\0\0\0\374\344\354\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377" + "\0\0\0\375\0\0\0\313\0\0\0)\0\0\0\1\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0(\0\0\0" + "\313\0\0\0\374\344\354\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\344\354\0\377\0\0\0\375\0\0\0\313\0\0" + "\0)\0\0\0\1\1\1\1\0\5\5\5\0\0\0\0\0\0\0\0&\0\0\0\304\0\0\0\373\330\341\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\330\341\0\377\0\0\0\373\0\0\0\305\0\0\0'\0\0\0\1\5\5\5\0\15\15" + "\15\0\0\0\0\0\0\0\0$\0\0\0\271\0\0\0\370\311\321\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\311\321\0" + "\377\0\0\0\371\0\0\0\272\0\0\0$\0\0\0\1\15\15\15\0\31\31\31\0\0\0\0\0\0\0" + "\0\40\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0" + "\0\0\255\0\0\0!\0\0\0\1\31\31\31\0)))\0\0\0\0\0\0\0\0\35\0\0\0\234\0\0\0" + "\361\240\246\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\240\246\0\377\0\0\0\361\0\0\0\235\0\0\0\35\0\0\0" + "\1)))\0===\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\206" + "\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1===\0UUU\0\0\0\0\0\0\0\0\23" + "\0\0\0t\0\0\0\347hk\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377hk\0\377\0\0\0\350\0\0\0u\0\0\0\23\0\0\0" + "\1UUU\0qqq\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\341FH\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\377\0" + "\0\0\342\0\0\0]\0\0\0\15\0\0\0\1qqq\0\221\221\221\0\0\0\0\0\0\0\0\6\0\0\0" + "B\0\0\0\331\40!\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\40!\0\377\0\0\0\332\0\0\0C\0\0\0\7\0\0\0\1\221" + "\221\221\0\265\265\265\0\0\0\0\0\0\0\0\1\0\0\0,\0\0\0\312\0\0\0\374\344\354" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377" + "\0\0\0\375\0\0\0\312\0\0\0,\0\0\0\2\0\0\0\1\265\265\265\0\335\335\335\0\0" + "\0\0\0\0\0\0\1\0\0\0!\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0\0\0\255\0\0" + "\0\"\0\0\0\1\0\0\0\1\335\335\335\0\377\377\377\0\15\15\15\0\0\0\0\0\0\0\0" + "\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\15" + "\15\15\0\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\17\0\0\0d\0\0\0\343" + "QT\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377QT\0\377\0" + "\0\0\344\0\0\0e\0\0\0\17\0\0\0\1===\0\377\377\377\0\377\377\377\0qqq\0\0" + "\0\0\0\0\0\0\6\0\0\0?\0\0\0\325\30\31\0\376\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\30\31\0\376\0\0\0\326\0\0\0?\0\0\0\6\0\0\0\1qqq\0\377" + "\377\377\0\377\377\377\0\251\251\251\0\0\0\0\0\0\0\0\1\0\0\0&\0\0\0\266\0" + "\0\0\367\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\335\345\0\377\261\270\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\0\0\0\370" + "\0\0\0\266\0\0\0'\0\0\0\2\0\0\0\1\251\251\251\0\377\377\377\0\377\377\377" + "\0\345\345\345\0\0\0\0\0\0\0\0\1\0\0\0\31\0\0\0\212\0\0\0\354\206\213\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\322\332\0\377\36" + "\37\0\377\10\10\0\377\265\273\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\316\326\0\377\230\235\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\0" + "\0\0\1\345\345\345\0\377\377\377\0\377\377\377\0\377\377\377\0---\0\0\0\0" + "\0\0\0\0\14\0\0\0Z\0\0\0\337BD\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\322\332\0\377\36\37\0\377\0\0\0\377\0\0\0\377\1\1\0\377\201\206\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\265\273\0\377\17" + "\20\0\377\2\2\0\377\224\231\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377BD\0\377\0\0\0\340\0\0\0Z\0\0\0" + "\15\0\0\0\1---\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "qqq\0\0\0\0\0\0\0\0\2\0\0\0.\0\0\0\305\0\0\0\372\340\350\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\322\332\0\377\36\37\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377SV\0\377\353\364\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377w{\0\377\1\1\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377;>\0\377<>\0\377<>\0\377\303\312\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340" + "\350\0\377\0\0\0\373\0\0\0\305\0\0\0/\0\0\0\3\0\0\0\1qqq\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0\0\0\0\0\0\0\0\1" + "\0\0\0\33\0\0\0\224\0\0\0\357\225\232\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377\26\26\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377.0\0\377" + "\330\340\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\345\355\0\377?A" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\1\1\0\377\310\320\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\225\232\0\377\0\0\0\360\0\0\0" + "\225\0\0\0\34\0\0\0\1\0\0\0\1\271\271\271\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\21\21\21\0\0\0\0\0\0\0\0\15\0\0\0" + "]\0\0\0\337FH\0\376\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\257\266\0\377\4\4\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\16\17\0\377\265\273" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\277\306\0\377\23\24\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\2\2\0\377\226\233\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377FH\0\377\0\0\0\337\0\0\0^\0\0\0\16\0\0\0\1\21\21" + "\21\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0aaa\0\0\0\0\0\0\0\0\3\0\0\0-\0\0\0\275\0\0\0\370\325\335" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\300\307\0\377\30\31\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377z\177\0\377\355\366" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377pt\0\377\2\2\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\2\2" + "\0\377\235\243\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\325\335\0\377\0\0\0\371" + "\0\0\0\276\0\0\0-\0\0\0\3\0\0\0\1aaa\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0" + "\0\0\1\0\0\0\27\0\0\0\204\0\0\0\353~\203\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\330\341\0\377&'\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\25\25\0\377\277\306\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\310\317\0\377" + "-/\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\25\25\0\377\267\275\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377~\203\0\377\0\0\0\354\0\0\0\205\0\0\0\30\0\0\0\1\0\0\0\1\265\265" + "\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\35\35\35\0\0\0\0\0\0\0\0\11\0\0\0I\0\0\0\324" + "$%\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\350\360\0\377EG\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\2\2\0\377mq\0\377\352\363\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\342\352\0" + "\377fi\0\377\2\2\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377*,\0\377\333\344\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377$%\0\375\0\0\0\325\0\0\0J\0\0\0\11\0\0" + "\0\1\35\35\35\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0\0\2" + "\0\0\0!\0\0\0\236\0\0\0\361\244\252\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\357\370\0\377tx\0\377\1\1\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\35\36\0\377\235\243\0" + "\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377\220" + "\225\0\377\15\16\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377gj\0\377\353\364\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377\0\0\0\362\0" + "\0\0\237\0\0\0\"\0\0\0\2\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\331\331\331\0\0\0\0\0\0\0\0\1\0\0\0\16\0\0\0\\\0\0\0\333BD\0\375\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\265\273\0" + "\377\13\14\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377-/\0\377\273\302\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\355\366\0\377\177\204" + "\0\377\15\16\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\11\12\0\377\246\254\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "BD\0\376\0\0\0\333\0\0\0]\0\0\0\17\0\0\0\1\0\0\0\1\331\331\331\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0'" + "\0\0\0\251\0\0\0\363\267\275\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\346\356\0\377HK\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377=?\0\377\247\255\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377\206\213\0\377" + "\34\35\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377/1\0\377\325\335\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\267\275\0\377\0\0\0\364\0\0\0\251\0\0\0(\0\0\0\3\0\0\0\1QQQ\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0\0" + "\0\0\0\0\0\0\1\0\0\0\20\0\0\0d\0\0\0\335FH\0\375\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\211\217\0\377\10\11\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\14\15\0\377qu\0\377" + "\343\353\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\356\367\0\377\254\263\0\377hl\0\377\5\5\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\16\17\0\377\231\237\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377FH\0\376\0\0\0\335\0\0\0d\0\0\0\20\0\0\0\1\0\0\0\1\271\271\271" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0)\0\0\0\251\0\0\0\363\244\252\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\346\356\0\377fi\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\4\4\0\377BD\0\377\206\213\0\377\301\310\0\377\354\365\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340\350\0\377\230\235\0" + "\377^b\0\377\25\25\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377Z^" + "\0\377\335\345\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377" + "\0\0\0\364\0\0\0\251\0\0\0*\0\0\0\3\0\0\0\1===\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\255" + "\255\255\0\0\0\0\0\0\0\0\1\0\0\0\17\0\0\0`\0\0\0\332+-\0\375\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\254\263\0\377\25\25\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\12\13\0\377;>\0\377UX\0" + "\377lp\0\377x|\0\377x|\0\377x|\0\377bf\0\377HK\0\377\6\6\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\37713\0\377\275\304" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "+-\0\375\0\0\0\332\0\0\0a\0\0\0\17\0\0\0\1\0\0\0\1\255\255\255\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0%\0\0\0\234\0\0\0" + "\360~\203\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357\370" + "\0\377\216\223\0\377\16\17\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + ",.\0\377\247\255\0\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377~\203\0\377\0\0\0\361\0\0\0\235\0\0\0%\0" + "\0\0\3\0\0\0\1===\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265" + "\265\265\0\0\0\0\0\0\0\0\1\0\0\0\13\0\0\0P\0\0\0\322\0\0\0\374\311\321\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\355" + "\366\0\377\213\221\0\377\17\20\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\37724\0\377\246\254\0\377\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\311\321\0\377\0\0\0\374\0\0\0\323\0\0\0Q\0\0\0\14\0\0\0\1\0\0\0\1" + "\265\265\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0\34\0\0\0\204\0\0\0\347FH\0\376\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\356\367\0\377\235\243\0\377(*\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\5\5\0" + "\377hl\0\377\270\276\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377FH\0\377\0\0\0\347\0\0\0\204\0\0\0\35\0\0\0\2\0\0\0\1QQQ\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321" + "\321\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0""1\0\0\0\263\0\0\0\365\206\213\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\320\330\0\377\\`\0\377\7" + "\7\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\3778:\0\377\226\233\0" + "\377\344\354\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\206\213\0\377\0\0\0\366\0\0\0\263\0\0\0""1\0\0\0\5\0\0\0\1\0\0\0\1" + "\321\321\321\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0\0\1\0\0\0\17\0\0\0a\0\0\0" + "\327\0\0\0\374\276\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\357\370\0\377\245\253\0\377w{\0\377:=\0\377\3\3\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377*,\0\377dh\0\377\217\224\0\377\350" + "\361\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\276\305\0\377\0\0\0\375\0\0\0\330\0\0\0b\0\0" + "\0\20\0\0\0\1\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + ")))\0\0\0\0\0\0\0\0\3\0\0\0\40\0\0\0\207\0\0\0\346/1\0\376\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\357\370\0\377\305\314\0\377\247\255\0\377" + "\212\220\0\377x}\0\377x}\0\377x}\0\377x}\0\377{\200\0\377\240\246\0\377\270" + "\276\0\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\357\370\0\377/1\0\376\0\0\0\347\0\0\0\210\0\0\0!\0\0\0\3\0\0\0" + "\1)))\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265" + "\265\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0""0\0\0\0\253\0\0\0\364\\`\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\\`\0\377\0\0\0\364\0\0\0\254\0\0\0""1" + "\0\0\0\6\0\0\0\1\0\0\0\1\265\265\265\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0\0\0\1\0\0\0\14\0\0" + "\0T\0\0\0\317\0\0\0\373\202\207\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\373" + "\0\0\0\317\0\0\0T\0\0\0\15\0\0\0\1\0\0\0\1qqq\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0""555\0\0\0\0\0\0\0\0\2\0\0\0\30\0\0\0q\0\0\0\332\0\0\0\374\240\246\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377\0" + "\0\0\374\0\0\0\333\0\0\0r\0\0\0\31\0\0\0\2\0\0\0\1""555\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\315\315\315\0\1\1\1\0\0\0\0\1\0\0\0\4\0\0\0\"\0" + "\0\0\205\0\0\0\342\2\2\0\375\267\275\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\267" + "\275\0\377\2\2\0\375\0\0\0\342\0\0\0\206\0\0\0#\0\0\0\4\0\0\0\1\1\1\1\1\315" + "\315\315\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\235\235" + "\235\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0+\0\0\0\226\0\0\0\350\25\25\0\376\306" + "\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\306\315\0\377\25\25\0\376\0\0\0\351\0\0\0\226\0\0\0+\0\0\0\6\0" + "\0\0\1\0\0\0\1\235\235\235\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0" + """2\0\0\0\244\0\0\0\355\40!\0\376\315\325\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377\40!\0\376\0\0\0\356\0\0\0\245\0\0\0""3\0" + "\0\0\7\0\0\0\1\0\0\0\1uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0UUU\0\0" + "\0\0\0\0\0\0\1\0\0\0\11\0\0\0@\0\0\0\260\0\0\0\360$%\0\376\315\325\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377$%\0\377\0\0\0\361\0\0\0\260\0\0\0A\0\0\0" + "\12\0\0\0\1\0\0\0\1UUU\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\375\375\375\0===\0\0\0\0\1\0\0\0\2\0\0\0\16\0\0\0H\0\0\0\264\0\0\0\360" + "\40!\0\376\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\306\315\0\377\40!\0\376\0\0\0\361\0\0\0\265\0\0\0I\0\0\0\16" + "\0\0\0\2\0\0\0\1===\1\375\375\375\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\351\351\351\0---\0\0\0\0\1\0\0\0\2\0\0\0\17" + "\0\0\0I\0\0\0\260\0\0\0\355\25\25\0\376\267\275\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\25\25\0\376\0\0\0\356\0\0\0\260\0\0\0I\0\0\0" + "\20\0\0\0\2\0\0\0\1---\1\351\351\351\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0" + "%%%\0\0\0\0\1\0\0\0\2\0\0\0\16\0\0\0@\0\0\0\244\0\0\0\350\2\2\0\375\240\246" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\240\246\0\377\2\2\0\375\0\0\0\351\0\0\0\245\0\0\0@\0\0\0\16" + "\0\0\0\2\0\0\0\1%%%\1\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\331\331\331\0%%%\0\0\0\0\1\0\0\0\2\0\0\0\12\0\0\0""2\0\0\0\226" + "\0\0\0\342\0\0\0\374\202\207\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\202\207\0\377\0\0\0\374\0\0\0\343\0\0\0\226\0\0\0""3\0\0\0" + "\12\0\0\0\2\0\0\0\1%%%\1\331\331\331\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0---\0\0\0\0\1\0\0" + "\0\1\0\0\0\6\0\0\0*\0\0\0\205\0\0\0\332\0\0\0\373\\`\0\377\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357" + "\370\0\377\\`\0\377\0\0\0\373\0\0\0\333\0\0\0\206\0\0\0+\0\0\0\6\0\0\0\1" + "\0\0\0\1---\1\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\351\351\351\0" + "===\0\0\0\0\1\0\0\0\1\0\0\0\5\0\0\0#\0\0\0r\0\0\0\317\0\0\0\364/1\0\376\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377/1\0\376" + "\0\0\0\365\0\0\0\317\0\0\0r\0\0\0#\0\0\0\6\0\0\0\1\0\0\0\1===\1\351\351\351" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\375\375\375" + "\0UUU\0\0\0\0\0\0\0\0\1\0\0\0\4\0\0\0\31\0\0\0S\0\0\0\253\0\0\0\346\0\0\0" + "\374\206\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\375\0\0\0\347\0\0\0" + "\254\0\0\0T\0\0\0\31\0\0\0\4\0\0\0\1\0\0\0\1UUU\0\375\375\375\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\1\0\0\0\3\0\0\0\14\0\0\0/\0\0\0\210\0" + "\0\0\327\0\0\0\366FH\0\376\311\321\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\311\321\0\377FH\0\377\0\0\0\367\0\0\0\330\0\0\0\210\0\0\0""0\0\0\0\15\0" + "\0\0\3\0\0\0\1\0\0\0\1uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\235\235\235\0\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\5\0" + "\0\0!\0\0\0a\0\0\0\263\0\0\0\347\0\0\0\374~\203\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377~\203\0\377\0\0\0" + "\374\0\0\0\347\0\0\0\264\0\0\0b\0\0\0!\0\0\0\6\0\0\0\1\0\0\0\1\1\1\1\1\235" + "\235\235\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\315\315\315\0""555\0\0\0\0\0\0\0\0\1\0" + "\0\0\3\0\0\0\17\0\0\0""1\0\0\0\204\0\0\0\322\0\0\0\361+-\0\375\244\252\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\244\252\0\377+-\0\375\0\0\0\361\0\0\0\323\0\0\0" + "\204\0\0\0""1\0\0\0\20\0\0\0\3\0\0\0\1\0\0\0\1""555\0\315\315\315\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0" + "\0\0\1\0\0\0\1\0\0\0\5\0\0\0\34\0\0\0P\0\0\0\234\0\0\0\332\0\0\0\364FH\0" + "\376\267\275\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377FH" + "\0\376\0\0\0\364\0\0\0\333\0\0\0\235\0\0\0Q\0\0\0\35\0\0\0\5\0\0\0\1\0\0" + "\0\1\0\0\0\1qqq\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0)))\0\0" + "\0\0\0\0\0\0\1\0\0\0\3\0\0\0\14\0\0\0%\0\0\0`\0\0\0\251\0\0\0\335\0\0\0\364" + "BD\0\376\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\244\252\0\377BD\0\376\0\0\0\364\0\0\0\336\0\0\0\251\0\0\0a\0\0\0&" + "\0\0\0\14\0\0\0\3\0\0\0\1\0\0\0\1)))\0\265\265\265\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0" + "\0\1\0\0\0\1\0\0\0\3\0\0\0\17\0\0\0)\0\0\0d\0\0\0\251\0\0\0\333\0\0\0\362" + "$%\0\375~\203\0\377\325\335\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\325\335\0\377~\203\0\377$%\0\375\0\0\0\362\0\0\0\334\0\0\0\252\0\0\0" + "d\0\0\0)\0\0\0\17\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1yyy\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\321\321\321\0QQQ\0\0\0\0\0\0\0\0\1\0\0\0\1\0" + "\0\0\4\0\0\0\20\0\0\0(\0\0\0\\\0\0\0\236\0\0\0\325\0\0\0\354\0\0\0\372FH" + "\0\377\225\232\0\377\340\350\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340" + "\350\0\377\225\232\0\377FH\0\377\0\0\0\372\0\0\0\354\0\0\0\325\0\0\0\236" + "\0\0\0]\0\0\0(\0\0\0\20\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1QQQ\0\321\321\321" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\265\265\265\0===\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\3\0\0\0" + "\16\0\0\0\"\0\0\0I\0\0\0\205\0\0\0\275\0\0\0\337\0\0\0\360\0\0\0\374BD\0" + "\377\206\213\0\377\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\206" + "\213\0\377BD\0\377\0\0\0\374\0\0\0\360\0\0\0\340\0\0\0\276\0\0\0\205\0\0" + "\0J\0\0\0\"\0\0\0\16\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1===\0\265\265\265\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\255\255" + "\255\0===\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\11\0\0\0\27\0\0\0-\0\0" + "\0]\0\0\0\225\0\0\0\305\0\0\0\337\0\0\0\355\0\0\0\370\30\31\0\376QT\0\377" + "\206\213\0\377\267\275\0\377\344\354\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\344\354\0\377\267\275\0\377\206\213\0\377QT\0\377\30\31\0\376\0\0\0\370" + "\0\0\0\355\0\0\0\340\0\0\0\306\0\0\0\225\0\0\0^\0\0\0-\0\0\0\30\0\0\0\11" + "\0\0\0\3\0\0\0\1\0\0\0\1\0\0\0\1===\0\255\255\255\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\271\271\271\0QQQ\0\0\0\0\0\0\0\0\1\0\0\0\1" + "\0\0\0\1\0\0\0\3\0\0\0\16\0\0\0\33\0\0\0/\0\0\0Z\0\0\0\212\0\0\0\266\0\0" + "\0\326\0\0\0\344\0\0\0\355\0\0\0\365\0\0\0\374\40!\0\377FH\0\377hk\0\377" + "\206\213\0\377\240\246\0\377\267\275\0\377\311\321\0\377\330\341\0\377\344" + "\354\0\377\344\354\0\377\330\341\0\377\311\321\0\377\267\275\0\377\240\246" + "\0\377\206\213\0\377hk\0\377FH\0\377\40!\0\377\0\0\0\375\0\0\0\366\0\0\0" + "\355\0\0\0\344\0\0\0\326\0\0\0\266\0\0\0\212\0\0\0Z\0\0\0/\0\0\0\33\0\0\0" + "\16\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1QQQ\0\271\271\271\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\331\331\331\0yyy\0\35\35\35\0\0\0\0\0\0\0\0\1\0" + "\0\0\1\0\0\0\1\0\0\0\3\0\0\0\15\0\0\0\31\0\0\0'\0\0\0@\0\0\0e\0\0\0\212\0" + "\0\0\255\0\0\0\312\0\0\0\332\0\0\0\342\0\0\0\350\0\0\0\355\0\0\0\361\0\0" + "\0\365\0\0\0\371\0\0\0\373\0\0\0\375\0\0\0\375\0\0\0\374\0\0\0\371\0\0\0" + "\366\0\0\0\362\0\0\0\355\0\0\0\350\0\0\0\342\0\0\0\332\0\0\0\313\0\0\0\255" + "\0\0\0\212\0\0\0e\0\0\0@\0\0\0'\0\0\0\31\0\0\0\16\0\0\0\4\0\0\0\2\0\0\0\1" + "\0\0\0\1\0\0\0\1\35\35\35\0yyy\0\331\331\331\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\265\265\265\0aaa\0\21\21\21\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0" + "\0\2\0\0\0\6\0\0\0\17\0\0\0\31\0\0\0!\0\0\0,\0\0\0C\0\0\0]\0\0\0u\0\0\0\212" + "\0\0\0\235\0\0\0\255\0\0\0\272\0\0\0\305\0\0\0\314\0\0\0\314\0\0\0\305\0" + "\0\0\272\0\0\0\255\0\0\0\235\0\0\0\212\0\0\0u\0\0\0]\0\0\0C\0\0\0,\0\0\0" + "\"\0\0\0\31\0\0\0\20\0\0\0\7\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\21\21" + "\21\0aaa\0\265\265\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0qqq\0---\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\2" + "\0\0\0\7\0\0\0\15\0\0\0\23\0\0\0\31\0\0\0\35\0\0\0!\0\0\0$\0\0\0'\0\0\0)" + "\0\0\0)\0\0\0'\0\0\0%\0\0\0!\0\0\0\36\0\0\0\31\0\0\0\24\0\0\0\16\0\0\0\10" + "\0\0\0\3\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1---\0qqq\0\271\271" + "\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\345\345\345\0" + "\251\251\251\0qqq\0===\0\15\15\15\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" + "\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" + "\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\15\15" + "\15\0===\0qqq\0\251\251\251\0\345\345\345\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0" + "\265\265\265\0\221\221\221\0qqq\0UUU\0===\0)))\0\31\31\31\0\15\15\15\0\5" + "\5\5\0\1\1\1\0\1\1\1\0\5\5\5\0\15\15\15\0\31\31\31\0)))\0===\0UUU\0qqq\0" + "\221\221\221\0\265\265\265\0\335\335\335\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0", +}; + diff --git a/samples/graphics/rsx_Basic/include/mesh.h b/samples/graphics/rsx_Basic/include/mesh.h new file mode 100644 index 00000000..63665494 --- /dev/null +++ b/samples/graphics/rsx_Basic/include/mesh.h @@ -0,0 +1,62 @@ +#ifndef __MESH_H__ +#define __MESH_H__ + +#include + +using namespace Vectormath::Aos; + +template< class T > +inline const T& min_(const T& a,const T& b) +{ + return a +inline const T& max_(const T& a,const T& b) +{ + return a +inline const T clamp(const T& val,const T& low,const T& high) +{ + return min_(max_(val,low),high); +} + +struct S3DVertex +{ + S3DVertex() {}; + S3DVertex(f32 x,f32 y,f32 z,f32 nx,f32 ny,f32 nz,f32 tu,f32 tv) + : pos(x,y,z),nrm(nx,ny,nz),u(tu),v(tv) {}; + + inline S3DVertex& operator=(const S3DVertex& other) + { + pos = other.pos; + nrm = other.nrm; + u = other.u; + v = other.v; + return *this; + } + + Vector3 pos; + Vector3 nrm; + + f32 u,v; +}; + +template< class T > +class CMeshBuffer +{ +public: + CMeshBuffer() : indices(NULL),cnt_indices(0),vertices(NULL),cnt_vertices(0) {}; + + u16 *indices; + u32 cnt_indices; + + S3DVertex *vertices; + u32 cnt_vertices; +}; + +typedef CMeshBuffer SMeshBuffer; + +#endif diff --git a/samples/graphics/rsx_Basic/include/rsxutil.h b/samples/graphics/rsx_Basic/include/rsxutil.h new file mode 100644 index 00000000..009bbb98 --- /dev/null +++ b/samples/graphics/rsx_Basic/include/rsxutil.h @@ -0,0 +1,36 @@ +#ifndef __RSXUTIL_H__ +#define __RSXUTIL_H__ + +#include +#include + +#define DEFUALT_CB_SIZE 0x80000 // 512Kb default command buffer size +#define HOST_STATE_CB_SIZE 0x10000 // 64Kb state command buffer size (used for resetting certain default states) +#define HOST_ADDR_ALIGNMENT (1024*1024) +#define HOSTBUFFER_SIZE (128*1024*1024) + +#define FRAME_BUFFER_COUNT 2 + +extern gcmContextData *context; + +extern u32 curr_fb; + +extern u32 display_width; +extern u32 display_height; + +extern u32 depth_pitch; +extern u32 depth_offset; +extern u32 *depth_buffer; + +extern u32 color_pitch; +extern u32 color_offset[FRAME_BUFFER_COUNT]; +extern u32 *color_buffer[FRAME_BUFFER_COUNT]; + +extern f32 aspect_ratio; + +void setRenderTarget(u32 index); +void init_screen(void *host_addr,u32 size); +void waitflip(); +void flip(); + +#endif diff --git a/samples/graphics/rsx_Basic/rsx_basic.png b/samples/graphics/rsx_Basic/rsx_basic.png new file mode 100644 index 0000000000000000000000000000000000000000..680f4afaf03f92272321f7f4235bcc761885dccc GIT binary patch literal 277138 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

5Q8f_cN>1Qw@y-8y( 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

k* zjX9n9DuOLprf&5)+%M2`N=h!;o@OB)vV?OPki@qQ-}GK~#ZViY?v~$896uX%pd8g0 z5_W&7Ba=qhW$v-DmOTzZGe~HGsmNrACd3uDQ2N|OBqB6suEz@#E*0pJLC}}CB0iv* zpk|WbMJMx$St$K1v%Sb_m1kkCs+x2#wBq5b2To2 zXaZ4@XTbYNJMuC&JZbI@T+^U|o37m7A>!qC3l9YaH@{Gn zb^NWab{((H%m(qW6lt4>HYjMBrv;{fGvhlp$kzQ6 zMhjPL&C0%Da>stH`~@J8y(q_Y+yhW44_S~6Q!xi4ww)a!67;O1{o=6+A_kC7rUz#Q z#Q+RL7i=7cT;xJ45#u8jct6~r?Dx%6pej(d{T#YU+Lh)o!-sfUYbXk|4Bd}6LS_gxHe~5acnKb)UB0{ z)>3}aA?p~6kyCb z!=~dDv&03qrdLt(yQh22i@OH7VEJ#bwVb%?2133S^sm}?9 zXl<;kKZ-GI->r!Gj?my8>2OR>Vw$EUL|osI7L?*&U-%)k0iLUP+4?h&kgzceBOG|( z<%fkW1ZA2!`ro2V-iPN2tfR~PmWSvv_dlbII!ta_h5SsoqD3!Chm>5*t2% zjfV^}s33ABwiv`|3g$kv|5a=w0g?EuhfrEUq9_9nSc#A&N?;5=vIIAS$>9h_?v*2t zqK90&H1>=@O&W&J(^KHiWIke3NQ z`iO}wWcj`^DiHc^cy9>0pC3tw%C-q<9Ms$C>ChN9A?RO!21sYH+c6}~JLTJTuv_&r zs44`iRs_2ZBHI?aj8WtS>X8VND%FG#t$(tv)eu0eDEIL~wH*y|!oic^Qy?S(HVOlU zjrQ`xQ(DihYS1oIJO@)H)6ayJ+mU`3P(v`o=8o|(dt!ldXc&JPOHDoi zwd{nP`uR^TN9(1jCss}8td0v?ikA;N`Tpwp)84O3Sp_-l(^GLi*RPK>=TY>7UECop zUNV;R{9ULMbvvZ38{e__HBsX{zqu=w8Z>)^s^jw!h(t2h93@VU`Ur164dI-ObH6U! z`l(qy_}SMzw6X1V`0SI-1=Yr>@ysjm*b!xzyA#7NWkRPg1OTeFAAkX77Z0t9cucWWC9(HSS4R&T} zd%Ntx646$-B3s;K2NF>GmyKHIt?A(yy&1q~!Zyeu%Jcg&dQ62$go7 z5H@fCg!Ai^+hx2;Cg75FV8g_W+Li)bFgR#5N!eWlC9MHrb=}1;WHF7Y`;OIJS(Jk7 z;?cNT&Q+jLY<*2sw1Ry5w+vfr5N~*}@5L8-&KHwNaauRKi`PLpxXi1C<+$y&fcL-h z?KH5PRI4dlML7t)UvNRu5s*6vHLG#?{Yq8!e$-zF7?b#_&K3vDGCJy;{{VwW_^db5Y6T@WQXHYbA zH8V+VHRdP;Q3hZORg0@58LyCZQB9wp6`rF^_)>wGfBo_OU2mO#@4Jbokz%3y`I)On zFT6hQA(+XuNrTZ>Qz&K_H=1GaGBP$8kIZ>I)V_U_!GS((NWD%0siTT}BE=fDN&0uR z$|kv2ej1KphT;mBPik>6ejO3oMlh9xn`r1nLHL*9aswKgz&R409Q_%Rl#VfOv3f{+ zjc2S9b0~8N41or~H)l>XGFnJVD>!)Hr*(6}KgG<`FJaE+0zr-0h<}C#LpS@5mAk}B z%hiiK64D)Pn^)Elat+Ln;t<)j#h4WP(Di%Xdy;m&p8_NlarduWQMs4Hc<%=2eV*q($4xQstI+(*0%4YeYe zOSt&tRUg&{N3iYQ=<`(YC<4qe>WTG@ddbJdmoi6kFXt`@+e%1Ywz(^`M{x4Gn4Gg@8h8nG8uxLdFAZ@|PpB(R@j#@`tvuyTi$98cw9~j_?XC$~efvgt zQ`3X^HmI228n^OMd{pFDbkcS=U3stndiUVyebAh@Y}f+ROzC~)_!Ms=3u`%e5jQm9 z^HcW=GMM_J@*w|+`pKRMhAq8g_yna56hOo(#S&rQ=qTIN|AM9ov`>h*Z8HS9KD3qf z;l5?J3)>>I`4O-wTX|e(ZYs32ExnKGGU>E*aW<@HrRhjLry!1PKyrv>V5I7GI|5$2 z$KF^7ECoN`=xNwG1O(TDRv4%{z6`}KB*?~oV}zsv0K}}+$e%YWOr>c67X%^ z-uZh2$>O-9*!pfWrg!`0|8bbOb$izZ%%?)K;aP)nKJE3y^mhzYrRMi_gV6!#Bxmrs z)M<AlpV5;V|^dW@LJ*$PQnU=lV9QkcSqzqTe=7Ca1v_fbP3*xW|vaG|s&A7TUw zs9NadQwd0@!T&gkOT9H7qP(I;t{+5s6S7w;pt3vy@cmr~;X+s*?1hD}zummrgSwf! z#nS+6{}peLdH;=0V^1#X0|Y-P?^!l6$nMlZr*aklmYGRL>JUbw^tbbBooXotFHB%P6Cil`JT2)K055QF1Pk6tyKu zTa3g67IZ6<(rdGf$7rf-Zsu^hayc7O)756qXu6{65^XD_Rr$r9$uAAwe+#P_)N=Lz ziH?iKUECUa@gZ^5>P1Bfi6zpjh$yQ1OcQfc7gn}2PN^cZwaJVZRCyHIM|%|0t5DJw zmJM9{EZxDI^+rr#Mh~{Fe-WH+EaM6D?4*P4Xwmi3fkUsC?0CIn|kKB@Z^`QK&FAc!Y-@Eh(&{&C}b zxXA|NHYvK|9%$*$FApLELW}5U@vkDtwHk@c?I|)h>Gr#}@C=Ap43~fiS?+PQL%|qy zc##b?Par%cs4uA*bPgq;(wTcLV{iw4-e9^~;O!cpkP-!ACHOyZ)E~wvfj4 zaIzvzK%^tu*ThT)7fXf|92K@<;q1!`Tmk!jv?#b52H16Wt%%E^Z7|1F!R{ZKYqx8I zNMyJ&VG(kj&Ky*umBSBu9uAubeW1V_7Kv>et7u zD6XPoY0x19C1E?0y4Q~uS#W|JIum1Ab@C}Lu<2=2acp^u)czsjN}l0 zRi;29qu#sqNOr%8WwijGS;Y>oVjz4*ZS&x-EB67lt#}s#)K*;Hi|3iZY&^rx{F0tj1TersR{6HxO`Ae|;bWgNhj7+tmMxr%Lrz z&V_@l)$|_i2sh?JW+C%RjCtZj7psQY+Kr$%Mt{fZzI&m2hHdF5CvL>XbWh*@r}-;y zWYJ~h$((lgzYkCzeDo%(vH3EQERS)t(?!73J{i&rAPdEi^MSEzKKQ(T**8L=|toS!`IPnmLt+ITF0)xRa-;Uw5j_t z!7PlLg%28$pluW8(NhKyjiXWk{vu7KsZ`<4dl=Q25mBTjY}vraN-phhvGD050;r@b zQ^b1LCSuRpmb(dF-D$I7685>@yaAKmD3R|Zv)5taK+)#Jijy^*@Ot@CUUhs)_RE+l z5?5Yc_mT|7)TqO>{}jb+zqGZ&(_+7CnJwn5ZCaF9)Yo~VEg{j=sLYG`pC&|E)p}1R z4kXOXIS}zv=E6_-5CVBtbk>Tn&&(`Z-k<9q{qi5qD6gT+zkr+rrew}B{4vK+g)Y~h z$NbNvU|V#O(6S2@>S$|xEa>`YD^6Z6ufA=wOZv>P_p4`^&Sq8E4G+HFi71h|OgtQX zz48Q%LWhwb7kcwqbIhK8Y=vcZ%I#T|5c) zGl-lDl5H!&FObhm#vc$e51+a%8L$nvNOC~cXa!_7Mu}97{xuLgGTbu;P;a>m*J~Zf z!~d1e%(kn{1HBAQ8KP@Ro7q+W9Gf6vjLY_^3Y*=1=i579!6tmCL8>P%!f7aD z6w|c}QFpPrf)@Qil1XiN&fN4zowZ9*`H5@oXgUl!Orib)D4rF){{p#$X}dHe#}`mp ziJ(u42J7C(jRl6*Qh+^b^xo91^LDA;-@iNwSR=NU`gyK%ECyRClK~=x<9s7T_)~>9 zS)H*C{!|VJ7o1YmY|k<vWS z_&Or_v3cTSs{zDu<@ne-=%su3b5|VM;ssLZ>rK$tTR}F@_`bH9VjQN<%>OQ_VpMq7 zN9RXe$jrLa&F9P;D;FO+?K>c&Yv)29^-X!g8@K~?|5e4+x~-JEFpvByj1t+Ko8-<= zoJ#IY7>SOpLNBV@6o#{&warUxW3Yr7y1^d8q!v51y-38;al*(5L)?SH5a=Cp)mCTv z?f+!32#Pl)0a2MtFph9!hB>@-J*9J0oQ`bZ=6bM>=?ua=9f$y?O_0jih@q8$gm9DO z7*?rQ2H*t~rP1}?H%(>11aVUk@j(I+!PP(yDyb^QnxzLDbd&1OV!Td)JplbgH* zOEAJ*l24cgIfOqlM;x;j?S#9IFh|gQ4ZhNmjD+bI8f@!W0mOwk-UwZ*S& z!Tg+5O()eh$1<4dKKxASdJGU%qE$0aD_w`8K;&w$hG@u&SEFK+Pz{o-n#L%dH*Djn znr~GcN(h2r>o-FE{sGQ`jC2a$dGSmcy~1c#WC)8_dB8&aQ$Pm2Tya7} zdQ1GG7YpHjiXG6Q)DRVKTG!TV+^^Hw7_iqGFKOR4pYS0y>{ZVW9wtX&U8I1l@un1* zZsU;3A{Tb>_KP>3#B6_Pps^*MEHVo}%WrH#s~ohd!FLd!RfDgs3ui&~jeZ`FPnJAV z&Ek+QteMsvlA02DA*R8!)^7LmQKtICL)(|bDs%8^PSe*-SDyN^)$dqcH=PuFh*c0* zwhGIH77LdqZ?;}hZDxe;mx>la%aHiUfb2?L#nYpP0gcS+IZ0bg1zguSQM<)Gs<(9L zPu0H10nNq!!WGO0L34^_i^)xCJ#P)dO|2~#u8hoHI-8^nt>Qad74Ld|1~NL$*eT2i zR|Oq*Wj?Jq3$WX2@I`GNE2+Ng*48qFVQ#rwkytSoCDihiVPzQU%1gm8w-UEvtKMDO$m;G@@|D{6WNgJ-@x>bnE5w6F?%kb^cUkt+a4&i)p#r{_U=$ zRa?izrzgiBoP1CD{qfV2ZI-So%6z}{hhug3kR>&_&cFu~3`e!c$r{hqEEsQFa-{Ic zcf*G#^GehyQ~T%mdW27Py;6ovux;I5e0OwuN$#vNSS76^UTqfY&H#H_>3U?JUD`^X z7>+nqF;zX3s?3U$9Mi26L(i@P*Ew>viHc!lZDCfNQ~4KvgK9oNBBsa(!x=uD=jJu< z!&V0;&V~Y3li2=nrU_h3z?yJiiRKmgHKBv3Q-wnfjv?pvxUDea46nGh0!rJCdI zt#>(0tli#z26H8I9Mf?cD9Q}_w7!czn?@Id%c`DtN&IF={A&I#7n9yAJYZyAnH2ji z;PUQ-xlQ%#Vj>G|+d~$rdj4M|-b{|UKE^!%JK5a=q(hCyM!(W2jvIXzM9K0jkYoW# zvKA!SM3Q)dB-O}ceJ-|!B*Fd5);Vg$SQzLaQK(MCD#?TCF`(52YhX2KQ}um5Cbx|} zKy7jlwE!JEFw_JC)UeJwK!h2ly5YkEUBSPb&boqeBxfFzTsbR?e4+Mce&~kRxNfw| z=j_;QtU6@goCHZEUvs9`92d+6yR{z z0p0(~0X1|cWi0MSb{vJ(P!k9N6ITKs7oR8CBKT2EEc=8LaX$lJ$wWk@rNFRwWMmfv zdG#H)#_tDlTHWu)RLGLOI6ngqMi(L%>dNx2GR{qVxqxaF*nQ~!wFM9hzB8+tK{wK%TsIo_D}D6&&&3i+=cJ_<$BV>7FT7E zvcCD|`JYT^g@)nJ;IBO_S4t!j(-jq#lvON38j6m2B)Ee1O)A zun!=c2DLk*n)!_xZjxAW2*_OIkmiRA`x^#KipR}Im21>0mRzs2n3mwsR>8!W_|Xzf zj3GKy06NBjK(1185Z*cA)`7^Z+9GO)o9Pt^9?Cu(5bN7N#%Q(EHuVm3U~1+F9l=Yh zZU`x2)c9qO0B1e={fU@)q>>Rc&l=DFs?P=lSQQ$Y(Y+Ns)^R*;H55ZN{Qm5eUl=?kd_J?5GHPJ{c;+{K zWNP<)2sG?1ej&DtQIh$(cf9TwvvNd!v4nUIk@1D~ww!+=>b-hp>k_1Ty> zUDus!t{a-IUN`b}<018<3O^*XsI&JH18~Mwz||BZeyPOBRN98Tp3}MYJ_*f(MzWbHU1)HX}`gd{z#>Yz(}*@F9^cFUf2F@jNTJb1no{}>8B%J z=*qnt)6dW!hvs|zg~W(6h19V)KndwVazvvNVsRwplxX~gzlE9?Jx@K+{pl%j(^I0d zp&IB}{Y3;RXLVHzxj4!bIj=?TFHHt4Pc;fQmo})sM4gia!e7hc%*EzQ!^GHw_`9fP znasqZnx)2!9B9__?3#V=&G>)_YJK&XHmCsg(!#A>7Y&zlKfud5<22!KQP)m0$X$yo z51(_bSQp;$_w(<4-`(1^Yt^S#!6w-1HYJsvz-xZEKvr>^2&wPs^Kglti-r8h-}pyAS)`*N+25r6P!x6Ly9CrG&@?3 zgkHh&4ywSa{F8Px2QppU#*CP3ATfBb!n5S8(n1;oJtn_PCg5VceER`rAL3fBe zrl{nBO8Ee<r@^`-6Kf)wHz`QZGJ;i3A*~WLH=IK=JP)Cl zYS_M_x1iEVR?0!8Q;=Qqvj~f*H92xKM}})r>wc0-XE2?e%o&9JFSQ{SZ4>hg*#;LO z${g9o6@)`_#TWIEeBIo?dr$`|C#-NCj?f%{@iPYS0emU`lL zw8b}$pTW@mBOat)VVgWCfI+sFSyJI(D8m*S{xhMcp1Ho`8 z)gIngA(oXn;SsLkWyGBUE=sXUxKaRD+y{{FR&&~a0k}M9mEdZMqZAp4$BPQX0GA12 z4qGdUVlyBw#CkUEBrcQDCIxNcr4nwawtBMTLMZ%A^v-Q2jk;;`bj9q&6GhX^$h%t$ zwmtFf;Z^PNM+&EIE@<7J6TYp`?v3ufFa!2B!9Xs%{+!P4(ocDnSW8ewc9lW!IYB%& z!#Zd!0U*HaL%68`UaYQ2i{s0HDTOJ@xCD2>D6N%(36f5SG%{Kl9+gX`VN1h%haI;Z zO;X=%vawhO{~(4+i}dt=f_2G0ExDl;qU60N@zqocQpz1HjYl2EL1TnFc!vH;>jTXe zt#$F3zx=sboF4b)W*|iA{5~ z%|4l$Gvd)=Ex)Q}cO`h)crCmowIJSDa8NEo7TGS}NU39Pe!roJ<)j(Xr^O0urGYk^ zW0mm^cOlPuF>}tX|NCuH+djMs@Qn61ODgcrQKzZd9-z2J6rhA6H(m_ z{?Xr_yL&~%iQ9)Kw}))SN3)`HFp5Pd@ZGzUlVK|u7a#u=zA{`8Oy<4fQ#VcugA804 z(U%|i)pW!HV@|rdFI@MNwLp>B@JBGKxR_8hL+q7Pbk|rM7s>2zt?1=QO9#Hik=O&n zz~a6r4yV2G0QScFQTN6L;gZ;7n3FsC$n~M9OV81lwjpcs1E_Q#MFdMJNw zz^~o-RrXKtQ(jSDIu5aov9X-3xV@kAym0&q#ZZq`;#cJ|{sOqG*pi!NUTELf6C1Pc zc7ttjaG)c9IWI@0s%O9r_p;l4c%B0{kmQON0p>m+w~Do@5JLCeq;s(TL~;*;<7jgG zH{_F1@M=+nDaZpAk(93Pi`DAFAll<(Jg(V`t~8RA4rk)kox&6}>%JLeB?3Dbo8h-!9Tm?naKj$2&FOK*>$?g`*37v{}8GmwyHh5_21Tqnq28Q;9A`|mieda}+J-2}zf#F1_67!&4N5M&|d~vSsn9W(R!=$8hQaYdJPnAxUlY+H4sSIwj85_8&LU0g?J#Z4vD;}I9aL+v* zQ;BIv=s3KAxw^HLn0iuwIU7&*o`nBPC_0ZqbLY-5c8;(>8O=HDd@VcI>{EXdUu)MH zphTA!S}+mLVR6_iQ0^nT>iUdnAoY=I4uG2;)A&mFcMQW^)gWq(`us{+xr=Lk%nf?-#)Eua$QJwVmM#cR4e_IAdEGN$<+op$ zl9s>QJ*;!Def<~ReN&Zo%g`JCMtSXnEx=xw(`ygEMWvflX!l6miHxGq8`k^TE3KH3 z1D2W;3h<)TVz^Rs-n^S)nj#Wxk9?;(Cz}1l<={5GbA<q4; zk&(Mc7+B_ihr5h2CyEyGRz+w5jnw1nqKjl9XOUJ>?IguqvpuqdtCNErGvDHjKo)M5 z-&IGt{jcT|3ROBGN9?h_ekh7vwc3oL(&EDOc$38wm+B=wy7)l%)>j+XEW4eZiy!Is zxEAD4n(h~$UuCzk`n&iL%1x!+lTU>>ocL?-(%kId&t2a9$)+3u*s@Gpwjd4!hLpAOFWYlM6Cwud(Kb4oc~_35Ipq5U^Di9LP` zKD`-WAHKA~c){$}S>ycb_Jpy%2-3uh-!UQ=T7Oo7A|>5IPF*_S)|%H4qjz>@@g*ql)piM$(qy&!$1gNfqOOJ{N6Yk=#|fNKmq z_PgI71Gb+Cxgq$4uN+ewGC&?CCW>#o72(UqZ4e%-W`Z)^ zZ0v|yF5qchp)TNQ(YOxL@JlSBnkzfe#X4Xae^rUoc!`@3ot~@TM*k&%pP_8xkBcY` zx<%jMYPb_Kh2U(neU_3?jP<|_J@F^gf#r5gEWL#~gJRx97w@8r#REYo<6O{|*`m$` z2yugRLH8AXE(mB%C7{EA<$_dnQO|*puGTrGoux4tG7}y(-G9i0IGaBgJaPo4JjsWr zl)0e)MXX6H8z)H+{>;MJ`EX@1T|G*%5BANnO5}F+tJNMtv-4LH{$Rk*hyw9{Ys1%3 zy%&S00RF|V76N`;B*}2%=Mg&vT|AF2xcD=CF$Ep)wrV(!ft)-4_amoC%&H!kBA-k; z*Mjf3OGs#5ENw-C=9d=xJV)e#)B{9`xO9SW;^xTg*hs!uju)8_f+;w6o$~~Ht^p1d zoK0UhV+srYA8lU(mt~dq%_44ySf-%JGBsst0;WcSES4eZB$BA50Xk0Nk}@SoK`d%% zji}7j3{wWEU@DGMuUle(ju~&65*XnUI%dkELMe-a!1w>3<$fMen|a^w`+mQ|^W5j$ z=Umry&V80mt+Hdo62A<2 z?#BA>+aqyfV8i1twEqL5tC{i4yr@k~^=_I&#r4V7EVUCWlrhA?NLA9^sOk@UdyxPA&2D69Ks2Udp&#>!~pGdh?mrSNKwm$vMg zOP8I&FgJ0T($<=+ZrYLtZ%v~pq+J^6G>z`?{q4YNr4Yu~8IN5MOqlkxM5-AgBd}^l zq+M3Yp2i9GO4nO0rV2sST!UZ$(pP(-is1E-tA#(Z{Ku@Xj)HVLGtzqpV(O|wM21`0 zizsTUn)DWuHv*Oap#f1)x3BO8wg3WWT1LWpu+4C}z+*y75H=Jr_ z1y)<8wryPTz+3xLO0C^mmq6Jf0<}e1e)~6>h$zuH8FeCt#V`xJiu{E=j3+nb6PP$N zEjJEFG*p~dY41E-Rb)B;*rUG5CF@swI~}&beK0GYc^qHUFeE3Og|9~d(hqS{hr?*M z&k8A)Uj|ImSI+P9)GXNZ&=wuw%riXe{e)Mbw3sf8fq1j~;sLlEBbzG_Gb{pTnWuP~ zVFBRdEV4X?IVOmax_LGuA)^Cu!d2sDEB0-UC zk?IGyORXIfUOQa%o&~@GK7$`f@pDm+UpZVgxA*M&5=6DIlKr?MQu=aIPSe)!Gf7({ zw>tCqMEhjspLGKjT`a2ehk+cRkws~IXsXMhs*0Y0b)b!NR_vJM5+8?@b09NE&wu)G z)in|W!2*YqZrw!SrC9=-5qP6twwJ#}>4s^b646G0bhrEx%hYq9=xNOlhdm5r>lP4q z%>ZN|&UAQfhw)m`4Bf$ue+a_wG2Z{3@&0z0kg;@R_A;nua5ecsV+}Zv3Y=>!mmAh{ zfjMXOmGg>lQSPQ`-HERmsBjvM1=8mX?TA>GTp-;SBU2SufLM z_Pgb!@wsDfZTs@pglbop?AXHcF{|e$zWJ96-IbXLH~CE^SjcG2!!!OF3>(vU{*eZn zjwMu-JWF#9{wTwa`WcM~00I0%gPOC?wLc02QV*n=ne!yOclJ5?$1d+o78XRStoLZ$ z;vZ>hls}WJeoI>z{#aBC>ljLhs&t7J-njkm#y_5MvU~FqD|G+brqp@3tODCM+~C>7*Lz_s%%#O)BcGBK{vsF52*ib|C`?vloEeTL^73FaR<2&4o?w|X2KpC zF=2wExfC|jxA5n^WX#Sy?{o9L{DuF3+Jf7A zvSwDgZc3V;>UaB*USHsiY}#_qM%Vo%w)=J!^VxQZ`UemeHt?YQ}350Kw;lv%Bp zs1^Ht{SNsFi!Be|Exu3(v)bKv)DE_ozi0(~e%DV+c_e5sVhq@9+S#+T2e!uO734B{ zyjZjGeApwt`JrM*K&+yXYVQCuDpW@Ws2YDv+L*Ffx5NYU%)2o6Im;vj*B9&u!; zY@Nx^&8PmD*;Kr2Ho4Dt#!y6xB`2+dfT+Kcj<11Qzv&G z=#0?JU~%4-igV-Ovvr80?`4a?U^|>wj2Sy$mz6uPX(K^~GoPz+_b>VM02<1@)_kLwyXc6E+~=Lo4r?JRm4Inp*|@GsQlaD$q8#TTqjRJ}Z9qcfPG3 zp2Tk!NgUSQqa6Rua~~T`dP`b19jZFDbCs{~epxL4#pb)LwM>*Qc%yecNUH3+KZP2OeGjd!B_Fqi2o4mgO^Wk)nH?~Bzj=u^iRQ`;SJ{yVivL}u?ATx=##`~VuKlyqUazeA#5O24>D3?SIN&}VB>2hR zS66)d>3h()w7mBobRCmnJ%Qpb>!ANwp}1RK62`?|+teHCxZco8*z)sgZ500c%S$Sv z+@WJp{`$3vi=o#zdSC&3IG}Zz>ADPl9I!1c?tL&I`jtheY<>a)2PI0(H=n>@z5t4{tu^uxPFEct>O-phpjk94WcI(s+P0Ri};#VE=@nnK5xFz~4cH!{IRFJq|#fK}}9M@C)}R z8eUvi`5~zIn9%0GL7;e9en0Wchci|~qo|n63i#UP#EOMdL1^osX3LKyv~`@8IKb#)!4!cQh@>x7*z?NA1B>EZQGmSlrZS~+t zf4KV~Z(>Qs)HbRgRnDLa-_yZZDlMl81h2#wIxe%Uet2?2N!FRlx&ui;so`~}1GcE_ za~t#j!~iWq1H=novgpzeUjMio92bfRFVy>tgD)|jm8^u4jV2+8pRH?f7-QBzTn@m| zg!%7a_dxXJF4;)fOoPuqcFHH(MkUob(wW1|P;+O_ziBd|b~s;|)LA4)XDFfY=`i9J14^jdjEs}CY#HW21?9c^7AYrZ_2aw= z&l^cV`m`8bYXU&sV3w581a=4F(qqRoodSNUpT@(2h<~0M{HV=l=p3n7l~V@wpwO}w zcA+J7A=GP+1lpsX5!77p3a<>T_%=%*|-!?HO_P_Vw}62sag*_$s}?p2e~s469b<*1>QpOn(Yn_}6o~K}E3elqH=e zLq_{tg@OI)BfTA`tw!6WM|$^h-r%|nY|?QVxc-lyz&qq`tDHS^{VA@E9P`d$;sz%&I8aReJ9i7Fz(!6H=#`=v`tt;zOz{&La+%m z4zt1@X!wGf@ShIMz4qX`3m!y@zsDtz z0UwXJuZczmP*wh>M58fp={XwV5ZlM6GM=W!YviRc@-3~Em+Dd3#EFcDU9JwM*vePxrlMCBfjjoZ z?QvL02*EuKEEU}E9dwa}F4hZxE)pLY&4ABfL4@rDHW;TB7|j4-ac>YI0T6DUn<*f4 z;Hm{zMV*Ho>s|!m|4PEcZU(}GvF79o!aI@h=LO;S3p!lmYN+eQNjXS5oClZ*N$lDm z8^N~bO@M5Ifae$D6AXO(iO(PW(XjO;jAM&UXY%byF`FZLr+skiP{{E5rc?gCg3kZ3 zd53*b;r&0~&Y4s1kqrJStZ3cTjwW_pg9+3XL+1|`k-v(G;*e^$oc^9-qc(PBx!Vd` z2&zVtCDGLcBE0cadav-V9=33^K%Bx^LEgPW(ag>wd{MZ?&T`iFJm2om^HP^I+rjyS zMz}pqk%HB$U$Dk7CvZnr1Qmf~bI>LkW-N1e3|zn>q+mJFX`_op!rIl=Oa8iogi)|^ z04GauNAK>X0kRuywv$Iq&!4MK9m3auRnrJ80Nx8H_ zL7{~w_ZbGWrO;YIYe|FIyy3!Vh3vyUEM|^t{zWT4wWa83X(^hH=@w?4I_gS?XVqH- zouB5}JlPm|zcke~_cxw+8L$-emLuzU^SQ9lWWh=|)G=X9P-vjd;X`9rN*A z1eOgi6!F=62Ifv>eYmYLAvH!{Gn54ruiY;xF~dSrEB`TrHd{I_fw&O{+2Qb!5v;>dm}8CWdRSSpD-yMC0gVQIrx z026V`mE)ObXOt}Ehyx8cA2&)?s-z|;(f}&hFqx^aRg9mT)GsW)cI||{CFiHM_i@Yr z$>;l3*CI0KpK2?67cl^ypk}hgVX!k5S4)`EVLzlbp-{>?s|;GSYMIPf(N5adlxL@u zi5<;jMh8m$DF#W7c4CHNybB})q5tZN7~~aQVPR2&BD%x=<&YCOsDh$X8R6~DQX{t* z8aXyJFGD?v=LpBUn3_6)loNw?J2BA_!336iY_l>#*xON!#)K_|L)iB^+VVJB)o%mx z>=^G}LC$OTs*Ic+#@j*rnf8O|3&=|sGd?ihx2S$XMukWDP*r1A6dYm{i0G&F9YmJM zYyJ7`@1$;1Df6hw)Ds|a*2XxK^-5qyb)tYlTdlMav8r*WYlo2%)=a=iz(F>bIIwJn z1Hk7@C>%6V2{#~C6lWPxTb~e$fSU(RfE7uLn$Tq;rAOWbHJ5?4hnPwuJGz1)SCIlQ@31&<2*B39%g$jA8 zz}Kt4qkYm?JZ7Z3_Ffw@M9MjTH)-^;%@Ou#w|&~PvI^r<&o%!!dD9$tek6%pe{Gx9 zc9tY;dMbza%#Y3Rt+1}?*x5+dwiu#&-VAaPf6{qAl7%^$Q| zWM}FaZP-Ossz1ZSV_a&1*i{pv#3suC;<17;bQ{;)NY+`ulN*fZnWFGrX4XkPp+hlwAS(G}o!m4%{R<*)pdTIbLzri9Ly-gMdNHK+9 zHyDp%9WGs}=Q3u5-1(8Jqsk)CwYRzH$iAz|=+n*2=#zk*(cI1`4UROt4FiektN_+6 z+%h`Ykn)G$eC0XZ0Fse;%+Ngft3d7*X;7jqvSx^|PPuScLFTH~kz@_XmYDlGem}}i zO|gqa$o!EA+5q+oGUCX9(Ja61S-OVlFzK92798X;g-i}~c*BeiHi);v-t1S@|3h=@ z`!^#Q7~el>3K$On`oDwzC-j~joy!g@Xd=UzAqS`Aon0|VGDp*vk58m17e>``3j2t9M3ZzCMt=##ZhD z{Z*MpG;BmRf|D3#4e35W<^bDF(0=TN=H={Ji~>4~ANFv5*u(jSqQhi#jt`q7utUcR z=DENhc7tF*OA?y@jwA}5YshRETZp5uQ`w&^C;aObVMpR)(M~my<@lmo!sI31l(5QP z?kG2%nIv}iCHa@1M;MhWWogp`LY)zZRFL6HKVC&**d>jLIhj1hCf<#33g_a_8%L?4 z23>Tj&@d~z#O_|8n{>uBKwG;20D0@I0Qr9BGH<`CDKSwDb5T<;SI!1@bfP3?rbGFV z+($*F8J=>OC5@lm6SR&XhqZk0D-+_=_V!(COLiX1hbE?>r;VEBqWp>Yxe!0y8$J|k zqT=CjA3MEp)G`%I<&JXRjr_U4G9i&Ihwe5ZSes;WdTqK@Sdqzzwds7LdPG2;4bK-? zo8~og@tY`Zw%|!;u+{!N%%D@eFAq6JSy~x6fZ_X-%BhnsOvYlDE7|9l+`(Z#KalAZ z9`n;96GoprAD2|_R?%v6>h|$>x2<@;d?pY69>zH{NuF}CAve#pIJ2-1ME|y*ErOD! z1T?p*VxX`WC6(Z;Qs(pPCi{=1o1StiIB8HMQ;I^Q$apC~xIz?yBriPH)*F&Y!f)jhgFoA| z>H#F?@&kWeZpbc8)@E`L`49wZc!$I)+2pPH*oiSG7%y&ud=|y=;zp|u#W@5_BG(vQ$r<7_0jXcI==?JF_)#toO=ceSxSPgq~PM-CgMdJ#1qqISzS#g$jeo6A8LLK zU*J8-Go#2LypgC3=ouKS zJPw}?zyYH5_$M)&&-Xewxb0HN@%sJe&OaO!@%q(EtCuFtTXL#P)&}f$_=>!6WX?vJ zd_tEkOP`|%@Ei}ikC5Trm;fze1=Cb4VWtrVL}%Nq4d=sf9iT7wpB`jfx4-|B6+0I1 z9;&)rUJmn`cr7so7v}dO?XT=_k`FdyTH9ndnf{10D{A_J6NWk?oVIK?b?dYa{ofp5 zz@c6iM|UEikr59BcBMdo%+ajU98(GCaj!5$>SRkbWP<~4L@1Py^w-^hxvU6!S%Id& zsNl!fI^aOwzJdJbIq>$77~E}9gdim;-5K~r_vO){M<9CNg-f50a4#g@`Z);Mg(rY6 zJUmDuVK4PsE>I!Ynumd{fPdNWRnH-$J+3}7P{am8Q1b+|unZaxK;1+)xWST4d;;=% zaI03&A&gj27(w?zat@mEk>1e&IV2`Z&vDad2x_h{6KG)nRxm3x@_JAhCCt<8VGc48 z&jp~xy{|#z6{Vuy8=zWW(koRiq`{8@ECDIm3h0XGgopxZht-HB=~=U@A`Y3KYfksP zS1U1*T(fI2F7jz}=8d(iC-}8o#HBT@*|D`{#yo<|Ow3F`QkI0@ZZIlHdWSa<7(x+9 zPN$r0^^wR$&y$C4BV;h~1*wKgC{qgo#Cv1adPugWHI>mg)fkenL~2-EZkg5-#=d+a z&G-%&E;3h6xMRgj?YQ-$$49%bkpWX;Bi4~a;SRHP7}^@3R{rHpxVxffQGeu_X3JeJ zSh1p@M3@p+`(9Ga>lbPluV1*Wu&m8}TwHDN%Khh8l$`I+<9@E-LExg#$X!&%9@7%o z7xTY}nje#z2%$}%zI70?D2s2WcdITJ(gOBs=j;f$I_d~;LhciC2HHdlp+)G_Oag~; zxWrBm<)3}BQdbUhQ<7o7)Pfz@%-s^YDQ0F_JxlGOcOAoAnW$JqL9gU}-a!%2rdmSG zg!?8yS<@L5U?qH6|Dm^eE#~Nk4>Maxja$N-&{+gH39B`@xhbKCP#kvWNHWpbhUZh>DI43}v8k2esIU}t$I;)86>Ks8!@h&k3q8|u}-QQZ4YGja< zD$5Q;my)6x(G{j~mvDO8@=#E&R5x~#wBJRd7og(tK_3}j-6YD#%^`mmi^Z1WuTWBF z>{kr8IL0EY@-atW&K5jl;U@edW=pbO9C5UKD-U7!X9+|z@;Wc7FL>l|&fZ|LXGQcw z6f(uRsG|&qe| zXLs4`R6?cXmr;{pu=7T|XD85mbIniqm>E>TON6`Jj!%)^XUY?9=E#zH4*_e(sb^R_jl=iH8Gl&(m|v@H)|NJ&M5V9V!fUA|k z1+~4yf*L6n6oLvDvoARz55dfB>PpR#oJ>JOHL*O8sNsZavr`!a=6OUD<{qm-qys>k zPAlVoL<|cCMRvuBON|Bi{cz&yD&pB9nT!)zCni~fduJJw59s7`@?L%^qWzs^tPE;h z?%G-O#ZYBxmi1qLmBfG&I916n8EGxKTqipHE+XNlFDHyHJ3lkY%Prv-n_SP2>MmMb zi=AB`D-Z7sM6*YPfC_Z$!YQN$`Jp&7s0j1n9P8DJ|mon49 zeyNEf`>zrN@61Ue6M!Wm3os3bhbb?JQh`ybl_dcc6?##_C^*1pDaxG%@knX=+>rc) z!CRrJE29*=qGY;F^)T56Mz=|V%{mC#1V3$if~&!P_&H=8iL)%+DYMEacLHxbq4F@$ zRmn0>8-#FG>;=N3e@BjZuCHN7Tyl{(6a0;fCe3>*GH3gDhwG{Zi0@0JjgHMgpD>xeo&2#>2 zA@lhr=?A{SMhT(hWFy?%Ua(_D{bR-=GYS%9Do^L)e6gYgnhc?Tt-u`)X=1IA#@1+S zO{8etC#22+!lb%FshR0&V;#!-rKUA(Ok-lViAi1`)jbg582)+um8>&0QQHfhQvEIr zba4ItaFsXr3g?u(^B+a`O4&ubTzOK=^%4AlMOAC%R%}+z+#`B}lY}7!MQ8gDzT7L= z$4j0~j&`Q*35^3p;}|>;x(({^Kic5cFI2f6W2en{{vJ zUxPEX?2WrogV$?z`&2^#LR;P>I7Ycc^lR=>1m7-qdKUk~D(gya-bdKvuR}~41}~qs z>%-s?wrf;ZMJHfKQ<8wYBc=emI3JP`$hN#B#0W5u&`01ficKJ6>H(`qY*CV8o(LXoZD*QO&nS8n5lt^b|En4|?uOiQrvzh`MMR))lG+K(L35>1

ol^X+D`WQTM;Vf265@Ky9pN(htYR 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 K?2=gvHvA8}vz;;k literal 0 HcmV?d00001 diff --git a/samples/graphics/rsx_Basic/shaders/diffuse_specular_shader.fcg b/samples/graphics/rsx_Basic/shaders/diffuse_specular_shader.fcg new file mode 100644 index 00000000..c378a720 --- /dev/null +++ b/samples/graphics/rsx_Basic/shaders/diffuse_specular_shader.fcg @@ -0,0 +1,9 @@ + +void main +( + float4 color_in : COLOR, + out float4 color_out : COLOR +) +{ + color_out = color_in; +} \ No newline at end of file diff --git a/samples/graphics/rsx_Basic/shaders/diffuse_specular_shader.vcg b/samples/graphics/rsx_Basic/shaders/diffuse_specular_shader.vcg new file mode 100644 index 00000000..0e2e3570 --- /dev/null +++ b/samples/graphics/rsx_Basic/shaders/diffuse_specular_shader.vcg @@ -0,0 +1,16 @@ +void main(float4 position : POSITION, + float4 color : COLOR0, + float2 texcoord : TEXCOORD0, + + uniform float4x4 modelViewProj, + + out float4 oPosition : POSITION, + out float4 oColor : COLOR0, + out float2 oTexCoord : TEXCOORD0 + ) + { + oPosition = mul(modelViewProj, position); + oColor = color; + oTexCoord = texcoord; + } + \ No newline at end of file diff --git a/samples/graphics/rsx_Basic/source/main.cpp b/samples/graphics/rsx_Basic/source/main.cpp new file mode 100644 index 00000000..42d7a73c --- /dev/null +++ b/samples/graphics/rsx_Basic/source/main.cpp @@ -0,0 +1,251 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "acid.h" +#include "mesh.h" +#include "rsxutil.h" + +#include "diffuse_specular_shader_vpo.h" +#include "diffuse_specular_shader_fpo.h" + +typedef struct +{ + float x, y, z; + u32 rgba; +} Vertex_t; + +Vertex_t* vertex_buffer; +u32 VertexBufferOffset; + +u32 running = 0; + +u32 fp_offset; +u32 *fp_buffer; + + + +// vertex shader +rsxProgramConst *projMatrix; + +rsxProgramAttrib* mPosIndex = NULL; + +rsxProgramAttrib* mColIndex = NULL; + +u32 color_index; +u32 position_index; + + +void *vp_ucode = NULL; +rsxVertexProgram *vpo = (rsxVertexProgram*)diffuse_specular_shader_vpo; + +void *fp_ucode = NULL; +rsxFragmentProgram *fpo = (rsxFragmentProgram*)diffuse_specular_shader_fpo; + + +SYS_PROCESS_PARAM(1001, 0x100000); + +extern "C" { +static void program_exit_callback() +{ + gcmSetWaitFlip(context); + rsxFinish(context,1); +} + +static void sysutil_exit_callback(u64 status,u64 param,void *usrdata) +{ + switch(status) { + case SYSUTIL_EXIT_GAME: + running = 0; + break; + case SYSUTIL_DRAW_BEGIN: + case SYSUTIL_DRAW_END: + break; + default: + break; + } +} +} + + +static void setDrawEnv() +{ + rsxSetColorMask(context,GCM_COLOR_MASK_B | + GCM_COLOR_MASK_G | + GCM_COLOR_MASK_R | + GCM_COLOR_MASK_A); + + rsxSetColorMaskMrt(context,0); + + u16 x,y,w,h; + f32 min, max; + f32 scale[4],offset[4]; + + x = 0; + y = 0; + w = display_width; + h = display_height; + min = 0.0f; + max = 1.0f; + scale[0] = w*0.5f; + scale[1] = h*-0.5f; + scale[2] = (max - min)*0.5f; + scale[3] = 0.0f; + offset[0] = x + w*0.5f; + offset[1] = y + h*0.5f; + offset[2] = (max + min)*0.5f; + offset[3] = 0.0f; + + rsxSetViewport(context,x, y, w, h, min, max, scale, offset); + rsxSetScissor(context,x,y,w,h); + + rsxSetDepthTestEnable(context,GCM_TRUE); + rsxSetDepthFunc(context,GCM_LESS); + rsxSetShadeModel(context,GCM_SHADE_MODEL_SMOOTH); + rsxSetDepthWriteEnable(context,1); + rsxSetFrontFace(context,GCM_FRONTFACE_CCW); +} + +void init_shader() +{ + u32 fpsize = 0; + u32 vpsize = 0; + + rsxVertexProgramGetUCode(vpo, &vp_ucode, &vpsize); + + projMatrix = rsxVertexProgramGetConst(vpo, "modelViewProj"); + + mPosIndex = rsxVertexProgramGetAttrib(vpo, "position"); + + mColIndex = rsxVertexProgramGetAttrib(vpo, "color"); + + rsxFragmentProgramGetUCode(fpo, &fp_ucode, &fpsize); + + fp_buffer = (u32*)rsxMemalign(64,fpsize); + memcpy(fp_buffer,fp_ucode,fpsize); + + position_index = mPosIndex->index; + color_index = mColIndex->index; + + rsxAddressToOffset(fp_buffer,&fp_offset); + +} + +void drawFrame() +{ + u32 i, color; + + setDrawEnv(); + + // Screen clear color between red and blue + static float count = 0; + count += 0.1f; + unsigned char red = ((int)count) % 255; + unsigned char green = 32; + unsigned char blue = (255 - (int)count) % 255; + color = (blue << 0) | (green << 8) | (red << 16) | (255 << 24); + // Otherwise + //color = 0; // -> Black; + + rsxSetClearColor(context,color); + rsxSetClearDepthStencil(context,0xffffff00); + rsxClearSurface(context,GCM_CLEAR_R | + GCM_CLEAR_G | + GCM_CLEAR_B | + GCM_CLEAR_A | + GCM_CLEAR_S | + GCM_CLEAR_Z); + + rsxSetZControl(context,0,1,1); + + for(i=0;i<8;i++) + rsxSetViewportClip(context,i,display_width,display_height); + + Matrix4 tempMatrix = transpose(Matrix4::identity()); + + rsxAddressToOffset((void*)vertex_buffer, &VertexBufferOffset); + + rsxBindVertexArrayAttrib(context, position_index, 0, VertexBufferOffset, sizeof(Vertex_t), 3, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context, color_index, 0, VertexBufferOffset + sizeof(float) * 3, sizeof(Vertex_t), 3, GCM_VERTEX_DATA_TYPE_U8, GCM_LOCATION_RSX); + + rsxLoadVertexProgram(context, vpo, vp_ucode); + rsxSetVertexProgramParameter(context, vpo, projMatrix, (float*)&tempMatrix); + rsxLoadFragmentProgramLocation(context, fpo, fp_offset, GCM_LOCATION_RSX); + rsxDrawVertexArray(context, GCM_TYPE_TRIANGLES, 0, 3); + +} + +int main(int argc,const char *argv[]) +{ + padInfo padinfo; + padData paddata; + void *host_addr = memalign(HOST_ADDR_ALIGNMENT,HOSTBUFFER_SIZE); + + printf("rsxtest started...\n"); + + init_screen(host_addr,HOSTBUFFER_SIZE); + ioPadInit(7); + + //Create Triangle + void* ret = rsxMemalign(128, sizeof(Vertex_t) * 3); + vertex_buffer = (Vertex_t*)ret; + vertex_buffer[0].x = -1.0f; + vertex_buffer[0].y = -1.0f; + vertex_buffer[0].z = -1.0f; + vertex_buffer[0].rgba = 0x00ff0000; + + vertex_buffer[1].x = 1.0f; + vertex_buffer[1].y = -1.0f; + vertex_buffer[1].z = -1.0f; + vertex_buffer[1].rgba = 0x0000ff00; + + vertex_buffer[2].x = 0.0f; + vertex_buffer[2].y = 1.0f; + vertex_buffer[2].z = -1.0f; + vertex_buffer[2].rgba = 0xff000000; + + init_shader(); + + + atexit(program_exit_callback); + sysUtilRegisterCallback(0,sysutil_exit_callback,NULL); + + setDrawEnv(); + setRenderTarget(curr_fb); + + running = 1; + while(running) { + sysUtilCheckCallback(); + + ioPadGetInfo(&padinfo); + for(int i=0; i < MAX_PADS; i++){ + if(padinfo.status[i]){ + ioPadGetData(i, &paddata); + + if(paddata.BTN_CROSS) + goto done; + } + + } + + drawFrame(); + + + flip(); + } + +done: + printf("rsxtest done...\n"); + program_exit_callback(); + return 0; +} diff --git a/samples/graphics/rsx_Basic/source/rsxutil.cpp b/samples/graphics/rsx_Basic/source/rsxutil.cpp new file mode 100644 index 00000000..e0efa8bf --- /dev/null +++ b/samples/graphics/rsx_Basic/source/rsxutil.cpp @@ -0,0 +1,204 @@ +#include +#include +#include +#include +#include +#include + +#include + + +#include "rsxutil.h" + +#define GCM_LABEL_INDEX 255 + +videoResolution vResolution; +gcmContextData *context = NULL; + +u32 curr_fb = 0; +u32 first_fb = 1; + +u32 display_width; +u32 display_height; + +u32 depth_pitch; +u32 depth_offset; +u32 *depth_buffer; + +u32 color_pitch; +u32 color_offset[FRAME_BUFFER_COUNT]; +u32 *color_buffer[FRAME_BUFFER_COUNT]; + +f32 aspect_ratio; + +static u32 sResolutionIds[] = { + VIDEO_RESOLUTION_960x1080, + VIDEO_RESOLUTION_720, + VIDEO_RESOLUTION_480, + VIDEO_RESOLUTION_576 +}; +static size_t RESOLUTION_ID_COUNT = sizeof(sResolutionIds)/sizeof(u32); + +static u32 sLabelVal = 1; + + +static void waitFinish() +{ + rsxSetWriteBackendLabel(context,GCM_LABEL_INDEX,sLabelVal); + + rsxFlushBuffer(context); + + while(*(vu32*)gcmGetLabelAddress(GCM_LABEL_INDEX)!=sLabelVal) + usleep(30); + + ++sLabelVal; +} + +static void waitRSXIdle() +{ + rsxSetWriteBackendLabel(context,GCM_LABEL_INDEX,sLabelVal); + rsxSetWaitLabel(context,GCM_LABEL_INDEX,sLabelVal); + + ++sLabelVal; + + waitFinish(); +} + +void initVideoConfiguration() +{ + s32 rval = 0; + s32 resId = 0; + + for (size_t i=0;i < RESOLUTION_ID_COUNT;i++) { + rval = videoGetResolutionAvailability(VIDEO_PRIMARY, sResolutionIds[i], VIDEO_ASPECT_AUTO, 0); + if (rval != 1) continue; + + resId = sResolutionIds[i]; + rval = videoGetResolution(resId, &vResolution); + if(!rval) break; + } + + if(rval) { + printf("Error: videoGetResolutionAvailability failed. No usable resolution.\n"); + exit(1); + } + + videoConfiguration config = { + (u8)resId, + VIDEO_BUFFER_FORMAT_XRGB, + VIDEO_ASPECT_AUTO, + {0,0,0,0,0,0,0,0,0}, + (u32)vResolution.width*4 + }; + + rval = videoConfigure(VIDEO_PRIMARY, &config, NULL, 0); + if(rval) { + printf("Error: videoConfigure failed.\n"); + exit(1); + } + + videoState state; + + rval = videoGetState(VIDEO_PRIMARY, 0, &state); + switch(state.displayMode.aspect) { + case VIDEO_ASPECT_4_3: + aspect_ratio = 4.0f/3.0f; + break; + case VIDEO_ASPECT_16_9: + aspect_ratio = 16.0f/9.0f; + break; + default: + printf("unknown aspect ratio %x\n", state.displayMode.aspect); + aspect_ratio = 16.0f/9.0f; + break; + } + + display_height = vResolution.height; + display_width = vResolution.width; +} + +void setRenderTarget(u32 index) +{ + gcmSurface sf; + + sf.colorFormat = GCM_SURFACE_X8R8G8B8; + sf.colorTarget = GCM_SURFACE_TARGET_0; + sf.colorLocation[0] = GCM_LOCATION_RSX; + sf.colorOffset[0] = color_offset[index]; + sf.colorPitch[0] = color_pitch; + + sf.colorLocation[1] = GCM_LOCATION_RSX; + sf.colorLocation[2] = GCM_LOCATION_RSX; + sf.colorLocation[3] = GCM_LOCATION_RSX; + sf.colorOffset[1] = 0; + sf.colorOffset[2] = 0; + sf.colorOffset[3] = 0; + sf.colorPitch[1] = 64; + sf.colorPitch[2] = 64; + sf.colorPitch[3] = 64; + + sf.depthFormat = GCM_SURFACE_ZETA_Z24S8; + sf.depthLocation = GCM_LOCATION_RSX; + sf.depthOffset = depth_offset; + sf.depthPitch = depth_pitch; + + sf.type = GCM_SURFACE_TYPE_LINEAR; + sf.antiAlias = GCM_SURFACE_CENTER_1; + + sf.width = display_width; + sf.height = display_height; + sf.x = 0; + sf.y = 0; + + rsxSetSurface(context,&sf); +} + +void init_screen(void *host_addr,u32 size) +{ + u32 zs_depth = 4; + u32 color_depth = 4; + + rsxInit(&context,DEFUALT_CB_SIZE,size,host_addr); + + initVideoConfiguration(); + + waitRSXIdle(); + + gcmSetFlipMode(GCM_FLIP_VSYNC); + + color_pitch = display_width*color_depth; + depth_pitch = display_width*zs_depth; + + for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { + color_buffer[i] = (u32*)rsxMemalign(64,(display_height*color_pitch)); + rsxAddressToOffset(color_buffer[i],&color_offset[i]); + gcmSetDisplayBuffer(i,color_offset[i],color_pitch,display_width,display_height); + } + + depth_buffer = (u32*)rsxMemalign(64, display_height*depth_pitch); + rsxAddressToOffset(depth_buffer,&depth_offset); + +} + +void waitflip() +{ + while(gcmGetFlipStatus()!=0) + usleep(200); + gcmResetFlipStatus(); +} + +void flip() +{ + if(!first_fb) waitflip(); + else gcmResetFlipStatus(); + + gcmSetFlip(context,curr_fb); + rsxFlushBuffer(context); + + gcmSetWaitFlip(context); + + curr_fb ^= 1; + setRenderTarget(curr_fb); + + first_fb = 0; +} From 2449900804e40c78144ef5071c21fa48626cee47 Mon Sep 17 00:00:00 2001 From: crystalct Date: Fri, 22 Jan 2021 12:05:09 +0100 Subject: [PATCH 43/56] Update Readme.md --- samples/graphics/rsx_Basic/Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/graphics/rsx_Basic/Readme.md b/samples/graphics/rsx_Basic/Readme.md index 42540369..96ae0c2a 100644 --- a/samples/graphics/rsx_Basic/Readme.md +++ b/samples/graphics/rsx_Basic/Readme.md @@ -1,4 +1,4 @@ A basic example of to use RSX and Vertex/Fragment without camera position, eyes position, lights, 3D objects, textures, etc etc. Just draw a tringle..... -![RSX basic example](https://github.com/crystalct/PS3LibrariesUpdate/blob/master/samples/cairo_clock/rsx_basic.png) +![RSX basic example](https://github.com/crystalct/PSL1GHT/edit/develop/samples/graphics/rsx_Basic/rsx_basic.png) From 24118d74e08965029e57818cf16b98ddf4a3feba Mon Sep 17 00:00:00 2001 From: crystalct Date: Fri, 22 Jan 2021 12:06:31 +0100 Subject: [PATCH 44/56] Update Readme.md --- samples/graphics/rsx_Basic/Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/graphics/rsx_Basic/Readme.md b/samples/graphics/rsx_Basic/Readme.md index 96ae0c2a..183e107e 100644 --- a/samples/graphics/rsx_Basic/Readme.md +++ b/samples/graphics/rsx_Basic/Readme.md @@ -1,4 +1,4 @@ A basic example of to use RSX and Vertex/Fragment without camera position, eyes position, lights, 3D objects, textures, etc etc. Just draw a tringle..... -![RSX basic example](https://github.com/crystalct/PSL1GHT/edit/develop/samples/graphics/rsx_Basic/rsx_basic.png) +![RSX basic example](https://github.com/crystalct/PSL1GHT/blob/develop/samples/graphics/rsx_Basic/rsx_basic.png?raw=true) From d6e350cdebd8bccc88aba07f441a1d681852c07b Mon Sep 17 00:00:00 2001 From: crystalct Date: Fri, 22 Jan 2021 12:07:05 +0100 Subject: [PATCH 45/56] Update Readme.md --- samples/graphics/rsx_Basic/Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/graphics/rsx_Basic/Readme.md b/samples/graphics/rsx_Basic/Readme.md index 183e107e..50b42f25 100644 --- a/samples/graphics/rsx_Basic/Readme.md +++ b/samples/graphics/rsx_Basic/Readme.md @@ -1,4 +1,4 @@ -A basic example of to use RSX and Vertex/Fragment without camera position, eyes position, lights, 3D objects, textures, etc etc. +A basic example to use RSX and Vertex/Fragment without camera position, eyes position, lights, 3D objects, textures, etc etc. Just draw a tringle..... ![RSX basic example](https://github.com/crystalct/PSL1GHT/blob/develop/samples/graphics/rsx_Basic/rsx_basic.png?raw=true) From fcfbf6cf54c150548ead0d1269f3f16625419215 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 24 Jan 2021 21:57:51 +0100 Subject: [PATCH 46/56] add RSX Basic Cube sample --- samples/graphics/rsx_Basic_Cube/Makefile | 164 + .../graphics/rsx_Basic_Cube/include/acid.h | 2939 +++++++++++++++++ .../graphics/rsx_Basic_Cube/include/mesh.h | 62 + .../graphics/rsx_Basic_Cube/include/rsxutil.h | 38 + .../shaders/diffuse_specular_shader.fcg | 13 + .../shaders/diffuse_specular_shader.vcg | 21 + .../graphics/rsx_Basic_Cube/source/main.cpp | 425 +++ .../graphics/rsx_Basic_Cube/source/perlin.cpp | 68 + .../rsx_Basic_Cube/source/rsxutil.cpp | 207 ++ 9 files changed, 3937 insertions(+) create mode 100644 samples/graphics/rsx_Basic_Cube/Makefile create mode 100644 samples/graphics/rsx_Basic_Cube/include/acid.h create mode 100644 samples/graphics/rsx_Basic_Cube/include/mesh.h create mode 100644 samples/graphics/rsx_Basic_Cube/include/rsxutil.h create mode 100644 samples/graphics/rsx_Basic_Cube/shaders/diffuse_specular_shader.fcg create mode 100644 samples/graphics/rsx_Basic_Cube/shaders/diffuse_specular_shader.vcg create mode 100644 samples/graphics/rsx_Basic_Cube/source/main.cpp create mode 100644 samples/graphics/rsx_Basic_Cube/source/perlin.cpp create mode 100644 samples/graphics/rsx_Basic_Cube/source/rsxutil.cpp diff --git a/samples/graphics/rsx_Basic_Cube/Makefile b/samples/graphics/rsx_Basic_Cube/Makefile new file mode 100644 index 00000000..c76ea9e9 --- /dev/null +++ b/samples/graphics/rsx_Basic_Cube/Makefile @@ -0,0 +1,164 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PSL1GHT)),) +$(error "Please set PSL1GHT in your environment. export PSL1GHT=") +endif + +include $(PSL1GHT)/ppu_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source ../debugfont_renderer/source +DATA := data +SHADERS := shaders ../debugfont_renderer/shaders +INCLUDES := include ../debugfont_renderer/include + +TITLE := RSX Test - PSL1GHT +APPID := RSX00003 +CONTENTID := UP0001-$(APPID)_00-0000000000000000 + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +CFLAGS = -Wall -mcpu=cell $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lsimdmath -lrsx -lgcm_sys -lio -lsysutil -lrt -llv2 -lm -lsysmodule -lpngdec + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ + $(foreach dir,$(SHADERS),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export BUILDDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) +VCGFILES := $(foreach dir,$(SHADERS),$(notdir $(wildcard $(dir)/*.vcg))) +FCGFILES := $(foreach dir,$(SHADERS),$(notdir $(wildcard $(dir)/*.fcg))) + +VPOFILES := $(VCGFILES:.vcg=.vpo) +FPOFILES := $(FCGFILES:.fcg=.fpo) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(addsuffix .o,$(VPOFILES)) \ + $(addsuffix .o,$(FPOFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES), -I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(LIBPSL1GHT_INC) \ + -I$(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + $(LIBPSL1GHT_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).self $(OUTPUT).fake.self + +#--------------------------------------------------------------------------------- +run: + ps3load $(OUTPUT).self + +#--------------------------------------------------------------------------------- +pkg: $(BUILD) $(OUTPUT).pkg + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).self: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .bin extension +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.vpo.o : %.vpo +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.fpo.o : %.fpo +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/samples/graphics/rsx_Basic_Cube/include/acid.h b/samples/graphics/rsx_Basic_Cube/include/acid.h new file mode 100644 index 00000000..d1643518 --- /dev/null +++ b/samples/graphics/rsx_Basic_Cube/include/acid.h @@ -0,0 +1,2939 @@ +/* GIMP RGBA C-Source image dump (acid.c) */ + +static const struct { + unsigned int width; + unsigned int height; + unsigned int bytes_per_pixel; /* 3:RGB, 4:RGBA */ + unsigned char pixel_data[128 * 128 * 4 + 1]; +} acid = { + 128, 128, 4, + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\335\335\335\0\265\265\265\0\221\221\221\0qqq\0UUU\0===\0)))\0\31\31" + "\31\0\15\15\15\0\5\5\5\0\1\1\1\0\1\1\1\0\5\5\5\0\15\15\15\0\31\31\31\0))" + ")\0===\0UUU\0qqq\0\221\221\221\0\265\265\265\0\335\335\335\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\345\345\345\0\251\251\251\0qqq\0===\0\15\15\15\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\15\15\15\0===\0qqq\0\251\251\251\0\345\345\345\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0qqq\0---\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\7\0\0\0\15\0\0\0" + "\23\0\0\0\30\0\0\0\35\0\0\0!\0\0\0$\0\0\0&\0\0\0(\0\0\0(\0\0\0'\0\0\0$\0" + "\0\0!\0\0\0\35\0\0\0\31\0\0\0\23\0\0\0\15\0\0\0\7\0\0\0\2\0\0\0\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0---\0qqq\0\271\271\271\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\265\265\265\0aaa\0\21\21\21\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0\17\0\0\0\30\0\0\0\40\0\0\0+\0\0\0B\0" + "\0\0\\\0\0\0t\0\0\0\211\0\0\0\234\0\0\0\254\0\0\0\271\0\0\0\304\0\0\0\313" + "\0\0\0\313\0\0\0\304\0\0\0\271\0\0\0\254\0\0\0\234\0\0\0\211\0\0\0t\0\0\0" + "\\\0\0\0B\0\0\0+\0\0\0!\0\0\0\30\0\0\0\17\0\0\0\6\0\0\0\1\0\0\0\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\21\21\21\0aaa\0\265\265\265\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\331\331\331\0yyy\0\35\35\35\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\2\0\0\0\14\0\0\0\30\0\0\0&\0\0\0?\0\0\0d\0\0\0\211\0\0\0\254\0\0\0\311" + "\0\0\0\331\0\0\0\341\0\0\0\347\0\0\0\354\0\0\0\361\0\0\0\365\0\0\0\370\0" + "\0\0\373\0\0\0\375\0\0\0\375\0\0\0\373\0\0\0\371\0\0\0\365\0\0\0\361\0\0" + "\0\355\0\0\0\347\0\0\0\341\0\0\0\331\0\0\0\312\0\0\0\254\0\0\0\211\0\0\0" + "d\0\0\0?\0\0\0&\0\0\0\30\0\0\0\15\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0" + "\35\35\35\0yyy\0\331\331\331\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0QQQ\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\16\0\0\0\33\0\0" + "\0.\0\0\0Y\0\0\0\211\0\0\0\265\0\0\0\325\0\0\0\343\0\0\0\354\0\0\0\365\0" + "\0\0\374\40!\0\377FH\0\377hk\0\377\206\213\0\377\240\246\0\377\267\275\0" + "\377\311\321\0\377\330\341\0\377\344\354\0\377\344\354\0\377\330\341\0\377" + "\311\321\0\377\267\275\0\377\240\246\0\377\206\213\0\377hk\0\377FH\0\377" + "\40!\0\377\0\0\0\375\0\0\0\365\0\0\0\355\0\0\0\343\0\0\0\326\0\0\0\266\0" + "\0\0\211\0\0\0Z\0\0\0/\0\0\0\33\0\0\0\16\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0" + "\0\0\0\0QQQ\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\255\255\255\0===\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\11\0\0\0" + "\27\0\0\0-\0\0\0]\0\0\0\224\0\0\0\304\0\0\0\336\0\0\0\354\0\0\0\367\30\31" + "\0\376QT\0\377\206\213\0\377\267\275\0\377\344\354\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\344\354\0\377\267\275\0\377\206\213\0\377QT\0\377\30\31\0" + "\376\0\0\0\367\0\0\0\354\0\0\0\337\0\0\0\305\0\0\0\224\0\0\0]\0\0\0-\0\0" + "\0\30\0\0\0\11\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0===\0\255\255\255\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265" + "\0===\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\15\0\0\0!\0\0\0H\0\0\0\204" + "\0\0\0\274\0\0\0\336\0\0\0\357\0\0\0\373BD\0\377\206\213\0\377\306\315\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\306\315\0\377\206\213\0\377BD\0\377\0\0\0\373" + "\0\0\0\357\0\0\0\337\0\0\0\275\0\0\0\204\0\0\0I\0\0\0!\0\0\0\15\0\0\0\3\0" + "\0\0\1\0\0\0\0\0\0\0\0===\0\265\265\265\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321\321\0QQ" + "Q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\17\0\0\0'\0\0\0[\0\0\0\236\0\0" + "\0\324\0\0\0\353\0\0\0\371FH\0\377\225\232\0\377\340\350\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\340\350\0\377\225\232\0\377FH\0\377\0\0\0\371\0" + "\0\0\353\0\0\0\324\0\0\0\236\0\0\0\\\0\0\0'\0\0\0\17\0\0\0\3\0\0\0\1\0\0" + "\0\0\0\0\0\0QQQ\0\321\321\321\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0yyy\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\16\0\0\0(\0\0\0c\0\0\0\250" + "\0\0\0\332\0\0\0\361$%\0\375~\203\0\377\325\335\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\325\335\0\377~\203\0\377$%\0\375\0\0\0\361\0\0\0" + "\333\0\0\0\251\0\0\0c\0\0\0(\0\0\0\17\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0yyy" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\265\265\265\0)))\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\13\0\0\0$\0\0\0_" + "\0\0\0\250\0\0\0\334\0\0\0\364BD\0\376\244\252\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\244\252\0\377BD\0\376\0\0\0\364\0\0\0" + "\335\0\0\0\250\0\0\0`\0\0\0%\0\0\0\13\0\0\0\2\0\0\0\1\0\0\0\0)))\0\265\265" + "\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\4\0\0\0\34\0\0\0O\0\0\0\234\0\0\0\332\0\0\0\364FH\0\376\267\275\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377FH\0\376\0\0\0\364\0" + "\0\0\332\0\0\0\234\0\0\0P\0\0\0\34\0\0\0\4\0\0\0\1\0\0\0\0\0\0\0\0qqq\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\315\315\315" + "\0""555\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\17\0\0\0""0\0\0\0\203\0\0\0\321\0" + "\0\0\360+-\0\375\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377+-\0\375" + "\0\0\0\360\0\0\0\322\0\0\0\203\0\0\0""1\0\0\0\17\0\0\0\3\0\0\0\1\0\0\0\0" + """555\0\315\315\315\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\235\235\235\0\1\1\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\4\0\0\0\40\0\0\0`\0\0\0\262\0\0\0\346\0\0\0\374~\203\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377~\203\0\377\0\0\0\374\0\0\0\347\0\0\0\263\0\0\0a\0\0\0\40\0\0\0\5\0\0" + "\0\1\0\0\0\0\1\1\1\0\235\235\235\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\14\0\0" + "\0/\0\0\0\207\0\0\0\326\0\0\0\366FH\0\376\311\321\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\311\321\0\377FH\0\376\0\0\0\366\0\0\0\327\0\0\0\207\0\0\0" + """0\0\0\0\14\0\0\0\2\0\0\0\1\0\0\0\0uuu\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\375" + "\375\375\0UUU\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\30\0\0\0S\0\0\0\252\0\0\0\345" + "\0\0\0\374\206\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\374\0\0\0\346" + "\0\0\0\253\0\0\0T\0\0\0\31\0\0\0\4\0\0\0\1\0\0\0\0UUU\0\375\375\375\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\351\351\351\0==" + "=\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\"\0\0\0q\0\0\0\316\0\0\0\363/1\0\376\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377/1\0\376" + "\0\0\0\364\0\0\0\316\0\0\0q\0\0\0\"\0\0\0\5\0\0\0\1\0\0\0\0===\0\351\351" + "\351\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0---\0\0\0\0\0\0\0\0\1" + "\0\0\0\6\0\0\0*\0\0\0\205\0\0\0\332\0\0\0\373\\`\0\377\357\370\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357\370" + "\0\377\\`\0\377\0\0\0\373\0\0\0\332\0\0\0\206\0\0\0+\0\0\0\6\0\0\0\1\0\0" + "\0\1---\0\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\331\331\331\0%%%\0\0\0\0\0\0\0\0\1\0\0\0\11" + "\0\0\0""2\0\0\0\225\0\0\0\342\0\0\0\374\202\207\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\374\0\0\0\342\0\0\0" + "\226\0\0\0""2\0\0\0\11\0\0\0\1\0\0\0\1%%%\0\331\331\331\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\335\335\335\0%%%\0\0\0\0\0\0\0\0\1\0\0\0\15" + "\0\0\0?\0\0\0\243\0\0\0\350\2\2\0\375\240\246\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377" + "\2\2\0\375\0\0\0\350\0\0\0\244\0\0\0@\0\0\0\15\0\0\0\2\0\0\0\1%%%\0\335\335" + "\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\351\351\351\0---\0\0\0\0\0\0\0\0\1\0\0\0\16" + "\0\0\0H\0\0\0\257\0\0\0\355\25\25\0\376\267\275\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\25\25\0\376\0\0\0\355\0\0\0\257\0\0\0H\0\0\0" + "\17\0\0\0\2\0\0\0\1---\0\351\351\351\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\375\375\375\0===\0\0\0\0\0\0\0\0\1\0\0\0\15" + "\0\0\0H\0\0\0\264\0\0\0\360\40!\0\376\306\315\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\40!\0\376\0\0\0" + "\360\0\0\0\265\0\0\0I\0\0\0\15\0\0\0\1\0\0\0\1===\0\375\375\375\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0UUU\0\0\0\0\0\0\0\0\1\0\0\0\11" + "\0\0\0?\0\0\0\257\0\0\0\360$%\0\376\315\325\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\315\325" + "\0\377$%\0\377\0\0\0\360\0\0\0\257\0\0\0@\0\0\0\11\0\0\0\1\0\0\0\1UUU\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0""2" + "\0\0\0\244\0\0\0\355\40!\0\376\315\325\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377\40!\0\376\0\0\0\355\0\0\0\244\0\0\0""2\0" + "\0\0\6\0\0\0\1\0\0\0\0uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\235\235\235\0\0\0\0\0\0\0\0\0\0\0" + "\0\4\0\0\0*\0\0\0\225\0\0\0\350\25\25\0\376\306\315\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\25" + "\25\0\376\0\0\0\350\0\0\0\226\0\0\0*\0\0\0\5\0\0\0\1\0\0\0\0\235\235\235" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\315\315\315\0" + "\1\1\1\0\0\0\0\0\0\0\0\3\0\0\0\"\0\0\0\205\0\0\0\342\2\2\0\375\267\275\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\267\275\0\377\2\2\0\375\0\0\0\342\0\0\0" + "\206\0\0\0#\0\0\0\4\0\0\0\1\1\1\1\0\315\315\315\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0""555\0\0\0\0\0\0\0\0\2\0\0\0\30\0\0\0q\0\0\0\332\0\0\0\374" + "\240\246\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\350\361\0\377\254\263\0\377.0\0\377\13\14\0\377UX\0\377" + "\317\327\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377\0" + "\0\0\374\0\0\0\333\0\0\0r\0\0\0\31\0\0\0\2\0\0\0\1""555\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\0\0\0\0\14\0\0\0S\0\0\0\316\0\0\0\373\202\207" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\310\317\0\3779<\0\377\0\0\0\3779<\0\377\310\317\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\306\315\0\377!\"\0\377\0\0\0\377\0\0\0\377\4\4\0\377PT\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\373" + "\0\0\0\316\0\0\0S\0\0\0\14\0\0\0\1\0\0\0\0qqq\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0" + "\0\0\0\0\0\0\4\0\0\0/\0\0\0\253\0\0\0\364\\`\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377/2\0\377\0\0\0\377\0\0\0\377\0\0\0\377/2\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\237\245\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\20\20\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\\`\0\377\0\0\0\364\0\0\0" + "\253\0\0\0""0\0\0\0\5\0\0\0\1\0\0\0\0\265\265\265\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0)))\0\0\0\0\0\0\0\0\3\0\0\0\40\0\0" + "\0\207\0\0\0\345/1\0\376\357\370\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\207\215\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\357\370\0\377/1\0\376\0\0\0\346" + "\0\0\0\207\0\0\0\40\0\0\0\3\0\0\0\1)))\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0yyy\0\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0`\0\0\0\326\0\0\0\374\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\5\5\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377\0\0\0\374\0\0\0" + "\327\0\0\0a\0\0\0\17\0\0\0\1\0\0\0\0yyy\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321\321\0" + "\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0""1\0\0\0\263\0\0\0\365\206\213\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0" + "\366\0\0\0\263\0\0\0""1\0\0\0\5\0\0\0\1\0\0\0\0\321\321\321\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0QQQ\0\0" + "\0\0\0\0\0\0\2\0\0\0\34\0\0\0\203\0\0\0\346FH\0\376\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\376" + "\0\0\0\347\0\0\0\204\0\0\0\34\0\0\0\2\0\0\0\1QQQ\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0\0\0\0" + "\0\0\0\12\0\0\0O\0\0\0\321\0\0\0\374\311\321\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\311\321" + "\0\377\0\0\0\374\0\0\0\322\0\0\0P\0\0\0\13\0\0\0\1\0\0\0\0\265\265\265\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0" + "\0%\0\0\0\234\0\0\0\360~\203\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377~\203\0\377\0\0\0\361\0\0\0\234\0\0\0%\0\0\0\3\0\0\0\1===\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\255\255\255\0\0\0\0\0\0\0\0\0\0\0\0\16\0\0\0_\0" + "\0\0\332+-\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377+-\0\375\0\0\0\332\0\0\0`\0\0\0\17\0\0\0\1\0\0\0\0\255\255" + "\255\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0(\0\0\0\250\0\0\0" + "\363\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\244\252\0\377\0\0\0\364\0\0\0\250\0\0\0)\0\0\0\3\0\0\0\1=" + "==\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\271\271\271\0\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0c\0\0\0\334FH" + "\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377FH\0\376\0\0\0\335\0\0\0d\0\0\0\17\0\0\0" + "\1\0\0\0\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0'\0\0\0\251\0\0\0\363\267\275" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\364\0\0\0\251\0\0\0" + "(\0\0\0\3\0\0\0\1QQQ\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\331\331\331\0\0\0\0\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\333BD\0\375\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377BD\0\376\0\0\0\333\0\0\0\\" + "\0\0\0\16\0\0\0\1\0\0\0\0\331\331\331\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0yyy\0\0\0\0\0\0\0\0\2\0\0\0!\0\0\0\235\0\0\0\361\244\252\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377\0\0\0\361\0" + "\0\0\236\0\0\0\"\0\0\0\2\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\35\35\35\0\0\0\0\0\0\0\0\10\0\0\0H\0\0\0\324$%\0\375\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377$%\0\375\0" + "\0\0\325\0\0\0I\0\0\0\11\0\0\0\1\35\35\35\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265" + "\265\0\0\0\0\0\0\0\0\0\0\0\0\26\0\0\0\203\0\0\0\353~\203\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377~\203" + "\0\377\0\0\0\353\0\0\0\204\0\0\0\27\0\0\0\1\0\0\0\0\265\265\265\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0aaa\0\0\0\0\0\0\0\0\3\0\0\0-\0\0\0\275\0\0\0\370\325\335\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\325\335\0\377\0\0\0\371\0\0\0\276\0\0\0-\0\0\0\3\0\0\0\1aaa\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\21\21\21\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\336FH\0\376\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377FH\0\377\0\0\0\337\0\0\0]\0\0\0\16\0\0\0\1\21\21\21\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0\0\0\0\0\0\0\0\0\0\0\0\32\0\0\0\223\0\0\0\357\225\232\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\225\232\0\377\0\0\0\357\0\0\0\224\0\0\0\33\0" + "\0\0\1\0\0\0\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\2\0\0\0.\0\0\0\305\0\0\0\372\340\350\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\340\350\0\377\0\0\0\373\0\0\0\305\0\0\0" + "/\0\0\0\3\0\0\0\1qqq\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0---\0\0\0\0\0\0\0\0\14\0\0\0Y\0\0\0\336BD\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377BD\0\377\0\0\0\337\0\0\0Z\0\0\0" + "\15\0\0\0\1---\0\377\377\377\0\377\377\377\0\377\377\377\0\345\345\345\0" + "\0\0\0\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\355\0" + "\0\0\211\0\0\0\30\0\0\0\1\0\0\0\0\345\345\345\0\377\377\377\0\377\377\377" + "\0\251\251\251\0\0\0\0\0\0\0\0\1\0\0\0&\0\0\0\265\0\0\0\367\306\315\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377" + "\0\0\0\367\0\0\0\266\0\0\0&\0\0\0\2\0\0\0\1\251\251\251\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\6\0\0\0?\0\0\0\325\30\31\0\376\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\30\31\0\376\0\0\0\326\0\0\0?\0\0\0\6\0\0\0\1qqq\0\377\377\377\0\377\377" + "\377\0===\0\0\0\0\0\0\0\0\17\0\0\0d\0\0\0\343QT\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377QT" + "\0\377\0\0\0\344\0\0\0e\0\0\0\17\0\0\0\1===\0\377\377\377\0\377\377\377\0" + "\15\15\15\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\15\15\15\0\377\377\377" + "\0\335\335\335\0\0\0\0\0\0\0\0\0\0\0\0\40\0\0\0\254\0\0\0\365\267\275\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\0\0\0\365\0\0\0\255\0\0\0!\0\0\0\1\0\0\0\0\335" + "\335\335\0\265\265\265\0\0\0\0\0\0\0\0\1\0\0\0+\0\0\0\311\0\0\0\374\344\354" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\210\215\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\7\7\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\344\354\0\377\0\0\0\374\0\0\0\312\0\0\0,\0\0\0\2" + "\0\0\0\1\265\265\265\0\221\221\221\0\0\0\0\0\0\0\0\6\0\0\0B\0\0\0\331\40" + "!\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\237\245\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\20\21\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\40!\0\377\0\0\0" + "\332\0\0\0B\0\0\0\7\0\0\0\1\221\221\221\0qqq\0\0\0\0\0\0\0\0\15\0\0\0\\\0" + "\0\0\341FH\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\37702\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\37702\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377!\"\0\377\0\0" + "\0\377\0\0\0\377\4\4\0\377PS\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\377\0\0\0\342\0" + "\0\0]\0\0\0\15\0\0\0\1qqq\0UUU\0\0\0\0\0\0\0\0\23\0\0\0t\0\0\0\347hk\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\310\317\0\3779<\0\377\0" + "\0\0\3779<\0\377\310\317\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\350\361\0\377\254\263\0\377.0\0\377" + "\13\14\0\377UX\0\377\317\327\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377hk\0\377\0\0\0\350\0" + "\0\0u\0\0\0\23\0\0\0\1UUU\0===\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206" + "\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1===" + "\0)))\0\0\0\0\0\0\0\0\35\0\0\0\234\0\0\0\361\240\246\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246" + "\0\377\0\0\0\361\0\0\0\235\0\0\0\35\0\0\0\1)))\0\31\31\31\0\0\0\0\0\0\0\0" + "\40\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0" + "\0\0\255\0\0\0!\0\0\0\1\31\31\31\0\15\15\15\0\0\0\0\0\0\0\0$\0\0\0\271\0" + "\0\0\370\311\321\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\311\321\0\377\0\0\0\371\0\0\0\272\0\0\0" + "$\0\0\0\1\15\15\15\0\5\5\5\0\0\0\0\0\0\0\0&\0\0\0\304\0\0\0\373\330\341\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\330\341\0\377\0\0\0\373\0\0\0\305\0\0\0'\0\0\0\1\5\5\5\0\1\1\1" + "\0\0\0\0\0\0\0\0(\0\0\0\313\0\0\0\374\344\354\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377" + "\0\0\0\375\0\0\0\313\0\0\0)\0\0\0\1\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0(\0\0\0" + "\313\0\0\0\374\344\354\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\344\354\0\377\0\0\0\375\0\0\0\313\0\0" + "\0)\0\0\0\1\1\1\1\0\5\5\5\0\0\0\0\0\0\0\0&\0\0\0\304\0\0\0\373\330\341\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\330\341\0\377\0\0\0\373\0\0\0\305\0\0\0'\0\0\0\1\5\5\5\0\15\15" + "\15\0\0\0\0\0\0\0\0$\0\0\0\271\0\0\0\370\311\321\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\311\321\0" + "\377\0\0\0\371\0\0\0\272\0\0\0$\0\0\0\1\15\15\15\0\31\31\31\0\0\0\0\0\0\0" + "\0\40\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0" + "\0\0\255\0\0\0!\0\0\0\1\31\31\31\0)))\0\0\0\0\0\0\0\0\35\0\0\0\234\0\0\0" + "\361\240\246\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\240\246\0\377\0\0\0\361\0\0\0\235\0\0\0\35\0\0\0" + "\1)))\0===\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\206" + "\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1===\0UUU\0\0\0\0\0\0\0\0\23" + "\0\0\0t\0\0\0\347hk\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377hk\0\377\0\0\0\350\0\0\0u\0\0\0\23\0\0\0" + "\1UUU\0qqq\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\341FH\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\377\0" + "\0\0\342\0\0\0]\0\0\0\15\0\0\0\1qqq\0\221\221\221\0\0\0\0\0\0\0\0\6\0\0\0" + "B\0\0\0\331\40!\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\40!\0\377\0\0\0\332\0\0\0C\0\0\0\7\0\0\0\1\221" + "\221\221\0\265\265\265\0\0\0\0\0\0\0\0\1\0\0\0,\0\0\0\312\0\0\0\374\344\354" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377" + "\0\0\0\375\0\0\0\312\0\0\0,\0\0\0\2\0\0\0\1\265\265\265\0\335\335\335\0\0" + "\0\0\0\0\0\0\1\0\0\0!\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0\0\0\255\0\0" + "\0\"\0\0\0\1\0\0\0\1\335\335\335\0\377\377\377\0\15\15\15\0\0\0\0\0\0\0\0" + "\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\15" + "\15\15\0\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\17\0\0\0d\0\0\0\343" + "QT\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377QT\0\377\0" + "\0\0\344\0\0\0e\0\0\0\17\0\0\0\1===\0\377\377\377\0\377\377\377\0qqq\0\0" + "\0\0\0\0\0\0\6\0\0\0?\0\0\0\325\30\31\0\376\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\30\31\0\376\0\0\0\326\0\0\0?\0\0\0\6\0\0\0\1qqq\0\377" + "\377\377\0\377\377\377\0\251\251\251\0\0\0\0\0\0\0\0\1\0\0\0&\0\0\0\266\0" + "\0\0\367\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\335\345\0\377\261\270\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\0\0\0\370" + "\0\0\0\266\0\0\0'\0\0\0\2\0\0\0\1\251\251\251\0\377\377\377\0\377\377\377" + "\0\345\345\345\0\0\0\0\0\0\0\0\1\0\0\0\31\0\0\0\212\0\0\0\354\206\213\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\322\332\0\377\36" + "\37\0\377\10\10\0\377\265\273\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\316\326\0\377\230\235\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\0" + "\0\0\1\345\345\345\0\377\377\377\0\377\377\377\0\377\377\377\0---\0\0\0\0" + "\0\0\0\0\14\0\0\0Z\0\0\0\337BD\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\322\332\0\377\36\37\0\377\0\0\0\377\0\0\0\377\1\1\0\377\201\206\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\265\273\0\377\17" + "\20\0\377\2\2\0\377\224\231\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377BD\0\377\0\0\0\340\0\0\0Z\0\0\0" + "\15\0\0\0\1---\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "qqq\0\0\0\0\0\0\0\0\2\0\0\0.\0\0\0\305\0\0\0\372\340\350\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\322\332\0\377\36\37\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377SV\0\377\353\364\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377w{\0\377\1\1\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377;>\0\377<>\0\377<>\0\377\303\312\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340" + "\350\0\377\0\0\0\373\0\0\0\305\0\0\0/\0\0\0\3\0\0\0\1qqq\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0\0\0\0\0\0\0\0\1" + "\0\0\0\33\0\0\0\224\0\0\0\357\225\232\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377\26\26\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377.0\0\377" + "\330\340\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\345\355\0\377?A" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\1\1\0\377\310\320\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\225\232\0\377\0\0\0\360\0\0\0" + "\225\0\0\0\34\0\0\0\1\0\0\0\1\271\271\271\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\21\21\21\0\0\0\0\0\0\0\0\15\0\0\0" + "]\0\0\0\337FH\0\376\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\257\266\0\377\4\4\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\16\17\0\377\265\273" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\277\306\0\377\23\24\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\2\2\0\377\226\233\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377FH\0\377\0\0\0\337\0\0\0^\0\0\0\16\0\0\0\1\21\21" + "\21\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0aaa\0\0\0\0\0\0\0\0\3\0\0\0-\0\0\0\275\0\0\0\370\325\335" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\300\307\0\377\30\31\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377z\177\0\377\355\366" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377pt\0\377\2\2\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\2\2" + "\0\377\235\243\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\325\335\0\377\0\0\0\371" + "\0\0\0\276\0\0\0-\0\0\0\3\0\0\0\1aaa\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0" + "\0\0\1\0\0\0\27\0\0\0\204\0\0\0\353~\203\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\330\341\0\377&'\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\25\25\0\377\277\306\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\310\317\0\377" + "-/\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\25\25\0\377\267\275\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377~\203\0\377\0\0\0\354\0\0\0\205\0\0\0\30\0\0\0\1\0\0\0\1\265\265" + "\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\35\35\35\0\0\0\0\0\0\0\0\11\0\0\0I\0\0\0\324" + "$%\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\350\360\0\377EG\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\2\2\0\377mq\0\377\352\363\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\342\352\0" + "\377fi\0\377\2\2\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377*,\0\377\333\344\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377$%\0\375\0\0\0\325\0\0\0J\0\0\0\11\0\0" + "\0\1\35\35\35\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0\0\2" + "\0\0\0!\0\0\0\236\0\0\0\361\244\252\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\357\370\0\377tx\0\377\1\1\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\35\36\0\377\235\243\0" + "\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377\220" + "\225\0\377\15\16\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377gj\0\377\353\364\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377\0\0\0\362\0" + "\0\0\237\0\0\0\"\0\0\0\2\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\331\331\331\0\0\0\0\0\0\0\0\1\0\0\0\16\0\0\0\\\0\0\0\333BD\0\375\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\265\273\0" + "\377\13\14\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377-/\0\377\273\302\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\355\366\0\377\177\204" + "\0\377\15\16\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\11\12\0\377\246\254\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "BD\0\376\0\0\0\333\0\0\0]\0\0\0\17\0\0\0\1\0\0\0\1\331\331\331\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0'" + "\0\0\0\251\0\0\0\363\267\275\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\346\356\0\377HK\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377=?\0\377\247\255\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377\206\213\0\377" + "\34\35\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377/1\0\377\325\335\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\267\275\0\377\0\0\0\364\0\0\0\251\0\0\0(\0\0\0\3\0\0\0\1QQQ\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0\0" + "\0\0\0\0\0\0\1\0\0\0\20\0\0\0d\0\0\0\335FH\0\375\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\211\217\0\377\10\11\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\14\15\0\377qu\0\377" + "\343\353\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\356\367\0\377\254\263\0\377hl\0\377\5\5\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\16\17\0\377\231\237\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377FH\0\376\0\0\0\335\0\0\0d\0\0\0\20\0\0\0\1\0\0\0\1\271\271\271" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0)\0\0\0\251\0\0\0\363\244\252\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\346\356\0\377fi\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\4\4\0\377BD\0\377\206\213\0\377\301\310\0\377\354\365\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340\350\0\377\230\235\0" + "\377^b\0\377\25\25\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377Z^" + "\0\377\335\345\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377" + "\0\0\0\364\0\0\0\251\0\0\0*\0\0\0\3\0\0\0\1===\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\255" + "\255\255\0\0\0\0\0\0\0\0\1\0\0\0\17\0\0\0`\0\0\0\332+-\0\375\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\254\263\0\377\25\25\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\12\13\0\377;>\0\377UX\0" + "\377lp\0\377x|\0\377x|\0\377x|\0\377bf\0\377HK\0\377\6\6\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\37713\0\377\275\304" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "+-\0\375\0\0\0\332\0\0\0a\0\0\0\17\0\0\0\1\0\0\0\1\255\255\255\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0%\0\0\0\234\0\0\0" + "\360~\203\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357\370" + "\0\377\216\223\0\377\16\17\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + ",.\0\377\247\255\0\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377~\203\0\377\0\0\0\361\0\0\0\235\0\0\0%\0" + "\0\0\3\0\0\0\1===\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265" + "\265\265\0\0\0\0\0\0\0\0\1\0\0\0\13\0\0\0P\0\0\0\322\0\0\0\374\311\321\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\355" + "\366\0\377\213\221\0\377\17\20\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\37724\0\377\246\254\0\377\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\311\321\0\377\0\0\0\374\0\0\0\323\0\0\0Q\0\0\0\14\0\0\0\1\0\0\0\1" + "\265\265\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0\34\0\0\0\204\0\0\0\347FH\0\376\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\356\367\0\377\235\243\0\377(*\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\5\5\0" + "\377hl\0\377\270\276\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377FH\0\377\0\0\0\347\0\0\0\204\0\0\0\35\0\0\0\2\0\0\0\1QQQ\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321" + "\321\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0""1\0\0\0\263\0\0\0\365\206\213\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\320\330\0\377\\`\0\377\7" + "\7\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\3778:\0\377\226\233\0" + "\377\344\354\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\206\213\0\377\0\0\0\366\0\0\0\263\0\0\0""1\0\0\0\5\0\0\0\1\0\0\0\1" + "\321\321\321\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0\0\1\0\0\0\17\0\0\0a\0\0\0" + "\327\0\0\0\374\276\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\357\370\0\377\245\253\0\377w{\0\377:=\0\377\3\3\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377*,\0\377dh\0\377\217\224\0\377\350" + "\361\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\276\305\0\377\0\0\0\375\0\0\0\330\0\0\0b\0\0" + "\0\20\0\0\0\1\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + ")))\0\0\0\0\0\0\0\0\3\0\0\0\40\0\0\0\207\0\0\0\346/1\0\376\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\357\370\0\377\305\314\0\377\247\255\0\377" + "\212\220\0\377x}\0\377x}\0\377x}\0\377x}\0\377{\200\0\377\240\246\0\377\270" + "\276\0\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\357\370\0\377/1\0\376\0\0\0\347\0\0\0\210\0\0\0!\0\0\0\3\0\0\0" + "\1)))\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265" + "\265\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0""0\0\0\0\253\0\0\0\364\\`\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\\`\0\377\0\0\0\364\0\0\0\254\0\0\0""1" + "\0\0\0\6\0\0\0\1\0\0\0\1\265\265\265\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0\0\0\1\0\0\0\14\0\0" + "\0T\0\0\0\317\0\0\0\373\202\207\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\373" + "\0\0\0\317\0\0\0T\0\0\0\15\0\0\0\1\0\0\0\1qqq\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0""555\0\0\0\0\0\0\0\0\2\0\0\0\30\0\0\0q\0\0\0\332\0\0\0\374\240\246\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377\0" + "\0\0\374\0\0\0\333\0\0\0r\0\0\0\31\0\0\0\2\0\0\0\1""555\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\315\315\315\0\1\1\1\0\0\0\0\1\0\0\0\4\0\0\0\"\0" + "\0\0\205\0\0\0\342\2\2\0\375\267\275\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\267" + "\275\0\377\2\2\0\375\0\0\0\342\0\0\0\206\0\0\0#\0\0\0\4\0\0\0\1\1\1\1\1\315" + "\315\315\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\235\235" + "\235\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0+\0\0\0\226\0\0\0\350\25\25\0\376\306" + "\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\306\315\0\377\25\25\0\376\0\0\0\351\0\0\0\226\0\0\0+\0\0\0\6\0" + "\0\0\1\0\0\0\1\235\235\235\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0" + """2\0\0\0\244\0\0\0\355\40!\0\376\315\325\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377\40!\0\376\0\0\0\356\0\0\0\245\0\0\0""3\0" + "\0\0\7\0\0\0\1\0\0\0\1uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0UUU\0\0" + "\0\0\0\0\0\0\1\0\0\0\11\0\0\0@\0\0\0\260\0\0\0\360$%\0\376\315\325\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377$%\0\377\0\0\0\361\0\0\0\260\0\0\0A\0\0\0" + "\12\0\0\0\1\0\0\0\1UUU\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\375\375\375\0===\0\0\0\0\1\0\0\0\2\0\0\0\16\0\0\0H\0\0\0\264\0\0\0\360" + "\40!\0\376\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\306\315\0\377\40!\0\376\0\0\0\361\0\0\0\265\0\0\0I\0\0\0\16" + "\0\0\0\2\0\0\0\1===\1\375\375\375\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\351\351\351\0---\0\0\0\0\1\0\0\0\2\0\0\0\17" + "\0\0\0I\0\0\0\260\0\0\0\355\25\25\0\376\267\275\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\25\25\0\376\0\0\0\356\0\0\0\260\0\0\0I\0\0\0" + "\20\0\0\0\2\0\0\0\1---\1\351\351\351\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0" + "%%%\0\0\0\0\1\0\0\0\2\0\0\0\16\0\0\0@\0\0\0\244\0\0\0\350\2\2\0\375\240\246" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\240\246\0\377\2\2\0\375\0\0\0\351\0\0\0\245\0\0\0@\0\0\0\16" + "\0\0\0\2\0\0\0\1%%%\1\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\331\331\331\0%%%\0\0\0\0\1\0\0\0\2\0\0\0\12\0\0\0""2\0\0\0\226" + "\0\0\0\342\0\0\0\374\202\207\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\202\207\0\377\0\0\0\374\0\0\0\343\0\0\0\226\0\0\0""3\0\0\0" + "\12\0\0\0\2\0\0\0\1%%%\1\331\331\331\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0---\0\0\0\0\1\0\0" + "\0\1\0\0\0\6\0\0\0*\0\0\0\205\0\0\0\332\0\0\0\373\\`\0\377\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357" + "\370\0\377\\`\0\377\0\0\0\373\0\0\0\333\0\0\0\206\0\0\0+\0\0\0\6\0\0\0\1" + "\0\0\0\1---\1\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\351\351\351\0" + "===\0\0\0\0\1\0\0\0\1\0\0\0\5\0\0\0#\0\0\0r\0\0\0\317\0\0\0\364/1\0\376\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377/1\0\376" + "\0\0\0\365\0\0\0\317\0\0\0r\0\0\0#\0\0\0\6\0\0\0\1\0\0\0\1===\1\351\351\351" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\375\375\375" + "\0UUU\0\0\0\0\0\0\0\0\1\0\0\0\4\0\0\0\31\0\0\0S\0\0\0\253\0\0\0\346\0\0\0" + "\374\206\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\375\0\0\0\347\0\0\0" + "\254\0\0\0T\0\0\0\31\0\0\0\4\0\0\0\1\0\0\0\1UUU\0\375\375\375\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\1\0\0\0\3\0\0\0\14\0\0\0/\0\0\0\210\0" + "\0\0\327\0\0\0\366FH\0\376\311\321\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\311\321\0\377FH\0\377\0\0\0\367\0\0\0\330\0\0\0\210\0\0\0""0\0\0\0\15\0" + "\0\0\3\0\0\0\1\0\0\0\1uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\235\235\235\0\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\5\0" + "\0\0!\0\0\0a\0\0\0\263\0\0\0\347\0\0\0\374~\203\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377~\203\0\377\0\0\0" + "\374\0\0\0\347\0\0\0\264\0\0\0b\0\0\0!\0\0\0\6\0\0\0\1\0\0\0\1\1\1\1\1\235" + "\235\235\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\315\315\315\0""555\0\0\0\0\0\0\0\0\1\0" + "\0\0\3\0\0\0\17\0\0\0""1\0\0\0\204\0\0\0\322\0\0\0\361+-\0\375\244\252\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\244\252\0\377+-\0\375\0\0\0\361\0\0\0\323\0\0\0" + "\204\0\0\0""1\0\0\0\20\0\0\0\3\0\0\0\1\0\0\0\1""555\0\315\315\315\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0" + "\0\0\1\0\0\0\1\0\0\0\5\0\0\0\34\0\0\0P\0\0\0\234\0\0\0\332\0\0\0\364FH\0" + "\376\267\275\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377FH" + "\0\376\0\0\0\364\0\0\0\333\0\0\0\235\0\0\0Q\0\0\0\35\0\0\0\5\0\0\0\1\0\0" + "\0\1\0\0\0\1qqq\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0)))\0\0" + "\0\0\0\0\0\0\1\0\0\0\3\0\0\0\14\0\0\0%\0\0\0`\0\0\0\251\0\0\0\335\0\0\0\364" + "BD\0\376\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\244\252\0\377BD\0\376\0\0\0\364\0\0\0\336\0\0\0\251\0\0\0a\0\0\0&" + "\0\0\0\14\0\0\0\3\0\0\0\1\0\0\0\1)))\0\265\265\265\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0" + "\0\1\0\0\0\1\0\0\0\3\0\0\0\17\0\0\0)\0\0\0d\0\0\0\251\0\0\0\333\0\0\0\362" + "$%\0\375~\203\0\377\325\335\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\325\335\0\377~\203\0\377$%\0\375\0\0\0\362\0\0\0\334\0\0\0\252\0\0\0" + "d\0\0\0)\0\0\0\17\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1yyy\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\321\321\321\0QQQ\0\0\0\0\0\0\0\0\1\0\0\0\1\0" + "\0\0\4\0\0\0\20\0\0\0(\0\0\0\\\0\0\0\236\0\0\0\325\0\0\0\354\0\0\0\372FH" + "\0\377\225\232\0\377\340\350\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340" + "\350\0\377\225\232\0\377FH\0\377\0\0\0\372\0\0\0\354\0\0\0\325\0\0\0\236" + "\0\0\0]\0\0\0(\0\0\0\20\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1QQQ\0\321\321\321" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\265\265\265\0===\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\3\0\0\0" + "\16\0\0\0\"\0\0\0I\0\0\0\205\0\0\0\275\0\0\0\337\0\0\0\360\0\0\0\374BD\0" + "\377\206\213\0\377\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\206" + "\213\0\377BD\0\377\0\0\0\374\0\0\0\360\0\0\0\340\0\0\0\276\0\0\0\205\0\0" + "\0J\0\0\0\"\0\0\0\16\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1===\0\265\265\265\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\255\255" + "\255\0===\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\11\0\0\0\27\0\0\0-\0\0" + "\0]\0\0\0\225\0\0\0\305\0\0\0\337\0\0\0\355\0\0\0\370\30\31\0\376QT\0\377" + "\206\213\0\377\267\275\0\377\344\354\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\344\354\0\377\267\275\0\377\206\213\0\377QT\0\377\30\31\0\376\0\0\0\370" + "\0\0\0\355\0\0\0\340\0\0\0\306\0\0\0\225\0\0\0^\0\0\0-\0\0\0\30\0\0\0\11" + "\0\0\0\3\0\0\0\1\0\0\0\1\0\0\0\1===\0\255\255\255\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\271\271\271\0QQQ\0\0\0\0\0\0\0\0\1\0\0\0\1" + "\0\0\0\1\0\0\0\3\0\0\0\16\0\0\0\33\0\0\0/\0\0\0Z\0\0\0\212\0\0\0\266\0\0" + "\0\326\0\0\0\344\0\0\0\355\0\0\0\365\0\0\0\374\40!\0\377FH\0\377hk\0\377" + "\206\213\0\377\240\246\0\377\267\275\0\377\311\321\0\377\330\341\0\377\344" + "\354\0\377\344\354\0\377\330\341\0\377\311\321\0\377\267\275\0\377\240\246" + "\0\377\206\213\0\377hk\0\377FH\0\377\40!\0\377\0\0\0\375\0\0\0\366\0\0\0" + "\355\0\0\0\344\0\0\0\326\0\0\0\266\0\0\0\212\0\0\0Z\0\0\0/\0\0\0\33\0\0\0" + "\16\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1QQQ\0\271\271\271\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\331\331\331\0yyy\0\35\35\35\0\0\0\0\0\0\0\0\1\0" + "\0\0\1\0\0\0\1\0\0\0\3\0\0\0\15\0\0\0\31\0\0\0'\0\0\0@\0\0\0e\0\0\0\212\0" + "\0\0\255\0\0\0\312\0\0\0\332\0\0\0\342\0\0\0\350\0\0\0\355\0\0\0\361\0\0" + "\0\365\0\0\0\371\0\0\0\373\0\0\0\375\0\0\0\375\0\0\0\374\0\0\0\371\0\0\0" + "\366\0\0\0\362\0\0\0\355\0\0\0\350\0\0\0\342\0\0\0\332\0\0\0\313\0\0\0\255" + "\0\0\0\212\0\0\0e\0\0\0@\0\0\0'\0\0\0\31\0\0\0\16\0\0\0\4\0\0\0\2\0\0\0\1" + "\0\0\0\1\0\0\0\1\35\35\35\0yyy\0\331\331\331\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\265\265\265\0aaa\0\21\21\21\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0" + "\0\2\0\0\0\6\0\0\0\17\0\0\0\31\0\0\0!\0\0\0,\0\0\0C\0\0\0]\0\0\0u\0\0\0\212" + "\0\0\0\235\0\0\0\255\0\0\0\272\0\0\0\305\0\0\0\314\0\0\0\314\0\0\0\305\0" + "\0\0\272\0\0\0\255\0\0\0\235\0\0\0\212\0\0\0u\0\0\0]\0\0\0C\0\0\0,\0\0\0" + "\"\0\0\0\31\0\0\0\20\0\0\0\7\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\21\21" + "\21\0aaa\0\265\265\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0qqq\0---\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\2" + "\0\0\0\7\0\0\0\15\0\0\0\23\0\0\0\31\0\0\0\35\0\0\0!\0\0\0$\0\0\0'\0\0\0)" + "\0\0\0)\0\0\0'\0\0\0%\0\0\0!\0\0\0\36\0\0\0\31\0\0\0\24\0\0\0\16\0\0\0\10" + "\0\0\0\3\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1---\0qqq\0\271\271" + "\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\345\345\345\0" + "\251\251\251\0qqq\0===\0\15\15\15\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" + "\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" + "\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\15\15" + "\15\0===\0qqq\0\251\251\251\0\345\345\345\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0" + "\265\265\265\0\221\221\221\0qqq\0UUU\0===\0)))\0\31\31\31\0\15\15\15\0\5" + "\5\5\0\1\1\1\0\1\1\1\0\5\5\5\0\15\15\15\0\31\31\31\0)))\0===\0UUU\0qqq\0" + "\221\221\221\0\265\265\265\0\335\335\335\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0", +}; + diff --git a/samples/graphics/rsx_Basic_Cube/include/mesh.h b/samples/graphics/rsx_Basic_Cube/include/mesh.h new file mode 100644 index 00000000..63665494 --- /dev/null +++ b/samples/graphics/rsx_Basic_Cube/include/mesh.h @@ -0,0 +1,62 @@ +#ifndef __MESH_H__ +#define __MESH_H__ + +#include + +using namespace Vectormath::Aos; + +template< class T > +inline const T& min_(const T& a,const T& b) +{ + return a +inline const T& max_(const T& a,const T& b) +{ + return a +inline const T clamp(const T& val,const T& low,const T& high) +{ + return min_(max_(val,low),high); +} + +struct S3DVertex +{ + S3DVertex() {}; + S3DVertex(f32 x,f32 y,f32 z,f32 nx,f32 ny,f32 nz,f32 tu,f32 tv) + : pos(x,y,z),nrm(nx,ny,nz),u(tu),v(tv) {}; + + inline S3DVertex& operator=(const S3DVertex& other) + { + pos = other.pos; + nrm = other.nrm; + u = other.u; + v = other.v; + return *this; + } + + Vector3 pos; + Vector3 nrm; + + f32 u,v; +}; + +template< class T > +class CMeshBuffer +{ +public: + CMeshBuffer() : indices(NULL),cnt_indices(0),vertices(NULL),cnt_vertices(0) {}; + + u16 *indices; + u32 cnt_indices; + + S3DVertex *vertices; + u32 cnt_vertices; +}; + +typedef CMeshBuffer SMeshBuffer; + +#endif diff --git a/samples/graphics/rsx_Basic_Cube/include/rsxutil.h b/samples/graphics/rsx_Basic_Cube/include/rsxutil.h new file mode 100644 index 00000000..4a87ea9b --- /dev/null +++ b/samples/graphics/rsx_Basic_Cube/include/rsxutil.h @@ -0,0 +1,38 @@ +#ifndef __RSXUTIL_H__ +#define __RSXUTIL_H__ + +#include +#include + +#include + +#define DEFUALT_CB_SIZE 0x80000 // 512Kb default command buffer size +#define HOST_STATE_CB_SIZE 0x10000 // 64Kb state command buffer size (used for resetting certain default states) +#define HOST_ADDR_ALIGNMENT (1024*1024) +#define HOSTBUFFER_SIZE (128*1024*1024) + +#define FRAME_BUFFER_COUNT 2 + +extern gcmContextData *context; + +extern u32 curr_fb; + +extern u32 display_width; +extern u32 display_height; + +extern u32 depth_pitch; +extern u32 depth_offset; +extern u32 *depth_buffer; + +extern u32 color_pitch; +extern u32 color_offset[FRAME_BUFFER_COUNT]; +extern u32 *color_buffer[FRAME_BUFFER_COUNT]; + +extern f32 aspect_ratio; + +void setRenderTarget(u32 index); +void init_screen(void *host_addr,u32 size); +void waitflip(); +void flip(); + +#endif diff --git a/samples/graphics/rsx_Basic_Cube/shaders/diffuse_specular_shader.fcg b/samples/graphics/rsx_Basic_Cube/shaders/diffuse_specular_shader.fcg new file mode 100644 index 00000000..7a9f6b40 --- /dev/null +++ b/samples/graphics/rsx_Basic_Cube/shaders/diffuse_specular_shader.fcg @@ -0,0 +1,13 @@ +void main +( + float2 texcoord : TEXCOORD2, + + + uniform sampler2D texture, + + out float4 oColor +) +{ + + oColor = tex2D(texture, texcoord); +} diff --git a/samples/graphics/rsx_Basic_Cube/shaders/diffuse_specular_shader.vcg b/samples/graphics/rsx_Basic_Cube/shaders/diffuse_specular_shader.vcg new file mode 100644 index 00000000..c11e6228 --- /dev/null +++ b/samples/graphics/rsx_Basic_Cube/shaders/diffuse_specular_shader.vcg @@ -0,0 +1,21 @@ +void main +( + float3 vertexPosition : POSITION, + float3 vertexNormal : NORMAL, + float2 vertexTexcoord : TEXCOORD0, + + uniform float4x4 projMatrix, + uniform float4x4 modelViewMatrix, + + out float4 ePosition : POSITION, + out float4 oPosition : TEXCOORD0, + out float3 oNormal : TEXCOORD1, + out float2 oTexcoord : TEXCOORD2 +) +{ + ePosition = mul(mul(projMatrix,modelViewMatrix),float4(vertexPosition,1.0f)); + + oPosition = float4(vertexPosition,1.0f); + oNormal = vertexNormal; + oTexcoord = vertexTexcoord; +} diff --git a/samples/graphics/rsx_Basic_Cube/source/main.cpp b/samples/graphics/rsx_Basic_Cube/source/main.cpp new file mode 100644 index 00000000..ed0f2e1a --- /dev/null +++ b/samples/graphics/rsx_Basic_Cube/source/main.cpp @@ -0,0 +1,425 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Stone_Texture_png_bin.h" +#include + +#include +#include +#include + +#include "acid.h" +#include "mesh.h" +#include "rsxutil.h" + +#include "diffuse_specular_shader_vpo.h" +#include "diffuse_specular_shader_fpo.h" + +#define DEGTORAD(a) ( (a) * 0.01745329252f ) +#define RADTODEG(a) ( (a) * 57.29577951f ) +#define TEXTURE_SIZE 128 +float perlin2d(float x, float y, float freq, int depth); + +u32 running = 0; + +u32 fp_offset; +u32 *fp_buffer; + +u32 *texture_buffer[3]; +u32 texture_buffer_idx = 0; +u32 texture_offset[3]; +pngData *png; +f32 rotx = 0.0f; +f32 roty = 0.0f; + +// vertex shader +rsxProgramConst *projMatrix; +rsxProgramConst *mvMatrix; + +// fragment shader +rsxProgramAttrib *textureUnit; + +Point3 eye_pos = Point3(0.0f,0.0f,20.0f); +Point3 eye_dir = Point3(0.0f,0.0f,0.0f); +Vector3 up_vec = Vector3(0.0f,1.0f,0.0f); + +void *vp_ucode = NULL; +rsxVertexProgram *vpo = (rsxVertexProgram*)diffuse_specular_shader_vpo; + +void *fp_ucode = NULL; +rsxFragmentProgram *fpo = (rsxFragmentProgram*)diffuse_specular_shader_fpo; + +static Matrix4 P; +static SMeshBuffer *cube = NULL; + +SYS_PROCESS_PARAM(1001, 0x100000); + +extern "C" { +static void program_exit_callback() +{ + gcmSetWaitFlip(context); + rsxFinish(context,1); +} + +static void sysutil_exit_callback(u64 status,u64 param,void *usrdata) +{ + switch(status) { + case SYSUTIL_EXIT_GAME: + running = 0; + break; + case SYSUTIL_DRAW_BEGIN: + case SYSUTIL_DRAW_END: + break; + default: + break; + } +} +} + + +static void init_texture() +{ + u32 i; + u8 *buffer; + + //Init png texture + const u8* data = (u8*)png->bmp_out; + texture_buffer[0] = (u32*)rsxMemalign(128, (png->height * png->pitch)); + + if(!texture_buffer[0]) return; + + rsxAddressToOffset(texture_buffer[0],&texture_offset[0]); + + buffer = (u8*)texture_buffer[0]; + for (i = 0; i < png->height * png->pitch; i += 4) { + buffer[i + 0] = *data++; + buffer[i + 1] = *data++; + buffer[i + 2] = *data++; + buffer[i + 3] = *data++; + } + + //Init acid pixel texture + data = acid.pixel_data; + texture_buffer[1] = (u32*)rsxMemalign(128,(acid.width*acid.height*4)); + + if (!texture_buffer[1]) return; + + rsxAddressToOffset(texture_buffer[1], &texture_offset[1]); + + buffer = (u8*)texture_buffer[1]; + for(i=0;icnt_indices = 36; + buffer->indices = (u16*)rsxMemalign(128,buffer->cnt_indices*sizeof(u16)); + + for(i=0;i<36;i++) buffer->indices[i] = u[i]; + + buffer->cnt_vertices = 12; + buffer->vertices = (S3DVertex*)rsxMemalign(128,buffer->cnt_vertices*sizeof(S3DVertex)); + + buffer->vertices[0] = S3DVertex(0,0,0, -1,-1,-1, 1, 0); + buffer->vertices[1] = S3DVertex(1,0,0, 1,-1,-1, 1, 1); + buffer->vertices[2] = S3DVertex(1,1,0, 1, 1,-1, 0, 1); + buffer->vertices[3] = S3DVertex(0,1,0, -1, 1,-1, 0, 0); + buffer->vertices[4] = S3DVertex(1,0,1, 1,-1, 1, 1, 0); + buffer->vertices[5] = S3DVertex(1,1,1, 1, 1, 1, 0, 0); + buffer->vertices[6] = S3DVertex(0,1,1, -1, 1, 1, 0, 1); + buffer->vertices[7] = S3DVertex(0,0,1, -1,-1, 1, 1, 1); + buffer->vertices[8] = S3DVertex(0,1,1, -1, 1, 1, 1, 0); + buffer->vertices[9] = S3DVertex(0,1,0, -1, 1,-1, 1, 1); + buffer->vertices[10] = S3DVertex(1,0,1, 1,-1, 1, 0, 1); + buffer->vertices[11] = S3DVertex(1,0,0, 1,-1,-1, 0, 0); + + for(i=0;i<12;i++) { + buffer->vertices[i].pos -= Vector3(0.5f,0.5f,0.5f); + buffer->vertices[i].pos *= size; + } + + return buffer; +} + +static void setTexture(u8 textureUnit, u32 numTex) +{ + u32 width = 128; + u32 height = 128; + u32 pitch = (width*4); + gcmTexture texture; + + if(!texture_buffer[0]) return; + + rsxInvalidateTextureCache(context,GCM_INVALIDATE_TEXTURE); + + //texture.format = (GCM_TEXTURE_FORMAT_A8R8G8B8 | GCM_TEXTURE_FORMAT_LIN); + texture.format = (GCM_TEXTURE_FORMAT_A8R8G8B8 | GCM_TEXTURE_FORMAT_LIN | GCM_TEXTURE_FORMAT_NRM); + + texture.mipmap = 1; + texture.dimension = GCM_TEXTURE_DIMS_2D; + texture.cubemap = GCM_FALSE; + texture.remap = ((GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_B_SHIFT) | + (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_G_SHIFT) | + (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_R_SHIFT) | + (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_A_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_B << GCM_TEXTURE_REMAP_COLOR_B_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_G << GCM_TEXTURE_REMAP_COLOR_G_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_R << GCM_TEXTURE_REMAP_COLOR_R_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_A << GCM_TEXTURE_REMAP_COLOR_A_SHIFT)); + texture.width = width; + texture.height = height; + texture.depth = 1; + texture.location = GCM_LOCATION_RSX; + texture.pitch = pitch; + texture.offset = texture_offset[numTex]; + rsxLoadTexture(context,textureUnit,&texture); + rsxTextureControl(context,textureUnit,GCM_TRUE,0<<8,12<<8,GCM_TEXTURE_MAX_ANISO_1); + rsxTextureFilter(context,textureUnit,0,GCM_TEXTURE_LINEAR,GCM_TEXTURE_LINEAR,GCM_TEXTURE_CONVOLUTION_QUINCUNX); + rsxTextureWrapMode(context,textureUnit,GCM_TEXTURE_CLAMP_TO_EDGE,GCM_TEXTURE_CLAMP_TO_EDGE,GCM_TEXTURE_CLAMP_TO_EDGE,0,GCM_TEXTURE_ZFUNC_LESS,0); +} + +static void setDrawEnv() +{ + rsxSetColorMask(context,GCM_COLOR_MASK_B | + GCM_COLOR_MASK_G | + GCM_COLOR_MASK_R | + GCM_COLOR_MASK_A); + + rsxSetColorMaskMrt(context,0); + + u16 x,y,w,h; + f32 min, max; + f32 scale[4],offset[4]; + + x = 0; + y = 0; + w = display_width; + h = display_height; + min = 0.0f; + max = 1.0f; + scale[0] = w*0.5f; + scale[1] = h*-0.5f; + scale[2] = (max - min)*0.5f; + scale[3] = 0.0f; + offset[0] = x + w*0.5f; + offset[1] = y + h*0.5f; + offset[2] = (max + min)*0.5f; + offset[3] = 0.0f; + + rsxSetViewport(context,x, y, w, h, min, max, scale, offset); + rsxSetScissor(context,x,y,w,h); + + rsxSetDepthTestEnable(context,GCM_TRUE); + rsxSetDepthFunc(context,GCM_LESS); + rsxSetShadeModel(context,GCM_SHADE_MODEL_SMOOTH); + rsxSetDepthWriteEnable(context,1); + rsxSetFrontFace(context,GCM_FRONTFACE_CCW); +} + +void init_shader() +{ + u32 fpsize = 0; + u32 vpsize = 0; + + rsxVertexProgramGetUCode(vpo, &vp_ucode, &vpsize); + printf("vpsize: %d\n", vpsize); + + projMatrix = rsxVertexProgramGetConst(vpo,"projMatrix"); + if (projMatrix) + printf("projMatrix OK\n"); + mvMatrix = rsxVertexProgramGetConst(vpo,"modelViewMatrix"); + if (mvMatrix) + printf("mvMatrix OK\n"); + rsxFragmentProgramGetUCode(fpo, &fp_ucode, &fpsize); + printf("fpsize: %d\n", fpsize); + + fp_buffer = (u32*)rsxMemalign(64,fpsize); + memcpy(fp_buffer,fp_ucode,fpsize); + rsxAddressToOffset(fp_buffer,&fp_offset); + + textureUnit = rsxFragmentProgramGetAttrib(fpo,"texture"); + if (textureUnit) + printf("textureUnit OK\n"); +} + +void drawFrame() +{ + u32 i,offset,color = 0; + Matrix4 rotX,rotY; + Matrix4 viewMatrix,modelMatrix,modelMatrixIT,modelViewMatrix; + + SMeshBuffer *mesh = NULL; + + setDrawEnv(); + setTexture(textureUnit->index, texture_buffer_idx); + + rsxSetClearColor(context,color); + rsxSetClearDepthStencil(context,0xffffff00); + rsxClearSurface(context,GCM_CLEAR_R | + GCM_CLEAR_G | + GCM_CLEAR_B | + GCM_CLEAR_A | + GCM_CLEAR_S | + GCM_CLEAR_Z); + + rsxSetZControl(context,0,1,1); + + for(i=0;i<8;i++) + rsxSetViewportClip(context,i,display_width,display_height); + + viewMatrix = Matrix4::lookAt(eye_pos,eye_dir,up_vec); + + mesh = cube; + rotX = Matrix4::rotationX(DEGTORAD(rotx)); + rotY = Matrix4::rotationY(DEGTORAD(roty)); + modelMatrix = rotX*rotY; + modelMatrixIT = Matrix4::identity();// inverse(modelMatrix); + modelViewMatrix = transpose(viewMatrix*modelMatrix); + + rsxAddressToOffset(&mesh->vertices[0].pos,&offset); + rsxBindVertexArrayAttrib(context,GCM_VERTEX_ATTRIB_POS,0,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxAddressToOffset(&mesh->vertices[0].nrm,&offset); + rsxBindVertexArrayAttrib(context,GCM_VERTEX_ATTRIB_NORMAL,0,offset,sizeof(S3DVertex),3,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxAddressToOffset(&mesh->vertices[0].u,&offset); + rsxBindVertexArrayAttrib(context,GCM_VERTEX_ATTRIB_TEX0,0,offset,sizeof(S3DVertex),2,GCM_VERTEX_DATA_TYPE_F32,GCM_LOCATION_RSX); + + rsxLoadVertexProgram(context,vpo,vp_ucode); + rsxSetVertexProgramParameter(context,vpo,projMatrix,(float*)&P); + rsxSetVertexProgramParameter(context,vpo,mvMatrix,(float*)&modelViewMatrix); + rsxLoadFragmentProgramLocation(context,fpo,fp_offset,GCM_LOCATION_RSX); + + rsxSetUserClipPlaneControl(context,GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE); + + rsxAddressToOffset(&mesh->indices[0],&offset); + rsxDrawIndexArray(context,GCM_TYPE_TRIANGLES,offset,mesh->cnt_indices,GCM_INDEX_TYPE_16B,GCM_LOCATION_RSX); + +} + +int main(int argc,const char *argv[]) +{ + padInfo padinfo; + padData paddata; + + if (sysModuleLoad(SYSMODULE_PNGDEC) != 0) exit(0); + + png = new pngData; + pngLoadFromBuffer(Stone_Texture_png_bin, Stone_Texture_png_bin_size, png); + + void *host_addr = memalign(HOST_ADDR_ALIGNMENT,HOSTBUFFER_SIZE); + + printf("rsxtest started...\n"); + + init_screen(host_addr,HOSTBUFFER_SIZE); + ioPadInit(7); + init_shader(); + init_texture(); + + DebugFont::init(); + DebugFont::setScreenRes(display_width, display_height); + + cube = createCube(5.0f); + + atexit(program_exit_callback); + sysUtilRegisterCallback(0,sysutil_exit_callback,NULL); + + P = transpose(Matrix4::perspective(DEGTORAD(45.0f),aspect_ratio,1.0f,3000.0f)); + + setDrawEnv(); + setRenderTarget(curr_fb); + + running = 1; + while(running) { + sysUtilCheckCallback(); + + ioPadGetInfo(&padinfo); + for(int i=0; i < MAX_PADS; i++){ + if(padinfo.status[i]){ + ioPadGetData(i, &paddata); + + if (paddata.BTN_DOWN) { + rotx += 0.5f; + } + if (paddata.BTN_UP) { + rotx -= 0.5f; + } + if (rotx >= 360.0f || rotx <= 0.0f) rotx = fmodf(rotx, 360.0f); + if (paddata.BTN_RIGHT) { + roty += 0.5f; + } + if (paddata.BTN_LEFT) { + roty -= 0.5f; + } + if (roty >= 360.0f || roty <= 0.0f) roty = fmodf(roty, 360.0f); + if (paddata.BTN_CROSS) + texture_buffer_idx++; + if (texture_buffer_idx == 3) + texture_buffer_idx = 0; + if(paddata.BTN_CIRCLE) + goto done; + } + + } + + drawFrame(); + int ypos = 10; + int xpos = 10; + DebugFont::setPosition(xpos, ypos); + DebugFont::setColor(1.0f, 1.0f, 1.0f, 1.0f); + + DebugFont::print("Left/Right Pad to Left/Right rotation"); + ypos += 12; + DebugFont::setPosition(xpos, ypos); + DebugFont::print("Up/Down Pad to Up/Down rotation"); + ypos += 12; + DebugFont::setPosition(xpos, ypos); + DebugFont::print("CROSS Button to change texture"); + ypos += 12; + DebugFont::setPosition(xpos, ypos); + DebugFont::print("CIRCLE Button to exit"); + + flip(); + } + +done: + printf("rsxtest done...\n"); + DebugFont::shutdown(); + program_exit_callback(); + return 0; +} diff --git a/samples/graphics/rsx_Basic_Cube/source/perlin.cpp b/samples/graphics/rsx_Basic_Cube/source/perlin.cpp new file mode 100644 index 00000000..14ec05ad --- /dev/null +++ b/samples/graphics/rsx_Basic_Cube/source/perlin.cpp @@ -0,0 +1,68 @@ +#include + +static int SEED = 0; + +static int hash[] = { 208,34,231,213,32,248,233,56,161,78,24,140,71,48,140,254,245,255,247,247,40, + 185,248,251,245,28,124,204,204,76,36,1,107,28,234,163,202,224,245,128,167,204, + 9,92,217,54,239,174,173,102,193,189,190,121,100,108,167,44,43,77,180,204,8,81, + 70,223,11,38,24,254,210,210,177,32,81,195,243,125,8,169,112,32,97,53,195,13, + 203,9,47,104,125,117,114,124,165,203,181,235,193,206,70,180,174,0,167,181,41, + 164,30,116,127,198,245,146,87,224,149,206,57,4,192,210,65,210,129,240,178,105, + 228,108,245,148,140,40,35,195,38,58,65,207,215,253,65,85,208,76,62,3,237,55,89, + 232,50,217,64,244,157,199,121,252,90,17,212,203,149,152,140,187,234,177,73,174, + 193,100,192,143,97,53,145,135,19,103,13,90,135,151,199,91,239,247,33,39,145, + 101,120,99,3,186,86,99,41,237,203,111,79,220,135,158,42,30,154,120,67,87,167, + 135,176,183,191,253,115,184,21,233,58,129,233,142,39,128,211,118,137,139,255, + 114,20,218,113,154,27,127,246,250,1,8,198,250,209,92,222,173,21,88,102,219 }; + +int noise2(int x, int y) +{ + int tmp = hash[(y + SEED) % 256]; + return hash[(tmp + x) % 256]; +} + +float lin_inter(float x, float y, float s) +{ + return x + s * (y - x); +} + +float smooth_inter(float x, float y, float s) +{ + return lin_inter(x, y, s * s * (3 - 2 * s)); +} + +float noise2d(float x, float y) +{ + int x_int = x; + int y_int = y; + float x_frac = x - x_int; + float y_frac = y - y_int; + int s = noise2(x_int, y_int); + int t = noise2(x_int + 1, y_int); + int u = noise2(x_int, y_int + 1); + int v = noise2(x_int + 1, y_int + 1); + float low = smooth_inter(s, t, x_frac); + float high = smooth_inter(u, v, x_frac); + return smooth_inter(low, high, y_frac); +} + +float perlin2d(float x, float y, float freq, int depth) +{ + float xa = x * freq; + float ya = y * freq; + float amp = 1.0; + float fin = 0; + float div = 0.0; + + int i; + for (i = 0; i < depth; i++) + { + div += 256 * amp; + fin += noise2d(xa, ya) * amp; + amp /= 2; + xa *= 2; + ya *= 2; + } + + return fin / div; +} \ No newline at end of file diff --git a/samples/graphics/rsx_Basic_Cube/source/rsxutil.cpp b/samples/graphics/rsx_Basic_Cube/source/rsxutil.cpp new file mode 100644 index 00000000..712c0017 --- /dev/null +++ b/samples/graphics/rsx_Basic_Cube/source/rsxutil.cpp @@ -0,0 +1,207 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "rsxutil.h" + +#define GCM_LABEL_INDEX 255 + +videoResolution vResolution; +gcmContextData *context = NULL; + +u32 curr_fb = 0; +u32 first_fb = 1; + +u32 display_width; +u32 display_height; + +u32 depth_pitch; +u32 depth_offset; +u32 *depth_buffer; + +u32 color_pitch; +u32 color_offset[FRAME_BUFFER_COUNT]; +u32 *color_buffer[FRAME_BUFFER_COUNT]; + +f32 aspect_ratio; + +static u32 sResolutionIds[] = { + VIDEO_RESOLUTION_960x1080, + VIDEO_RESOLUTION_720, + VIDEO_RESOLUTION_480, + VIDEO_RESOLUTION_576 +}; +static size_t RESOLUTION_ID_COUNT = sizeof(sResolutionIds)/sizeof(u32); + +static u32 sLabelVal = 1; + +static RSXDebugFontRenderer *debugFontRenderer; + +static void waitFinish() +{ + rsxSetWriteBackendLabel(context,GCM_LABEL_INDEX,sLabelVal); + + rsxFlushBuffer(context); + + while(*(vu32*)gcmGetLabelAddress(GCM_LABEL_INDEX)!=sLabelVal) + usleep(30); + + ++sLabelVal; +} + +static void waitRSXIdle() +{ + rsxSetWriteBackendLabel(context,GCM_LABEL_INDEX,sLabelVal); + rsxSetWaitLabel(context,GCM_LABEL_INDEX,sLabelVal); + + ++sLabelVal; + + waitFinish(); +} + +void initVideoConfiguration() +{ + s32 rval = 0; + s32 resId = 0; + + for (size_t i=0;i < RESOLUTION_ID_COUNT;i++) { + rval = videoGetResolutionAvailability(VIDEO_PRIMARY, sResolutionIds[i], VIDEO_ASPECT_AUTO, 0); + if (rval != 1) continue; + + resId = sResolutionIds[i]; + rval = videoGetResolution(resId, &vResolution); + if(!rval) break; + } + + if(rval) { + printf("Error: videoGetResolutionAvailability failed. No usable resolution.\n"); + exit(1); + } + + videoConfiguration config = { + (u8)resId, + VIDEO_BUFFER_FORMAT_XRGB, + VIDEO_ASPECT_AUTO, + {0,0,0,0,0,0,0,0,0}, + (u32)vResolution.width*4 + }; + + rval = videoConfigure(VIDEO_PRIMARY, &config, NULL, 0); + if(rval) { + printf("Error: videoConfigure failed.\n"); + exit(1); + } + + videoState state; + + rval = videoGetState(VIDEO_PRIMARY, 0, &state); + switch(state.displayMode.aspect) { + case VIDEO_ASPECT_4_3: + aspect_ratio = 4.0f/3.0f; + break; + case VIDEO_ASPECT_16_9: + aspect_ratio = 16.0f/9.0f; + break; + default: + printf("unknown aspect ratio %x\n", state.displayMode.aspect); + aspect_ratio = 16.0f/9.0f; + break; + } + + display_height = vResolution.height; + display_width = vResolution.width; +} + +void setRenderTarget(u32 index) +{ + gcmSurface sf; + + sf.colorFormat = GCM_SURFACE_X8R8G8B8; + sf.colorTarget = GCM_SURFACE_TARGET_0; + sf.colorLocation[0] = GCM_LOCATION_RSX; + sf.colorOffset[0] = color_offset[index]; + sf.colorPitch[0] = color_pitch; + + sf.colorLocation[1] = GCM_LOCATION_RSX; + sf.colorLocation[2] = GCM_LOCATION_RSX; + sf.colorLocation[3] = GCM_LOCATION_RSX; + sf.colorOffset[1] = 0; + sf.colorOffset[2] = 0; + sf.colorOffset[3] = 0; + sf.colorPitch[1] = 64; + sf.colorPitch[2] = 64; + sf.colorPitch[3] = 64; + + sf.depthFormat = GCM_SURFACE_ZETA_Z24S8; + sf.depthLocation = GCM_LOCATION_RSX; + sf.depthOffset = depth_offset; + sf.depthPitch = depth_pitch; + + sf.type = GCM_SURFACE_TYPE_LINEAR; + sf.antiAlias = GCM_SURFACE_CENTER_1; + + sf.width = display_width; + sf.height = display_height; + sf.x = 0; + sf.y = 0; + + rsxSetSurface(context,&sf); +} + +void init_screen(void *host_addr,u32 size) +{ + u32 zs_depth = 4; + u32 color_depth = 4; + + rsxInit(&context,DEFUALT_CB_SIZE,size,host_addr); + + initVideoConfiguration(); + + waitRSXIdle(); + + gcmSetFlipMode(GCM_FLIP_VSYNC); + + color_pitch = display_width*color_depth; + depth_pitch = display_width*zs_depth; + + for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { + color_buffer[i] = (u32*)rsxMemalign(64,(display_height*color_pitch)); + rsxAddressToOffset(color_buffer[i],&color_offset[i]); + gcmSetDisplayBuffer(i,color_offset[i],color_pitch,display_width,display_height); + } + + depth_buffer = (u32*)rsxMemalign(64, display_height*depth_pitch); + rsxAddressToOffset(depth_buffer,&depth_offset); + + debugFontRenderer = new RSXDebugFontRenderer(context); +} + +void waitflip() +{ + while(gcmGetFlipStatus()!=0) + usleep(200); + gcmResetFlipStatus(); +} + +void flip() +{ + if(!first_fb) waitflip(); + else gcmResetFlipStatus(); + + gcmSetFlip(context,curr_fb); + rsxFlushBuffer(context); + + gcmSetWaitFlip(context); + + curr_fb ^= 1; + setRenderTarget(curr_fb); + + first_fb = 0; +} From e12643ec66cebb835f80b0f2ebd5a978db518e2b Mon Sep 17 00:00:00 2001 From: crystalct Date: Mon, 25 Jan 2021 13:05:40 +0100 Subject: [PATCH 47/56] add data folder to RSX Basic Cube sample --- .../rsx_Basic_Cube/data/Stone_Texture_png.bin | Bin 0 -> 46588 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 samples/graphics/rsx_Basic_Cube/data/Stone_Texture_png.bin diff --git a/samples/graphics/rsx_Basic_Cube/data/Stone_Texture_png.bin b/samples/graphics/rsx_Basic_Cube/data/Stone_Texture_png.bin new file mode 100644 index 0000000000000000000000000000000000000000..26777bb18cdc00bfe700f77ad004aba8cea88b66 GIT binary patch literal 46588 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�j!$EDnuog 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 Date: Mon, 25 Jan 2021 13:07:20 +0100 Subject: [PATCH 48/56] fixed VIDEO_RESOLUTION_1080 inside rsxutil.cpp --- samples/graphics/rsx_Basic_Cube/source/rsxutil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/graphics/rsx_Basic_Cube/source/rsxutil.cpp b/samples/graphics/rsx_Basic_Cube/source/rsxutil.cpp index 712c0017..4319d8e6 100644 --- a/samples/graphics/rsx_Basic_Cube/source/rsxutil.cpp +++ b/samples/graphics/rsx_Basic_Cube/source/rsxutil.cpp @@ -33,7 +33,7 @@ u32 *color_buffer[FRAME_BUFFER_COUNT]; f32 aspect_ratio; static u32 sResolutionIds[] = { - VIDEO_RESOLUTION_960x1080, + VIDEO_RESOLUTION_1080, VIDEO_RESOLUTION_720, VIDEO_RESOLUTION_480, VIDEO_RESOLUTION_576 From 9f1c338b02cae2045cf48632fccba6ff42672654 Mon Sep 17 00:00:00 2001 From: crystalct Date: Mon, 25 Jan 2021 13:08:03 +0100 Subject: [PATCH 49/56] fixed VIDEO_RESOLUTION_1080 inside rsxutil.cpp --- samples/graphics/rsx_Basic/source/rsxutil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/graphics/rsx_Basic/source/rsxutil.cpp b/samples/graphics/rsx_Basic/source/rsxutil.cpp index e0efa8bf..b86f41af 100644 --- a/samples/graphics/rsx_Basic/source/rsxutil.cpp +++ b/samples/graphics/rsx_Basic/source/rsxutil.cpp @@ -32,7 +32,7 @@ u32 *color_buffer[FRAME_BUFFER_COUNT]; f32 aspect_ratio; static u32 sResolutionIds[] = { - VIDEO_RESOLUTION_960x1080, + VIDEO_RESOLUTION_1080, VIDEO_RESOLUTION_720, VIDEO_RESOLUTION_480, VIDEO_RESOLUTION_576 From f9351eb0bab16387e40af2adafa9df7e499b1932 Mon Sep 17 00:00:00 2001 From: crystalct Date: Mon, 25 Jan 2021 13:17:00 +0100 Subject: [PATCH 50/56] Add files via upload --- .../graphics/rsx_Basic_Cube/rsx_basic_cube.png | Bin 0 -> 85883 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 samples/graphics/rsx_Basic_Cube/rsx_basic_cube.png diff --git a/samples/graphics/rsx_Basic_Cube/rsx_basic_cube.png b/samples/graphics/rsx_Basic_Cube/rsx_basic_cube.png new file mode 100644 index 0000000000000000000000000000000000000000..75b48136c0e8b2729a678f646555102d5f5d48a3 GIT binary patch literal 85883 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 z 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 Date: Mon, 25 Jan 2021 13:17:51 +0100 Subject: [PATCH 51/56] Add files via upload --- samples/graphics/rsx_Basic_Cube/Readme.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 samples/graphics/rsx_Basic_Cube/Readme.md diff --git a/samples/graphics/rsx_Basic_Cube/Readme.md b/samples/graphics/rsx_Basic_Cube/Readme.md new file mode 100644 index 00000000..a7dd8bfd --- /dev/null +++ b/samples/graphics/rsx_Basic_Cube/Readme.md @@ -0,0 +1,4 @@ +A basic example to use RSX and Vertex/Fragment without camera position, eyes position, lights, 3D objects, textures, etc etc. +Just draw a tringle..... + +![RSX basic example](https://github.com/crystalct/PSL1GHT/blob/develop/samples/graphics/rsx_Basic/rsx_basic.png?raw=true) From 4196a980a04ee2d9eeb7e9f3ba74d0de36ac5b91 Mon Sep 17 00:00:00 2001 From: crystalct Date: Mon, 25 Jan 2021 13:20:30 +0100 Subject: [PATCH 52/56] Update Readme.md --- samples/graphics/rsx_Basic_Cube/Readme.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/samples/graphics/rsx_Basic_Cube/Readme.md b/samples/graphics/rsx_Basic_Cube/Readme.md index a7dd8bfd..beaecd63 100644 --- a/samples/graphics/rsx_Basic_Cube/Readme.md +++ b/samples/graphics/rsx_Basic_Cube/Readme.md @@ -1,4 +1,3 @@ -A basic example to use RSX and Vertex/Fragment without camera position, eyes position, lights, 3D objects, textures, etc etc. -Just draw a tringle..... +A basic cube example to use atexture on a cube and to rotate it. -![RSX basic example](https://github.com/crystalct/PSL1GHT/blob/develop/samples/graphics/rsx_Basic/rsx_basic.png?raw=true) +![RSX basic cube example](rsx_basic_cube.png?raw=true) From 5b072d52325024fb50887332591b8ff119641a86 Mon Sep 17 00:00:00 2001 From: crystalct Date: Mon, 25 Jan 2021 13:20:46 +0100 Subject: [PATCH 53/56] Update Readme.md --- samples/graphics/rsx_Basic_Cube/Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/graphics/rsx_Basic_Cube/Readme.md b/samples/graphics/rsx_Basic_Cube/Readme.md index beaecd63..588a40dd 100644 --- a/samples/graphics/rsx_Basic_Cube/Readme.md +++ b/samples/graphics/rsx_Basic_Cube/Readme.md @@ -1,3 +1,3 @@ -A basic cube example to use atexture on a cube and to rotate it. +A basic cube example to use a texture on a cube and to rotate it. ![RSX basic cube example](rsx_basic_cube.png?raw=true) From 7ff764667223f9da801658122d2c36c8455a65a6 Mon Sep 17 00:00:00 2001 From: crystalct Date: Mon, 25 Jan 2021 13:21:34 +0100 Subject: [PATCH 54/56] Update Readme.md --- samples/graphics/rsx_Basic/Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/graphics/rsx_Basic/Readme.md b/samples/graphics/rsx_Basic/Readme.md index 50b42f25..c5ae4d7d 100644 --- a/samples/graphics/rsx_Basic/Readme.md +++ b/samples/graphics/rsx_Basic/Readme.md @@ -1,4 +1,4 @@ A basic example to use RSX and Vertex/Fragment without camera position, eyes position, lights, 3D objects, textures, etc etc. Just draw a tringle..... -![RSX basic example](https://github.com/crystalct/PSL1GHT/blob/develop/samples/graphics/rsx_Basic/rsx_basic.png?raw=true) +![RSX basic example](rsx_basic.png?raw=true) From fb147a61f3cd201e6e4c45265f9e99bddeed16ba Mon Sep 17 00:00:00 2001 From: Salvo Cristaldi Date: Wed, 27 Jan 2021 12:40:27 +0100 Subject: [PATCH 55/56] add RSX basic wallparer example --- samples/graphics/rsx_Basic_Wallpaper/Makefile | 164 + .../graphics/rsx_Basic_Wallpaper/Readme.md | 3 + .../rsx_Basic_Wallpaper/include/acid.h | 2939 +++++++++++++++++ .../rsx_Basic_Wallpaper/include/geometry.h | 12 + .../include/irrallocator.h | 73 + .../rsx_Basic_Wallpaper/include/irrarray.h | 162 + .../rsx_Basic_Wallpaper/include/mesh.h | 128 + .../rsx_Basic_Wallpaper/include/rsxutil.h | 40 + .../rsx_basic_wallpaper.png | Bin 0 -> 1492477 bytes .../shaders/diffuse_specular_shader.vcg | 16 + .../rsx_Basic_Wallpaper/shaders/scanlines.fcg | 44 + .../rsx_Basic_Wallpaper/source/geometry.cpp | 302 ++ .../rsx_Basic_Wallpaper/source/main.cpp | 343 ++ .../rsx_Basic_Wallpaper/source/rsxutil.cpp | 208 ++ 14 files changed, 4434 insertions(+) create mode 100644 samples/graphics/rsx_Basic_Wallpaper/Makefile create mode 100644 samples/graphics/rsx_Basic_Wallpaper/Readme.md create mode 100644 samples/graphics/rsx_Basic_Wallpaper/include/acid.h create mode 100644 samples/graphics/rsx_Basic_Wallpaper/include/geometry.h create mode 100644 samples/graphics/rsx_Basic_Wallpaper/include/irrallocator.h create mode 100644 samples/graphics/rsx_Basic_Wallpaper/include/irrarray.h create mode 100644 samples/graphics/rsx_Basic_Wallpaper/include/mesh.h create mode 100644 samples/graphics/rsx_Basic_Wallpaper/include/rsxutil.h create mode 100644 samples/graphics/rsx_Basic_Wallpaper/rsx_basic_wallpaper.png create mode 100644 samples/graphics/rsx_Basic_Wallpaper/shaders/diffuse_specular_shader.vcg create mode 100644 samples/graphics/rsx_Basic_Wallpaper/shaders/scanlines.fcg create mode 100644 samples/graphics/rsx_Basic_Wallpaper/source/geometry.cpp create mode 100644 samples/graphics/rsx_Basic_Wallpaper/source/main.cpp create mode 100644 samples/graphics/rsx_Basic_Wallpaper/source/rsxutil.cpp diff --git a/samples/graphics/rsx_Basic_Wallpaper/Makefile b/samples/graphics/rsx_Basic_Wallpaper/Makefile new file mode 100644 index 00000000..c76ea9e9 --- /dev/null +++ b/samples/graphics/rsx_Basic_Wallpaper/Makefile @@ -0,0 +1,164 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(PSL1GHT)),) +$(error "Please set PSL1GHT in your environment. export PSL1GHT=") +endif + +include $(PSL1GHT)/ppu_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source ../debugfont_renderer/source +DATA := data +SHADERS := shaders ../debugfont_renderer/shaders +INCLUDES := include ../debugfont_renderer/include + +TITLE := RSX Test - PSL1GHT +APPID := RSX00003 +CONTENTID := UP0001-$(APPID)_00-0000000000000000 + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +CFLAGS = -Wall -mcpu=cell $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lsimdmath -lrsx -lgcm_sys -lio -lsysutil -lrt -llv2 -lm -lsysmodule -lpngdec + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ + $(foreach dir,$(SHADERS),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export BUILDDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) +VCGFILES := $(foreach dir,$(SHADERS),$(notdir $(wildcard $(dir)/*.vcg))) +FCGFILES := $(foreach dir,$(SHADERS),$(notdir $(wildcard $(dir)/*.fcg))) + +VPOFILES := $(VCGFILES:.vcg=.vpo) +FPOFILES := $(FCGFILES:.fcg=.fpo) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(addsuffix .o,$(VPOFILES)) \ + $(addsuffix .o,$(FPOFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES), -I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(LIBPSL1GHT_INC) \ + -I$(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + $(LIBPSL1GHT_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).self $(OUTPUT).fake.self + +#--------------------------------------------------------------------------------- +run: + ps3load $(OUTPUT).self + +#--------------------------------------------------------------------------------- +pkg: $(BUILD) $(OUTPUT).pkg + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).self: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .bin extension +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.vpo.o : %.vpo +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.fpo.o : %.fpo +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/samples/graphics/rsx_Basic_Wallpaper/Readme.md b/samples/graphics/rsx_Basic_Wallpaper/Readme.md new file mode 100644 index 00000000..45745d77 --- /dev/null +++ b/samples/graphics/rsx_Basic_Wallpaper/Readme.md @@ -0,0 +1,3 @@ +A basic background wallpaper using vertex, fragment and texture with a scanline shader. + +![RSX basic cube example](rsx_basic_wallpaper.png?raw=true) diff --git a/samples/graphics/rsx_Basic_Wallpaper/include/acid.h b/samples/graphics/rsx_Basic_Wallpaper/include/acid.h new file mode 100644 index 00000000..d1643518 --- /dev/null +++ b/samples/graphics/rsx_Basic_Wallpaper/include/acid.h @@ -0,0 +1,2939 @@ +/* GIMP RGBA C-Source image dump (acid.c) */ + +static const struct { + unsigned int width; + unsigned int height; + unsigned int bytes_per_pixel; /* 3:RGB, 4:RGBA */ + unsigned char pixel_data[128 * 128 * 4 + 1]; +} acid = { + 128, 128, 4, + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\335\335\335\0\265\265\265\0\221\221\221\0qqq\0UUU\0===\0)))\0\31\31" + "\31\0\15\15\15\0\5\5\5\0\1\1\1\0\1\1\1\0\5\5\5\0\15\15\15\0\31\31\31\0))" + ")\0===\0UUU\0qqq\0\221\221\221\0\265\265\265\0\335\335\335\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\345\345\345\0\251\251\251\0qqq\0===\0\15\15\15\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\15\15\15\0===\0qqq\0\251\251\251\0\345\345\345\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0qqq\0---\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\7\0\0\0\15\0\0\0" + "\23\0\0\0\30\0\0\0\35\0\0\0!\0\0\0$\0\0\0&\0\0\0(\0\0\0(\0\0\0'\0\0\0$\0" + "\0\0!\0\0\0\35\0\0\0\31\0\0\0\23\0\0\0\15\0\0\0\7\0\0\0\2\0\0\0\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0---\0qqq\0\271\271\271\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\265\265\265\0aaa\0\21\21\21\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0\17\0\0\0\30\0\0\0\40\0\0\0+\0\0\0B\0" + "\0\0\\\0\0\0t\0\0\0\211\0\0\0\234\0\0\0\254\0\0\0\271\0\0\0\304\0\0\0\313" + "\0\0\0\313\0\0\0\304\0\0\0\271\0\0\0\254\0\0\0\234\0\0\0\211\0\0\0t\0\0\0" + "\\\0\0\0B\0\0\0+\0\0\0!\0\0\0\30\0\0\0\17\0\0\0\6\0\0\0\1\0\0\0\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\21\21\21\0aaa\0\265\265\265\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\331\331\331\0yyy\0\35\35\35\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\2\0\0\0\14\0\0\0\30\0\0\0&\0\0\0?\0\0\0d\0\0\0\211\0\0\0\254\0\0\0\311" + "\0\0\0\331\0\0\0\341\0\0\0\347\0\0\0\354\0\0\0\361\0\0\0\365\0\0\0\370\0" + "\0\0\373\0\0\0\375\0\0\0\375\0\0\0\373\0\0\0\371\0\0\0\365\0\0\0\361\0\0" + "\0\355\0\0\0\347\0\0\0\341\0\0\0\331\0\0\0\312\0\0\0\254\0\0\0\211\0\0\0" + "d\0\0\0?\0\0\0&\0\0\0\30\0\0\0\15\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0" + "\35\35\35\0yyy\0\331\331\331\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0QQQ\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\16\0\0\0\33\0\0" + "\0.\0\0\0Y\0\0\0\211\0\0\0\265\0\0\0\325\0\0\0\343\0\0\0\354\0\0\0\365\0" + "\0\0\374\40!\0\377FH\0\377hk\0\377\206\213\0\377\240\246\0\377\267\275\0" + "\377\311\321\0\377\330\341\0\377\344\354\0\377\344\354\0\377\330\341\0\377" + "\311\321\0\377\267\275\0\377\240\246\0\377\206\213\0\377hk\0\377FH\0\377" + "\40!\0\377\0\0\0\375\0\0\0\365\0\0\0\355\0\0\0\343\0\0\0\326\0\0\0\266\0" + "\0\0\211\0\0\0Z\0\0\0/\0\0\0\33\0\0\0\16\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0" + "\0\0\0\0QQQ\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\255\255\255\0===\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\11\0\0\0" + "\27\0\0\0-\0\0\0]\0\0\0\224\0\0\0\304\0\0\0\336\0\0\0\354\0\0\0\367\30\31" + "\0\376QT\0\377\206\213\0\377\267\275\0\377\344\354\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\344\354\0\377\267\275\0\377\206\213\0\377QT\0\377\30\31\0" + "\376\0\0\0\367\0\0\0\354\0\0\0\337\0\0\0\305\0\0\0\224\0\0\0]\0\0\0-\0\0" + "\0\30\0\0\0\11\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0===\0\255\255\255\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265" + "\0===\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\15\0\0\0!\0\0\0H\0\0\0\204" + "\0\0\0\274\0\0\0\336\0\0\0\357\0\0\0\373BD\0\377\206\213\0\377\306\315\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\306\315\0\377\206\213\0\377BD\0\377\0\0\0\373" + "\0\0\0\357\0\0\0\337\0\0\0\275\0\0\0\204\0\0\0I\0\0\0!\0\0\0\15\0\0\0\3\0" + "\0\0\1\0\0\0\0\0\0\0\0===\0\265\265\265\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321\321\0QQ" + "Q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\17\0\0\0'\0\0\0[\0\0\0\236\0\0" + "\0\324\0\0\0\353\0\0\0\371FH\0\377\225\232\0\377\340\350\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\340\350\0\377\225\232\0\377FH\0\377\0\0\0\371\0" + "\0\0\353\0\0\0\324\0\0\0\236\0\0\0\\\0\0\0'\0\0\0\17\0\0\0\3\0\0\0\1\0\0" + "\0\0\0\0\0\0QQQ\0\321\321\321\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0yyy\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\16\0\0\0(\0\0\0c\0\0\0\250" + "\0\0\0\332\0\0\0\361$%\0\375~\203\0\377\325\335\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\325\335\0\377~\203\0\377$%\0\375\0\0\0\361\0\0\0" + "\333\0\0\0\251\0\0\0c\0\0\0(\0\0\0\17\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0yyy" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\265\265\265\0)))\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\13\0\0\0$\0\0\0_" + "\0\0\0\250\0\0\0\334\0\0\0\364BD\0\376\244\252\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\244\252\0\377BD\0\376\0\0\0\364\0\0\0" + "\335\0\0\0\250\0\0\0`\0\0\0%\0\0\0\13\0\0\0\2\0\0\0\1\0\0\0\0)))\0\265\265" + "\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\4\0\0\0\34\0\0\0O\0\0\0\234\0\0\0\332\0\0\0\364FH\0\376\267\275\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377FH\0\376\0\0\0\364\0" + "\0\0\332\0\0\0\234\0\0\0P\0\0\0\34\0\0\0\4\0\0\0\1\0\0\0\0\0\0\0\0qqq\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\315\315\315" + "\0""555\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\17\0\0\0""0\0\0\0\203\0\0\0\321\0" + "\0\0\360+-\0\375\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377+-\0\375" + "\0\0\0\360\0\0\0\322\0\0\0\203\0\0\0""1\0\0\0\17\0\0\0\3\0\0\0\1\0\0\0\0" + """555\0\315\315\315\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\235\235\235\0\1\1\1\0\0\0\0" + "\0\0\0\0\0\0\0\0\4\0\0\0\40\0\0\0`\0\0\0\262\0\0\0\346\0\0\0\374~\203\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377~\203\0\377\0\0\0\374\0\0\0\347\0\0\0\263\0\0\0a\0\0\0\40\0\0\0\5\0\0" + "\0\1\0\0\0\0\1\1\1\0\235\235\235\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\14\0\0" + "\0/\0\0\0\207\0\0\0\326\0\0\0\366FH\0\376\311\321\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\311\321\0\377FH\0\376\0\0\0\366\0\0\0\327\0\0\0\207\0\0\0" + """0\0\0\0\14\0\0\0\2\0\0\0\1\0\0\0\0uuu\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\375" + "\375\375\0UUU\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\30\0\0\0S\0\0\0\252\0\0\0\345" + "\0\0\0\374\206\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\374\0\0\0\346" + "\0\0\0\253\0\0\0T\0\0\0\31\0\0\0\4\0\0\0\1\0\0\0\0UUU\0\375\375\375\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\351\351\351\0==" + "=\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\"\0\0\0q\0\0\0\316\0\0\0\363/1\0\376\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377/1\0\376" + "\0\0\0\364\0\0\0\316\0\0\0q\0\0\0\"\0\0\0\5\0\0\0\1\0\0\0\0===\0\351\351" + "\351\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0---\0\0\0\0\0\0\0\0\1" + "\0\0\0\6\0\0\0*\0\0\0\205\0\0\0\332\0\0\0\373\\`\0\377\357\370\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357\370" + "\0\377\\`\0\377\0\0\0\373\0\0\0\332\0\0\0\206\0\0\0+\0\0\0\6\0\0\0\1\0\0" + "\0\1---\0\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\331\331\331\0%%%\0\0\0\0\0\0\0\0\1\0\0\0\11" + "\0\0\0""2\0\0\0\225\0\0\0\342\0\0\0\374\202\207\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\374\0\0\0\342\0\0\0" + "\226\0\0\0""2\0\0\0\11\0\0\0\1\0\0\0\1%%%\0\331\331\331\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\335\335\335\0%%%\0\0\0\0\0\0\0\0\1\0\0\0\15" + "\0\0\0?\0\0\0\243\0\0\0\350\2\2\0\375\240\246\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377" + "\2\2\0\375\0\0\0\350\0\0\0\244\0\0\0@\0\0\0\15\0\0\0\2\0\0\0\1%%%\0\335\335" + "\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\351\351\351\0---\0\0\0\0\0\0\0\0\1\0\0\0\16" + "\0\0\0H\0\0\0\257\0\0\0\355\25\25\0\376\267\275\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\25\25\0\376\0\0\0\355\0\0\0\257\0\0\0H\0\0\0" + "\17\0\0\0\2\0\0\0\1---\0\351\351\351\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\375\375\375\0===\0\0\0\0\0\0\0\0\1\0\0\0\15" + "\0\0\0H\0\0\0\264\0\0\0\360\40!\0\376\306\315\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\40!\0\376\0\0\0" + "\360\0\0\0\265\0\0\0I\0\0\0\15\0\0\0\1\0\0\0\1===\0\375\375\375\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0UUU\0\0\0\0\0\0\0\0\1\0\0\0\11" + "\0\0\0?\0\0\0\257\0\0\0\360$%\0\376\315\325\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\315\325" + "\0\377$%\0\377\0\0\0\360\0\0\0\257\0\0\0@\0\0\0\11\0\0\0\1\0\0\0\1UUU\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0""2" + "\0\0\0\244\0\0\0\355\40!\0\376\315\325\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377\40!\0\376\0\0\0\355\0\0\0\244\0\0\0""2\0" + "\0\0\6\0\0\0\1\0\0\0\0uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\235\235\235\0\0\0\0\0\0\0\0\0\0\0" + "\0\4\0\0\0*\0\0\0\225\0\0\0\350\25\25\0\376\306\315\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\25" + "\25\0\376\0\0\0\350\0\0\0\226\0\0\0*\0\0\0\5\0\0\0\1\0\0\0\0\235\235\235" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\315\315\315\0" + "\1\1\1\0\0\0\0\0\0\0\0\3\0\0\0\"\0\0\0\205\0\0\0\342\2\2\0\375\267\275\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\267\275\0\377\2\2\0\375\0\0\0\342\0\0\0" + "\206\0\0\0#\0\0\0\4\0\0\0\1\1\1\1\0\315\315\315\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0""555\0\0\0\0\0\0\0\0\2\0\0\0\30\0\0\0q\0\0\0\332\0\0\0\374" + "\240\246\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\350\361\0\377\254\263\0\377.0\0\377\13\14\0\377UX\0\377" + "\317\327\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377\0" + "\0\0\374\0\0\0\333\0\0\0r\0\0\0\31\0\0\0\2\0\0\0\1""555\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\0\0\0\0\14\0\0\0S\0\0\0\316\0\0\0\373\202\207" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\310\317\0\3779<\0\377\0\0\0\3779<\0\377\310\317\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\306\315\0\377!\"\0\377\0\0\0\377\0\0\0\377\4\4\0\377PT\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\373" + "\0\0\0\316\0\0\0S\0\0\0\14\0\0\0\1\0\0\0\0qqq\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0" + "\0\0\0\0\0\0\4\0\0\0/\0\0\0\253\0\0\0\364\\`\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377/2\0\377\0\0\0\377\0\0\0\377\0\0\0\377/2\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\237\245\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\20\20\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\\`\0\377\0\0\0\364\0\0\0" + "\253\0\0\0""0\0\0\0\5\0\0\0\1\0\0\0\0\265\265\265\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0)))\0\0\0\0\0\0\0\0\3\0\0\0\40\0\0" + "\0\207\0\0\0\345/1\0\376\357\370\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\207\215\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\357\370\0\377/1\0\376\0\0\0\346" + "\0\0\0\207\0\0\0\40\0\0\0\3\0\0\0\1)))\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0yyy\0\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0`\0\0\0\326\0\0\0\374\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\5\5\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377\0\0\0\374\0\0\0" + "\327\0\0\0a\0\0\0\17\0\0\0\1\0\0\0\0yyy\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321\321\0" + "\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0""1\0\0\0\263\0\0\0\365\206\213\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0" + "\366\0\0\0\263\0\0\0""1\0\0\0\5\0\0\0\1\0\0\0\0\321\321\321\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0QQQ\0\0" + "\0\0\0\0\0\0\2\0\0\0\34\0\0\0\203\0\0\0\346FH\0\376\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\376" + "\0\0\0\347\0\0\0\204\0\0\0\34\0\0\0\2\0\0\0\1QQQ\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0\0\0\0" + "\0\0\0\12\0\0\0O\0\0\0\321\0\0\0\374\311\321\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\311\321" + "\0\377\0\0\0\374\0\0\0\322\0\0\0P\0\0\0\13\0\0\0\1\0\0\0\0\265\265\265\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0" + "\0%\0\0\0\234\0\0\0\360~\203\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377~\203\0\377\0\0\0\361\0\0\0\234\0\0\0%\0\0\0\3\0\0\0\1===\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\255\255\255\0\0\0\0\0\0\0\0\0\0\0\0\16\0\0\0_\0" + "\0\0\332+-\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377+-\0\375\0\0\0\332\0\0\0`\0\0\0\17\0\0\0\1\0\0\0\0\255\255" + "\255\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0(\0\0\0\250\0\0\0" + "\363\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\244\252\0\377\0\0\0\364\0\0\0\250\0\0\0)\0\0\0\3\0\0\0\1=" + "==\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\271\271\271\0\0\0\0\0\0\0\0\0\0\0\0\17\0\0\0c\0\0\0\334FH" + "\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377FH\0\376\0\0\0\335\0\0\0d\0\0\0\17\0\0\0" + "\1\0\0\0\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0'\0\0\0\251\0\0\0\363\267\275" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\364\0\0\0\251\0\0\0" + "(\0\0\0\3\0\0\0\1QQQ\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\331\331\331\0\0\0\0\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\333BD\0\375\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377BD\0\376\0\0\0\333\0\0\0\\" + "\0\0\0\16\0\0\0\1\0\0\0\0\331\331\331\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0yyy\0\0\0\0\0\0\0\0\2\0\0\0!\0\0\0\235\0\0\0\361\244\252\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377\0\0\0\361\0" + "\0\0\236\0\0\0\"\0\0\0\2\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\35\35\35\0\0\0\0\0\0\0\0\10\0\0\0H\0\0\0\324$%\0\375\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377$%\0\375\0" + "\0\0\325\0\0\0I\0\0\0\11\0\0\0\1\35\35\35\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265" + "\265\0\0\0\0\0\0\0\0\0\0\0\0\26\0\0\0\203\0\0\0\353~\203\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377~\203" + "\0\377\0\0\0\353\0\0\0\204\0\0\0\27\0\0\0\1\0\0\0\0\265\265\265\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0aaa\0\0\0\0\0\0\0\0\3\0\0\0-\0\0\0\275\0\0\0\370\325\335\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\325\335\0\377\0\0\0\371\0\0\0\276\0\0\0-\0\0\0\3\0\0\0\1aaa\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\21\21\21\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\336FH\0\376\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377FH\0\377\0\0\0\337\0\0\0]\0\0\0\16\0\0\0\1\21\21\21\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0\0\0\0\0\0\0\0\0\0\0\0\32\0\0\0\223\0\0\0\357\225\232\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\225\232\0\377\0\0\0\357\0\0\0\224\0\0\0\33\0" + "\0\0\1\0\0\0\0\271\271\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\2\0\0\0.\0\0\0\305\0\0\0\372\340\350\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\340\350\0\377\0\0\0\373\0\0\0\305\0\0\0" + "/\0\0\0\3\0\0\0\1qqq\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0---\0\0\0\0\0\0\0\0\14\0\0\0Y\0\0\0\336BD\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377BD\0\377\0\0\0\337\0\0\0Z\0\0\0" + "\15\0\0\0\1---\0\377\377\377\0\377\377\377\0\377\377\377\0\345\345\345\0" + "\0\0\0\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\203\210\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\355\0" + "\0\0\211\0\0\0\30\0\0\0\1\0\0\0\0\345\345\345\0\377\377\377\0\377\377\377" + "\0\251\251\251\0\0\0\0\0\0\0\0\1\0\0\0&\0\0\0\265\0\0\0\367\306\315\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377" + "\0\0\0\367\0\0\0\266\0\0\0&\0\0\0\2\0\0\0\1\251\251\251\0\377\377\377\0\377" + "\377\377\0qqq\0\0\0\0\0\0\0\0\6\0\0\0?\0\0\0\325\30\31\0\376\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\30\31\0\376\0\0\0\326\0\0\0?\0\0\0\6\0\0\0\1qqq\0\377\377\377\0\377\377" + "\377\0===\0\0\0\0\0\0\0\0\17\0\0\0d\0\0\0\343QT\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377QT" + "\0\377\0\0\0\344\0\0\0e\0\0\0\17\0\0\0\1===\0\377\377\377\0\377\377\377\0" + "\15\15\15\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\6\6\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\15\15\15\0\377\377\377" + "\0\335\335\335\0\0\0\0\0\0\0\0\0\0\0\0\40\0\0\0\254\0\0\0\365\267\275\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\203\210\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\6\6\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\0\0\0\365\0\0\0\255\0\0\0!\0\0\0\1\0\0\0\0\335" + "\335\335\0\265\265\265\0\0\0\0\0\0\0\0\1\0\0\0+\0\0\0\311\0\0\0\374\344\354" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\210\215\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\7\7\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\344\354\0\377\0\0\0\374\0\0\0\312\0\0\0,\0\0\0\2" + "\0\0\0\1\265\265\265\0\221\221\221\0\0\0\0\0\0\0\0\6\0\0\0B\0\0\0\331\40" + "!\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\237\245\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\20\21\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\40!\0\377\0\0\0" + "\332\0\0\0B\0\0\0\7\0\0\0\1\221\221\221\0qqq\0\0\0\0\0\0\0\0\15\0\0\0\\\0" + "\0\0\341FH\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\37702\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\37702\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377!\"\0\377\0\0" + "\0\377\0\0\0\377\4\4\0\377PS\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\377\0\0\0\342\0" + "\0\0]\0\0\0\15\0\0\0\1qqq\0UUU\0\0\0\0\0\0\0\0\23\0\0\0t\0\0\0\347hk\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\310\317\0\3779<\0\377\0" + "\0\0\3779<\0\377\310\317\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\350\361\0\377\254\263\0\377.0\0\377" + "\13\14\0\377UX\0\377\317\327\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377hk\0\377\0\0\0\350\0" + "\0\0u\0\0\0\23\0\0\0\1UUU\0===\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206" + "\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1===" + "\0)))\0\0\0\0\0\0\0\0\35\0\0\0\234\0\0\0\361\240\246\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246" + "\0\377\0\0\0\361\0\0\0\235\0\0\0\35\0\0\0\1)))\0\31\31\31\0\0\0\0\0\0\0\0" + "\40\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0" + "\0\0\255\0\0\0!\0\0\0\1\31\31\31\0\15\15\15\0\0\0\0\0\0\0\0$\0\0\0\271\0" + "\0\0\370\311\321\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\311\321\0\377\0\0\0\371\0\0\0\272\0\0\0" + "$\0\0\0\1\15\15\15\0\5\5\5\0\0\0\0\0\0\0\0&\0\0\0\304\0\0\0\373\330\341\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\330\341\0\377\0\0\0\373\0\0\0\305\0\0\0'\0\0\0\1\5\5\5\0\1\1\1" + "\0\0\0\0\0\0\0\0(\0\0\0\313\0\0\0\374\344\354\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377" + "\0\0\0\375\0\0\0\313\0\0\0)\0\0\0\1\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0(\0\0\0" + "\313\0\0\0\374\344\354\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\344\354\0\377\0\0\0\375\0\0\0\313\0\0" + "\0)\0\0\0\1\1\1\1\0\5\5\5\0\0\0\0\0\0\0\0&\0\0\0\304\0\0\0\373\330\341\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\330\341\0\377\0\0\0\373\0\0\0\305\0\0\0'\0\0\0\1\5\5\5\0\15\15" + "\15\0\0\0\0\0\0\0\0$\0\0\0\271\0\0\0\370\311\321\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\311\321\0" + "\377\0\0\0\371\0\0\0\272\0\0\0$\0\0\0\1\15\15\15\0\31\31\31\0\0\0\0\0\0\0" + "\0\40\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0" + "\0\0\255\0\0\0!\0\0\0\1\31\31\31\0)))\0\0\0\0\0\0\0\0\35\0\0\0\234\0\0\0" + "\361\240\246\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\240\246\0\377\0\0\0\361\0\0\0\235\0\0\0\35\0\0\0" + "\1)))\0===\0\0\0\0\0\0\0\0\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\206" + "\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1===\0UUU\0\0\0\0\0\0\0\0\23" + "\0\0\0t\0\0\0\347hk\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377hk\0\377\0\0\0\350\0\0\0u\0\0\0\23\0\0\0" + "\1UUU\0qqq\0\0\0\0\0\0\0\0\15\0\0\0\\\0\0\0\341FH\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377FH\0\377\0" + "\0\0\342\0\0\0]\0\0\0\15\0\0\0\1qqq\0\221\221\221\0\0\0\0\0\0\0\0\6\0\0\0" + "B\0\0\0\331\40!\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\40!\0\377\0\0\0\332\0\0\0C\0\0\0\7\0\0\0\1\221" + "\221\221\0\265\265\265\0\0\0\0\0\0\0\0\1\0\0\0,\0\0\0\312\0\0\0\374\344\354" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377" + "\0\0\0\375\0\0\0\312\0\0\0,\0\0\0\2\0\0\0\1\265\265\265\0\335\335\335\0\0" + "\0\0\0\0\0\0\1\0\0\0!\0\0\0\254\0\0\0\365\267\275\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\267\275\0\377\0\0\0\365\0\0\0\255\0\0" + "\0\"\0\0\0\1\0\0\0\1\335\335\335\0\377\377\377\0\15\15\15\0\0\0\0\0\0\0\0" + "\30\0\0\0\211\0\0\0\354\206\213\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\15" + "\15\15\0\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\17\0\0\0d\0\0\0\343" + "QT\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377QT\0\377\0" + "\0\0\344\0\0\0e\0\0\0\17\0\0\0\1===\0\377\377\377\0\377\377\377\0qqq\0\0" + "\0\0\0\0\0\0\6\0\0\0?\0\0\0\325\30\31\0\376\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\30\31\0\376\0\0\0\326\0\0\0?\0\0\0\6\0\0\0\1qqq\0\377" + "\377\377\0\377\377\377\0\251\251\251\0\0\0\0\0\0\0\0\1\0\0\0&\0\0\0\266\0" + "\0\0\367\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\335\345\0\377\261\270\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\0\0\0\370" + "\0\0\0\266\0\0\0'\0\0\0\2\0\0\0\1\251\251\251\0\377\377\377\0\377\377\377" + "\0\345\345\345\0\0\0\0\0\0\0\0\1\0\0\0\31\0\0\0\212\0\0\0\354\206\213\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\322\332\0\377\36" + "\37\0\377\10\10\0\377\265\273\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\316\326\0\377\230\235\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\206\213\0\377\0\0\0\355\0\0\0\212\0\0\0\31\0\0\0\1\0" + "\0\0\1\345\345\345\0\377\377\377\0\377\377\377\0\377\377\377\0---\0\0\0\0" + "\0\0\0\0\14\0\0\0Z\0\0\0\337BD\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\322\332\0\377\36\37\0\377\0\0\0\377\0\0\0\377\1\1\0\377\201\206\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\265\273\0\377\17" + "\20\0\377\2\2\0\377\224\231\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377BD\0\377\0\0\0\340\0\0\0Z\0\0\0" + "\15\0\0\0\1---\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "qqq\0\0\0\0\0\0\0\0\2\0\0\0.\0\0\0\305\0\0\0\372\340\350\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\322\332\0\377\36\37\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377SV\0\377\353\364\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377w{\0\377\1\1\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377;>\0\377<>\0\377<>\0\377\303\312\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340" + "\350\0\377\0\0\0\373\0\0\0\305\0\0\0/\0\0\0\3\0\0\0\1qqq\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0\0\0\0\0\0\0\0\1" + "\0\0\0\33\0\0\0\224\0\0\0\357\225\232\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377\26\26\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377.0\0\377" + "\330\340\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\345\355\0\377?A" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\1\1\0\377\310\320\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\225\232\0\377\0\0\0\360\0\0\0" + "\225\0\0\0\34\0\0\0\1\0\0\0\1\271\271\271\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\21\21\21\0\0\0\0\0\0\0\0\15\0\0\0" + "]\0\0\0\337FH\0\376\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\257\266\0\377\4\4\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\16\17\0\377\265\273" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\277\306\0\377\23\24\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\2\2\0\377\226\233\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377FH\0\377\0\0\0\337\0\0\0^\0\0\0\16\0\0\0\1\21\21" + "\21\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0aaa\0\0\0\0\0\0\0\0\3\0\0\0-\0\0\0\275\0\0\0\370\325\335" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\300\307\0\377\30\31\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377z\177\0\377\355\366" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377pt\0\377\2\2\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\2\2" + "\0\377\235\243\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\325\335\0\377\0\0\0\371" + "\0\0\0\276\0\0\0-\0\0\0\3\0\0\0\1aaa\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0\0\0\0\0\0" + "\0\0\1\0\0\0\27\0\0\0\204\0\0\0\353~\203\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\330\341\0\377&'\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\25\25\0\377\277\306\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\310\317\0\377" + "-/\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\25\25\0\377\267\275\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377~\203\0\377\0\0\0\354\0\0\0\205\0\0\0\30\0\0\0\1\0\0\0\1\265\265" + "\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\35\35\35\0\0\0\0\0\0\0\0\11\0\0\0I\0\0\0\324" + "$%\0\375\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\350\360\0\377EG\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\2\2\0\377mq\0\377\352\363\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\342\352\0" + "\377fi\0\377\2\2\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377*,\0\377\333\344\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377$%\0\375\0\0\0\325\0\0\0J\0\0\0\11\0\0" + "\0\1\35\35\35\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0\0\2" + "\0\0\0!\0\0\0\236\0\0\0\361\244\252\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\357\370\0\377tx\0\377\1\1\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\35\36\0\377\235\243\0" + "\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\356\367\0\377\220" + "\225\0\377\15\16\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377gj\0\377\353\364\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377\0\0\0\362\0" + "\0\0\237\0\0\0\"\0\0\0\2\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\331\331\331\0\0\0\0\0\0\0\0\1\0\0\0\16\0\0\0\\\0\0\0\333BD\0\375\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\265\273\0" + "\377\13\14\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377-/\0\377\273\302\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\355\366\0\377\177\204" + "\0\377\15\16\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\11\12\0\377\246\254\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "BD\0\376\0\0\0\333\0\0\0]\0\0\0\17\0\0\0\1\0\0\0\1\331\331\331\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0'" + "\0\0\0\251\0\0\0\363\267\275\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\346\356\0\377HK\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377=?\0\377\247\255\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\344\354\0\377\206\213\0\377" + "\34\35\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377/1\0\377\325\335\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\267\275\0\377\0\0\0\364\0\0\0\251\0\0\0(\0\0\0\3\0\0\0\1QQQ\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271\271\0\0" + "\0\0\0\0\0\0\1\0\0\0\20\0\0\0d\0\0\0\335FH\0\375\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\211\217\0\377\10\11\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\14\15\0\377qu\0\377" + "\343\353\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\356\367\0\377\254\263\0\377hl\0\377\5\5\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\16\17\0\377\231\237\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377FH\0\376\0\0\0\335\0\0\0d\0\0\0\20\0\0\0\1\0\0\0\1\271\271\271" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0)\0\0\0\251\0\0\0\363\244\252\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\346\356\0\377fi\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\4\4\0\377BD\0\377\206\213\0\377\301\310\0\377\354\365\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340\350\0\377\230\235\0" + "\377^b\0\377\25\25\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377Z^" + "\0\377\335\345\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\244\252\0\377" + "\0\0\0\364\0\0\0\251\0\0\0*\0\0\0\3\0\0\0\1===\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\255" + "\255\255\0\0\0\0\0\0\0\0\1\0\0\0\17\0\0\0`\0\0\0\332+-\0\375\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\254\263\0\377\25\25\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\12\13\0\377;>\0\377UX\0" + "\377lp\0\377x|\0\377x|\0\377x|\0\377bf\0\377HK\0\377\6\6\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\37713\0\377\275\304" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "+-\0\375\0\0\0\332\0\0\0a\0\0\0\17\0\0\0\1\0\0\0\1\255\255\255\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0===\0\0\0\0\0\0\0\0\3\0\0\0%\0\0\0\234\0\0\0" + "\360~\203\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357\370" + "\0\377\216\223\0\377\16\17\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + ",.\0\377\247\255\0\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377~\203\0\377\0\0\0\361\0\0\0\235\0\0\0%\0" + "\0\0\3\0\0\0\1===\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265" + "\265\265\0\0\0\0\0\0\0\0\1\0\0\0\13\0\0\0P\0\0\0\322\0\0\0\374\311\321\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\355" + "\366\0\377\213\221\0\377\17\20\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\37724\0\377\246\254\0\377\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\311\321\0\377\0\0\0\374\0\0\0\323\0\0\0Q\0\0\0\14\0\0\0\1\0\0\0\1" + "\265\265\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0QQQ\0\0\0\0\0\0\0\0\2\0\0\0\34\0\0\0\204\0\0\0\347FH\0\376\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\356\367\0\377\235\243\0\377(*\0\377\0\0\0\377\0\0\0\377\0\0\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\5\5\0" + "\377hl\0\377\270\276\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377FH\0\377\0\0\0\347\0\0\0\204\0\0\0\35\0\0\0\2\0\0\0\1QQQ\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\321\321" + "\321\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0""1\0\0\0\263\0\0\0\365\206\213\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\320\330\0\377\\`\0\377\7" + "\7\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" + "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" + "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\1\1\0\3778:\0\377\226\233\0" + "\377\344\354\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\206\213\0\377\0\0\0\366\0\0\0\263\0\0\0""1\0\0\0\5\0\0\0\1\0\0\0\1" + "\321\321\321\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0\0\1\0\0\0\17\0\0\0a\0\0\0" + "\327\0\0\0\374\276\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\357\370\0\377\245\253\0\377w{\0\377:=\0\377\3\3\0\377" + "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" + "\0\0\377\0\0\0\377\0\0\0\377\1\1\0\377*,\0\377dh\0\377\217\224\0\377\350" + "\361\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\276\305\0\377\0\0\0\375\0\0\0\330\0\0\0b\0\0" + "\0\20\0\0\0\1\0\0\0\1yyy\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + ")))\0\0\0\0\0\0\0\0\3\0\0\0\40\0\0\0\207\0\0\0\346/1\0\376\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\357\370\0\377\305\314\0\377\247\255\0\377" + "\212\220\0\377x}\0\377x}\0\377x}\0\377x}\0\377{\200\0\377\240\246\0\377\270" + "\276\0\377\357\370\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\357\370\0\377/1\0\376\0\0\0\347\0\0\0\210\0\0\0!\0\0\0\3\0\0\0" + "\1)))\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265" + "\265\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0""0\0\0\0\253\0\0\0\364\\`\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\\`\0\377\0\0\0\364\0\0\0\254\0\0\0""1" + "\0\0\0\6\0\0\0\1\0\0\0\1\265\265\265\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0\0\0\1\0\0\0\14\0\0" + "\0T\0\0\0\317\0\0\0\373\202\207\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\202\207\0\377\0\0\0\373" + "\0\0\0\317\0\0\0T\0\0\0\15\0\0\0\1\0\0\0\1qqq\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0""555\0\0\0\0\0\0\0\0\2\0\0\0\30\0\0\0q\0\0\0\332\0\0\0\374\240\246\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\240\246\0\377\0" + "\0\0\374\0\0\0\333\0\0\0r\0\0\0\31\0\0\0\2\0\0\0\1""555\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\315\315\315\0\1\1\1\0\0\0\0\1\0\0\0\4\0\0\0\"\0" + "\0\0\205\0\0\0\342\2\2\0\375\267\275\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\267" + "\275\0\377\2\2\0\375\0\0\0\342\0\0\0\206\0\0\0#\0\0\0\4\0\0\0\1\1\1\1\1\315" + "\315\315\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\235\235" + "\235\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0+\0\0\0\226\0\0\0\350\25\25\0\376\306" + "\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\306\315\0\377\25\25\0\376\0\0\0\351\0\0\0\226\0\0\0+\0\0\0\6\0" + "\0\0\1\0\0\0\1\235\235\235\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0" + """2\0\0\0\244\0\0\0\355\40!\0\376\315\325\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377\40!\0\376\0\0\0\356\0\0\0\245\0\0\0""3\0" + "\0\0\7\0\0\0\1\0\0\0\1uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0UUU\0\0" + "\0\0\0\0\0\0\1\0\0\0\11\0\0\0@\0\0\0\260\0\0\0\360$%\0\376\315\325\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\315\325\0\377$%\0\377\0\0\0\361\0\0\0\260\0\0\0A\0\0\0" + "\12\0\0\0\1\0\0\0\1UUU\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\375\375\375\0===\0\0\0\0\1\0\0\0\2\0\0\0\16\0\0\0H\0\0\0\264\0\0\0\360" + "\40!\0\376\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\306\315\0\377\40!\0\376\0\0\0\361\0\0\0\265\0\0\0I\0\0\0\16" + "\0\0\0\2\0\0\0\1===\1\375\375\375\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\351\351\351\0---\0\0\0\0\1\0\0\0\2\0\0\0\17" + "\0\0\0I\0\0\0\260\0\0\0\355\25\25\0\376\267\275\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\267\275\0\377\25\25\0\376\0\0\0\356\0\0\0\260\0\0\0I\0\0\0" + "\20\0\0\0\2\0\0\0\1---\1\351\351\351\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0" + "%%%\0\0\0\0\1\0\0\0\2\0\0\0\16\0\0\0@\0\0\0\244\0\0\0\350\2\2\0\375\240\246" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\240\246\0\377\2\2\0\375\0\0\0\351\0\0\0\245\0\0\0@\0\0\0\16" + "\0\0\0\2\0\0\0\1%%%\1\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\331\331\331\0%%%\0\0\0\0\1\0\0\0\2\0\0\0\12\0\0\0""2\0\0\0\226" + "\0\0\0\342\0\0\0\374\202\207\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\202\207\0\377\0\0\0\374\0\0\0\343\0\0\0\226\0\0\0""3\0\0\0" + "\12\0\0\0\2\0\0\0\1%%%\1\331\331\331\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0---\0\0\0\0\1\0\0" + "\0\1\0\0\0\6\0\0\0*\0\0\0\205\0\0\0\332\0\0\0\373\\`\0\377\357\370\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\357" + "\370\0\377\\`\0\377\0\0\0\373\0\0\0\333\0\0\0\206\0\0\0+\0\0\0\6\0\0\0\1" + "\0\0\0\1---\1\335\335\335\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\351\351\351\0" + "===\0\0\0\0\1\0\0\0\1\0\0\0\5\0\0\0#\0\0\0r\0\0\0\317\0\0\0\364/1\0\376\276" + "\305\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\276\305\0\377/1\0\376" + "\0\0\0\365\0\0\0\317\0\0\0r\0\0\0#\0\0\0\6\0\0\0\1\0\0\0\1===\1\351\351\351" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\375\375\375" + "\0UUU\0\0\0\0\0\0\0\0\1\0\0\0\4\0\0\0\31\0\0\0S\0\0\0\253\0\0\0\346\0\0\0" + "\374\206\213\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\206\213\0\377\0\0\0\375\0\0\0\347\0\0\0" + "\254\0\0\0T\0\0\0\31\0\0\0\4\0\0\0\1\0\0\0\1UUU\0\375\375\375\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0uuu\0\0\0\0\0\0\0\0\1\0\0\0\3\0\0\0\14\0\0\0/\0\0\0\210\0" + "\0\0\327\0\0\0\366FH\0\376\311\321\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\311\321\0\377FH\0\377\0\0\0\367\0\0\0\330\0\0\0\210\0\0\0""0\0\0\0\15\0" + "\0\0\3\0\0\0\1\0\0\0\1uuu\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\235\235\235\0\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\5\0" + "\0\0!\0\0\0a\0\0\0\263\0\0\0\347\0\0\0\374~\203\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377~\203\0\377\0\0\0" + "\374\0\0\0\347\0\0\0\264\0\0\0b\0\0\0!\0\0\0\6\0\0\0\1\0\0\0\1\1\1\1\1\235" + "\235\235\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\315\315\315\0""555\0\0\0\0\0\0\0\0\1\0" + "\0\0\3\0\0\0\17\0\0\0""1\0\0\0\204\0\0\0\322\0\0\0\361+-\0\375\244\252\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\244\252\0\377+-\0\375\0\0\0\361\0\0\0\323\0\0\0" + "\204\0\0\0""1\0\0\0\20\0\0\0\3\0\0\0\1\0\0\0\1""555\0\315\315\315\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0qqq\0\0\0\0\0\0" + "\0\0\1\0\0\0\1\0\0\0\5\0\0\0\34\0\0\0P\0\0\0\234\0\0\0\332\0\0\0\364FH\0" + "\376\267\275\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\267\275\0\377FH" + "\0\376\0\0\0\364\0\0\0\333\0\0\0\235\0\0\0Q\0\0\0\35\0\0\0\5\0\0\0\1\0\0" + "\0\1\0\0\0\1qqq\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\265\265\265\0)))\0\0" + "\0\0\0\0\0\0\1\0\0\0\3\0\0\0\14\0\0\0%\0\0\0`\0\0\0\251\0\0\0\335\0\0\0\364" + "BD\0\376\244\252\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\244\252\0\377BD\0\376\0\0\0\364\0\0\0\336\0\0\0\251\0\0\0a\0\0\0&" + "\0\0\0\14\0\0\0\3\0\0\0\1\0\0\0\1)))\0\265\265\265\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0yyy\0\0\0\0\0\0\0" + "\0\1\0\0\0\1\0\0\0\3\0\0\0\17\0\0\0)\0\0\0d\0\0\0\251\0\0\0\333\0\0\0\362" + "$%\0\375~\203\0\377\325\335\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\325\335\0\377~\203\0\377$%\0\375\0\0\0\362\0\0\0\334\0\0\0\252\0\0\0" + "d\0\0\0)\0\0\0\17\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1yyy\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\321\321\321\0QQQ\0\0\0\0\0\0\0\0\1\0\0\0\1\0" + "\0\0\4\0\0\0\20\0\0\0(\0\0\0\\\0\0\0\236\0\0\0\325\0\0\0\354\0\0\0\372FH" + "\0\377\225\232\0\377\340\350\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\340" + "\350\0\377\225\232\0\377FH\0\377\0\0\0\372\0\0\0\354\0\0\0\325\0\0\0\236" + "\0\0\0]\0\0\0(\0\0\0\20\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1QQQ\0\321\321\321" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\265\265\265\0===\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\3\0\0\0" + "\16\0\0\0\"\0\0\0I\0\0\0\205\0\0\0\275\0\0\0\337\0\0\0\360\0\0\0\374BD\0" + "\377\206\213\0\377\306\315\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\306\315\0\377\206" + "\213\0\377BD\0\377\0\0\0\374\0\0\0\360\0\0\0\340\0\0\0\276\0\0\0\205\0\0" + "\0J\0\0\0\"\0\0\0\16\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\1===\0\265\265\265\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\255\255" + "\255\0===\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\11\0\0\0\27\0\0\0-\0\0" + "\0]\0\0\0\225\0\0\0\305\0\0\0\337\0\0\0\355\0\0\0\370\30\31\0\376QT\0\377" + "\206\213\0\377\267\275\0\377\344\354\0\377\360\371\0\377\360\371\0\377\360" + "\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371" + "\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0" + "\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377\360\371\0\377" + "\344\354\0\377\267\275\0\377\206\213\0\377QT\0\377\30\31\0\376\0\0\0\370" + "\0\0\0\355\0\0\0\340\0\0\0\306\0\0\0\225\0\0\0^\0\0\0-\0\0\0\30\0\0\0\11" + "\0\0\0\3\0\0\0\1\0\0\0\1\0\0\0\1===\0\255\255\255\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\271\271\271\0QQQ\0\0\0\0\0\0\0\0\1\0\0\0\1" + "\0\0\0\1\0\0\0\3\0\0\0\16\0\0\0\33\0\0\0/\0\0\0Z\0\0\0\212\0\0\0\266\0\0" + "\0\326\0\0\0\344\0\0\0\355\0\0\0\365\0\0\0\374\40!\0\377FH\0\377hk\0\377" + "\206\213\0\377\240\246\0\377\267\275\0\377\311\321\0\377\330\341\0\377\344" + "\354\0\377\344\354\0\377\330\341\0\377\311\321\0\377\267\275\0\377\240\246" + "\0\377\206\213\0\377hk\0\377FH\0\377\40!\0\377\0\0\0\375\0\0\0\366\0\0\0" + "\355\0\0\0\344\0\0\0\326\0\0\0\266\0\0\0\212\0\0\0Z\0\0\0/\0\0\0\33\0\0\0" + "\16\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1QQQ\0\271\271\271\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\331\331\331\0yyy\0\35\35\35\0\0\0\0\0\0\0\0\1\0" + "\0\0\1\0\0\0\1\0\0\0\3\0\0\0\15\0\0\0\31\0\0\0'\0\0\0@\0\0\0e\0\0\0\212\0" + "\0\0\255\0\0\0\312\0\0\0\332\0\0\0\342\0\0\0\350\0\0\0\355\0\0\0\361\0\0" + "\0\365\0\0\0\371\0\0\0\373\0\0\0\375\0\0\0\375\0\0\0\374\0\0\0\371\0\0\0" + "\366\0\0\0\362\0\0\0\355\0\0\0\350\0\0\0\342\0\0\0\332\0\0\0\313\0\0\0\255" + "\0\0\0\212\0\0\0e\0\0\0@\0\0\0'\0\0\0\31\0\0\0\16\0\0\0\4\0\0\0\2\0\0\0\1" + "\0\0\0\1\0\0\0\1\35\35\35\0yyy\0\331\331\331\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\265\265\265\0aaa\0\21\21\21\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0" + "\0\2\0\0\0\6\0\0\0\17\0\0\0\31\0\0\0!\0\0\0,\0\0\0C\0\0\0]\0\0\0u\0\0\0\212" + "\0\0\0\235\0\0\0\255\0\0\0\272\0\0\0\305\0\0\0\314\0\0\0\314\0\0\0\305\0" + "\0\0\272\0\0\0\255\0\0\0\235\0\0\0\212\0\0\0u\0\0\0]\0\0\0C\0\0\0,\0\0\0" + "\"\0\0\0\31\0\0\0\20\0\0\0\7\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\21\21" + "\21\0aaa\0\265\265\265\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\271\271" + "\271\0qqq\0---\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\2" + "\0\0\0\7\0\0\0\15\0\0\0\23\0\0\0\31\0\0\0\35\0\0\0!\0\0\0$\0\0\0'\0\0\0)" + "\0\0\0)\0\0\0'\0\0\0%\0\0\0!\0\0\0\36\0\0\0\31\0\0\0\24\0\0\0\16\0\0\0\10" + "\0\0\0\3\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1---\0qqq\0\271\271" + "\271\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\345\345\345\0" + "\251\251\251\0qqq\0===\0\15\15\15\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" + "\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" + "\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\15\15" + "\15\0===\0qqq\0\251\251\251\0\345\345\345\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\335\335\335\0" + "\265\265\265\0\221\221\221\0qqq\0UUU\0===\0)))\0\31\31\31\0\15\15\15\0\5" + "\5\5\0\1\1\1\0\1\1\1\0\5\5\5\0\15\15\15\0\31\31\31\0)))\0===\0UUU\0qqq\0" + "\221\221\221\0\265\265\265\0\335\335\335\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377" + "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0" + "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377" + "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377" + "\377\0\377\377\377\0\377\377\377\0\377\377\377\0", +}; + diff --git a/samples/graphics/rsx_Basic_Wallpaper/include/geometry.h b/samples/graphics/rsx_Basic_Wallpaper/include/geometry.h new file mode 100644 index 00000000..8be11b93 --- /dev/null +++ b/samples/graphics/rsx_Basic_Wallpaper/include/geometry.h @@ -0,0 +1,12 @@ +#ifndef __GEOMETRY_H__ +#define __GEOMETRY_H__ + +#include "mesh.h" + +SMeshBuffer* createCube(f32 size); +SMeshBuffer* createDonut(f32 outerRadius,f32 innerRadius,u32 polyCntX,u32 polyCntY); +SMeshBuffer* createSphere(f32 radius,u32 polyCntX,u32 polyCntY); +SMeshBuffer* createQuad(f32 size, float z); +SMeshBuffer* createQuad(Point3 P1, Point3 P2, Point3 P3, Point3 P4); + +#endif diff --git a/samples/graphics/rsx_Basic_Wallpaper/include/irrallocator.h b/samples/graphics/rsx_Basic_Wallpaper/include/irrallocator.h new file mode 100644 index 00000000..47feeab2 --- /dev/null +++ b/samples/graphics/rsx_Basic_Wallpaper/include/irrallocator.h @@ -0,0 +1,73 @@ +#ifndef __IRRALLOCATOR_H__ +#define __IRRALLOCATOR_H__ + +#include + +namespace irr +{ + namespace core + { + template< typename T > + class allocator + { + public: + T* allocate(u32 cnt) + { + return (T*)operator new(cnt*sizeof(T)); + } + + void deallocate(T *ptr) + { + operator delete(ptr); + } + + void construct(T *ptr,const T& elem) + { + new ((void*)ptr) T(elem); + } + + void destruct(T *ptr) + { + ptr->~T(); + } + }; + + template< typename T > + class allocatorRSX + { + public: + T* allocate(u32 cnt) + { + return (T*)operator new(cnt*sizeof(T)); + } + + void deallocate(T *ptr) + { + operator delete(ptr); + } + + void construct(T *ptr,const T& elem) + { + new ((void*)ptr) T(elem); + } + + void destruct(T *ptr) + { + ptr->~T(); + } + + protected: + void* operator new(size_t size) + { + return rsxMemalign(64,size); + } + + void operator delete(void *ptr) + { + rsxFree(ptr); + } + }; + } +} + +#endif diff --git a/samples/graphics/rsx_Basic_Wallpaper/include/irrarray.h b/samples/graphics/rsx_Basic_Wallpaper/include/irrarray.h new file mode 100644 index 00000000..e9c98f0d --- /dev/null +++ b/samples/graphics/rsx_Basic_Wallpaper/include/irrarray.h @@ -0,0 +1,162 @@ +#ifndef __IRRARRAY_H__ +#define __IRRARRAY_H__ + +#include "irrallocator.h" + +namespace irr +{ + namespace core + { + template< class T, typename TAlloc = allocator > + class array + { + public: + array() : data(NULL),used(0),allocated(0),free_it(true),is_sorted(true) {}; + array(u32 start_cnt) : data(NULL),used(0),allocated(0),free_it(true),is_sorted(true) + { + reallocate(start_cnt); + } + array(const array& other) : data(NULL) + { + *this = other; + } + + ~array() + { + u32 i; + + if(free_it==true) { + for(i=0;iallocated) { + T e(elem); + u32 newAlloc; + + newAlloc = used + 1 + (allocated<500 ? (allocated<5 ? 5 : used) : used>>2); + + reallocate(newAlloc); + allocator.construct(&data[used++],e); + } else + allocator.construct(&data[used++],elem); + + is_sorted = false; + } + + void push_front(const T& elem) + { + insert(elem); + } + + void insert(const T& elem,u32 index = 0) + { + u32 i; + + if(index>used) return; + if((used + 1)>allocated) reallocate(used + 1); + + for(i=used;i>index;i--) { + if(iindex) allocator.destruct(&data[index]); + + allocator.construct(&data[index],elem); + is_sorted = false; + used++; + } + + void erase(u32 index) + { + u32 i; + + for(i=index+1;iused) reallocate(new_cnt); + used = new_cnt; + } + + void clear() + { + u32 i; + + for(i=0;i +#include + +#include + +#include "irrarray.h" + +using namespace irr; +using namespace Vectormath::Aos; + +template< class T > +inline const T& min_(const T& a,const T& b) +{ + return a +inline const T& max_(const T& a,const T& b) +{ + return a +inline const T clamp(const T& val,const T& low,const T& high) +{ + return min_(max_(val,low),high); +} + +class SColor +{ +public: + SColor() : color(0) {}; + SColor(u32 c) : color(c) {}; + SColor(u8 a,u8 r,u8 g,u8 b) : R(r),G(g),B(b),A(a) {}; + SColor(const SColor& other) : color(other.color) {}; + + u8 getRed() const { return R; } + u8 getGreen() const { return G; } + u8 getBlue() const { return B; } + u8 getAlpha() const { return A; } + + union { + u32 color; + struct { + u8 R,G,B,A; + }; + }; +}; + +struct S3DVertex +{ + S3DVertex() {}; + S3DVertex(f32 x,f32 y,f32 z,f32 nx,f32 ny,f32 nz,SColor c,f32 tu,f32 tv) + : pos(x,y,z),nrm(nx,ny,nz),col(c),u(tu),v(tv) {}; + S3DVertex(const Vector3& _pos,const Vector3& _nrm,const SColor& c,f32 tu,f32 tv) + : pos(_pos),nrm(_nrm),col(c),u(tu),v(tv) {}; + + inline S3DVertex& operator=(const S3DVertex& other) + { + pos = other.pos; + nrm = other.nrm; + col = other.col; + u = other.u; + v = other.v; + return *this; + } + + Vector3 pos; + Vector3 nrm; + SColor col; + + f32 u,v; +}; + + +class SMeshBuffer +{ +public: + SMeshBuffer() : pos_off(0),nrm_off(0),col_off(0),uv_off(0),ind_off(0) {}; + virtual ~SMeshBuffer() + { + } + + virtual const void* getVertices() const + { + return vertices.const_pointer(); + } + + virtual void* getVertices() + { + return vertices.pointer(); + } + + virtual u32 getVertexCount() const + { + return vertices.size(); + } + + virtual const u32* getIndices() const + { + return indices.const_pointer(); + } + + virtual u32* getIndices() + { + return indices.pointer(); + } + + virtual u32 getIndexCount() const + { + return indices.size(); + } + + core::array< u32, core::allocatorRSX< u32 > > indices; + core::array< S3DVertex, core::allocatorRSX< S3DVertex > > vertices; + + u32 pos_off; + u32 nrm_off; + u32 col_off; + u32 uv_off; + + u32 ind_off; +}; + +#endif diff --git a/samples/graphics/rsx_Basic_Wallpaper/include/rsxutil.h b/samples/graphics/rsx_Basic_Wallpaper/include/rsxutil.h new file mode 100644 index 00000000..7b77b7e3 --- /dev/null +++ b/samples/graphics/rsx_Basic_Wallpaper/include/rsxutil.h @@ -0,0 +1,40 @@ +#ifndef __RSXUTIL_H__ +#define __RSXUTIL_H__ + +#include +#include + +#include + + +#define DEFUALT_CB_SIZE 0x80000 // 512Kb default command buffer size +#define HOST_STATE_CB_SIZE 0x10000 // 64Kb state command buffer size (used for resetting certain default states) +#define HOST_ADDR_ALIGNMENT (1024*1024) +#define HOSTBUFFER_SIZE (128*1024*1024) +#define CB_SIZE 0x100000 + +#define FRAME_BUFFER_COUNT 2 + +extern gcmContextData *context; + +extern u32 curr_fb; + +extern u32 display_width; +extern u32 display_height; + +extern u32 depth_pitch; +extern u32 depth_offset; +extern u32 *depth_buffer; + +extern u32 color_pitch; +extern u32 color_offset[FRAME_BUFFER_COUNT]; +extern u32 *color_buffer[FRAME_BUFFER_COUNT]; + +extern f32 aspect_ratio; + +void setRenderTarget(u32 index); +void init_screen(void *host_addr,u32 size); +void waitflip(); +void flip(); + +#endif diff --git a/samples/graphics/rsx_Basic_Wallpaper/rsx_basic_wallpaper.png b/samples/graphics/rsx_Basic_Wallpaper/rsx_basic_wallpaper.png new file mode 100644 index 0000000000000000000000000000000000000000..0488089dc55c360cc5339979355d0faf062f33ad GIT binary patch literal 1492477 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

  • 4*C9) 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 z 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

    +FkzEhT8L!$$GU18w0 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�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

    mCoujz_Zc;nVEnbCgLUHN#W0( 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

    H&VLxJeMDZw%`4IC(2-QKJ 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 =S%FP{k$8oMUSCx}~t>T(GFO7%>7y(twpgq-8e zpADjEE&E5uwXw#>(~?_rhDCS@3wv$-rY=)Kv$fUzL~H%PhZzyqiJKxq_?}NbzuxKx zG}7&9>ds9ch+1;_m@ERxm`}E0mh`>ZmL8JZe{#F~-hYls{bUlwd`K1T_$6)sq|VQ~ zZl(%RolrRtVOe7hpwT0jvdWX*cW)=EEE^Jk-Tjcw#ZvXwlW7cce`d6Vy>zW}dPEQ~7s#JBIC$tM*-oV;d~oqW$}-=e9Cu&w^>R1u zproxeSDkpupyQcx;>1)ymnQ9aet~>hTUy-sczC{KcMY-l5Jj|9CW#?fQ^9>b`SG`n z%uuqBQmB!`H~wy_T4zvTI*s~(eVm3udpG<3%xuVp`mFO%bBbeNcl+%?AP<92dn7=B zndlrb|E8WI1;495S~_uCkp3bZOV<9?f9-|6VNQQ0R)4W*_&|G=BJh3^dY$9c;A(!W zqj=@c1BIje0RUh$<7~hSyYuiNy{0#yc**G(z?TVe*s`Fc;Sir9XST3hs)i-Xx6Z{X zx$oBq!z@W>z9v`-XD_yQi`#s(2zxNwug+MZz`10eNIwd+M8vgk`atS6IVS%Q-!WB6 zHkxjX6LkLEY6Omet8Zk!ByhS?T_^aE;R+q|Xq(n1z1vH7vzk4et%^rOZDaz~*yr^Z z==6+^yCfOInLeGG23~(`?E5*CvoTt=UZrJfP5QIZUo7qoVV(62WP^$&XAowfcRv1; z$KohUEm?5O3F9!hx9c&z;BuhQ@P(=kK(?)VR0JKESex4=^QjP7hg-N*ly?2@ldvJP zplW>>2&yPNn#J_QMna$FG`_NJkvQZ$v|u@|Wpq^ErujT|?D@1bwf3<)TT}OlyzT4q zj&h0Q4XuxxW!HztztwFty$5jznS1Jl$gPX?wy95K7PkY%4l(K--i_;a^jogic*l*<(l-UB+HKB)u8VT-+JkeIQ1&r^N?lWl0enfu-56PV?*-HvbQp=L4hwlQZ6pO+@ z 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 oz@1`S@NRf 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 K>3+ShP1B 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%0Dy)+IULt@=qH&*qC_^wEi>R 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 literal 0 HcmV?d00001 diff --git a/samples/graphics/rsx_Basic_Wallpaper/shaders/diffuse_specular_shader.vcg b/samples/graphics/rsx_Basic_Wallpaper/shaders/diffuse_specular_shader.vcg new file mode 100644 index 00000000..0e2e3570 --- /dev/null +++ b/samples/graphics/rsx_Basic_Wallpaper/shaders/diffuse_specular_shader.vcg @@ -0,0 +1,16 @@ +void main(float4 position : POSITION, + float4 color : COLOR0, + float2 texcoord : TEXCOORD0, + + uniform float4x4 modelViewProj, + + out float4 oPosition : POSITION, + out float4 oColor : COLOR0, + out float2 oTexCoord : TEXCOORD0 + ) + { + oPosition = mul(modelViewProj, position); + oColor = color; + oTexCoord = texcoord; + } + \ No newline at end of file diff --git a/samples/graphics/rsx_Basic_Wallpaper/shaders/scanlines.fcg b/samples/graphics/rsx_Basic_Wallpaper/shaders/scanlines.fcg new file mode 100644 index 00000000..35bcba34 --- /dev/null +++ b/samples/graphics/rsx_Basic_Wallpaper/shaders/scanlines.fcg @@ -0,0 +1,44 @@ +void main +( + float4 position : TEXCOORD0, + float3 normal : TEXCOORD1, + float2 texcoord : TEXCOORD0, + + uniform float4 scanline, + uniform sampler2D texture, + + out float4 oColor +) +{ + //Scanline parameters + //------------------------------------------------- + //scanline.x => scanline density, values 100 - 200 + //scanline.y => scanline depth / contrast, values 2 - 10 + //scanline.z => scanline brightnes, value 0 - 1 + //scanline.w => scanline type horizontal (value 1.0f), vertical (value 2.0f), none (val 0.0f) + + float dark = scanline.z; + + //pos Absolute from [0,1] to [-1,1] + float posxAbs = position.x * 2.0 - 1.0; + float posyAbs = position.y * 2.0 - 1.0; + + //vertical scanline + if (scanline.w > 0.19) { + dark += (sin(3.1415926f * posxAbs * scanline.x) / scanline.y); + } + else + //horizontal scanline + if (scanline.w > 0.09) { + dark += (sin(3.1415926f * posyAbs * scanline.x) / scanline.y); + + } + //no scanline + else + dark = 1.0f; + + float4 texel = tex2D(texture,texcoord); + float3 color = texel.xyz * dark; + + oColor = float4(color, texel.w); +} diff --git a/samples/graphics/rsx_Basic_Wallpaper/source/geometry.cpp b/samples/graphics/rsx_Basic_Wallpaper/source/geometry.cpp new file mode 100644 index 00000000..081f6ff8 --- /dev/null +++ b/samples/graphics/rsx_Basic_Wallpaper/source/geometry.cpp @@ -0,0 +1,302 @@ +#include + +#include "geometry.h" + + +SMeshBuffer* createQuad(Point3 P1, Point3 P2, Point3 P3, Point3 P4) +{ + u32 i; + SColor col(255, 255, 255, 255); + SMeshBuffer* buffer = new SMeshBuffer(); + const u16 u[6] = { 0,1,2, 0,3,1 }; + + buffer->indices.set_used(6); + + for (i = 0; i < 6; i++) buffer->indices[i] = u[i]; + + buffer->vertices.set_used(4); + + // position, normal, texture + buffer->vertices[0] = S3DVertex(P1.getX(), P1.getY(), P1.getZ(), -1, -1, -1, col, 0, 1); + buffer->vertices[1] = S3DVertex(P2.getX(), P2.getY(), P2.getZ(), 1, -1, -1, col, 1, 0); + buffer->vertices[2] = S3DVertex(P3.getX(), P3.getY(), P3.getZ(), 1, 1, -1, col, 0, 0); + buffer->vertices[3] = S3DVertex(P4.getX(), P4.getY(), P4.getZ(), -1, 1, -1, col, 1, 1); + + + rsxAddressToOffset(&buffer->vertices[0].pos, &buffer->pos_off); + rsxAddressToOffset(&buffer->vertices[0].nrm, &buffer->nrm_off); + rsxAddressToOffset(&buffer->vertices[0].col, &buffer->col_off); + rsxAddressToOffset(&buffer->vertices[0].u, &buffer->uv_off); + rsxAddressToOffset(&buffer->indices[0], &buffer->ind_off); + + return buffer; +} + +SMeshBuffer* createQuad(f32 size, float z) +{ + u32 i; + SColor col(255, 255, 255, 255); + SMeshBuffer* buffer = new SMeshBuffer(); + const u16 u[6] = { 0,1,2, 0,3,1 }; + + buffer->indices.set_used(6); + + for (i = 0; i < 6; i++) buffer->indices[i] = u[i]; + + buffer->vertices.set_used(4); + + // position, normal, texture + buffer->vertices[0] = S3DVertex(-1.0, -1.0, z, -1, -1, -1, col, 0, 1); + buffer->vertices[1] = S3DVertex( 1.0, 1.0, z, 1, -1, -1, col, 1, 0); + buffer->vertices[2] = S3DVertex(-1.0, 1.0, z, 1, 1, -1, col, 0, 0); + buffer->vertices[3] = S3DVertex( 1.0, -1.0, z, -1, 1, -1, col, 1, 1); + + //centre and resize + //for (i = 0; i < 4; i++) { + // //center + // buffer->vertices[i].pos += Vector3(-0.5f, 0.5f, 0.0f); + // //resize + // buffer->vertices[i].pos *= size; + //} + + rsxAddressToOffset(&buffer->vertices[0].pos, &buffer->pos_off); + rsxAddressToOffset(&buffer->vertices[0].nrm, &buffer->nrm_off); + rsxAddressToOffset(&buffer->vertices[0].col, &buffer->col_off); + rsxAddressToOffset(&buffer->vertices[0].u, &buffer->uv_off); + rsxAddressToOffset(&buffer->indices[0], &buffer->ind_off); + + return buffer; +} + +SMeshBuffer* createCube(f32 size) +{ + u32 i; + SColor col(255,255,255,255); + SMeshBuffer *buffer = new SMeshBuffer(); + const u16 u[36] = { 0,1,2, 0,2,3, 1,4,5, 1,5,2, 4,7,6, 4,6,5, + 7,0,3, 7,3,6, 9,2,5, 9,5,8, 0,10,11, 0,7,10}; + + buffer->indices.set_used(36); + for(i=0;i<36;i++) buffer->indices[i] = u[i]; + + buffer->vertices.set_used(12); + + buffer->vertices[0] = S3DVertex(0,0,0, -1,-1,-1, col, 1, 0); + buffer->vertices[1] = S3DVertex(1,0,0, 1,-1,-1, col, 1, 1); + buffer->vertices[2] = S3DVertex(1,1,0, 1, 1,-1, col, 0, 1); + buffer->vertices[3] = S3DVertex(0,1,0, -1, 1,-1, col, 0, 0); + buffer->vertices[4] = S3DVertex(1,0,1, 1,-1, 1, col, 1, 0); + buffer->vertices[5] = S3DVertex(1,1,1, 1, 1, 1, col, 0, 0); + buffer->vertices[6] = S3DVertex(0,1,1, -1, 1, 1, col, 0, 1); + buffer->vertices[7] = S3DVertex(0,0,1, -1,-1, 1, col, 1, 1); + buffer->vertices[8] = S3DVertex(0,1,1, -1, 1, 1, col, 1, 0); + buffer->vertices[9] = S3DVertex(0,1,0, -1, 1,-1, col, 1, 1); + buffer->vertices[10] = S3DVertex(1,0,1, 1,-1, 1, col, 0, 1); + buffer->vertices[11] = S3DVertex(1,0,0, 1,-1,-1, col, 0, 0); + + for(i=0;i<12;i++) { + buffer->vertices[i].pos -= Vector3(0.5f,0.5f,0.5f); + buffer->vertices[i].pos *= size; + } + + rsxAddressToOffset(&buffer->vertices[0].pos,&buffer->pos_off); + rsxAddressToOffset(&buffer->vertices[0].nrm,&buffer->nrm_off); + rsxAddressToOffset(&buffer->vertices[0].col,&buffer->col_off); + rsxAddressToOffset(&buffer->vertices[0].u,&buffer->uv_off); + rsxAddressToOffset(&buffer->indices[0],&buffer->ind_off); + + return buffer; +} + +SMeshBuffer* createDonut(f32 outerRadius,f32 innerRadius,u32 polyCntX,u32 polyCntY) +{ + u32 i,x,y,level; + SColor col(100,255,255,255); + SMeshBuffer *buffer = new SMeshBuffer(); + + if(polyCntX<2) polyCntX = 2; + if(polyCntY<2) polyCntY = 2; + while(polyCntX*polyCntY>32767) { + polyCntX /= 2; + polyCntY /= 2; + } + + f32 ay = 0; + const f32 angleX = 2*M_PI/polyCntX; + const f32 angleY = 2*M_PI/polyCntY; + const u32 polyCntXpitch = polyCntX +1; + const u32 polyCntYpitch = polyCntY + 1; + + buffer->vertices.set_used(polyCntYpitch*polyCntXpitch); + buffer->indices.set_used(polyCntY*polyCntX*6); + + rsxAddressToOffset(&buffer->vertices[0].pos,&buffer->pos_off); + rsxAddressToOffset(&buffer->vertices[0].nrm,&buffer->nrm_off); + rsxAddressToOffset(&buffer->vertices[0].col,&buffer->col_off); + rsxAddressToOffset(&buffer->vertices[0].u,&buffer->uv_off); + rsxAddressToOffset(&buffer->indices[0],&buffer->ind_off); + + i = 0; + for(y=0;y<=polyCntY;y++) { + f32 axz = 0; + + const f32 sinay = sinf(ay); + const f32 cosay = cosf(ay); + const f32 tu = (f32)y/(f32)polyCntY; + for(x=0;x<=polyCntX;x++) { + const Vector3 pos(static_cast((outerRadius - (innerRadius*cosf(axz)))*cosay), + static_cast((outerRadius - (innerRadius*cosf(axz)))*sinay), + static_cast(innerRadius*sinf(axz))); + + const Vector3 nrm(static_cast(-cosf(axz)*cosay), + static_cast(-cosf(axz)*sinay), + static_cast(sinf(axz))); + + buffer->vertices[i] = S3DVertex(pos,nrm,col,tu,(f32)x/(f32)polyCntX); + + axz += angleX; + i++; + } + ay += angleY; + } + + i = 0; + level = 0; + for(y=0;yindices[i++] = curr; + buffer->indices[i++] = curr + polyCntXpitch; + buffer->indices[i++] = curr + 1 + polyCntXpitch; + + buffer->indices[i++] = curr; + buffer->indices[i++] = curr + 1 + polyCntXpitch; + buffer->indices[i++] = curr + 1; + } + + buffer->indices[i++] = level + polyCntX; + buffer->indices[i++] = level + polyCntX - 1; + buffer->indices[i++] = level + polyCntX - 1 + polyCntXpitch; + + buffer->indices[i++] = level + polyCntX; + buffer->indices[i++] = level + polyCntX - 1 + polyCntXpitch; + buffer->indices[i++] = level + polyCntX + polyCntXpitch; + + level += polyCntXpitch; + } + + return buffer; +} + +SMeshBuffer* createSphere(f32 radius,u32 polyCntX,u32 polyCntY) +{ + u32 i,p1,p2,level; + u32 x,y,polyCntXpitch; + const f32 RECIPROCAL_PI = 1.0f/M_PI; + SMeshBuffer *buffer = new SMeshBuffer(); + + if(polyCntX<2) polyCntX = 2; + if(polyCntY<2) polyCntY = 2; + if(polyCntX*polyCntY>32767) { + if(polyCntX>polyCntY) + polyCntX = 32767/polyCntY-1; + else + polyCntY = 32767/(polyCntX+1); + } + polyCntXpitch = polyCntX+1; + + buffer->vertices.set_used((polyCntXpitch*polyCntY) + 2); + buffer->indices.set_used((polyCntX*polyCntY)*6); + + rsxAddressToOffset(&buffer->vertices[0].pos,&buffer->pos_off); + rsxAddressToOffset(&buffer->vertices[0].nrm,&buffer->nrm_off); + rsxAddressToOffset(&buffer->vertices[0].col,&buffer->col_off); + rsxAddressToOffset(&buffer->vertices[0].u,&buffer->uv_off); + rsxAddressToOffset(&buffer->indices[0],&buffer->ind_off); + + i = 0; + level = 0; + for(p1=0;p1indices[i++] = curr; + buffer->indices[i++] = curr + polyCntXpitch; + buffer->indices[i++] = curr + 1 + polyCntXpitch; + + buffer->indices[i++] = curr; + buffer->indices[i++] = curr + 1 + polyCntXpitch; + buffer->indices[i++] = curr + 1; + } + + buffer->indices[i++] = level + polyCntX; + buffer->indices[i++] = level + polyCntX - 1; + buffer->indices[i++] = level + polyCntX - 1 + polyCntXpitch; + + buffer->indices[i++] = level + polyCntX; + buffer->indices[i++] = level + polyCntX - 1 + polyCntXpitch; + buffer->indices[i++] = level + polyCntX + polyCntXpitch; + + level += polyCntXpitch; + } + + const u32 polyCntSq = polyCntXpitch*polyCntY; + const u32 polyCntSq1 = polyCntSq+1; + const u32 polyCntSqM1 = (polyCntY-1)*polyCntXpitch; + + for(p2=0;p2indices[i++] = polyCntSq; + buffer->indices[i++] = p2; + buffer->indices[i++] = p2+1; + + buffer->indices[i++] = polyCntSq1; + buffer->indices[i++] = polyCntSqM1+p2; + buffer->indices[i++] = polyCntSqM1+p2+1; + } + + buffer->indices[i++] = polyCntSq; + buffer->indices[i++] = polyCntX-1; + buffer->indices[i++] = polyCntX; + + buffer->indices[i++] = polyCntSq1; + buffer->indices[i++] = polyCntSqM1; + buffer->indices[i++] = polyCntSqM1+polyCntX-1; + + f32 axz; + f32 ay = 0; + SColor col(100,255,255,255); + const f32 angelX = 2*M_PI/polyCntX; + const f32 angelY = M_PI/polyCntY; + + i = 0; + for(y=0;y(radius*cosf(axz)*sinay), static_cast(radius*cosf(ay)), static_cast(radius*sinf(axz)*sinay)); + + Vector3 normal = normalize(pos); + + f32 tu = 0.5F; + if(y==0) { + if(normal.getY()!=-1.0F && normal.getY()!=1.0F) + tu = static_cast(acosf(clamp(normal.getX()/sinay,-1.0f,1.0f))*0.5F*RECIPROCAL_PI); + if(normal.getZ()<0.0F) + tu = 1-tu; + } else + tu = buffer->vertices[i - polyCntXpitch].u; + + buffer->vertices[i] = S3DVertex(pos,normal,col,tu,static_cast(ay*RECIPROCAL_PI)); + axz += angelX; + i++; + } + buffer->vertices[i] = S3DVertex(buffer->vertices[i-polyCntX]); + buffer->vertices[i].u = 1.0F; + i++; + } + + buffer->vertices[i++] = S3DVertex(0.0F,radius,0.0F,0.0F,1.0F,0.0F,col,0.5F,0.0F); + buffer->vertices[i] = S3DVertex(0.0F,-radius,0.0F,0.0F,-1.0F,0.0F,col,0.5F,1.0F); + + return buffer; +} diff --git a/samples/graphics/rsx_Basic_Wallpaper/source/main.cpp b/samples/graphics/rsx_Basic_Wallpaper/source/main.cpp new file mode 100644 index 00000000..e69262c2 --- /dev/null +++ b/samples/graphics/rsx_Basic_Wallpaper/source/main.cpp @@ -0,0 +1,343 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wall1_png_bin.h" + +#include + +#include +#include +#include + +#include "acid.h" +#include "mesh.h" +#include "geometry.h" +#include "rsxutil.h" + +#include "diffuse_specular_shader_vpo.h" +#include "scanlines_fpo.h" + +typedef struct +{ + float x, y, z; + u32 rgba; +} Vertex_t; + +Vertex_t* vertex_buffer; +u32 VertexBufferOffset; + +u32 running = 0; + +u32 fp_offset; +u32 *fp_buffer; + +u32* texture_buffer; +u32 texture_offset; + +pngData* png; + + +SMeshBuffer* quad = NULL; + +// vertex shader +rsxProgramConst *projMatrix; + +rsxProgramAttrib* mPosIndex = NULL; + +rsxProgramAttrib* mColIndex = NULL; + +// fragment shader +rsxProgramConst* scanlines; +rsxProgramAttrib* textureUnit; + +u32 color_index; +u32 position_index; + + +void *vp_ucode = NULL; +rsxVertexProgram *vpo = (rsxVertexProgram*)diffuse_specular_shader_vpo; + +void *fp_ucode = NULL; +rsxFragmentProgram *fpo = (rsxFragmentProgram*)scanlines_fpo; + +// setting default scanline parameters +Vector4 scanlineParams(200.0f, 2.0f, 0.7f, 0.0f); + +SYS_PROCESS_PARAM(1001, 0x100000); + +extern "C" { +static void program_exit_callback() +{ + gcmSetWaitFlip(context); + rsxFinish(context,1); +} + +static void sysutil_exit_callback(u64 status,u64 param,void *usrdata) +{ + switch(status) { + case SYSUTIL_EXIT_GAME: + running = 0; + break; + case SYSUTIL_DRAW_BEGIN: + case SYSUTIL_DRAW_END: + break; + default: + break; + } +} +} + +static void init_texture() +{ + + u32 i; + u8* buffer; + + //Init png texture + const u8* data = (u8*)png->bmp_out; + texture_buffer = (u32*)rsxMemalign(128, (png->height * png->pitch)); + + if (!texture_buffer) return; + + rsxAddressToOffset(texture_buffer, &texture_offset); + + buffer = (u8*)texture_buffer; + for (i = 0; i < png->height * png->pitch; i += 4) { + buffer[i + 0] = *data++; + buffer[i + 1] = *data++; + buffer[i + 2] = *data++; + buffer[i + 3] = *data++; + } +} + + +static void setTexture(u8 textureUnit) +{ + u32 width = png->width; + u32 height = png->height; + u32 pitch = png->pitch; + gcmTexture texture; + + if (!texture_buffer) return; + + rsxInvalidateTextureCache(context, GCM_INVALIDATE_TEXTURE); + + texture.format = (GCM_TEXTURE_FORMAT_A8R8G8B8 | GCM_TEXTURE_FORMAT_LIN); + texture.mipmap = 1; + texture.dimension = GCM_TEXTURE_DIMS_2D; + texture.cubemap = GCM_FALSE; + texture.remap = ((GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_B_SHIFT) | + (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_G_SHIFT) | + (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_R_SHIFT) | + (GCM_TEXTURE_REMAP_TYPE_REMAP << GCM_TEXTURE_REMAP_TYPE_A_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_B << GCM_TEXTURE_REMAP_COLOR_B_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_G << GCM_TEXTURE_REMAP_COLOR_G_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_R << GCM_TEXTURE_REMAP_COLOR_R_SHIFT) | + (GCM_TEXTURE_REMAP_COLOR_A << GCM_TEXTURE_REMAP_COLOR_A_SHIFT)); + texture.width = width; + texture.height = height; + texture.depth = 1; + texture.location = GCM_LOCATION_RSX; + texture.pitch = pitch; + texture.offset = texture_offset; + rsxLoadTexture(context, textureUnit, &texture); + rsxTextureControl(context, textureUnit, GCM_TRUE, 0 << 8, 12 << 8, GCM_TEXTURE_MAX_ANISO_1); + rsxTextureFilter(context, textureUnit, 0, GCM_TEXTURE_LINEAR, GCM_TEXTURE_LINEAR, GCM_TEXTURE_CONVOLUTION_QUINCUNX); + rsxTextureWrapMode(context, textureUnit, GCM_TEXTURE_CLAMP_TO_EDGE, GCM_TEXTURE_CLAMP_TO_EDGE, GCM_TEXTURE_CLAMP_TO_EDGE, 0, GCM_TEXTURE_ZFUNC_LESS, 0); +} + + +static void setDrawEnv() +{ + rsxSetColorMask(context,GCM_COLOR_MASK_B | + GCM_COLOR_MASK_G | + GCM_COLOR_MASK_R | + GCM_COLOR_MASK_A); + + rsxSetColorMaskMrt(context,0); + + u16 x,y,w,h; + f32 min, max; + f32 scale[4],offset[4]; + + x = 0; + y = 0; + w = display_width; + h = display_height; + min = 0.0f; + max = 1.0f; + scale[0] = w*0.5f; + scale[1] = h*-0.5f; + scale[2] = (max - min)*0.5f; + scale[3] = 0.0f; + offset[0] = x + w*0.5f; + offset[1] = y + h*0.5f; + offset[2] = (max + min)*0.5f; + offset[3] = 0.0f; + + rsxSetViewport(context,x, y, w, h, min, max, scale, offset); + rsxSetScissor(context,x,y,w,h); + + rsxSetDepthTestEnable(context, GCM_FALSE); + rsxSetDepthFunc(context,GCM_LESS); + rsxSetShadeModel(context,GCM_SHADE_MODEL_SMOOTH); + rsxSetDepthWriteEnable(context,1); + rsxSetFrontFace(context,GCM_FRONTFACE_CCW); + rsxSetBlendEnable(context, GCM_TRUE); + rsxSetBlendFunc(context, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA, GCM_SRC_COLOR, GCM_DST_COLOR); + rsxSetBlendEquation(context, GCM_FUNC_ADD, GCM_FUNC_ADD); +} + +void init_shader() +{ + u32 fpsize = 0; + u32 vpsize = 0; + + rsxVertexProgramGetUCode(vpo, &vp_ucode, &vpsize); + + projMatrix = rsxVertexProgramGetConst(vpo, "modelViewProj"); + + mPosIndex = rsxVertexProgramGetAttrib(vpo, "position"); + + mColIndex = rsxVertexProgramGetAttrib(vpo, "color"); + + rsxFragmentProgramGetUCode(fpo, &fp_ucode, &fpsize); + + fp_buffer = (u32*)rsxMemalign(64,fpsize); + memcpy(fp_buffer,fp_ucode,fpsize); + + rsxAddressToOffset(fp_buffer,&fp_offset); + scanlines = rsxFragmentProgramGetConst(fpo, "scanline"); + textureUnit = rsxFragmentProgramGetAttrib(fpo, "texture"); + if (textureUnit) + printf("textureUnit OK\n"); + +} + + +void drawFrame() +{ + u32 i, offset; + SMeshBuffer* mesh = NULL; + + setDrawEnv(); + + + rsxSetClearColor(context,0); + rsxSetClearDepthStencil(context,0xffffff00); + rsxClearSurface(context,GCM_CLEAR_R | + GCM_CLEAR_G | + GCM_CLEAR_B | + GCM_CLEAR_A | + GCM_CLEAR_S | + GCM_CLEAR_Z); + + rsxSetZControl(context,0,1,1); + + for(i=0;i<8;i++) + rsxSetViewportClip(context,i,display_width,display_height); + + Matrix4 tempMatrix = transpose(Matrix4::identity()); + + mesh = quad; + setTexture(textureUnit->index); + + rsxBindVertexArrayAttrib(context, GCM_VERTEX_ATTRIB_POS, 0, mesh->pos_off, sizeof(S3DVertex), 3, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context, GCM_VERTEX_ATTRIB_NORMAL, 0, mesh->nrm_off, sizeof(S3DVertex), 3, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX); + rsxBindVertexArrayAttrib(context, GCM_VERTEX_ATTRIB_TEX0, 0, mesh->uv_off, sizeof(S3DVertex), 2, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX); + + rsxLoadVertexProgram(context, vpo, vp_ucode); + rsxSetVertexProgramParameter(context, vpo, projMatrix, (float*)&tempMatrix); + + rsxSetFragmentProgramParameter(context, fpo, scanlines, (float*)&scanlineParams, fp_offset, GCM_LOCATION_RSX); + rsxLoadFragmentProgramLocation(context, fpo, fp_offset, GCM_LOCATION_RSX); + rsxAddressToOffset(&mesh->indices[0], &offset); + rsxDrawIndexArray(context, GCM_TYPE_TRIANGLES, mesh->ind_off, mesh->getIndexCount(), GCM_INDEX_TYPE_32B, GCM_LOCATION_RSX); + + +} + +int main(int argc,const char *argv[]) +{ + padInfo padinfo; + padData paddata; + void *host_addr = memalign(HOST_ADDR_ALIGNMENT,HOSTBUFFER_SIZE); + + printf("rsxtest started...\n"); + + init_screen(host_addr,HOSTBUFFER_SIZE); + ioPadInit(7); + + //Init background Image + png = new pngData; + pngLoadFromBuffer(wall1_png_bin, wall1_png_bin_size, png); + + //Create quad + quad = createQuad(Point3(-1.0, -1.0, 0), Point3(1.0, 1.0, 0), Point3(-1.0, 1.0, 0), Point3(1.0, -1.0, 0)); + + + init_shader(); + init_texture(); + + DebugFont::init(); + DebugFont::setScreenRes(display_width, display_height); + + atexit(program_exit_callback); + sysUtilRegisterCallback(0,sysutil_exit_callback,NULL); + + setDrawEnv(); + setRenderTarget(curr_fb); + + running = 1; + while(running) { + sysUtilCheckCallback(); + + ioPadGetInfo(&padinfo); + for(int i=0; i < MAX_PADS; i++){ + if(padinfo.status[i]){ + ioPadGetData(i, &paddata); + + if(paddata.BTN_CIRCLE) + goto done; + if (paddata.BTN_CROSS) + scanlineParams.setW(0.1f); + if (paddata.BTN_SQUARE) + scanlineParams.setW(0.2f); + if (paddata.BTN_TRIANGLE) + scanlineParams.setW(0.0f); + } + + } + + drawFrame(); + int ypos = 53; + int xpos = 10; + DebugFont::setPosition(xpos, ypos); + DebugFont::setColor(0.5f, 1.0f, 0.5f, 1.0f); + + DebugFont::print("CROSS Button to enable horizontal scanlines"); + ypos += 12; + DebugFont::setPosition(xpos, ypos); + DebugFont::print("SQUARE Button to enable vertical scanlines"); + ypos += 12; + DebugFont::setPosition(xpos, ypos); + DebugFont::print("TRIANGLE Button to disable scanlines"); + ypos += 12; + DebugFont::setPosition(xpos, ypos); + DebugFont::print("CIRCLE Button to exit"); + + flip(); + } + +done: + printf("rsxtest done...\n"); + DebugFont::shutdown(); + program_exit_callback(); + return 0; +} diff --git a/samples/graphics/rsx_Basic_Wallpaper/source/rsxutil.cpp b/samples/graphics/rsx_Basic_Wallpaper/source/rsxutil.cpp new file mode 100644 index 00000000..c1bf2877 --- /dev/null +++ b/samples/graphics/rsx_Basic_Wallpaper/source/rsxutil.cpp @@ -0,0 +1,208 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "rsxutil.h" + +#define GCM_LABEL_INDEX 255 + +videoResolution vResolution; +gcmContextData *context = NULL; + +u32 curr_fb = 0; +u32 first_fb = 1; + +u32 display_width; +u32 display_height; + +u32 depth_pitch; +u32 depth_offset; +u32 *depth_buffer; + +u32 color_pitch; +u32 color_offset[FRAME_BUFFER_COUNT]; +u32 *color_buffer[FRAME_BUFFER_COUNT]; + +f32 aspect_ratio; + +static u32 sResolutionIds[] = { + VIDEO_RESOLUTION_1080, + VIDEO_RESOLUTION_720, + VIDEO_RESOLUTION_480, + VIDEO_RESOLUTION_576 +}; +static size_t RESOLUTION_ID_COUNT = sizeof(sResolutionIds)/sizeof(u32); + +static u32 sLabelVal = 1; + +extern void dbg_printf(const char *fmt,...); + +static RSXDebugFontRenderer* debugFontRenderer; + +static void waitFinish() +{ + rsxSetWriteBackendLabel(context,GCM_LABEL_INDEX,sLabelVal); + + rsxFlushBuffer(context); + + while(*(vu32*)gcmGetLabelAddress(GCM_LABEL_INDEX)!=sLabelVal) + usleep(30); + + ++sLabelVal; +} + +static void waitRSXIdle() +{ + rsxSetWriteBackendLabel(context,GCM_LABEL_INDEX,sLabelVal); + rsxSetWaitLabel(context,GCM_LABEL_INDEX,sLabelVal); + + ++sLabelVal; + + waitFinish(); +} + +void initVideoConfiguration() +{ + s32 rval = 0; + s32 resId = 0; + + for (size_t i=0;i < RESOLUTION_ID_COUNT;i++) { + rval = videoGetResolutionAvailability(VIDEO_PRIMARY, sResolutionIds[i], VIDEO_ASPECT_AUTO, 0); + if (rval != 1) continue; + + resId = sResolutionIds[i]; + rval = videoGetResolution(resId, &vResolution); + if(!rval) break; + } + + if(rval) { + printf("Error: videoGetResolutionAvailability failed. No usable resolution.\n"); + exit(1); + } + + videoConfiguration config = { + (u8)resId, + VIDEO_BUFFER_FORMAT_XRGB, + VIDEO_ASPECT_AUTO, + {0,0,0,0,0,0,0,0,0}, + (u32)vResolution.width*4 + }; + + rval = videoConfigure(VIDEO_PRIMARY, &config, NULL, 0); + if(rval) { + printf("Error: videoConfigure failed.\n"); + exit(1); + } + + videoState state; + + rval = videoGetState(VIDEO_PRIMARY, 0, &state); + switch(state.displayMode.aspect) { + case VIDEO_ASPECT_4_3: + aspect_ratio = 4.0f/3.0f; + break; + case VIDEO_ASPECT_16_9: + aspect_ratio = 16.0f/9.0f; + break; + default: + printf("unknown aspect ratio %x\n", state.displayMode.aspect); + aspect_ratio = 16.0f/9.0f; + break; + } + + display_height = vResolution.height; + display_width = vResolution.width; +} + +void setRenderTarget(u32 index) +{ + gcmSurface sf; + + sf.colorFormat = GCM_SURFACE_X8R8G8B8; + sf.colorTarget = GCM_SURFACE_TARGET_0; + sf.colorLocation[0] = GCM_LOCATION_RSX; + sf.colorOffset[0] = color_offset[index]; + sf.colorPitch[0] = color_pitch; + + sf.colorLocation[1] = GCM_LOCATION_RSX; + sf.colorLocation[2] = GCM_LOCATION_RSX; + sf.colorLocation[3] = GCM_LOCATION_RSX; + sf.colorOffset[1] = 0; + sf.colorOffset[2] = 0; + sf.colorOffset[3] = 0; + sf.colorPitch[1] = 64; + sf.colorPitch[2] = 64; + sf.colorPitch[3] = 64; + + sf.depthFormat = GCM_SURFACE_ZETA_Z16; + sf.depthLocation = GCM_LOCATION_RSX; + sf.depthOffset = depth_offset; + sf.depthPitch = depth_pitch; + + sf.type = GCM_SURFACE_TYPE_LINEAR; + sf.antiAlias = GCM_SURFACE_CENTER_1; + + sf.width = display_width; + sf.height = display_height; + sf.x = 0; + sf.y = 0; + + rsxSetSurface(context,&sf); +} + +void init_screen(void *host_addr,u32 size) +{ + u32 zs_depth = 4; + u32 color_depth = 4; + + rsxInit(&context,CB_SIZE,size,host_addr); + + initVideoConfiguration(); + + waitRSXIdle(); + + gcmSetFlipMode(GCM_FLIP_VSYNC); + + color_pitch = display_width*color_depth; + depth_pitch = display_width*zs_depth; + + for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { + color_buffer[i] = (u32*)rsxMemalign(64,(display_height*color_pitch)); + rsxAddressToOffset(color_buffer[i],&color_offset[i]); + gcmSetDisplayBuffer(i,color_offset[i],color_pitch,display_width,display_height); + } + + depth_buffer = (u32*)rsxMemalign(64,(display_height*depth_pitch)*2); + rsxAddressToOffset(depth_buffer,&depth_offset); + + debugFontRenderer = new RSXDebugFontRenderer(context); +} + +void waitflip() +{ + while(gcmGetFlipStatus()!=0) + usleep(200); + gcmResetFlipStatus(); +} + +void flip() +{ + if(!first_fb) waitflip(); + else gcmResetFlipStatus(); + + gcmSetFlip(context,curr_fb); + rsxFlushBuffer(context); + + gcmSetWaitFlip(context); + + curr_fb ^= 1; + setRenderTarget(curr_fb); + + first_fb = 0; +} From f7eda8960670cf67a63fcc22e11c9b4e485b6e9d Mon Sep 17 00:00:00 2001 From: CrystalCT Date: Tue, 16 Mar 2021 09:23:02 +0100 Subject: [PATCH 56/56] rsx_Basic_Wallpaper fixed --- .../rsx_Basic_Wallpaper/include/mesh.h | 93 +----- .../rsx_Basic_Wallpaper/include/rsxutil.h | 34 +- .../rsx_Basic_Wallpaper/source/main.cpp | 150 +++++---- .../rsx_Basic_Wallpaper/source/rsxutil.cpp | 300 +++++++++++++----- 4 files changed, 351 insertions(+), 226 deletions(-) diff --git a/samples/graphics/rsx_Basic_Wallpaper/include/mesh.h b/samples/graphics/rsx_Basic_Wallpaper/include/mesh.h index 4ccfcd38..e12193c0 100644 --- a/samples/graphics/rsx_Basic_Wallpaper/include/mesh.h +++ b/samples/graphics/rsx_Basic_Wallpaper/include/mesh.h @@ -1,14 +1,8 @@ #ifndef __MESH_H__ #define __MESH_H__ -#include #include -#include - -#include "irrarray.h" - -using namespace irr; using namespace Vectormath::Aos; template< class T > @@ -29,100 +23,41 @@ inline const T clamp(const T& val,const T& low,const T& high) return min_(max_(val,low),high); } -class SColor -{ -public: - SColor() : color(0) {}; - SColor(u32 c) : color(c) {}; - SColor(u8 a,u8 r,u8 g,u8 b) : R(r),G(g),B(b),A(a) {}; - SColor(const SColor& other) : color(other.color) {}; - - u8 getRed() const { return R; } - u8 getGreen() const { return G; } - u8 getBlue() const { return B; } - u8 getAlpha() const { return A; } - - union { - u32 color; - struct { - u8 R,G,B,A; - }; - }; -}; - struct S3DVertex { S3DVertex() {}; - S3DVertex(f32 x,f32 y,f32 z,f32 nx,f32 ny,f32 nz,SColor c,f32 tu,f32 tv) - : pos(x,y,z),nrm(nx,ny,nz),col(c),u(tu),v(tv) {}; - S3DVertex(const Vector3& _pos,const Vector3& _nrm,const SColor& c,f32 tu,f32 tv) - : pos(_pos),nrm(_nrm),col(c),u(tu),v(tv) {}; + S3DVertex(f32 x,f32 y,f32 z,f32 nx,f32 ny,f32 nz,f32 tu,f32 tv, u32 c) + : pos(x, y, z), nrm(nx, ny, nz), u(tu), v(tv), col(c) {}; inline S3DVertex& operator=(const S3DVertex& other) { pos = other.pos; nrm = other.nrm; - col = other.col; u = other.u; v = other.v; + col = other.col; return *this; } Vector3 pos; Vector3 nrm; - SColor col; - - f32 u,v; + f32 u, v; + u32 col; }; - -class SMeshBuffer +template< class T > +class CMeshBuffer { public: - SMeshBuffer() : pos_off(0),nrm_off(0),col_off(0),uv_off(0),ind_off(0) {}; - virtual ~SMeshBuffer() - { - } + CMeshBuffer() : indices(NULL),cnt_indices(0),vertices(NULL),cnt_vertices(0) {}; - virtual const void* getVertices() const - { - return vertices.const_pointer(); - } + u16 *indices; + u32 cnt_indices; - virtual void* getVertices() - { - return vertices.pointer(); - } - - virtual u32 getVertexCount() const - { - return vertices.size(); - } - - virtual const u32* getIndices() const - { - return indices.const_pointer(); - } - - virtual u32* getIndices() - { - return indices.pointer(); - } - - virtual u32 getIndexCount() const - { - return indices.size(); - } - - core::array< u32, core::allocatorRSX< u32 > > indices; - core::array< S3DVertex, core::allocatorRSX< S3DVertex > > vertices; - - u32 pos_off; - u32 nrm_off; - u32 col_off; - u32 uv_off; - - u32 ind_off; + S3DVertex *vertices; + u32 cnt_vertices; }; +typedef CMeshBuffer SMeshBuffer; + #endif diff --git a/samples/graphics/rsx_Basic_Wallpaper/include/rsxutil.h b/samples/graphics/rsx_Basic_Wallpaper/include/rsxutil.h index 7b77b7e3..2d2d3a39 100644 --- a/samples/graphics/rsx_Basic_Wallpaper/include/rsxutil.h +++ b/samples/graphics/rsx_Basic_Wallpaper/include/rsxutil.h @@ -1,21 +1,24 @@ #ifndef __RSXUTIL_H__ #define __RSXUTIL_H__ -#include #include #include - -#define DEFUALT_CB_SIZE 0x80000 // 512Kb default command buffer size +#define DEFAULT_CB_SIZE 0x80000 // 512Kb default command buffer size #define HOST_STATE_CB_SIZE 0x10000 // 64Kb state command buffer size (used for resetting certain default states) #define HOST_ADDR_ALIGNMENT (1024*1024) -#define HOSTBUFFER_SIZE (128*1024*1024) -#define CB_SIZE 0x100000 -#define FRAME_BUFFER_COUNT 2 +#define GCM_PREPARED_BUFFER_INDEX 65 +#define GCM_BUFFER_STATUS_INDEX 66 +#define GCM_WAIT_LABEL_INDEX 255 + +#define MAX_BUFFER_QUEUE_SIZE 1 -extern gcmContextData *context; +#define BUFFER_IDLE 0 +#define BUFFER_BUSY 1 + +#define FRAME_BUFFER_COUNT 4 extern u32 curr_fb; @@ -24,17 +27,22 @@ extern u32 display_height; extern u32 depth_pitch; extern u32 depth_offset; -extern u32 *depth_buffer; +extern void *depth_buffer; extern u32 color_pitch; extern u32 color_offset[FRAME_BUFFER_COUNT]; -extern u32 *color_buffer[FRAME_BUFFER_COUNT]; +extern void *color_buffer[FRAME_BUFFER_COUNT]; + +extern void *state_buffer; +extern u32 state_offset; + extern f32 aspect_ratio; -void setRenderTarget(u32 index); -void init_screen(void *host_addr,u32 size); -void waitflip(); +void initScreen(u32 hostBufferSize); void flip(); +void finish(); + +void setRenderTarget(u32 index); -#endif +#endif // __RSXUTIL_H__ diff --git a/samples/graphics/rsx_Basic_Wallpaper/source/main.cpp b/samples/graphics/rsx_Basic_Wallpaper/source/main.cpp index e69262c2..4ed8026c 100644 --- a/samples/graphics/rsx_Basic_Wallpaper/source/main.cpp +++ b/samples/graphics/rsx_Basic_Wallpaper/source/main.cpp @@ -17,23 +17,18 @@ #include "acid.h" #include "mesh.h" -#include "geometry.h" #include "rsxutil.h" #include "diffuse_specular_shader_vpo.h" #include "scanlines_fpo.h" -typedef struct -{ - float x, y, z; - u32 rgba; -} Vertex_t; - -Vertex_t* vertex_buffer; -u32 VertexBufferOffset; +#define GCM_APP_WAIT_LABEL_INDEX 128 +#define HOSTBUFFER_SIZE (128*1024*1024) u32 running = 0; +vu32 *wait_label = NULL; + u32 fp_offset; u32 *fp_buffer; @@ -71,11 +66,12 @@ Vector4 scanlineParams(200.0f, 2.0f, 0.7f, 0.0f); SYS_PROCESS_PARAM(1001, 0x100000); +static u32 sLabelValue = 0; + extern "C" { static void program_exit_callback() { - gcmSetWaitFlip(context); - rsxFinish(context,1); + finish(); } static void sysutil_exit_callback(u64 status,u64 param,void *usrdata) @@ -93,6 +89,38 @@ static void sysutil_exit_callback(u64 status,u64 param,void *usrdata) } } +SMeshBuffer* createQuad(Point3 P1, Point3 P2, Point3 P3, Point3 P4) +{ + u32 i; + u32 col = 0xFFFFFFFF; + + SMeshBuffer* buffer = new SMeshBuffer(); + const u16 u[6] = { 0,1,2, 0,3,1 }; + + buffer->cnt_indices = 6; + buffer->indices = (u16*)rsxMemalign(128, buffer->cnt_indices * sizeof(u16)); + + for (i = 0; i < 6; i++) buffer->indices[i] = u[i]; + + buffer->cnt_vertices = 4; + buffer->vertices = (S3DVertex*)rsxMemalign(128, buffer->cnt_vertices * sizeof(S3DVertex)); + + // position, normal, texture + buffer->vertices[0] = S3DVertex(P1.getX(), P1.getY(), P1.getZ(), -1, -1, -1, 0, 1, col); + buffer->vertices[1] = S3DVertex(P2.getX(), P2.getY(), P2.getZ(), 1, -1, -1, 1, 0, col); + buffer->vertices[2] = S3DVertex(P3.getX(), P3.getY(), P3.getZ(), 1, 1, -1, 0, 0, col); + buffer->vertices[3] = S3DVertex(P4.getX(), P4.getY(), P4.getZ(), -1, 1, -1, 1, 1, col); + + + /*rsxAddressToOffset(&buffer->vertices[0].pos, &buffer->pos_off); + rsxAddressToOffset(&buffer->vertices[0].nrm, &buffer->nrm_off); + rsxAddressToOffset(&buffer->vertices[0].col, &buffer->col_off); + rsxAddressToOffset(&buffer->vertices[0].u, &buffer->uv_off); + rsxAddressToOffset(&buffer->indices[0], &buffer->ind_off);*/ + + return buffer; +} + static void init_texture() { @@ -126,7 +154,7 @@ static void setTexture(u8 textureUnit) if (!texture_buffer) return; - rsxInvalidateTextureCache(context, GCM_INVALIDATE_TEXTURE); + rsxInvalidateTextureCache(gGcmContext, GCM_INVALIDATE_TEXTURE); texture.format = (GCM_TEXTURE_FORMAT_A8R8G8B8 | GCM_TEXTURE_FORMAT_LIN); texture.mipmap = 1; @@ -146,22 +174,15 @@ static void setTexture(u8 textureUnit) texture.location = GCM_LOCATION_RSX; texture.pitch = pitch; texture.offset = texture_offset; - rsxLoadTexture(context, textureUnit, &texture); - rsxTextureControl(context, textureUnit, GCM_TRUE, 0 << 8, 12 << 8, GCM_TEXTURE_MAX_ANISO_1); - rsxTextureFilter(context, textureUnit, 0, GCM_TEXTURE_LINEAR, GCM_TEXTURE_LINEAR, GCM_TEXTURE_CONVOLUTION_QUINCUNX); - rsxTextureWrapMode(context, textureUnit, GCM_TEXTURE_CLAMP_TO_EDGE, GCM_TEXTURE_CLAMP_TO_EDGE, GCM_TEXTURE_CLAMP_TO_EDGE, 0, GCM_TEXTURE_ZFUNC_LESS, 0); + rsxLoadTexture(gGcmContext, textureUnit, &texture); + rsxTextureControl(gGcmContext, textureUnit, GCM_TRUE, 0 << 8, 12 << 8, GCM_TEXTURE_MAX_ANISO_1); + rsxTextureFilter(gGcmContext, textureUnit, 0, GCM_TEXTURE_LINEAR, GCM_TEXTURE_LINEAR, GCM_TEXTURE_CONVOLUTION_QUINCUNX); + rsxTextureWrapMode(gGcmContext, textureUnit, GCM_TEXTURE_CLAMP_TO_EDGE, GCM_TEXTURE_CLAMP_TO_EDGE, GCM_TEXTURE_CLAMP_TO_EDGE, 0, GCM_TEXTURE_ZFUNC_LESS, 0); } static void setDrawEnv() { - rsxSetColorMask(context,GCM_COLOR_MASK_B | - GCM_COLOR_MASK_G | - GCM_COLOR_MASK_R | - GCM_COLOR_MASK_A); - - rsxSetColorMaskMrt(context,0); - u16 x,y,w,h; f32 min, max; f32 scale[4],offset[4]; @@ -181,17 +202,13 @@ static void setDrawEnv() offset[2] = (max + min)*0.5f; offset[3] = 0.0f; - rsxSetViewport(context,x, y, w, h, min, max, scale, offset); - rsxSetScissor(context,x,y,w,h); - - rsxSetDepthTestEnable(context, GCM_FALSE); - rsxSetDepthFunc(context,GCM_LESS); - rsxSetShadeModel(context,GCM_SHADE_MODEL_SMOOTH); - rsxSetDepthWriteEnable(context,1); - rsxSetFrontFace(context,GCM_FRONTFACE_CCW); - rsxSetBlendEnable(context, GCM_TRUE); - rsxSetBlendFunc(context, GCM_SRC_ALPHA, GCM_ONE_MINUS_SRC_ALPHA, GCM_SRC_COLOR, GCM_DST_COLOR); - rsxSetBlendEquation(context, GCM_FUNC_ADD, GCM_FUNC_ADD); + rsxSetCallCommand(gGcmContext, state_offset); + while(*wait_label != sLabelValue) + usleep(10); + sLabelValue++; + + rsxSetViewport(gGcmContext,x, y, w, h, min, max, scale, offset); + rsxSetScissor(gGcmContext,x,y,w,h); } void init_shader() @@ -223,43 +240,60 @@ void init_shader() void drawFrame() { - u32 i, offset; + u32 i,offset; SMeshBuffer* mesh = NULL; setDrawEnv(); - rsxSetClearColor(context,0); - rsxSetClearDepthStencil(context,0xffffff00); - rsxClearSurface(context,GCM_CLEAR_R | + rsxSetClearColor(gGcmContext,0); + rsxSetClearDepthStencil(gGcmContext,0xffffff00); + rsxClearSurface(gGcmContext,GCM_CLEAR_R | GCM_CLEAR_G | GCM_CLEAR_B | GCM_CLEAR_A | GCM_CLEAR_S | GCM_CLEAR_Z); - rsxSetZControl(context,0,1,1); + rsxSetZControl(gGcmContext,0,1,1); for(i=0;i<8;i++) - rsxSetViewportClip(context,i,display_width,display_height); + rsxSetViewportClip(gGcmContext,i,display_width,display_height); Matrix4 tempMatrix = transpose(Matrix4::identity()); mesh = quad; setTexture(textureUnit->index); - rsxBindVertexArrayAttrib(context, GCM_VERTEX_ATTRIB_POS, 0, mesh->pos_off, sizeof(S3DVertex), 3, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX); - rsxBindVertexArrayAttrib(context, GCM_VERTEX_ATTRIB_NORMAL, 0, mesh->nrm_off, sizeof(S3DVertex), 3, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX); - rsxBindVertexArrayAttrib(context, GCM_VERTEX_ATTRIB_TEX0, 0, mesh->uv_off, sizeof(S3DVertex), 2, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX); + rsxAddressToOffset(&mesh->vertices[0].pos, &offset); + rsxBindVertexArrayAttrib(gGcmContext, GCM_VERTEX_ATTRIB_POS, 0, offset, sizeof(S3DVertex), 3, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX); + rsxAddressToOffset(&mesh->vertices[0].nrm, &offset); + rsxBindVertexArrayAttrib(gGcmContext, GCM_VERTEX_ATTRIB_NORMAL, 0, offset, sizeof(S3DVertex), 3, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX); + rsxAddressToOffset(&mesh->vertices[0].u, &offset); + rsxBindVertexArrayAttrib(gGcmContext, GCM_VERTEX_ATTRIB_TEX0, 0, offset, sizeof(S3DVertex), 2, GCM_VERTEX_DATA_TYPE_F32, GCM_LOCATION_RSX); + rsxAddressToOffset(&mesh->vertices[0].col, &offset); + rsxBindVertexArrayAttrib(gGcmContext, GCM_VERTEX_ATTRIB_COLOR0, 0, offset, sizeof(S3DVertex), 4, GCM_VERTEX_DATA_TYPE_U8, GCM_LOCATION_RSX); - rsxLoadVertexProgram(context, vpo, vp_ucode); - rsxSetVertexProgramParameter(context, vpo, projMatrix, (float*)&tempMatrix); + rsxLoadVertexProgram(gGcmContext, vpo, vp_ucode); + rsxSetVertexProgramParameter(gGcmContext, vpo, projMatrix, (float*)&tempMatrix); + + rsxSetFragmentProgramParameter(gGcmContext, fpo, scanlines, (float*)&scanlineParams, fp_offset, GCM_LOCATION_RSX); + rsxLoadFragmentProgramLocation(gGcmContext, fpo, fp_offset, GCM_LOCATION_RSX); + + rsxSetWriteTextureLabel(gGcmContext, GCM_APP_WAIT_LABEL_INDEX, sLabelValue); + + rsxSetUserClipPlaneControl(gGcmContext, GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE, + GCM_USER_CLIP_PLANE_DISABLE); - rsxSetFragmentProgramParameter(context, fpo, scanlines, (float*)&scanlineParams, fp_offset, GCM_LOCATION_RSX); - rsxLoadFragmentProgramLocation(context, fpo, fp_offset, GCM_LOCATION_RSX); rsxAddressToOffset(&mesh->indices[0], &offset); - rsxDrawIndexArray(context, GCM_TYPE_TRIANGLES, mesh->ind_off, mesh->getIndexCount(), GCM_INDEX_TYPE_32B, GCM_LOCATION_RSX); + rsxDrawIndexArray(gGcmContext, GCM_TYPE_TRIANGLES, offset, mesh->cnt_indices, GCM_INDEX_TYPE_16B, GCM_LOCATION_RSX); + rsxSetWriteTextureLabel(gGcmContext, GCM_APP_WAIT_LABEL_INDEX, sLabelValue); + rsxFlushBuffer(gGcmContext); } @@ -267,19 +301,27 @@ int main(int argc,const char *argv[]) { padInfo padinfo; padData paddata; - void *host_addr = memalign(HOST_ADDR_ALIGNMENT,HOSTBUFFER_SIZE); - + + if (sysModuleLoad(SYSMODULE_PNGDEC) != 0) exit(0); + printf("rsxtest started...\n"); - init_screen(host_addr,HOSTBUFFER_SIZE); + initScreen(HOSTBUFFER_SIZE); ioPadInit(7); + wait_label = gcmGetLabelAddress(GCM_APP_WAIT_LABEL_INDEX); + *wait_label = sLabelValue; + //Init background Image png = new pngData; pngLoadFromBuffer(wall1_png_bin, wall1_png_bin_size, png); //Create quad - quad = createQuad(Point3(-1.0, -1.0, 0), Point3(1.0, 1.0, 0), Point3(-1.0, 1.0, 0), Point3(1.0, -1.0, 0)); + Point3 P1 = Point3(-1.0, -1.0, 0); + Point3 P2 = Point3(1.0, 1.0, 0); + Point3 P3 = Point3(-1.0, 1.0, 0); + Point3 P4 = Point3(1.0, -1.0, 0); + quad = createQuad(P1, P2, P3, P4); init_shader(); @@ -291,8 +333,6 @@ int main(int argc,const char *argv[]) atexit(program_exit_callback); sysUtilRegisterCallback(0,sysutil_exit_callback,NULL); - setDrawEnv(); - setRenderTarget(curr_fb); running = 1; while(running) { @@ -338,6 +378,6 @@ int main(int argc,const char *argv[]) done: printf("rsxtest done...\n"); DebugFont::shutdown(); - program_exit_callback(); + finish(); return 0; } diff --git a/samples/graphics/rsx_Basic_Wallpaper/source/rsxutil.cpp b/samples/graphics/rsx_Basic_Wallpaper/source/rsxutil.cpp index c1bf2877..63715949 100644 --- a/samples/graphics/rsx_Basic_Wallpaper/source/rsxutil.cpp +++ b/samples/graphics/rsx_Basic_Wallpaper/source/rsxutil.cpp @@ -5,53 +5,113 @@ #include #include +#include #include #include -#include "rsxutil.h" -#define GCM_LABEL_INDEX 255 +#include "rsxutil.h" videoResolution vResolution; -gcmContextData *context = NULL; u32 curr_fb = 0; -u32 first_fb = 1; u32 display_width; u32 display_height; u32 depth_pitch; u32 depth_offset; -u32 *depth_buffer; +void *depth_buffer; u32 color_pitch; u32 color_offset[FRAME_BUFFER_COUNT]; -u32 *color_buffer[FRAME_BUFFER_COUNT]; +void *color_buffer[FRAME_BUFFER_COUNT]; + +void *state_buffer; +u32 state_offset; f32 aspect_ratio; +u32 fbOnDisplay = 0; +u32 fbFlipped = 0; +bool fbOnFlip = false; +sys_event_queue_t flipEventQueue; +sys_event_port_t flipEventPort; + +gcmSurface surface; + +static u32 sLabelVal = 1; +static RSXDebugFontRenderer *fontRenderer; + static u32 sResolutionIds[] = { - VIDEO_RESOLUTION_1080, + VIDEO_RESOLUTION_1600x1080, + VIDEO_RESOLUTION_1440x1080, + VIDEO_RESOLUTION_1280x1080, + VIDEO_RESOLUTION_960x1080, VIDEO_RESOLUTION_720, VIDEO_RESOLUTION_480, VIDEO_RESOLUTION_576 }; static size_t RESOLUTION_ID_COUNT = sizeof(sResolutionIds)/sizeof(u32); -static u32 sLabelVal = 1; +extern "C" { +static void flipHandler(const u32 head) +{ + (void)head; + u32 v = fbFlipped; + + for (u32 i = fbOnDisplay; i != v; i=(i + 1)%FRAME_BUFFER_COUNT) { + *((vu32*) gcmGetLabelAddress(GCM_BUFFER_STATUS_INDEX + i)) = BUFFER_IDLE; + } + fbOnDisplay = v; + fbOnFlip = false; + + sysEventPortSend(flipEventPort, 0, 0, 0); +} -extern void dbg_printf(const char *fmt,...); +static void vblankHandler(const u32 head) +{ + (void)head; + u32 data; + u32 bufferToFlip; + u32 indexToFlip; + + data = *((vu32*) gcmGetLabelAddress(GCM_PREPARED_BUFFER_INDEX)); + bufferToFlip = (data >> 8); + indexToFlip = (data & 0x07); + + if (!fbOnFlip) { + if (bufferToFlip != fbOnDisplay) { + s32 ret = gcmSetFlipImmediate(indexToFlip); + if (ret != 0) { + printf("flip immediate failed\n"); + return; + } + fbFlipped = bufferToFlip; + fbOnFlip = true; + } + } +} +} -static RSXDebugFontRenderer* debugFontRenderer; +static void syncPPUGPU() +{ + vu32 *label = (vu32*) gcmGetLabelAddress(GCM_PREPARED_BUFFER_INDEX); + while(((curr_fb + FRAME_BUFFER_COUNT - ((*label)>>8))%FRAME_BUFFER_COUNT) > MAX_BUFFER_QUEUE_SIZE) { + sys_event_t event; -static void waitFinish() + sysEventQueueReceive(flipEventQueue, &event, 0); + sysEventQueueDrain(flipEventQueue); + } +} + +static void waitRSXFinish() { - rsxSetWriteBackendLabel(context,GCM_LABEL_INDEX,sLabelVal); + rsxSetWriteBackendLabel(gGcmContext,GCM_WAIT_LABEL_INDEX,sLabelVal); - rsxFlushBuffer(context); + rsxFlushBuffer(gGcmContext); - while(*(vu32*)gcmGetLabelAddress(GCM_LABEL_INDEX)!=sLabelVal) + while(*(vu32*)gcmGetLabelAddress(GCM_WAIT_LABEL_INDEX)!=sLabelVal) usleep(30); ++sLabelVal; @@ -59,12 +119,12 @@ static void waitFinish() static void waitRSXIdle() { - rsxSetWriteBackendLabel(context,GCM_LABEL_INDEX,sLabelVal); - rsxSetWaitLabel(context,GCM_LABEL_INDEX,sLabelVal); + rsxSetWriteBackendLabel(gGcmContext,GCM_WAIT_LABEL_INDEX,sLabelVal); + rsxSetWaitLabel(gGcmContext,GCM_WAIT_LABEL_INDEX,sLabelVal); ++sLabelVal; - waitFinish(); + waitRSXFinish(); } void initVideoConfiguration() @@ -91,7 +151,7 @@ void initVideoConfiguration() VIDEO_BUFFER_FORMAT_XRGB, VIDEO_ASPECT_AUTO, {0,0,0,0,0,0,0,0,0}, - (u32)vResolution.width*4 + gcmGetTiledPitchSize(vResolution.width*4) }; rval = videoConfigure(VIDEO_PRIMARY, &config, NULL, 0); @@ -120,89 +180,171 @@ void initVideoConfiguration() display_width = vResolution.width; } +void initFlipEvent() +{ + sys_event_queue_attr_t queueAttr = { SYS_EVENT_QUEUE_PRIO, SYS_EVENT_QUEUE_PPU, "\0" }; + + sysEventQueueCreate(&flipEventQueue, &queueAttr, SYS_EVENT_QUEUE_KEY_LOCAL, 32); + sysEventPortCreate(&flipEventPort, SYS_EVENT_PORT_LOCAL, SYS_EVENT_PORT_NO_NAME); + sysEventPortConnectLocal(flipEventPort, flipEventQueue); + + gcmSetFlipHandler(flipHandler); + gcmSetVBlankHandler(vblankHandler); +} + +void initRenderTarget() +{ + memset(&surface, 0, sizeof(gcmSurface)); + + surface.colorFormat = GCM_SURFACE_X8R8G8B8; + surface.colorTarget = GCM_SURFACE_TARGET_0; + surface.colorLocation[0] = GCM_LOCATION_RSX; + surface.colorOffset[0] = color_offset[curr_fb]; + surface.colorPitch[0] = color_pitch; + + for(u32 i=1; i< GCM_MAX_MRT_COUNT;i++) { + surface.colorLocation[i] = GCM_LOCATION_RSX; + surface.colorOffset[i] = color_offset[curr_fb]; + surface.colorPitch[i] = 64; + } + + surface.depthFormat = GCM_SURFACE_ZETA_Z24S8; + surface.depthLocation = GCM_LOCATION_RSX; + surface.depthOffset = depth_offset; + surface.depthPitch = depth_pitch; + + surface.type = GCM_SURFACE_TYPE_LINEAR; + surface.antiAlias = GCM_SURFACE_CENTER_1; + + surface.width = display_width; + surface.height = display_height; + surface.x = 0; + surface.y = 0; +} + void setRenderTarget(u32 index) { - gcmSurface sf; - - sf.colorFormat = GCM_SURFACE_X8R8G8B8; - sf.colorTarget = GCM_SURFACE_TARGET_0; - sf.colorLocation[0] = GCM_LOCATION_RSX; - sf.colorOffset[0] = color_offset[index]; - sf.colorPitch[0] = color_pitch; - - sf.colorLocation[1] = GCM_LOCATION_RSX; - sf.colorLocation[2] = GCM_LOCATION_RSX; - sf.colorLocation[3] = GCM_LOCATION_RSX; - sf.colorOffset[1] = 0; - sf.colorOffset[2] = 0; - sf.colorOffset[3] = 0; - sf.colorPitch[1] = 64; - sf.colorPitch[2] = 64; - sf.colorPitch[3] = 64; - - sf.depthFormat = GCM_SURFACE_ZETA_Z16; - sf.depthLocation = GCM_LOCATION_RSX; - sf.depthOffset = depth_offset; - sf.depthPitch = depth_pitch; - - sf.type = GCM_SURFACE_TYPE_LINEAR; - sf.antiAlias = GCM_SURFACE_CENTER_1; - - sf.width = display_width; - sf.height = display_height; - sf.x = 0; - sf.y = 0; - - rsxSetSurface(context,&sf); + surface.colorOffset[0] = color_offset[index]; + rsxSetSurface(gGcmContext,&surface); } -void init_screen(void *host_addr,u32 size) +void initDefaultStateCommands() +{ + rsxSetCurrentBuffer(nullptr, (u32*)state_buffer, HOST_STATE_CB_SIZE); + { + rsxSetBlendEnable(gGcmContext, GCM_FALSE); + rsxSetBlendFunc(gGcmContext, GCM_ONE, GCM_ZERO, GCM_ONE, GCM_ZERO); + rsxSetBlendEquation(gGcmContext, GCM_FUNC_ADD, GCM_FUNC_ADD); + rsxSetDepthWriteEnable(gGcmContext, GCM_TRUE); + rsxSetDepthFunc(gGcmContext, GCM_LESS); + rsxSetDepthTestEnable(gGcmContext, GCM_TRUE); + rsxSetClearDepthStencil(gGcmContext,0xffffff00); + rsxSetShadeModel(gGcmContext,GCM_SHADE_MODEL_SMOOTH); + rsxSetFrontFace(gGcmContext, GCM_FRONTFACE_CCW); + rsxSetClearReport(gGcmContext, GCM_ZPASS_PIXEL_CNT); + rsxSetZControl(gGcmContext, GCM_TRUE, GCM_FALSE, GCM_FALSE); + rsxSetZCullControl(gGcmContext, GCM_ZCULL_LESS, GCM_ZCULL_LONES); + rsxSetSCullControl(gGcmContext, GCM_SCULL_SFUNC_LESS, 1, 0xff); + rsxSetColorMaskMrt(gGcmContext, 0); + rsxSetColorMask(gGcmContext,GCM_COLOR_MASK_B | + GCM_COLOR_MASK_G | + GCM_COLOR_MASK_R | + GCM_COLOR_MASK_A); + rsxSetReturnCommand(gGcmContext); + } + rsxSetDefaultCommandBuffer(nullptr); +} + +void initScreen(u32 hostBufferSize) { u32 zs_depth = 4; u32 color_depth = 4; + u32 bufferSize = rsxAlign(HOST_ADDR_ALIGNMENT, (DEFAULT_CB_SIZE + HOST_STATE_CB_SIZE + hostBufferSize)); - rsxInit(&context,CB_SIZE,size,host_addr); + gcmInitDefaultFifoMode(GCM_DEFAULT_FIFO_MODE_CONDITIONAL); - initVideoConfiguration(); + void *hostAddr = memalign(HOST_ADDR_ALIGNMENT, bufferSize); + rsxInit(nullptr, DEFAULT_CB_SIZE, bufferSize, hostAddr); - waitRSXIdle(); + state_buffer = (void*)((intptr_t)hostAddr + DEFAULT_CB_SIZE); + rsxAddressToOffset(state_buffer, &state_offset); + printf("state_cmd: %p [%08x]\n", state_buffer, state_offset); - gcmSetFlipMode(GCM_FLIP_VSYNC); + initDefaultStateCommands(); + initVideoConfiguration(); - color_pitch = display_width*color_depth; - depth_pitch = display_width*zs_depth; + fontRenderer = new RSXDebugFontRenderer(gGcmContext); - for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { - color_buffer[i] = (u32*)rsxMemalign(64,(display_height*color_pitch)); - rsxAddressToOffset(color_buffer[i],&color_offset[i]); - gcmSetDisplayBuffer(i,color_offset[i],color_pitch,display_width,display_height); - } + waitRSXIdle(); - depth_buffer = (u32*)rsxMemalign(64,(display_height*depth_pitch)*2); - rsxAddressToOffset(depth_buffer,&depth_offset); + gcmSetFlipMode(GCM_FLIP_HSYNC); - debugFontRenderer = new RSXDebugFontRenderer(context); -} + color_pitch = gcmGetTiledPitchSize(display_width*color_depth); + depth_pitch = gcmGetTiledPitchSize(display_width*zs_depth); -void waitflip() -{ - while(gcmGetFlipStatus()!=0) - usleep(200); - gcmResetFlipStatus(); + u32 tileIndex = 0; + u32 bufferHeight = rsxAlign(GCM_TILE_LOCAL_ALIGN_HEIGHT, display_height); + u32 colorBufferSize = bufferHeight*color_pitch; + u32 depthBufferSize = bufferHeight*depth_pitch; + for (u32 i=0; i < FRAME_BUFFER_COUNT;i++, tileIndex++) { + bufferSize = rsxAlign(GCM_TILE_ALIGN_OFFSET, colorBufferSize); + color_buffer[i] = rsxMemalign(GCM_TILE_ALIGN_SIZE, bufferSize); + rsxAddressToOffset(color_buffer[i], &color_offset[i]); + gcmSetDisplayBuffer(i, color_offset[i], color_pitch, display_width, display_height); + gcmSetTileInfo(tileIndex, GCM_LOCATION_RSX, color_offset[i], bufferSize, color_pitch, GCM_COMPMODE_DISABLED, 0, 0); + gcmBindTile(tileIndex); + printf("fb[%d]: %p (%08x) [%dx%d] %d\n", i, color_buffer[i], color_offset[i], display_width, display_height, color_pitch); + } + + bufferSize = rsxAlign(GCM_TILE_ALIGN_OFFSET, depthBufferSize); + depth_buffer = rsxMemalign(GCM_TILE_ALIGN_SIZE, bufferSize); + rsxAddressToOffset(depth_buffer, &depth_offset); + gcmSetTileInfo(tileIndex, GCM_LOCATION_RSX, depth_offset, bufferSize, depth_pitch, GCM_COMPMODE_Z32_SEPSTENCIL, 0, 2); + gcmBindTile(tileIndex); + + gcmSetZcull(0, depth_offset, rsxAlign(64, display_width), rsxAlign(64, display_height), 0, GCM_ZCULL_Z24S8, GCM_SURFACE_CENTER_1, GCM_ZCULL_LESS, GCM_ZCULL_LONES, GCM_SCULL_SFUNC_LESS, 1, 0xff); + + for (u32 i=0;i < FRAME_BUFFER_COUNT;i++) { + *((vu32*) gcmGetLabelAddress(GCM_BUFFER_STATUS_INDEX + i)) = BUFFER_IDLE; + } + *((vu32*) gcmGetLabelAddress(GCM_PREPARED_BUFFER_INDEX)) = (fbOnDisplay << 8); + *((vu32*) gcmGetLabelAddress(GCM_BUFFER_STATUS_INDEX + fbOnDisplay)) = BUFFER_BUSY; + + curr_fb = (fbOnDisplay + 1)%FRAME_BUFFER_COUNT; + + initFlipEvent(); + initRenderTarget(); + + rsxSetWriteCommandLabel(gGcmContext, GCM_BUFFER_STATUS_INDEX + curr_fb, BUFFER_BUSY); } void flip() { - if(!first_fb) waitflip(); - else gcmResetFlipStatus(); + s32 qid = gcmSetPrepareFlip(gGcmContext, curr_fb); + while (qid < 0) { + usleep(100); + qid = gcmSetPrepareFlip(gGcmContext, curr_fb); + } + + rsxSetWriteBackendLabel(gGcmContext, GCM_PREPARED_BUFFER_INDEX, ((curr_fb << 8) | qid)); + rsxFlushBuffer(gGcmContext); + + syncPPUGPU(); - gcmSetFlip(context,curr_fb); - rsxFlushBuffer(context); + curr_fb = (curr_fb + 1)%FRAME_BUFFER_COUNT; - gcmSetWaitFlip(context); + rsxSetWaitLabel(gGcmContext, GCM_BUFFER_STATUS_INDEX + curr_fb, BUFFER_IDLE); + rsxSetWriteCommandLabel(gGcmContext, GCM_BUFFER_STATUS_INDEX + curr_fb, BUFFER_BUSY); - curr_fb ^= 1; - setRenderTarget(curr_fb); + setRenderTarget(curr_fb); +} + +void finish() +{ + rsxFinish(gGcmContext,1); - first_fb = 0; + u32 data = *((vu32*) gcmGetLabelAddress(GCM_PREPARED_BUFFER_INDEX)); + u32 lastBuffer = (data >> 8); + while (lastBuffer != fbOnDisplay) + usleep(100); }

    abmF+sF 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�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