#36460 - Kyoufu Kawa - Mon Feb 21, 2005 3:03 pm
This post is no longer relevant. The problem described in it has been solved below.
Last edited by Kyoufu Kawa on Sat Feb 26, 2005 4:21 pm; edited 1 time in total
#36462 - Kyoufu Kawa - Mon Feb 21, 2005 5:00 pm
This post has been deprecated as well.
Last edited by Kyoufu Kawa on Sat Feb 26, 2005 4:22 pm; edited 1 time in total
#36529 - Kyoufu Kawa - Wed Feb 23, 2005 6:34 pm
It's done! All it needs now is simple math and subroutine support. I decided to post the thing in it's entirity to help others, being such a nice guy (ehem cough cough). Many thanks to Cearn for helping me out with this.
Most of the functions and variables referred to herein won't be available for you but the logic and math stuff's clear.
This goes in a .c file, ofcourse...
Code: |
#define SC_NOP 0x00 // No parms
#define SC_VBLANK 0x01 // No parms
#define SC_IF 0x02 // u8 val u32 target
#define SC_IFTRUE 0x03 // u32 target
#define SC_GOTO 0x04 // u32 target
#define SC_SETPLOTFLAG 0x05 // u8 flag
#define SC_CLEARPLOTFLAG 0x06 // u8 flag
#define SC_CHECKPLOTFLAG 0x07 // u8 flag
#define SC_TEXT 0x10 // u32 text
#define SC_ERROR 0x11 // u32 text
#define SC_ASK 0x18 // u32 text u8 ccnt
#define SC_WAITFORKEY 0x19 // No parms
#define SC_PLAYSONG 0x20 // u16 song
#define SC_STOPSONG 0x21 // No parms
#define SC_PLAYSOUND 0x22 // u16 song
#define SC_REVEALCGPIC 0x23 // u8 pic
#define SC_SHOWPICTURE 0x24 // u8 pic
#define SC_HALTANIMATION 0x25 // No parms
#define SC_FADE 0x26 // u8 inout
#define SC_FLASH 0x27 // No parms
#define GetScriptByte MyByte = ScriptData[ScriptCursor++];
#define GetScriptHWord MyHWord = ScriptData[ScriptCursor++]; \
MyHWord |= ScriptData[ScriptCursor++] << 8;
#define GetScriptWord MyWord = ScriptData[ScriptCursor++]; \
MyWord |= ScriptData[ScriptCursor++] << 8; \
MyWord |= ScriptData[ScriptCursor++] << 16; \
MyWord |= ScriptData[ScriptCursor++] << 24;
extern const u8 ScriptData[];
u16 ScriptCursor;
u8 SC_LastResult;
void ScriptTick(void)
{
u16* pal = (u16*)0x5000000;
u8 MyByte;
u16 MyHWord;
u32 MyWord;
GetScriptByte;
WriteText(1,31,2,31,"Cmd:\nPC: \nLst:",0);
DrawNum(MyByte,5,2);
DrawNum(ScriptCursor,5,3);
DrawNum(SC_LastResult,5,4);
switch(MyByte)
{
case SC_NOP: break;
case SC_VBLANK:
DoVBlank();
break;
case SC_IF:
GetScriptByte;
GetScriptWord;
if(SC_LastResult == MyByte) ScriptCursor = MyWord - ((u32)ScriptData);
break;
case SC_IFTRUE:
GetScriptWord;
if(SC_LastResult) ScriptCursor = MyWord - ((u32)ScriptData);
break;
case SC_GOTO:
GetScriptWord;
ScriptCursor = MyWord - ((u32)ScriptData);
break;
case SC_SETPLOTFLAG:
GetScriptByte;
data.PlotFlags |= MyByte;
break;
case SC_CLEARPLOTFLAG:
GetScriptByte;
//TODO: Find proper bit operand to clear a bit.
break;
case SC_CHECKPLOTFLAG:
GetScriptByte;
SC_LastResult = (data.PlotFlags & MyByte) ? 1 : 0;
//if(data.PlotFlags & MyByte)
// SC_LastResult = 1;
//else
// SC_LastResult = 0;
break;
case SC_TEXT:
//DEBUG ONLY: Shouldn't be used in final script builds!
GetScriptWord;
WriteText(1,31,1,31," ",0);
WriteText(1,31,1,31,(u8*)MyWord,0);
break;
case SC_ASK:
GetScriptWord;
GetScriptByte;
SC_LastResult = GetChoice((u8*)MyWord,MyByte);
break;
case SC_WAITFORKEY:
Trg = 0;
while(!(Trg & A_BUTTON))
{
DoVBlank();
KeyRead();
}
break;
case SC_PLAYSONG:
GetScriptHWord;
PlaySong(MyHWord);
break;
case SC_STOPSONG:
PlaySong(0);
break;
case SC_PLAYSOUND:
GetScriptHWord;
m4aSongNumStart(MyHWord);
break;
case SC_REVEALCGPIC:
GetScriptByte;
data.ImageFlags |= MyByte;
break;
case SC_SHOWPICTURE:
GetScriptByte;
ShowPic(MyByte);
break;
case SC_HALTANIMATION:
nextPicTimer=0;
break;
case SC_FADE:
GetScriptByte;
switch(MyByte)
{
case 0: FadeOut(); break;
case 1: FadeIn(); break;
default: RaiseError("Invalid para-\nmeter for\nFADE.\n \n0 - out\n1 - in");
}
break;
case SC_FLASH:
Flash();
break;
default:
break;
}
} |
Put this in a .s file...
Code: |
.section .rodata
.align 2
.equ NOP, 0x00 @ No parms
.equ VBLANK, 0x01 @ No parms
.equ IF, 0x02 @ u8 val u32 target
.equ IFTRUE, 0x03 @ u32 target
.equ GOTO, 0x04 @ u32 target
.equ SETPLOTFLAG, 0x05 @ u8 flag
.equ CLEARPLOTFLAG, 0x06 @ u8 flag
.equ CHECKPLOTFLAG, 0x07 @ u8 flag
.equ TEXT, 0x10 @ u32 text
.equ ERROR, 0x11 @ u32 text
.equ ASK, 0x18 @ u32 text u8 ccnt
.equ WAITFORKEY, 0x19 @ No parms
.equ PLAYSONG, 0x20 @ u16 song
.equ STOPSONG, 0x21 @ No parms
.equ PLAYSOUND, 0x22 @ u16 song
.equ REVEALCGPIC, 0x23 @ u8 pic
.equ SHOWPICTURE, 0x24 @ u8 pic
.equ HALTANIMATION, 0x25 @ No parms
.equ FADE, 0x26 @ u8 inout
.equ FLASH, 0x27 @ No parms
@ TODO: release this on GBADev.
@ TODO: think of a nice name for this little scripting language.
@ TODO: add simple math functions.
.global ScriptData
ScriptData:
SillyQuestionTest:
.byte PLAYSONG
.short 0x0002
.byte SHOWPICTURE
.byte 6
.byte FADE
.byte 1
AskIt:
.byte ASK
.word Questions
.byte 4
.byte IF
.byte 1
.word AnswerAChosen
.byte IF
.byte 2
.word AnswerBChosen
.byte IF
.byte 3
.word AnswerCChosen
.byte IF
.byte 4
.word AnswerDChosen
.byte GOTO
.word EndLoop
AnswerAChosen:
.byte TEXT
.word AnswerAText
.byte WAITFORKEY
.byte GOTO
.word EndLoop
AnswerBChosen:
.byte TEXT
.word AnswerBText
.byte WAITFORKEY
.byte GOTO
.word EndLoop
AnswerCChosen:
.byte TEXT
.word AnswerCText
.byte WAITFORKEY
.byte GOTO
.word EndLoop
AnswerDChosen:
.byte FADE
.byte 17 @ Fade can only go in (1) or out (0).
EndLoop:
.byte VBLANK
.byte GOTO
.word AskIt
Questions: .asciz "Answer A\nAnswer B\nFuck you!\nCause an error"
AnswerAText: .asciz "You chose Answer A."
AnswerBText: .asciz "You chose Answer B."
AnswerCText: .asciz "Yo momma!" |
Cearn, your turn to comment on this stuff ;)
#36569 - pollier - Thu Feb 24, 2005 7:56 pm
I'm not Cearn, but I'd like to make comments anyways~
This is pretty cool and has some elegant compact code. However, the ask-a-question, do-an-if-test-4-times bit in the example seem like a waste of space; you might as well just build the gotos straight into the syntax of the SC_IF.
You might like to have a SC_DELAY opcode too. And if you don't need too much math, you could have an opcode that takes a pointer to a regular C function and a few arguments, and calls it.
(Of course, if this was me writing the code, I'd be obsessed with compressing this as much as possible, packing my bits, but then I wouldn't be able to write it in assembly.)
Oh, and I think you can clear a bit by writing data.PlotFlags &= !(u32)MyByte; .
In any case, this is a great little engine! Add some object animation support in there and your game'll look spectacular.
-Paul
_________________
(Works for me!)
#36570 - Kyoufu Kawa - Thu Feb 24, 2005 8:48 pm
Thanks. I already figured out the bit clearing part by myself, added Script Memory (256 bytes worth of temporary variables) and some commands to use it.
Thanks for the suggestion on the multiple IFs, but I'm not you and like it this way. Add it yourself if you want to, see below. If by object animation you mean sprite manipulation support, that's not needed in my game - it uses full-screen FMV.
Anybody who needs a scripting engine and is too mushed in the head to write one may use this one under very non-limiting conditions.
1) Somewhere in the finished product must be a line that reads "JESUS scripting engine by Kyoufu Kawa and Cearn" or something to that effect.
2) I want to know when, where and by who it's used.
3) Any really cool additions would be nice to know of.
That's all really. Yes, the engine's called JESUS. It's an acronym you don't want to know the meaning of.
#36571 - sajiimori - Thu Feb 24, 2005 8:54 pm
It's good that you started with hand-typed bytecode like that. It really makes you focus on data requirements.
If you end up having lots of scripting to do, consider writing a compiler that will let you write scripts in a more structured and readable manner. It's a very fun and challenging task if you haven't done it before. Code: |
function answerAChosen()
text("You chose Answer A.")
waitForKey()
end
while 1 do
playSong(2)
showPicture(6)
fade(1)
ask(
["Answer A", answerAChosen],
["Answer B", answerBChosen],
["Darn you!", answerCChosen],
["Cause an error", answerDChosen])
vBlank()
end |
Or choose your favorite syntax. ^_^
#36572 - Kyoufu Kawa - Thu Feb 24, 2005 9:25 pm
I would, but all I ever managed to write compiler-wise was Rubikon, for Pokemon Advance. Compilers fall under Rule Three ;)
#36573 - sajiimori - Thu Feb 24, 2005 9:42 pm
I don't know what Rule Three is, but if you need any help I'd be happy to give you some tips (or even some source code). With the right approach, compiler writing can be a very satisfying experience.
#36575 - Kyoufu Kawa - Thu Feb 24, 2005 9:50 pm
Quote: |
3) Any really cool additions would be nice to know of. |
That's rule three, and maybe I should rephrase that.
Quote: |
3) Any really cool additions would be nice to see. |
In other words, I can't write a compiler, but if you can provide at least a framework, that'd be very swell.
#36578 - sajiimori - Fri Feb 25, 2005 12:24 am
Heheh... I wasn't looking for a project for myself. It was just a suggestion for you. ^_^ I can offer guidance, but if the motivation isn't there for you then I won't be of much use.
#36594 - Kyoufu Kawa - Fri Feb 25, 2005 2:29 pm
Don't worry. That's what Google and stuff are for ;)
#36622 - AnthC - Sat Feb 26, 2005 4:59 am
Kyoufu Kawa wrote: |
Being about halfway through writing a quite solid codebase for my game, I've been contemplating to write a reasonably simple scripting engine. It need not be too complex, but by now it's common knowledge that pointers don't like me and vice versa.
I thought I could just use an .s file full of .byte and .word statements with predefined (more like equ) commands and stuff...
Code: | .byte PLAYSOUND
.byte 6
.byte FADE
.byte 0 @ fade in/out param
.byte SHOWTEXT
.word (some pointer thingy here) |
<ANTH : SNIP>
|
Rather than going to ASM why not code these in C?
I did a similar thing in Cauldron using some macros I wrote.
e.g.
#define SPEED(A) (U8)A_CMD_SPEED,(U8)(A),
#define LOOPS(A) (U8)A_CMD_LOOPS,(U8)(A),
#define SETFR(A) (U8)A_CMD_SETFR,(U8)(A),(U8)((A)>>8),
etc etc.
const U8 AnmWitchStirR[]=
{
SPEED(8)
LOOPS(INFINATE)
SETFR(WITCHSTIR)
NEXTFR
NEXTFR
LOOPE
};
<EDIT : Just looked you have goto's which is a problem since you need labels to access them, ASM Macro's would do the equivalent and give you more flexibility - the main point of the macros is to avoid simple bugs creeping in>
Anth
#36787 - Kyoufu Kawa - Tue Mar 01, 2005 8:19 pm
And now, allow me to present you the final 1.0 release of the JESUS scripting engine kit!
Zip contents:
* HEROD compiler (more like translator but hey)
* Sample script
* How to do loops, for the n00bs
* A bare-bones JESUS interpreter. Just the system commands, nearly nothing from my game left.
JESUS has:
* Subroutines, thanks to a crude stack of user-definable size
* No word-alignment issues.
* Reasonable speed.
Get it here.