Initial CALLHLL and Math.dll implementation

HLL libraries are compiled into the executable rather than dynamically
loaded as in early versions of System 4. These days AliceSoft does the
same thing.
This commit is contained in:
Nunuhara Cabbage
2019-11-04 20:35:42 -08:00
parent cb236965d8
commit edffc0ad76
21 changed files with 457 additions and 22 deletions
+2 -2
View File
@@ -448,8 +448,8 @@ static bool read_tag(struct ain_reader *r, struct ain *ain)
} else if (TAG_EQ("MSGF")) {
ain->msgf = read_int32(r);
} else if (TAG_EQ("HLL0")) {
int32_t count = read_int32(r);
ain->libraries = read_libraries(r, count);
ain->nr_libraries = read_int32(r);
ain->libraries = read_libraries(r, ain->nr_libraries);
} else if (TAG_EQ("SWI0")) {
int32_t count = read_int32(r);
ain->switches = read_switches(r, count);
+4 -2
View File
@@ -149,6 +149,7 @@ struct ain_hll_argument {
struct ain_hll_function {
char *name;
void (*fun)(void);
int32_t data_type;
int32_t nr_arguments;
struct ain_hll_argument *arguments;
@@ -188,14 +189,15 @@ struct ain {
uint8_t *code;
size_t code_size;
struct ain_function *functions;
struct ain_global *globals;
int32_t nr_globals;
struct ain_initval *global_initvals;
struct ain_global *globals;
int32_t nr_initvals;
struct ain_initval *global_initvals;
struct ain_struct *structures;
struct string **messages;
int32_t main;
int32_t msgf;
int32_t nr_libraries;
struct ain_library *libraries;
struct ain_switch *switches;
int32_t game_version;
+59
View File
@@ -0,0 +1,59 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* Credit to SLC for reverse engineering AIN formats.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#ifndef SYSTEM4_HLL_H
#define SYSTEM4_HLL_H
/*
* DSL for implementing for implementing libraries.
*/
#include "../vm.h"
#include "../page.h"
#define hll_defun(fname) \
static void _hllfun_ ## fname(void); \
static struct hll_function _hllstruct_ ## fname = { \
.name = #fname, \
.fun = _hllfun_ ## fname \
}; \
static void _hllfun_ ## fname(void)
#define hll_unimplemented(fname) \
hll_defun(fname) { \
ERROR("Unimplemented HLL function: " #fname); \
}
#define hll_export(fname) &_hllstruct_ ## fname
#define hll_deflib(lname, ...) \
static struct hll_function *_lib_ ## lname[]; \
struct library lib_ ## lname = { \
.name = #lname, \
.functions = _lib_ ## lname \
}; \
static struct hll_function *_lib_ ## lname[] =
#define hll_return(v) stack_push(vm_value_cast(v))
static inline union vm_value hll_arg(int n)
{
return stack[stack_ptr + n];
}
#endif /* SYSTEM4_HLL_H */
+136
View File
@@ -0,0 +1,136 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* Credit to SLC for reverse engineering AIN formats.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <math.h>
#include "../system4.h"
#include "hll.h"
static inline float deg2rad(float deg)
{
return deg * (M_PI / 180.0);
}
hll_defun(Cos) {
hll_return(cosf(deg2rad(hll_arg(0).f)));
}
hll_defun(Sin) {
hll_return(sinf(deg2rad(hll_arg(0).f)));
}
hll_defun(Sqrt) {
hll_return(sqrtf(hll_arg(0).f));
}
hll_defun(Atan) {
hll_return(atanf(hll_arg(0).f));
}
hll_defun(Atan2) {
hll_return(atan2f(hll_arg(0).f, hll_arg(1).f));
}
hll_defun(Abs) {
hll_return(abs(hll_arg(0).i));
}
hll_defun(AbsF) {
hll_return(fabsf(hll_arg(0).f));
}
hll_defun(Pow) {
hll_return(powf(hll_arg(0).f, hll_arg(1).f));
}
hll_defun(SetSeed) {
srand(hll_arg(0).i);
hll_return(0);
}
hll_unimplemented(SetRandMode)
hll_defun(Rand) {
hll_return(rand());
}
// TODO
hll_unimplemented(RandF)
hll_unimplemented(RandTableInit)
hll_unimplemented(RandTable)
hll_unimplemented(RandTable2Init)
hll_unimplemented(RandTable2)
hll_defun(Min) {
int a = hll_arg(0).i, b = hll_arg(1).i;
hll_return(a < b ? a : b);
}
hll_defun(MinF) {
float a = hll_arg(0).f, b = hll_arg(1).f;
hll_return(a < b ? a : b);
}
hll_defun(Max) {
int a = hll_arg(0).i, b = hll_arg(1).i;
hll_return(a > b ? a : b);
}
hll_defun(MaxF) {
float a = hll_arg(0).f, b = hll_arg(1).f;
hll_return(a > b ? a : b);
}
hll_defun(Swap) {
int tmp = *hll_arg(0).iref;
*hll_arg(0).iref = *hll_arg(1).iref;
*hll_arg(1).iref = tmp;
}
hll_defun(SwapF) {
float tmp = *hll_arg(0).fref;
*hll_arg(0).fref = *hll_arg(1).fref;
*hll_arg(1).fref = tmp;
}
hll_deflib(Math) {
hll_export(Cos),
hll_export(Sin),
hll_export(Sqrt),
hll_export(Atan),
hll_export(Atan2),
hll_export(Abs),
hll_export(AbsF),
hll_export(Pow),
hll_export(SetSeed),
hll_export(SetRandMode),
hll_export(Rand),
hll_export(RandF),
hll_export(RandTableInit),
hll_export(RandTable),
hll_export(RandTable2Init),
hll_export(RandTable2),
hll_export(Min),
hll_export(MinF),
hll_export(Max),
hll_export(MaxF),
hll_export(Swap),
hll_export(SwapF),
NULL
};
+1 -1
View File
@@ -136,7 +136,7 @@ const struct instruction instructions[NR_OPCODES] = {
OP(C_REF, 0),
OP(C_ASSIGN, 0),
OP(MSG, 0), // TODO
OP(CALLHLL, 0), // TODO
OP(CALLHLL, 2, INSTR_INT, INSTR_INT),
OP(PUSHSTRUCTPAGE, 0),
JMP(CALLMETHOD, 1, INSTR_FUN),
OP(SH_GLOBALREF, 1, INSTR_INT),
+5 -2
View File
@@ -1,4 +1,7 @@
project('system4', 'c', default_options : ['c_std=c11'])
system4 = ['system4.c', 'ain.c', 'instructions.c', 'vm.c', 'string.c', 'page.c', 'utfsjis.c']
system4 = ['system4.c', 'ain.c', 'instructions.c', 'vm.c', 'string.c', 'page.c', 'utfsjis.c', 'hll/math.c']
zdep = dependency('zlib')
executable('system4', system4, dependencies: zdep)
mdep = meson.get_compiler('c').find_library('m', required: false)
executable('system4', system4, dependencies: [mdep, zdep])
+11
View File
@@ -59,8 +59,19 @@ struct page {
union vm_value values[];
};
static inline union vm_value page_get(int pageno, int varno)
{
return heap[pageno].page->values[varno];
}
static inline void page_set(int pageno, int varno, union vm_value v)
{
heap[pageno].page->values[varno] = v;
}
// variables
union vm_value variable_initval(enum ain_data_type type);
void variable_fini(union vm_value v, enum ain_data_type type);
enum ain_data_type variable_type(struct page *page, int varno, enum ain_data_type *struct_type);
// pages
+2 -1
View File
@@ -1,4 +1,5 @@
OBJ/
Run/
Source/System/Etc/DLL/Math.dll
Source/System/System42/System40.exe
Source/System/System42/DLL/Sys42VM.dll
Source/System/System42/DLL/Sys42VM.dll
+12
View File
@@ -0,0 +1,12 @@
SystemSource = {
"HLL\Math.hll", "Math",
}
CopyToRun = {
"SACT_DLL.inc",
}
CopyToDLL = {
"DLL\Math.dll",
}
+50
View File
@@ -0,0 +1,50 @@
////////////////////////////////////////////////////////////
// Math.hll
////////////////////////////////////////////////////////////
// 数学
float Cos(float fDeg); // コサイン(角度)
float Sin(float fDeg); // サイン(角度)
float Sqrt(float fValue); // 平方根
float Atan(float x); // アークタンジェント
float Atan2(float y, float x); // アークタンジェント y/x
int Abs(int nValue); // 絶対値を得る
float AbsF(float fValue); // 絶対値float
float Pow(float fBase, float fExponent); // fBaseのfExponet乗を返します
////////////////////////////////////////////////////////////
// 乱数
void SetSeed(int nSeed); // 乱数の種セット
void SetRandMode(int nMode); // 乱数のモードをセット(0:標準(default) 1:正規分布 2:1/f分布)
int Rand(void); // 0~0x7FFFFFFFの範囲で乱数生成
float RandF(void); // 0~1の範囲で乱数生成
// テーブルを作成して、その中身をシャッフルすることで
// ランダムを取り出す関数、使う前に、SetSeedは必要。
void RandTableInit(int nNum, int nSize); // nSize個のランダムテーブルを作成、0~nSize-1 までの数値が入る
int RandTable(int nNum); // テーブルの数値を取得 nNumはテーブル番号
// ランダムテーブル、VM内の配列を使う版 ※配列の要素数が、そのままテーブルの大きさになります。
// pArrayはテーブル配列を指定(Allocはスクリプトで確保すること)
void RandTable2Init(int nNum, array@int pArray);
// テーブルの数値を取得 nNumはテーブル番号
int RandTable2(int nNum);
////////////////////////////////////////////////////////////
// その他
int Min(int x, int y);
float MinF(float x, float y);
int Max(int x, int y);
float MaxF(float x, float y);
void Swap(intp x, intp y); // 入れ替え
void SwapF(floatp x, floatp y);
+3
View File
@@ -0,0 +1,3 @@
LoadDLL = {
"Math.dll",
}
+18
View File
@@ -0,0 +1,18 @@
Note
----
I am not 100% sure about the legality of distributing System40.exe and related
DLLs here, so I've left them out. They can be found in the System 4 SDK, which
which you'll want to have anyways to compile these tests.
Once you've copied the files, this directory should contain the following:
Etc/DLL/Math.dll (not included)
Etc/HLL/Math.hll
Etc/Etc.inc
Etc/SACT_DLL.inc
System42/DLL/Sys42VM.dll (not included)
System42/System40.exe (not included)
System42/System40.ini
System42/System42.inc
System.inc
+1
View File
@@ -1,3 +1,4 @@
Source = {
"System42\System42.inc",
"Etc\Etc.inc",
}
-12
View File
@@ -1,12 +0,0 @@
Note
----
I am not 100% sure about the legality of distributing System40.exe and
Sys42VM.dll here, so I've left them out. They can be found in the System 4 SDK,
which you'll want to have anyways to compile these tests.
Once you've copied the files, this directory should contain the following:
System40.exe
System40.ini
DLL\Sys42VM.dll
+5
View File
@@ -26,6 +26,11 @@ SaveFolder = "SaveData"
; DLLフォルダ設定
DLL = "DLL"
; DLLファイル設定
LoadDLL = {
"SACT_DLL.inc",
}
; スキップ名設定
MsgSkipName = "既読スキップ"
+1 -1
View File
@@ -1,7 +1,7 @@
CopyToRun = {
"System40.exe",
"System40.ini"
"System40.ini",
}
CopyToDLL = {
+15
View File
@@ -31,6 +31,17 @@ void test_equal(string msg, int actual, int expected)
}
}
void test_float(string msg, float actual, float expected)
{
float d = actual - expected;
if (d > 0.001 || d < -0.001) {
system.Output("FAIL: " + msg + " (expected " + string(expected) + "; got " + string(actual) + ")\n");
tests_failed++;
} else {
tests_passed++;
}
}
void test_bool(string msg, bool actual, bool expected)
{
if (actual != expected) {
@@ -73,6 +84,10 @@ int main(void)
test_arrays();
test_end("arrays");
test_start("Math.dll");
test_math();
test_end("Math.dll");
if (total_failed > 0) {
system.Output(string(total_failed) + " tests failed.\n");
} else {
+32
View File
@@ -0,0 +1,32 @@
// -*-mode: C; coding: sjis; -*-
void test_math(void)
{
int ia, ib;
float fa, fb;
test_float("Math.Cos(40)", Math.Cos(40), 0.766044);
test_float("Math.Sin(40)", Math.Sin(40), 0.642788);
test_float("Math.Atan(40)", Math.Atan(40), 1.545802);
test_float("Math.Atan2(20, 40)", Math.Atan2(20, 40), 0.463648);
test_float("Math.Pow(0.5, 1.5)", Math.Pow(0.5, 1.5), 0.353553);
test_equal("Math.Abs(-3)", Math.Abs(-3), 3);
test_float("Math.AbsF(-3.5)", Math.Abs(-3.5), 3.0); // XXX: why not 3.5?
test_equal("Math.Min(1, 2)", Math.Min(1, 2), 1);
test_equal("Math.Min(2, 1)", Math.Min(2, 1), 1);
test_float("Math.MinF(1.0, 2.0)", Math.MinF(1.0, 2.0), 1.0);
test_float("Math.MinF(2.0, 1.0)", Math.MinF(2.0, 1.0), 1.0);
test_equal("Math.Max(1, 2)", Math.Max(1, 2), 2);
test_equal("Math.Max(2, 1)", Math.Max(2, 1), 2);
test_float("Math.MaxF(1.0, 2.0)", Math.MaxF(1.0, 2.0), 2.0);
test_float("Math.MaxF(2.0, 1.0)", Math.MaxF(2.0, 1.0), 2.0);
ia = 1, ib = 2;
Math.Swap(ia, ib);
test_equal("Math.Swap(a, b), a", ia, 2);
test_equal("Math.Swap(a, b), b", ib, 1);
fa = 1.0, fb = 2.0;
Math.SwapF(fa, fb);
test_float("Math.SwapF(a, b), a", fa, 2.0);
test_float("Math.SwapF(a, b), b", fb, 1.0);
}
+2 -1
View File
@@ -4,6 +4,7 @@ Source = {
"arithmetic.jaf",
"strings.jaf",
"structs.jaf",
"arrays.jaf"
"arrays.jaf",
"math.jaf"
}
+85
View File
@@ -305,6 +305,49 @@ void vm_call(int fno, int struct_page)
instr_ptr = saved_ip;
}
static void hll_call(int libno, int fno)
{
struct ain_hll_function *f = &ain->libraries[libno].functions[fno];
if (!f->fun)
EXECUTION_ERROR("Unimplemented HLL function: %s", f->name);
// convert non-heap ref types to pointers
for (int i = f->nr_arguments - 1; i >= 0; i--) {
switch(f->arguments[i].data_type) {
case AIN_REF_INT:
case AIN_REF_BOOL:
case AIN_REF_FLOAT:
stack_ptr -= 2;
break;
default:
stack_ptr--;
}
}
for (int i = 0, src = 0; i < f->nr_arguments; i++, src++) {
int pageno, varno;
switch (f->arguments[i].data_type) {
case AIN_REF_INT:
case AIN_REF_BOOL:
pageno = stack[stack_ptr+src].i;
varno = stack[stack_ptr+src+1].i;
stack[stack_ptr+i].iref = &heap[pageno].page->values[varno].i;
src++;
break;
case AIN_REF_FLOAT:
pageno = stack[stack_ptr+src].i;
varno = stack[stack_ptr+src+1].i;
stack[stack_ptr+i].fref = &heap[pageno].page->values[varno].f;
src++;
break;
}
}
f->fun();
for (int i = 0; i < f->nr_arguments; i++) {
variable_fini(stack[stack_ptr + i], f->arguments[i].data_type);
}
}
static void function_return(void)
{
call_stack_ptr--;
@@ -522,6 +565,9 @@ static void execute_instruction(int16_t opcode)
case CALLMETHOD:
method_call(get_argument(0), instr_ptr + instruction_width(CALLMETHOD));
break;
case CALLHLL:
hll_call(get_argument(0), get_argument(1));
break;
case RETURN:
function_return();
break;
@@ -1028,6 +1074,44 @@ static void vm_execute(void)
}
}
extern struct library lib_Math;
struct library *libraries[] = {
&lib_Math,
NULL
};
static void link_library(struct ain_library *ainlib, struct library *lib)
{
for (int i = 0; i < ainlib->nr_functions; i++) {
bool linked = false;
for (int j = 0; lib->functions[j]; j++) {
if (!strcmp(ainlib->functions[i].name, lib->functions[j]->name)) {
ainlib->functions[i].fun = lib->functions[j]->fun;
linked = true;
break;
}
}
if (!linked)
WARNING("Unimplemented library function: %s", ainlib->functions[i].name);
}
}
static void link_libraries(void)
{
for (int i = 0; i < ain->nr_libraries; i++) {
bool linked = false;
for (int j = 0; libraries[j]; j++) {
if (!strcmp(ain->libraries[i].name, libraries[j]->name)) {
link_library(&ain->libraries[i], libraries[j]);
linked = true;
break;
}
}
if (!linked)
WARNING("Unimplemented library: %s", ain->libraries[i].name);
}
}
void vm_execute_ain(struct ain *program)
{
@@ -1077,6 +1161,7 @@ void vm_execute_ain(struct ain *program)
}
}
link_libraries();
vm_call(ain->main, -1);
}
+13
View File
@@ -25,6 +25,9 @@ union vm_value {
int32_t i;
int64_t i64;
float f;
// HLL only
int *iref;
float *fref;
};
enum vm_pointer_type {
@@ -48,6 +51,16 @@ struct vm_pointer {
};
};
struct hll_function {
char *name;
void (*fun)(void);
};
struct library {
char *name;
struct hll_function **functions;
};
struct ain;
struct vm_pointer *heap;