diff --git a/SMILEBASIC/TEST.txt b/SMILEBASIC/TEST.txt index 246946c..203a13e 100644 --- a/SMILEBASIC/TEST.txt +++ b/SMILEBASIC/TEST.txt @@ -1,3 +1,8 @@ +ASSERT__ SUBST$("0123456789",0,"B")=="B","SUBST$" +ASSERT__ SUBST$("0123456789",0,4,"B")=="B456789","SUBST$" +ASSERT__ SUBST$("",0,4,"B")=="B","SUBST$" +WHILE 1 +WEND FOR I=0TO 511'-500 SPSET I,I'304'-I+304 SPOFS I, (I*16) MOD 400, FLOOR((I*16)/400)*16 diff --git a/SMILEBASIC/VM.d b/SMILEBASIC/VM.d index 444f832..80b7064 100644 --- a/SMILEBASIC/VM.d +++ b/SMILEBASIC/VM.d @@ -49,6 +49,10 @@ class VM this.functions = functions; this.globalDataTable = gdt; } + Code getCurrent() + { + return code[pc]; + } void run() { bp = 0;//globalを実行なのでbaseは0(グローバル変数をスタックに取るようにしない限り)(挙動的にスタックに確保していなさそう) @@ -110,6 +114,38 @@ class VM { pc = code.length; } + void dump() + { + foreach(i, c; code) + writefln("%04X:%s", i, c.toString(this)); + } + wstring getGlobalVarName(int index) + { + foreach(k, v; globalTable) + { + if(v.index == index) return k; + } + return "undefined variable"; + } + Value readData() + { + Value value; + this.globalDataTable.read(value, this); + return value; + } + void restoreData(wstring label) + { + this.globalDataTable.dataIndex = this.globalDataTable.label[label]; + } + int olddti; + void pushDataIndex() + { + olddti = this.globalDataTable.dataIndex; + } + void popDataIndex() + { + this.globalDataTable.dataIndex = olddti; + } } enum CodeType { @@ -135,6 +171,10 @@ abstract class Code { CodeType type; abstract void execute(VM vm); + string toString(VM vm) + { + return super.toString(); + } } class PrintCode : Code { @@ -174,6 +214,10 @@ class PrintCode : Code } stdout.flush(); } + override string toString(VM vm) + { + return "print"; + } } /* * スタックにPush @@ -190,6 +234,10 @@ class Push : Code { vm.push(imm); } + override string toString(VM vm) + { + return "push " ~ imm.toString; + } } class PushG : Code @@ -204,6 +252,10 @@ class PushG : Code { vm.push(vm.global[var]); } + override string toString(VM vm) + { + return "pushglobal " ~ vm.getGlobalVarName(var).to!string; + } } class PopG : Code { @@ -239,6 +291,10 @@ class PopG : Code } vm.global[var] = v; } + override string toString(VM vm) + { + return "popglobal " ~ vm.getGlobalVarName(var).to!string; + } } class PushL : Code { @@ -252,6 +308,10 @@ class PushL : Code { vm.push(vm.stack[vm.bp + var]); } + override string toString(VM vm) + { + return "pushlocal " ~ var.to!string; + } } class PopL : Code { @@ -287,6 +347,10 @@ class PopL : Code } vm.stack[vm.bp + var] = v; } + override string toString(VM vm) + { + return "poplocal " ~ var.to!string; + } } class Operate : Code { @@ -479,6 +543,10 @@ class Operate : Code l.doubleValue = ld; vm.push(l); } + override string toString(VM vm) + { + return "operate " ~ operator.to!string; + } } class GotoAddr : Code { @@ -492,6 +560,10 @@ class GotoAddr : Code { vm.pc = address - 1; } + override string toString(VM vm) + { + return "goto " ~ address.to!string(16); + } } class GotoS : Code { @@ -523,6 +595,10 @@ class GotoTrue : Code if(cond.boolValue) vm.pc = address - 1; } + override string toString(VM vm) + { + return "gototrue " ~ address.to!string(16); + } } class GotoFalse : Code { @@ -539,6 +615,10 @@ class GotoFalse : Code if(!cond.boolValue) vm.pc = address - 1; } + override string toString(VM vm) + { + return "gotofalse " ~ address.to!string(16); + } } class GosubAddr : Code { @@ -553,6 +633,10 @@ class GosubAddr : Code vm.push(Value(vm.pc)); vm.pc = address - 1; } + override string toString(VM vm) + { + return "gosub " ~ address.to!string(16); + } } class GosubS : Code { @@ -591,6 +675,10 @@ class ReturnSubroutine : Code } vm.pc = pc.integerValue; } + override string toString(VM vm) + { + return "returnsubroutine "; + } } class EndVM : Code { @@ -601,6 +689,10 @@ class EndVM : Code { vm.end(); } + override string toString(VM vm) + { + return "endvm"; + } } class NewArray : Code { @@ -652,6 +744,10 @@ class NewArray : Code } vm.push(array); } + override string toString(VM vm) + { + return "newarray " ~ dim.to!string; + } } class PushArray : Code { @@ -712,6 +808,10 @@ class PushArray : Code } throw new TypeMismatch(); } + override string toString(VM vm) + { + return "pusharray " ~ dim.to!string; + } } class PopArray : Code { @@ -787,6 +887,10 @@ class PopArray : Code } throw new TypeMismatch(); } + override string toString(VM vm) + { + return "poparray " ~ dim.to!string ~ ", " ~ var.to!string ~ ", " ~ local.to!string; + } } class ReturnFunction : Code { @@ -823,6 +927,10 @@ class ReturnFunction : Code vm.pc = pc.integerValue; vm.bp = bp.integerValue; } + override string toString(VM vm) + { + return "returnfunc " ~ func.name.to!string; + } } class CallFunctionCode : Code { @@ -846,15 +954,15 @@ class CallFunctionCode : Code Function func = vm.functions.get(name, null); if(!func) { - throw new SyntaxError(); + throw new SyntaxError(name); } if(func.argCount != this.argCount) { - throw new IllegalFunctionCall(); + throw new IllegalFunctionCall(name.to!string); } if(func.outArgCount != this.outArgCount) { - throw new IllegalFunctionCall(); + throw new IllegalFunctionCall(name.to!string); } //TODO:args auto bp = vm.stacki; @@ -871,6 +979,10 @@ class CallFunctionCode : Code } } } + override string toString(VM vm) + { + return "callfunc " ~ name.to!string; + } } import otya.smilebasic.builtinfunctions; class CallBuiltinFunction : Code @@ -914,6 +1026,10 @@ class CallBuiltinFunction : Code vm.push(result[i]); } } + override string toString(VM vm) + { + return "callbuiltin " ~ func.name.to!string; + } } class IncCodeG : Code { @@ -952,6 +1068,10 @@ class IncCodeG : Code vm.global[var] = Value(l ~ r); } } + override string toString(VM vm) + { + return "incglobal " ~ var.to!string; + } } class IncCodeL : Code { @@ -990,6 +1110,10 @@ class IncCodeL : Code *g = Value(l ~ r); } } + override string toString(VM vm) + { + return "inclocal " ~ var.to!string; + } } class OnBase : Code { @@ -1043,6 +1167,10 @@ class OnGoto : OnBase if(index < 0) return; vm.pc = index - 1; } + override string toString(VM vm) + { + return "ongoto " ~ labels.to!string; + } } class OnGosub : OnBase { @@ -1057,6 +1185,10 @@ class OnGosub : OnBase vm.push(Value(vm.pc)); vm.pc = index - 1; } + override string toString(VM vm) + { + return "ongosub " ~ labels.to!string; + } } import std.string; class InputCode : Code @@ -1141,6 +1273,10 @@ class InputCode : Code } } while(error); } + override string toString(VM vm) + { + return "input " ~ count.to!string; + } } class ReadCode : Code { @@ -1158,6 +1294,10 @@ class ReadCode : Code vm.push(data); } } + override string toString(VM vm) + { + return "read " ~ count.to!string; + } } class RestoreCodeS : Code { @@ -1185,6 +1325,10 @@ class RestoreCode : Code { datatable.dataIndex = label; } + override string toString(VM vm) + { + return "restore " ~ label.to!string; + } } class PushSystemVariable : Code { @@ -1197,6 +1341,10 @@ class PushSystemVariable : Code { vm.push(var.value); } + override string toString(VM vm) + { + return "pushsysvar " ~ var.to!string; + } } class PopSystemVariable : Code { @@ -1211,4 +1359,8 @@ class PopSystemVariable : Code vm.pop(v); var.value = v; } + override string toString(VM vm) + { + return "popsysvar " ~ var.to!string; + } } diff --git a/SMILEBASIC/builtinfunctions.d b/SMILEBASIC/builtinfunctions.d index e133e37..fb1ac05 100644 --- a/SMILEBASIC/builtinfunctions.d +++ b/SMILEBASIC/builtinfunctions.d @@ -11,6 +11,7 @@ import otya.smilebasic.error; import otya.smilebasic.type; import otya.smilebasic.petitcomputer; import otya.smilebasic.sprite; +import otya.smilebasic.vm; //プチコンの引数省略は特殊なので //LOCATE ,,0のように省略できる struct DefaultValue(T, bool skippable = true) @@ -52,14 +53,16 @@ class BuiltinFunction void function(PetitComputer, Value[], Value[]) func; int startskip; bool variadic; + string name; this(BuiltinFunctionArgument[] argments, ValueType result, void function(PetitComputer, Value[], Value[]) func, int startskip, - bool variadic) + bool variadic, string name) { this.argments = argments; this.result = result; this.func = func; this.startskip = startskip; this.variadic = variadic; + this.name = name; } bool hasSkipArgument() { @@ -99,6 +102,11 @@ class BuiltinFunction time.setDefaultValue(1); p.vsync(cast(int)time); } + static void WAIT(PetitComputer p, DefaultValue!int time) + { + time.setDefaultValue(1); + p.vsync(cast(int)time); + } //TODO:プチコンのCLSには引数の個数制限がない static void CLS(PetitComputer p/*vaarg*/) { @@ -121,9 +129,13 @@ class BuiltinFunction } static void XSCREEN(PetitComputer p, int mode, DefaultValue!(int, false) a, DefaultValue!(int, false) b) { + a.setDefaultValue(512); + b.setDefaultValue(512); + p.xscreen(mode, cast(int)a, cast(int)b); } - static void DISPLAY(PetitComputer p, DefaultValue!(int, false) display) + static void DISPLAY(PetitComputer p, int display) { + p.display(display); } static void GCLS(PetitComputer p, DefaultValue!(int, false) color) { @@ -157,6 +169,11 @@ class BuiltinFunction static void GPRIO(PetitComputer p, int z) { } + static void GPAGE(PetitComputer p, int showPage, int usePage) + { + p.showGRP = showPage; + p.useGRP = usePage; + } static void BGMPLAY(PetitComputer p, int music) { } @@ -181,7 +198,7 @@ class BuiltinFunction static int RND(int max) { import std.random; - return uniform(0, max - 1); + return uniform(0, max - 1 + 1); } static void DTREAD(DefaultValue!(wstring, false) date, out int Y, out int M, out int D/*W*/) { @@ -207,12 +224,19 @@ class BuiltinFunction } static double VAL(wstring str) { - if(str[0..2] == "&H") + try { - return str[2..$].to!int(16); + if(str[0..2] == "&H") + { + return str[2..$].to!int(16); + } + double val = str.to!double; + return val; + } + catch(Exception e) + { + return 0;//toriaezu } - double val = str.to!double; - return val; } static double FLOOR(double val) { @@ -220,9 +244,63 @@ class BuiltinFunction } static wstring MID(wstring str, int i, int len) { + if(i + len > str.length) + { + return "";//範囲外で空文字 + } //挙動未定 return str[i..i + len]; } + //INSTRSUSBTLEFT + static wstring LEFT(wstring str, int len) + { + return str[0..len]; + } + static wstring SUBST(wstring str, int i, Value alen, DefaultValue!(Value,false) areplace) + { + int len = 1; + wstring replace = ""; + if(alen.isNumber) + { + len = alen.castInteger; + replace = areplace.castString; + } + else + { + replace = alen.castString; + //省略されたらi以降の全文字を置換 + return str[0..i] ~ replace; + } + if(str.length <= i + len) + { + return str[0..i] ~ replace; + } + str.replaceInPlace(i, i + len, replace); + return str; + } + static int INSTR(Value vstart, Value vstr1, DefaultValue!(wstring, false) vstr2) + { + import std.string; + int start = 0; + wstring str1, str2; + if(!vstr2.isDefault) + { + start = vstart.castInteger; + str1 = vstr1.castString; + str2 = cast(wstring)vstr2; + } + else + { + str1 = vstart.castString; + str2 = vstr1.castString; + } + int aaa = str1[start..$].indexOf(str2, CaseSensitive.no); + return str1[start..$].indexOf(str2, CaseSensitive.no); + } + static int ASC(wstring str) + { + return cast(int)str[0]; + } static void SPSET(PetitComputer p, int id, int defno) { p.sprite.spset(id, defno); @@ -241,7 +319,7 @@ class BuiltinFunction } static void SPANIM(PetitComputer p, Value[] va_args) { - writeln("NOTIMPL:SPANIM"); + //TODO:配列 auto args = retro(va_args); int no = args[0].castInteger; double[] animdata = new double[args.length - 2]; @@ -255,6 +333,79 @@ class BuiltinFunction if(args[1].isNumber) p.sprite.spanim(no, cast(SpriteAnimTarget)(args[1].castInteger), animdata); } + static void SPDEF(PetitComputer p, Value[] va_args) + { + switch(va_args.length) + { + case 1://array + { + if(va_args[0].isNumberArray) + { + writeln("NOTIMPL:SPDEF ARRAY"); + //return; + } + if(va_args[0].isString) + { + VM vm = p.vm; + vm.pushDataIndex(); + vm.restoreData(va_args[0].castString); + auto count = vm.readData().castInteger;//読み込むスプライト数 + int defno = 0;//? + for(int i = 0; i < count; i++) + { + int U = vm.readData().castInteger; + int V = vm.readData().castInteger; + int W = vm.readData().castInteger; + int H = vm.readData().castInteger; + int HX = vm.readData().castInteger; + int HY = vm.readData().castInteger; + int ATTR = vm.readData().castInteger; + p.sprite.SPDEFTable[defno] = SpriteDef(U, V, W, H, HX, HY, cast(SpriteAttr)ATTR); + defno++; + } + vm.popDataIndex(); + return; + } + throw new IllegalFunctionCall("SPDEF"); + return; + } + default: + } + { + int defno = va_args[0].castInteger; + int U = va_args[1].castInteger; + int V = va_args[2].castInteger; + int W = 16, H = 16, HX = 0, HY = 0, ATTR = 1; + if(va_args.length > 3) + { + W = va_args[3].castInteger; + } + if(va_args.length > 4) + { + H = va_args[4].castInteger; + } + if(va_args.length > 5) + { + HX = va_args[5].castInteger; + } + if(va_args.length > 6) + { + HY = va_args[6].castInteger; + } + if(va_args.length > 7) + { + ATTR = va_args[7].castInteger; + } + p.sprite.SPDEFTable[defno] = SpriteDef(U, V, W, H, HX, HY, cast(SpriteAttr)ATTR); + } + } + static void SPCLR(PetitComputer p, DefaultValue!(int, false) i) + { + if(i.isDefault) + p.sprite.spclr(); + else + p.sprite.spclr(cast(int)i); + } static void BGMSTOP(PetitComputer p) { writeln("NOTIMPL:BGMSTOP"); @@ -341,6 +492,7 @@ class BuiltinFunction { return sqrt(a1); } + //GalateaTalk利用面倒くさい... static void TALK(wstring a1) { } @@ -365,6 +517,7 @@ class BuiltinFunction mixin(AddFunc!(BuiltinFunction, name)), GetStartSkip!(BuiltinFunction, name), IsVariadic!(BuiltinFunction, name), + name, ); writeln(AddFunc!(BuiltinFunction, name)); } @@ -384,6 +537,14 @@ template GetStartSkip(T, string N) { enum SkipSkip = I - is(P[0] : PetitComputer); } + else static if(is(P[I] == DefaultValue!(wstring, false))) + { + enum SkipSkip = I - is(P[0] : PetitComputer); + } + else static if(is(P[I] == DefaultValue!(Value, false))) + { + enum SkipSkip = I - is(P[0] : PetitComputer); + } else { enum SkipSkip = SkipSkip!(I + 1, P); @@ -419,7 +580,7 @@ template AddFunc(T, string N) { static if(is(ReturnType!(__traits(getMember, T, N)) == double) || is(ReturnType!(__traits(getMember, T, N)) == int)) { - const string AddFunc = "function void(PetitComputer p, Value[] arg, Value[] ret){if(ret.length != 1){throw new IllegalFunctionCall();}ret[0] = Value(" ~ N ~ "(" ~ + const string AddFunc = "function void(PetitComputer p, Value[] arg, Value[] ret){if(ret.length != 1){throw new IllegalFunctionCall(\"" ~ N ~ "\");}ret[0] = Value(" ~ N ~ "(" ~ AddFuncArg!(/*ParameterTypeTuple!(__traits(getMember, T, N)).length*/GetArgumentCount!(T,N) - 1, 0, 0, 0, T, N , ParameterTypeTuple!(__traits(getMember, T, N))) ~ "));}"; } @@ -427,13 +588,13 @@ template AddFunc(T, string N) { pragma(msg, GetArgumentCount!(T,N)); - const string AddFunc = "function void(PetitComputer p, Value[] arg, Value[] ret){/*if(ret.length != 0){throw new IllegalFunctionCall();}*/" ~ OutArgsInit!(T,N) ~ N ~ "(" ~ + const string AddFunc = "function void(PetitComputer p, Value[] arg, Value[] ret){/*if(ret.length != 0){throw new IllegalFunctionCall(\"" ~ N ~ "\");}*/" ~ OutArgsInit!(T,N) ~ N ~ "(" ~ AddFuncArg!(/*ParameterTypeTuple!(__traits(getMember, T, N)).length*/GetArgumentCount!(T,N) - 1, 0, 0, 0, T, N, ParameterTypeTuple!(__traits(getMember, T, N))) ~ ");}"; } else static if(is(ReturnType!(__traits(getMember, T, N)) == wstring)) { - const string AddFunc = "function void(PetitComputer p, Value[] arg, Value[] ret){if(ret.length != 1){throw new IllegalFunctionCall();}ret[0] = Value(" ~ N ~ "(" ~ + const string AddFunc = "function void(PetitComputer p, Value[] arg, Value[] ret){if(ret.length != 1){throw new IllegalFunctionCall(\"" ~ N ~ "\");}ret[0] = Value(" ~ N ~ "(" ~ AddFuncArg!(/*ParameterTypeTuple!(__traits(getMember, T, N)).length*/GetArgumentCount!(T,N) - 1, 0, 0, 0, T, N , ParameterTypeTuple!(__traits(getMember, T, N))) ~ "));}"; } @@ -471,6 +632,20 @@ DefaultValue!(wstring, false) fromStringToSkip(Value v) else return DefaultValue!(wstring, false)(true); } +DefaultValue!Value fromValueToDefault(Value v) +{ + if(v.type != ValueType.Void) + return DefaultValue!Value(v); + else + return DefaultValue!Value(true); +} +DefaultValue!(Value, false) fromValueToSkip(Value v) +{ + if(v.type != ValueType.Void) + return DefaultValue!(Value, false)(v); + else + return DefaultValue!(Value, false)(true); +} template GetFunctionParamType(T, string N) { enum GetFunctionParamType = mixin("[" ~ Array!(ParameterTypeTuple!(__traits(getMember, T, N))) ~ "]"); @@ -515,6 +690,14 @@ template GetFunctionParamType(T, string N) { const string arg = ""; } + else static if(is(P[0] == DefaultValue!(Value)) || is(P[0] == Value)) + { + const string arg = "ValueType.Void, false"; + } + else static if(is(P[0] == DefaultValue!(Value, false))) + { + const string arg = "ValueType.Void, true"; + } else static if(is(P[0] == PetitComputer)) { static if(P.length != 0) @@ -602,10 +785,29 @@ template AddFuncArg(int L, int N, int M, int O, T, string NAME, P...) { const string arg = "arg"; } + else static if(is(P[0] == Value)) + { + enum add = 1; + enum outadd = 0; + const string arg = "arg[" ~ I.to!string ~ "]"; + } + else static if(is(P[0] == DefaultValue!Value)) + { + enum add = 1; + enum outadd = 0; + const string arg = "fromValueToDefault(arg[" ~ I.to!string ~ "])"; + } + else static if(is(P[0] == DefaultValue!(Value, false))) + { + enum add = 1; + enum outadd = 0; + const string arg = "fromValueToSkip(arg[" ~ I.to!string ~ "])"; + } else { enum add = 1; enum outadd = 0; + pragma(msg, P[0]); static assert(false, "Invalid type"); const string arg = ""; } diff --git a/SMILEBASIC/error.d b/SMILEBASIC/error.d index 2901aa5..f1fdf49 100644 --- a/SMILEBASIC/error.d +++ b/SMILEBASIC/error.d @@ -1,11 +1,13 @@ module otya.smilebasic.error; import std.exception; import std.string; +import std.conv; class SmileBasicError : Exception { int errnum; int errline; int errprg; + string message2; this(int slot, int line, string message) { super(format("%s in %d:%d", message, slot, line)); @@ -18,6 +20,11 @@ class SmileBasicError : Exception { super(message); } + this(string message, string message2) + { + super(message); + this.message2 = message2; + } } class SyntaxError : SmileBasicError { @@ -26,13 +33,18 @@ class SyntaxError : SmileBasicError this.errnum = 3; super("Syntax error"); } + this(wstring func) + { + this(); + this.message2 = "Undefine function (" ~ func.to!string ~ ")"; + } } class IllegalFunctionCall : SmileBasicError { - this() + this(string func) { this.errnum = 4; - super("Illegal function call"); + super("Illegal function call(" ~ func ~ ")"); } } class TypeMismatch : SmileBasicError diff --git a/SMILEBASIC/parser.d b/SMILEBASIC/parser.d index b57b82e..0a0cf64 100644 --- a/SMILEBASIC/parser.d +++ b/SMILEBASIC/parser.d @@ -142,7 +142,7 @@ class Lexical wstring iden; for(;i < code.length;i++) { - c = code[i]; + c = cast(wchar)code[i].toUpper;; if(!c.isAlpha() && !c.isDigit() && c != '_') { break; diff --git a/SMILEBASIC/petitcomputer.d b/SMILEBASIC/petitcomputer.d index 0c03f6a..91b49ff 100644 --- a/SMILEBASIC/petitcomputer.d +++ b/SMILEBASIC/petitcomputer.d @@ -11,6 +11,7 @@ import std.c.stdio; import core.sync.mutex; import otya.smilebasic.parser; import otya.smilebasic.sprite; +import otya.smilebasic.error; enum Button { NONE = 0, @@ -89,7 +90,7 @@ class PetitComputer { this() { - new Test(); + //new Test(); } static const string resourceDirName = "resources"; static const string resourcePath = "./resources"; @@ -99,10 +100,16 @@ class PetitComputer static const string fontTableFile = resourcePath ~ "/fonttable.txt"; int screenWidth; int screenHeight; + int screenWidthDisplay1; + int screenHeightDisplay1; int fontWidth; int fontHeight; int consoleWidth; int consoleHeight; + int consoleWidthDisplay1; + int consoleHeightDisplay1; + int consoleHeightC, consoleWidthC; + ConsoleCharacter[][] consoleC; int[] consoleColor = [ 0x00000000, @@ -141,6 +148,7 @@ class PetitComputer } Button button; ConsoleCharacter[][] console; + ConsoleCharacter[][] consoleDisplay1; bool visibleGRP = true; int showGRP; int useGRP; @@ -426,17 +434,41 @@ class PetitComputer writeln("OK"); screenWidth = 400; screenHeight = 240; + screenWidthDisplay1 = 320; + screenHeightDisplay1 = 240; fontWidth = 8; fontHeight = 8; consoleWidth = screenWidth / fontWidth; consoleHeight = screenHeight / fontHeight; + consoleWidthDisplay1 = screenWidthDisplay1 / fontWidth; + consoleHeightDisplay1 = screenHeightDisplay1 / fontHeight; console = new ConsoleCharacter[][consoleHeight]; + consoleDisplay1 = new ConsoleCharacter[][consoleHeightDisplay1]; consoleForeColor = 15;//#T_WHITE for(int i = 0; i < console.length; i++) { console[i] = new ConsoleCharacter[consoleWidth]; console[i][] = ConsoleCharacter(0, consoleForeColor, consoleBackColor); } + for(int i = 0; i < consoleDisplay1.length; i++) + { + consoleDisplay1[i] = new ConsoleCharacter[consoleWidthDisplay1]; + consoleDisplay1[i][] = ConsoleCharacter(0, consoleForeColor, consoleBackColor); + } + display(0); + } + void display(int number) + { + if(number) + { + consoleHeightC = consoleHeightDisplay1; + consoleWidthC = consoleWidthDisplay1; + consoleC = consoleDisplay1; + return; + } + consoleHeightC = consoleHeight; + consoleWidthC = consoleWidth; + consoleC = console; } void cls() { @@ -470,6 +502,7 @@ class PetitComputer } Mutex grpmutex; otya.smilebasic.draw.Draw draw; + bool displaynum; void renderGraphic() { //grpmutex.lock(); @@ -504,6 +537,24 @@ class PetitComputer } Button[] buttonTable; Sprite sprite; + int xscreenmode = 0; + void xscreen(int mode, int sprite, int bg) + { + int mode2 = mode / 2; + if(mode2 == 0) + { + SDL_SetWindowSize(window, 400, 240); + } + if(mode2 == 1) + { + SDL_SetWindowSize(window, 400, 480); + } + if(mode == 4) + { + SDL_SetWindowSize(window, 320, 240); + } + xscreenmode = mode2; + } void render() { buttonTable = new Button[SDL_SCANCODE_SLEEP + 1]; @@ -512,7 +563,7 @@ class PetitComputer buttonTable[SDL_SCANCODE_LEFT] = Button.LEFT; buttonTable[SDL_SCANCODE_RIGHT] = Button.RIGHT; buttonTable[SDL_SCANCODE_SPACE] = Button.A; - bool renderprofile = true; + bool renderprofile;// = true; try { version(Windows) @@ -556,7 +607,7 @@ class PetitComputer glEnable(GL_ALPHA_TEST); draw = new otya.smilebasic.draw.Draw(this); // sprite.spset(0, 0); - // sprite.spofs(0, 9, 8); + // sprite.spofs(0, 9, 8); while(true) { auto profile = SDL_GetTicks(); @@ -570,10 +621,18 @@ class PetitComputer loopcnt = 0; } } + if(xscreenmode == 1) + { + glViewport(0, 240, 400, 240); + } renderGraphic(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); renderGraphicPage(); renderConsoleGL(); + if(xscreenmode == 1) + { + glViewport(0, 240, 400, 240); + } sprite.render(); /* if(this.sprite.sprites[0].define) if(this.sprite.sprites[0].u == 0) @@ -653,6 +712,7 @@ class PetitComputer } } SDL_Window* window; + otya.smilebasic.vm.VM vm; void run() { init(); @@ -675,11 +735,12 @@ class PetitComputer //とりあえず auto parser = new Parser( //readText("./SYS/GAME6TALK.TXT").to!wstring + readText("./SYS/GAME2RPG.TXT").to!wstring //readText("./SYS/GAME1DOTRC.TXT").to!wstring //readText(input("LOAD PROGRAM:", true).to!string).to!wstring //readText("./SYS/EX1TEXT.TXT").to!wstring //readText("FIZZBUZZ.TXT").to!wstring - readText("TEST.TXT").to!wstring + //readText("TEST.TXT").to!wstring /*"?ABS(-1) LOCATE 0,10 COLOR 5 @@ -722,6 +783,8 @@ class PetitComputer auto vm = parser.compile(); bool running = true; vm.init(this); + vm.dump; + this.vm = vm; //gpset(0, 10, 10, 0xFF00FF00); //gline(0, 0, 0, 399, 239, RGB(0, 255, 0)); //gfill(0, 78, 78, 40, 40, RGB(0, 255, 255)); @@ -734,7 +797,11 @@ class PetitComputer { try { - if(!vsyncFrame && running) running = vm.runStep(); + if(!vsyncFrame && running) + { + //writefln("%04X:%s", vm.pc, vm.getCurrent); + running = vm.runStep(); + } } catch(SmileBasicError sbe) { @@ -786,6 +853,8 @@ class PetitComputer } wstring input(wstring prompt, bool useClipBoard) { + auto olddisplay = displaynum; + displaynum = 0; printConsole(prompt); clearKeyBuffer(); wstring buffer; @@ -836,6 +905,7 @@ class PetitComputer } } showCursor = false; + display = olddisplay; return buffer; } int CSRX; @@ -1028,6 +1098,55 @@ class PetitComputer glVertex3f((x * 8 + 8) / 200f - 1, 1 - (y * 8 + 8) / 120f, 0); } glEnd(); + if(xscreenmode != 1) + { + return; + } + //下画面 + glViewport(40, 0, 400, 240); + glBindTexture(GL_TEXTURE_2D, GRPF.glTexture); + glDisable(GL_TEXTURE_2D); + glBegin(GL_QUADS); + for(int y = 0; y < consoleHeightDisplay1; y++) + for(int x = 0; x < consoleWidthDisplay1; x++) + { + auto back = consoleColorGL[consoleDisplay1[y][x].backColor]; + glColor4ubv(cast(ubyte*)&back); + glVertex3f((x * 8) / 200f - 1, 1 - (y * 8 + 8) / 120f, 0.9f); + glVertex3f((x * 8) / 200f - 1, 1 - (y * 8) / 120f, 0.9f); + glVertex3f((x * 8 + 8) / 200f - 1, 1 - (y * 8) / 120f, 0.9f); + glVertex3f((x * 8 + 8) / 200f - 1, 1 - (y * 8 + 8) / 120f, 0.9f); + } + if(showCursor && animationCursor) + { + glColor4ubv(cast(ubyte*)&consoleColorGL[15]); + glVertex3f((CSRX * 8) / 200f - 1, 1 - (CSRY * 8 + 8) / 120f, -0.9f); + glVertex3f((CSRX * 8) / 200f - 1, 1 - (CSRY * 8) / 120f, -0.9f); + glVertex3f((CSRX * 8 + 2) / 200f - 1, 1 - (CSRY * 8) / 120f, -0.9f); + glVertex3f((CSRX * 8 + 2) / 200f - 1, 1 - (CSRY * 8 + 8) / 120f, -0.9f); + } + glEnd(); + glEnable(GL_TEXTURE_2D); + + //glAlphaFunc(GL_GEQUAL, 0.5); + //glEnable(GL_ALPHA_TEST); + glBegin(GL_QUADS); + for(int y = 0; y < consoleHeightDisplay1; y++) + for(int x = 0; x < consoleWidthDisplay1; x++) + { + auto fore = consoleColorGL[consoleDisplay1[y][x].foreColor]; + auto rect = &fontTable[consoleDisplay1[y][x].character]; + glColor4ubv(cast(ubyte*)&fore); + glTexCoord2f((rect.x) / 512f - 1 , (rect.y + 8) / 512f - 1); + glVertex3f((x * 8) / 200f - 1, 1 - (y * 8 + 8) / 120f, 0); + glTexCoord2f((rect.x) / 512f - 1, (rect.y) / 512f - 1); + glVertex3f((x * 8) / 200f - 1, 1 - (y * 8) / 120f, 0); + glTexCoord2f((rect.x + 8) / 512f - 1, (rect.y) / 512f - 1); + glVertex3f((x * 8 + 8) / 200f - 1, 1 - (y * 8) / 120f, 0); + glTexCoord2f((rect.x + 8) / 512f - 1, (rect.y +8) / 512f - 1); + glVertex3f((x * 8 + 8) / 200f - 1, 1 - (y * 8 + 8) / 120f, 0); + } + glEnd(); // glFlush(); } void printConsole(T...)(T args) @@ -1039,115 +1158,39 @@ class PetitComputer } void printConsoleString(wstring text) { - consolem.lock(); - scope(exit) consolem.unlock(); + //consolem.lock(); + //scope(exit) consolem.unlock(); //write(text); foreach(wchar c; text) { - if(CSRY >= consoleHeight) + if(CSRY >= consoleHeightC) { - CSRY = consoleHeight - 1; + CSRY = consoleHeightC - 1; } if(c != '\r' && c != '\n') { - console[CSRY][CSRX].character = c; - console[CSRY][CSRX].foreColor = consoleForeColor; - console[CSRY][CSRX].backColor = consoleBackColor; + consoleC[CSRY][CSRX].character = c; + consoleC[CSRY][CSRX].foreColor = consoleForeColor; + consoleC[CSRY][CSRX].backColor = consoleBackColor; } CSRX++; - if(CSRX >= consoleWidth || c == '\n' || c == '\r') + if(CSRX >= consoleWidthC || c == '\n' || c == '\r') { CSRX = 0; CSRY++; } - if(CSRY >= consoleHeight) + if(CSRY >= consoleHeightC) { - auto tmp = console[0]; - for(int i = 0; i < consoleHeight - 1; i++) + auto tmp = consoleC[0]; + for(int i = 0; i < consoleHeightC - 1; i++) { - console[i] = console[i + 1]; + consoleC[i] = consoleC[i + 1]; } - console[consoleHeight - 1] = tmp; + consoleC[consoleHeightC - 1] = tmp; tmp[] = ConsoleCharacter(0, consoleForeColor, consoleBackColor); //assert(console[0] != console[2]); - CSRY = consoleHeight - 1; + CSRY = consoleHeightC - 1; } } } } -import std.typecons; -import std.typetuple; -import std.traits; -import otya.smilebasic.error; -import otya.smilebasic.type; -static string func; -class Test -{ - import otya.smilebasic.type; - int a;/* - static Value abs(Value[] a) - { - return Value((a[0].castDouble < 0 ? -a[0].castDouble : a[0].castDouble)); - }*/ - static double ABS(double a) - { - return a < 0 ? -a : a; - } - wstring[] ah; - void*[] ahe; - alias void function(Value[], Value[]) BuiltinFunc; - BuiltinFunc[] builtinFunctions; - this() - { - foreach(name; __traits(derivedMembers, Test)) - { - //writeln(name); - ah ~= name; - // foreach (t; __traits(getVirtualFunctions, Test, name)) - { - static if(__traits(isStaticFunction, __traits(getMember, Test, name))) - { - ahe ~= cast(void*)&__traits(getMember, Test, name); - writeln(name); - auto m = typeid(typeof(__traits(getMember, Test, name))); - writeln(m); - writeln(AddFunc!(Test,name)); - builtinFunctions ~= mixin(AddFunc!(Test,name)); - } - } - } - } - -} -template AddFunc(T, string N) -{ - static if(is(ReturnType!(__traits(getMember, T, N)) == double)) - { - const string AddFunc = "function void(Value[] arg, Value[] ret){if(ret.length != 1){throw new IllegalFunctionCall();}ret[0] = Value(" ~ N ~ "(" ~ - AddFuncArg!(Tuple!(ParameterTypeTuple!(__traits(getMember, T, N))), 0) ~ "));}"; - } - else - { - const string AddFunc = ""; - } -} -template AddFuncArg(P, int N = 0) -{ - static if(is(typeof(P[N]) == double)) - { - const string arg = "arg[" ~ N.to!string ~ "].castDouble"; - } - else - { - const string arg = ""; - static assert(false, "Invalid type"); - } - static if(N + 1 == P.length) - { - const string AddFuncArg = arg; - } - else - { - const string AddFuncArg = arg ~ ", " ~ AddFuncArg!(P, N + 1); - } -} diff --git a/SMILEBASIC/sprite.d b/SMILEBASIC/sprite.d index 5fbb900..bfe2c87 100644 --- a/SMILEBASIC/sprite.d +++ b/SMILEBASIC/sprite.d @@ -5,6 +5,7 @@ import derelict.sdl2.sdl; import derelict.opengl3.gl; enum SpriteAttr { + none = 0, show = 0b00001, rotate90 = 0b00010, rotate180 = 0b00100, @@ -113,7 +114,7 @@ struct SpriteAnimData this.data.var = data[i++]; break; default: - throw new IllegalFunctionCall(); + throw new IllegalFunctionCall("SPANIM"); } return i; } @@ -179,6 +180,10 @@ struct SpriteData this.anim[sat] = anim; isAnim = true; } + void clear() + { + this.define = false; + } } struct SpriteDef { @@ -284,7 +289,6 @@ class Sprite } if(sprite.attr & SpriteAttr.show) { - glLoadIdentity(); int x = cast(int)sprite.x - sprite.homex; int y = cast(int)sprite.y - sprite.homey; int w = sprite.w; @@ -327,6 +331,7 @@ class Sprite glTexCoord2f(u2 / 512f - 1, v / 512f - 1); glVertex3f(sprite.w / 200f, 0, z);//4 glEnd(); + glLoadIdentity(); continue; } if((sprite.attr & SpriteAttr.rotate270) == SpriteAttr.rotate270) @@ -340,6 +345,7 @@ class Sprite glTexCoord2f(u2 / 512f - 1, v2 / 512f - 1);//4 glVertex3f(w / 200f, 0, z);//4 glEnd(); + glLoadIdentity(); continue; } if((sprite.attr & SpriteAttr.rotate90) == SpriteAttr.rotate90) @@ -353,6 +359,7 @@ class Sprite glTexCoord2f(u / 512f - 1, v / 512f - 1);//1 glVertex3f(w / 200f, 0, z);//4 glEnd(); + glLoadIdentity(); continue; } if((sprite.attr & SpriteAttr.rotate180) == SpriteAttr.rotate180) @@ -366,9 +373,11 @@ class Sprite glTexCoord2f(u / 512f - 1 , v2 / 512f - 1);//2 glVertex3f(w / 200f, 0, z);//4 glEnd(); + glLoadIdentity(); continue; } glEnd(); + glLoadIdentity(); continue; } } @@ -430,4 +439,15 @@ class Sprite } sprites[id].setAnimation(animdata, target); } + void spclr(int id) + { + sprites[id].clear; + } + void spclr() + { + for(int i = 0; i < sprites.length; i++) + { + spclr(i); + } + } } diff --git a/SMILEBASIC/type.d b/SMILEBASIC/type.d index 2e50372..df1cd10 100644 --- a/SMILEBASIC/type.d +++ b/SMILEBASIC/type.d @@ -78,6 +78,10 @@ struct Value return this.type == ValueType.IntegerArray || this.type == ValueType.DoubleArray || this.type == ValueType.StringArray || this.type == ValueType.String; } + bool isNumberArray() + { + return this.type == ValueType.IntegerArray || this.type == ValueType.DoubleArray; + } bool isNumber() { return this.type == ValueType.Integer || this.type == ValueType.Double;