1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-02 23:14:57 +03:00

IE-0022: Tidy and reoganise the startup sequence (https://github.com/ganelson/inform-evolution/pull/22)

This commit is contained in:
Dannii Willis 2023-06-13 12:13:23 +10:00
parent d96e3477b8
commit aa7c11eb0f
15 changed files with 480 additions and 335 deletions

View file

@ -147,16 +147,58 @@ The language of play is a natural language that varies.
The parameter-object is an object that varies.
The parameter-object variable is defined by Inter as "parameter_value".
Chapter - Startup
Startup rules is a rulebook.
The startup rulebook is accessible to Inter as "STARTUP_RB".
Startup rules have outcomes allow startup (success) and deny startup (failure).
Shutdown rules is a rulebook.
The shutdown rulebook is accessible to Inter as "SHUTDOWN_RB".
Starting the virtual machine (documented at act_startvm) is an activity.
The starting the virtual machine activity is accessible to Inter as "STARTING_VIRTUAL_MACHINE_ACT".
The final code startup rule is listed first in for starting the virtual machine.
The final code startup rule is defined by Inter as "FINAL_CODE_STARTUP_R".
The for starting the virtual machine rules have default no outcome.
First startup rule (this is the virtual machine startup rule):
carry out the starting the virtual machine activity.
Section - Startup A (for Glulx only)
The start capturing startup text rule is listed in the before starting the virtual machine rules.
The start capturing startup text rule translates into Inter as "CAPTURE_STARTUP_TEXT_R".
The enable Glulx acceleration rule is listed in the before starting the virtual machine rules.
The enable Glulx acceleration rule translates into Inter as "FINAL_CODE_STARTUP_R".
Section - Startup B
The initialise memory rule is listed in the before starting the virtual machine rules.
The initialise memory rule translates into Inter as "INITIALISE_MEMORY_R".
The seed random number generator rule is listed in the before starting the virtual machine rules.
The seed random number generator rule translates into Inter as "SEED_RANDOM_NUMBER_GENERATOR_R".
Section - Startup C (for Z-Machine only)
The final code startup rule is listed in the for starting the virtual machine rules.
The final code startup rule translates into Inter as "FINAL_CODE_STARTUP_R".
Section - Startup D (for Glulx only)
The recover Glk objects rule is listed in the before starting the virtual machine rules.
The recover Glk objects rule translates into Inter as "GGRecoverObjects".
The sound channel initialisation rule is listed in the for starting the virtual machine rules.
The sound channel initialisation rule translates into Inter as "SOUND_CHANNEL_INIT_R".
The open built-in windows rule is listed in the for starting the virtual machine rules.
The open built-in windows rule translates into Inter as "OPEN_BUILT_IN_WINDOWS_R".
The display captured startup text rule is listed in the for starting the virtual machine rules.
The display captured startup text rule translates into Inter as "END_CAPTURE_STARTUP_TEXT_R".
Chapter - Printing activities
Printing the name of something (hidden in RULES command) (documented at act_pn) is an activity.
The printing the name activity is accessible to Inter as "PRINTING_THE_NAME_ACT".

View file

@ -2,30 +2,8 @@ Startup Template.
How programs for this architecture start up the virtual machine.
@ |VM_PreInitialise()| is called; then the "starting the virtual machine"
activity runs; and then |VM_Initialise()| is called.
@ |VM_Check_Functionality()| doesn't need to do anything in Z-Code.
=
[ VM_PreInitialise;
];
[ VM_Initialise i;
standard_interpreter = HDR_TERPSTANDARD-->0;
dict_start = HDR_DICTIONARY-->0;
dict_entry_size = dict_start->(dict_start->0 + 1);
dict_start = dict_start + dict_start->0 + 4;
dict_end = dict_start + ((dict_start - 2)-->0) * dict_entry_size;
buffer->0 = INPUT_BUFFER_LEN;
buffer2->0 = INPUT_BUFFER_LEN;
buffer3->0 = INPUT_BUFFER_LEN;
parse->0 = 15;
parse2->0 = 15;
if (BasicInformKit`FIX_RNG_CFGF) {
@random 10000 -> i;
i = -i-2000;
@random i -> i;
}
[ VM_Check_Functionality;
];

View file

@ -1,6 +1,6 @@
Capabilities Template.
Miscellaneous capabilities of the 16-bit architecture.
Miscellaneous capabilities of the 32-bit architecture.
@h Summary.
The 32-bit architecture is currently the default targeted by Inform, and is

View file

@ -882,6 +882,81 @@ Constant wintype_TextGrid = 4;
return ret;
];
@ |GGRecoverObjects| handles recovering the Glk objects after restarting or restoring.
=
[ GGRecoverObjects id;
! If GGRecoverObjects() has been called, all these stored IDs are
! invalid, so we start by clearing them all out.
! (In fact, after a restoreundo, some of them may still be good.
! For simplicity, though, we assume the general case.)
gg_mainwin = 0;
gg_statuswin = 0;
gg_quotewin = 0;
gg_scriptfref = 0;
gg_scriptstr = 0;
gg_savestr = 0;
statuswin_cursize = 0;
gg_foregroundchan = 0;
gg_backgroundchan = 0;
gg_commandstr = 0;
gg_command_reading = false;
! Also tell the game to clear its object references.
IdentifyGlkObject(0);
id = glk_stream_iterate(0, gg_arguments);
while (id) {
switch (gg_arguments-->0) {
GG_SAVESTR_ROCK: gg_savestr = id;
GG_SCRIPTSTR_ROCK: gg_scriptstr = id;
GG_COMMANDWSTR_ROCK: gg_commandstr = id;
gg_command_reading = false;
GG_COMMANDRSTR_ROCK: gg_commandstr = id;
gg_command_reading = true;
default: IdentifyGlkObject(1, 1, id, gg_arguments-->0);
}
id = glk_stream_iterate(id, gg_arguments);
}
id = glk_window_iterate(0, gg_arguments);
while (id) {
switch (gg_arguments-->0) {
GG_MAINWIN_ROCK: gg_mainwin = id;
GG_STATUSWIN_ROCK: gg_statuswin = id;
GG_QUOTEWIN_ROCK: gg_quotewin = id;
default: IdentifyGlkObject(1, 0, id, gg_arguments-->0);
}
id = glk_window_iterate(id, gg_arguments);
}
id = glk_fileref_iterate(0, gg_arguments);
while (id) {
switch (gg_arguments-->0) {
GG_SCRIPTFREF_ROCK: gg_scriptfref = id;
default: IdentifyGlkObject(1, 2, id, gg_arguments-->0);
}
id = glk_fileref_iterate(id, gg_arguments);
}
if (glk_gestalt(gestalt_Sound, 0)) {
id = glk_schannel_iterate(0, gg_arguments);
while (id) {
switch (gg_arguments-->0) {
GG_FOREGROUNDCHAN_ROCK: gg_foregroundchan = id;
GG_BACKGROUNDCHAN_ROCK: gg_backgroundchan = id;
default: IdentifyGlkObject(1, 3, id, gg_arguments-->0);
}
id = glk_schannel_iterate(id, gg_arguments);
}
if (gg_foregroundchan ~= 0) { glk_schannel_stop(gg_foregroundchan); }
if (gg_backgroundchan ~= 0) { glk_schannel_stop(gg_backgroundchan); }
}
! Tell the game to tie up any loose ends.
IdentifyGlkObject(2);
rfalse;
];
@h Debugging verb.
This powers the GLKLIST command, when there's a command parser to read it.

View file

@ -2,176 +2,121 @@ Startup Template.
How programs for this architecture start up the virtual machine.
@ This variable is set at startup, to avoid asking the interpreter over
and over again.
@ |VM_Check_Functionality| checks that we are running in a Glk interpreter,
as well as that it supports the minimum requirements, which are currently
that Glk supports unicode, and Glulx supports real numbers. If one of those
requirements is not met an error message will be shown before quitting.
=
Global unicode_gestalt_ok; ! Set if interpreter supports Unicode
[ VM_Check_Functionality res;
@gestalt 4 2 res; ! Test if this interpreter has Glk...
if (res == 0) quit; ! ...without which there would be nothing we could do
@ |VM_Initialise()| is almost the first routine called, except that the
"starting the virtual machine" activity is allowed to go first; and,
come to think of it, memory allocation has to be set up before even that,
and that in turn calls |VM_PreInitialise()| to do the absolute minimum.
! Set the VM's I/O system to be Glk.
@setiosys 2 0;
Arrangements are a little different here compared with the 16-bit architecture,
because some data is retained in the case of a restart.
if (~~glk_gestalt(gestalt_Unicode, 0)) {
Fatal_Error("This storyfile can only be run in an unicode interpreter.");
}
(Many thanks are due to Eliuk Blau, who found several tricky timing errors
here and elsewhere in the Glulx-specific code. Frankly, I feel like hanging
a sign on the following routines which reads "Congratulations on bringing
light to the Dark Room.")
=
[ VM_PreInitialise res;
@gestalt 4 2 res; ! Test if this interpreter has Glk...
if (res == 0) quit; ! ...without which there would be nothing we could do
unicode_gestalt_ok = false;
if (glk_gestalt(gestalt_Unicode, 0))
unicode_gestalt_ok = true;
! Set the VM's I/O system to be Glk.
@setiosys 2 0;
@gestalt 11 0 res; ! Test if this interpreter supports real numbers...
if (res == 0) {
Fatal_Error("This storyfile can only be run in a Glulx interpreter that supports floating-point arithmetic.");
}
];
[ VM_Initialise res sty i;
@gestalt 4 2 res; ! Test if this interpreter has Glk...
if (res == 0) quit; ! ...without which there would be nothing we could do
[ Fatal_Error msg;
gg_mainwin = glk_window_open(0, 0, 0, wintype_TextBuffer, GG_MAINWIN_ROCK);
glk_set_window(gg_mainwin);
print "Fatal Error: ", (string) msg, "^";
quit;
];
! First, we must go through all the Glk objects that exist, and see
! if we created any of them. One might think this strange, since the
! program has just started running, but remember that the player might
! have just typed "restart".
@h Capture startup text.
It is a semi-common problem for authors to do something which results in text
being printed before any Glk windows have been opened, which results in a fatal
interpreter error. (Or even worse, an extension is the cause, which the author
should not be expected to know the internal details of.) This is very frustrating,
as the author often will not know what is the cause of the error as any error
messages can't be shown until a Glk window is created; indeed it's often an error
message that is trying to be printed, which the author will never see. To help
authors, while we wait for the virtual machine to be set up (including a Glk
window) we can instead send any text to a memory stream. Then once the VM is ready
we can check if any text was sent to the memory stream, and then finally display
it in the window. We only capture 256 bytes, which may not be all that is attempted
to be printed, but hopefully it gives authors enough to identify the problem.
GGRecoverObjects();
=
Constant PRE_STARTUP_TEXT_CAPTURE_MAX_LEN = 256;
Array Pre_Startup_Text_Capture_Buffer -> PRE_STARTUP_TEXT_CAPTURE_MAX_LEN;
Array Pre_Startup_Text_Capture_Results --> 2;
Global Pre_Startup_Text_Capture_Stream;
! Sound channel initialisation, and RNG fixing, must be done now rather
! than later in case InitGlkWindow() returns a non-zero value.
[ CAPTURE_STARTUP_TEXT_R;
Pre_Startup_Text_Capture_Stream = glk_stream_open_memory(Pre_Startup_Text_Capture_Buffer, PRE_STARTUP_TEXT_CAPTURE_MAX_LEN, filemode_Write, 0);
glk_stream_set_current(Pre_Startup_Text_Capture_Stream);
rfalse;
];
[ END_CAPTURE_STARTUP_TEXT_R len;
glk_stream_close(Pre_Startup_Text_Capture_Stream, Pre_Startup_Text_Capture_Results);
len = Pre_Startup_Text_Capture_Results-->1;
if (len) {
if (len > PRE_STARTUP_TEXT_CAPTURE_MAX_LEN) {
len = PRE_STARTUP_TEXT_CAPTURE_MAX_LEN;
}
glk_put_buffer(Pre_Startup_Text_Capture_Buffer, len);
}
rfalse;
];
@h Set up the Glk objects.
=
[ SOUND_CHANNEL_INIT_R;
if (glk_gestalt(gestalt_Sound, 0)) {
if (gg_foregroundchan == 0)
if (gg_foregroundchan == 0) {
gg_foregroundchan = glk_schannel_create(GG_FOREGROUNDCHAN_ROCK);
if (gg_backgroundchan == 0)
}
if (gg_backgroundchan == 0) {
gg_backgroundchan = glk_schannel_create(GG_BACKGROUNDCHAN_ROCK);
}
if (BasicInformKit`FIX_RNG_CFGF) {
@random 10000 i;
i = -i-2000;
@setrandom i;
}
res = InitGlkWindow(0);
if (res ~= 0) return;
! Now, gg_mainwin and gg_storywin might already be set. If not, set them.
if (gg_mainwin == 0) {
! Open the story window.
res = InitGlkWindow(GG_MAINWIN_ROCK);
if (res == 0) {
! Left-justify the header style
glk_stylehint_set(wintype_TextBuffer, style_Header, stylehint_Justification, 0);
! Try to make emphasized type in italics and not boldface
glk_stylehint_set(wintype_TextBuffer, style_Emphasized, stylehint_Weight, 0);
glk_stylehint_set(wintype_TextBuffer, style_Emphasized, stylehint_Oblique, 1);
gg_mainwin = glk_window_open(0, 0, 0, wintype_TextBuffer, GG_MAINWIN_ROCK);
}
if (gg_mainwin == 0) quit; ! If we can't even open one window, give in
} else {
! There was already a story window. We should erase it.
glk_window_clear(gg_mainwin);
}
if (gg_statuswin == 0) {
res = InitGlkWindow(GG_STATUSWIN_ROCK);
if (res == 0) {
statuswin_cursize = statuswin_size;
for (sty=0: sty<style_NUMSTYLES: sty++)
glk_stylehint_set(wintype_TextGrid, sty, stylehint_ReverseColor, 1);
gg_statuswin =
glk_window_open(gg_mainwin, winmethod_Fixed + winmethod_Above,
statuswin_cursize, wintype_TextGrid, GG_STATUSWIN_ROCK);
}
}
! It's possible that the status window couldn't be opened, in which case
! gg_statuswin is now zero. We must allow for that later on.
glk_set_window(gg_mainwin);
InitGlkWindow(1);
! Empty the parse buffer (see bug 0001451)
buffer3-->0 = 0;
}
rfalse;
];
[ GGRecoverObjects id;
! If GGRecoverObjects() has been called, all these stored IDs are
! invalid, so we start by clearing them all out.
! (In fact, after a restoreundo, some of them may still be good.
! For simplicity, though, we assume the general case.)
gg_mainwin = 0;
gg_statuswin = 0;
gg_quotewin = 0;
gg_scriptfref = 0;
gg_scriptstr = 0;
gg_savestr = 0;
statuswin_cursize = 0;
gg_foregroundchan = 0;
gg_backgroundchan = 0;
gg_commandstr = 0;
gg_command_reading = false;
! Also tell the game to clear its object references.
IdentifyGlkObject(0);
[ OPEN_BUILT_IN_WINDOWS_R res sty;
! gg_mainwin and gg_storywin might already be set. If not, create them.
id = glk_stream_iterate(0, gg_arguments);
while (id) {
switch (gg_arguments-->0) {
GG_SAVESTR_ROCK: gg_savestr = id;
GG_SCRIPTSTR_ROCK: gg_scriptstr = id;
GG_COMMANDWSTR_ROCK: gg_commandstr = id;
gg_command_reading = false;
GG_COMMANDRSTR_ROCK: gg_commandstr = id;
gg_command_reading = true;
default: IdentifyGlkObject(1, 1, id, gg_arguments-->0);
}
id = glk_stream_iterate(id, gg_arguments);
}
id = glk_window_iterate(0, gg_arguments);
while (id) {
switch (gg_arguments-->0) {
GG_MAINWIN_ROCK: gg_mainwin = id;
GG_STATUSWIN_ROCK: gg_statuswin = id;
GG_QUOTEWIN_ROCK: gg_quotewin = id;
default: IdentifyGlkObject(1, 0, id, gg_arguments-->0);
}
id = glk_window_iterate(id, gg_arguments);
}
id = glk_fileref_iterate(0, gg_arguments);
while (id) {
switch (gg_arguments-->0) {
GG_SCRIPTFREF_ROCK: gg_scriptfref = id;
default: IdentifyGlkObject(1, 2, id, gg_arguments-->0);
}
id = glk_fileref_iterate(id, gg_arguments);
}
if (glk_gestalt(gestalt_Sound, 0)) {
id = glk_schannel_iterate(0, gg_arguments);
while (id) {
switch (gg_arguments-->0) {
GG_FOREGROUNDCHAN_ROCK: gg_foregroundchan = id;
GG_BACKGROUNDCHAN_ROCK: gg_backgroundchan = id;
default: IdentifyGlkObject(1, 3, id, gg_arguments-->0);
}
id = glk_schannel_iterate(id, gg_arguments);
if (gg_mainwin == 0) {
! Open the story window.
! Left-justify the header style
glk_stylehint_set(wintype_TextBuffer, style_Header, stylehint_Justification, 0);
! Try to make emphasized type in italics and not boldface
glk_stylehint_set(wintype_TextBuffer, style_Emphasized, stylehint_Weight, 0);
glk_stylehint_set(wintype_TextBuffer, style_Emphasized, stylehint_Oblique, 1);
gg_mainwin = glk_window_open(0, 0, 0, wintype_TextBuffer, GG_MAINWIN_ROCK);
if (gg_mainwin == 0) {
! If we can't even open one window, give in
quit;
}
if (gg_foregroundchan ~= 0) { glk_schannel_stop(gg_foregroundchan); }
if (gg_backgroundchan ~= 0) { glk_schannel_stop(gg_backgroundchan); }
} else {
! There was already a story window. We should erase it.
glk_window_clear(gg_mainwin);
}
! Tell the game to tie up any loose ends.
IdentifyGlkObject(2);
if (gg_statuswin == 0) {
statuswin_cursize = statuswin_size;
for (sty=0: sty < style_NUMSTYLES: sty++) {
glk_stylehint_set(wintype_TextGrid, sty, stylehint_ReverseColor, 1);
}
gg_statuswin =
glk_window_open(gg_mainwin, winmethod_Fixed + winmethod_Above,
statuswin_cursize, wintype_TextGrid, GG_STATUSWIN_ROCK);
}
! It's possible that the status window couldn't be opened, in which case
! gg_statuswin is now zero. We must allow for that later on.
glk_set_window(gg_mainwin);
rfalse;
];

View file

@ -214,7 +214,6 @@ Constant RTP_FILEIOERROR = 48;
Constant RTP_HEAPERROR = 49;
Constant RTP_LISTRANGEERROR = 50;
Constant RTP_REGEXPSYNTAXERROR = 51;
Constant RTP_NOGLULXUNICODE = 52;
Constant RTP_BACKDROPONLY = 53;
Constant RTP_NOTTHING = 54;
Constant RTP_SCENEHASNTSTARTED = 55;

View file

@ -154,8 +154,6 @@ Array RTP_Buffer --> (-1) 0 0 0 0 0 0;
"always be 0 or more.^";
RTP_REGEXPSYNTAXERROR:
print "Syntax error in regular expression.^";
RTP_NOGLULXUNICODE:
print "This interpreter does not support Unicode.^";
RTP_NEGATIVEROOT:
print "You can't take the square root of a negative number.^";
RTP_CANTITERATE:

View file

@ -2,26 +2,124 @@ Startup Template.
How the program starts up, in a Basic Inform project.
@ Because the functions below are replaced by |WorldModelKit| alternatives
@h Summary.
These rules perform the essential tasks required for setting up the virtual
machine. This includes checking that we are running in a compatible interpreter,
and setting up various built in Glk objects (when running in a Glk interpreter).
These rules here are the ones which get the basic machinery working
to the point where it is safe to run arbitrary I7 source text. They necessarily
do very low-level things, and it is not guaranteed that I7 phrases will behave
to specification if executed before these early rules have finished. So it
is hazardous to obstruct or alter them.
Arrangements are a little different here on the Z-machine, because some
data is retained in the case of a restart.
(Many thanks are due to Eliuk Blau, who found several tricky timing errors
here and elsewhere in the Glulx-specific code. Frankly, I feel like hanging
a sign on the following routines which reads "Congratulations on bringing
light to the Dark Room.")
@h The startup sequence.
As of IE-0022 we have a simplified startup sequence. |Main| calls the |Startup|
function, which does two things: run an Architecture16Kit/Architecture32Kit function
|VM_Check_Functionality| which checks the VM meets the minimum requirements for Inform
(in Glulx it would test a few Glulx/Glk gestalts; in Z-Code it would probably do
nothing.) Then it runs the Startup Rules. And that's it - everything else in the
startup sequence is now in Inform-accessible rules.
Because the functions below are replaced by |WorldModelKit| alternatives
if that kit is present, the following applies only to Basic Inform programs
where |WorldModelKit| is not involved.
=
[ Main;
self = COL_HSIZE; ! To ensure this definition is not optimised out
Startup();
Submain();
];
[ Startup;
VM_Check_Functionality();
FollowRulebook(STARTUP_RB);
rfalse;
];
@h Initialise Memory Rule.
This rule amalgamates some minimal initialisations which all need to happen
before we can risk using some of the more exotic I7 kinds:
(a) The language definition might call for initialisation, although the
default language of play (English) does not.
(b) We start the machinery needed to check that property accesses are
valid during play.
(c) We initialise the memory allocation heap, and expand the literal
constants, as hinted above: these are called "block constants" since
they occupy blocks of memory.
(d) Some platform-specific dictionary and parse buffer related globals
are initialised.
=
[ INITIALISE_MEMORY_R;
VM_PreInitialise();
LanguageInitialise();
HeapInitialise(); ! Create a completely unused memory allocation heap
StackFramingInitialise(); ! Create an empty stack
CreateDynamicRelations(); ! Create relation structures on the heap
#Ifdef TARGET_GLULX;
! Empty the parse buffer (see bug 0001451)
buffer3-->0 = 0;
#Ifnot;
standard_interpreter = HDR_TERPSTANDARD-->0;
dict_start = HDR_DICTIONARY-->0;
dict_entry_size = dict_start->(dict_start->0 + 1);
dict_start = dict_start + dict_start->0 + 4;
dict_end = dict_start + ((dict_start - 2)-->0) * dict_entry_size;
buffer->0 = INPUT_BUFFER_LEN;
buffer2->0 = INPUT_BUFFER_LEN;
buffer3->0 = INPUT_BUFFER_LEN;
parse->0 = 15;
parse2->0 = 15;
#Endif;
rfalse;
];
[ Main;
self = COL_HSIZE; ! To ensure this definition is not optimised out
VM_Initialise();
INITIALISE_MEMORY_R();
SEED_RANDOM_NUMBER_GENERATOR_R();
Submain();
@h Seed Random Number Generator Rule.
Unless a seed is provided by Inform, and it won't be for released story files,
the VM's interpreter is supposed to start up with a good seed in its random
number generator: something usually derived from, say, the milliseconds part
of the current time of day, which is unlikely to repeat or show any pattern
in real-world use. However, early Z-machine interpreters often did this quite
badly, starting with poor seed values which meant that the first few random
numbers always had something in common (being fairly small in their range,
for instance). To obviate this we extract and throw away 100 random numbers
to get the generator going, shaking out more obvious early patterns, but
that cannot really help much if the VM interpreter's RNG is badly written.
"Anyone who considers arithmetical methods of producing random digits is,
of course, in a state of sin" (von Neumann).
=
[ SEED_RANDOM_NUMBER_GENERATOR_R i;
if (BasicInformKit`FIX_RNG_CFGF) {
#Ifdef TARGET_GLULX;
@random 10000 i;
i = -i-2000;
@setrandom i;
#Ifnot;
@random 10000 -> i;
i = -i-2000;
@random i -> i;
#Endif;
}
if (RNG_SEED_AT_START_OF_PLAY) VM_Seed_RNG(RNG_SEED_AT_START_OF_PLAY);
for (i=1: i<=100: i++) random(i);
rfalse;
];

View file

@ -284,49 +284,45 @@ it's clearer to give two definitions, so:
FlexError("ran out with too many simultaneous text conversions");
}
if (unicode_gestalt_ok) {
SuspendRTP();
.RetryWithLargerBuffer;
saved_stream = glk_stream_get_current();
stream = glk_stream_open_memory_uni(buffer, RawBufferSize, filemode_Write, 0);
glk_stream_set_current(stream);
SuspendRTP();
.RetryWithLargerBuffer;
saved_stream = glk_stream_get_current();
stream = glk_stream_open_memory_uni(buffer, RawBufferSize, filemode_Write, 0);
glk_stream_set_current(stream);
@push say__p; @push say__pc;
ClearParagraphing(7);
if (from_snippet) print (PrintSnippet) from_value;
else print (PrintI6Text) from_value;
@pull say__pc; @pull say__p;
@push say__p; @push say__pc;
ClearParagraphing(7);
if (from_snippet) print (PrintSnippet) from_value;
else print (PrintI6Text) from_value;
@pull say__pc; @pull say__p;
results = buffer + buffer_size - 2*WORDSIZE;
glk_stream_close(stream, results);
if (saved_stream) glk_stream_set_current(saved_stream);
ResumeRTP();
results = buffer + buffer_size - 2*WORDSIZE;
glk_stream_close(stream, results);
if (saved_stream) glk_stream_set_current(saved_stream);
ResumeRTP();
len = results-->1;
if (len > RawBufferSize-1) {
! Glulx had to truncate text output because the buffer ran out:
! len is the number of characters which it tried to print
news = RawBufferSize;
while (news < len) news=news*2;
i = VM_AllocateMemory(news*WORDSIZE);
if (i ~= 0) {
if (memory_to_free) VM_FreeMemory(memory_to_free);
memory_to_free = i;
buffer = i;
RawBufferSize = news;
buffer_size = (RawBufferSize + 2)*WORDSIZE;
jump RetryWithLargerBuffer;
}
! Memory allocation refused: all we can do is to truncate the text
len = RawBufferSize-1;
len = results-->1;
if (len > RawBufferSize-1) {
! Glulx had to truncate text output because the buffer ran out:
! len is the number of characters which it tried to print
news = RawBufferSize;
while (news < len) news=news*2;
i = VM_AllocateMemory(news*WORDSIZE);
if (i ~= 0) {
if (memory_to_free) VM_FreeMemory(memory_to_free);
memory_to_free = i;
buffer = i;
RawBufferSize = news;
buffer_size = (RawBufferSize + 2)*WORDSIZE;
jump RetryWithLargerBuffer;
}
buffer-->(len) = 0;
TEXT_TY_CastPrimitiveNesting--;
BlkValueMassCopyFromArray(to_txt, buffer, 4, len+1);
} else {
RunTimeProblem(RTP_NOGLULXUNICODE);
! Memory allocation refused: all we can do is to truncate the text
len = RawBufferSize-1;
}
buffer-->(len) = 0;
TEXT_TY_CastPrimitiveNesting--;
BlkValueMassCopyFromArray(to_txt, buffer, 4, len+1);
if (memory_to_free) VM_FreeMemory(memory_to_free);
];
#endif;

View file

@ -318,27 +318,6 @@ This is not quite so efficient as Memcpy, but not terrible.
rfalse;
];
@h Seed Random Number Generator Rule.
Unless a seed is provided by Inform, and it won't be for released story files,
the VM's interpreter is supposed to start up with a good seed in its random
number generator: something usually derived from, say, the milliseconds part
of the current time of day, which is unlikely to repeat or show any pattern
in real-world use. However, early Z-machine interpreters often did this quite
badly, starting with poor seed values which meant that the first few random
numbers always had something in common (being fairly small in their range,
for instance). To obviate this we extract and throw away 100 random numbers
to get the generator going, shaking out more obvious early patterns, but
that cannot really help much if the VM interpreter's RNG is badly written.
"Anyone who considers arithmetical methods of producing random digits is,
of course, in a state of sin" (von Neumann).
=
[ SEED_RANDOM_NUMBER_GENERATOR_R i;
if (RNG_SEED_AT_START_OF_PLAY) VM_Seed_RNG(RNG_SEED_AT_START_OF_PLAY);
for (i=1: i<=100: i++) random(i);
rfalse;
];
@h Regarding.
These are used with adaptive text.

View file

@ -23,7 +23,7 @@
"defines-Main": false,
"provides-kinds": [ "Macros.neptune", "Protocols.neptune", "Core.neptune", "Punctuation.neptune", "Files.neptune" ],
"indexes-with-structure": "Basic.indext",
"configuration-flags": [ "AMERICAN_DIALECT", "AUTHORIAL_MODESTY", "ECHO_COMMANDS", "FIX_RNG", "MEMORY_ECONOMY", "NO_DEPRECATED", "NUMBERED_RULES", "PRINT_ENGINEER_EXPS", "SERIAL_COMMA", "NO_AUTO_PLURAL_NAMES" ],
"configuration-flags": [ "AMERICAN_DIALECT", "AUTHORIAL_MODESTY", "ECHO_COMMANDS", "FIX_RNG", "MEMORY_ECONOMY", "NO_AUTO_PLURAL_NAMES", "NO_DEPRECATED", "NUMBERED_RULES", "PRINT_ENGINEER_EXPS", "SERIAL_COMMA" ],
"configuration-values": [ "STACK_FRAME_CAPACITY", "TEXT_BUFFER_SIZE", "DICT_RESOLUTION" ]
}
}

View file

@ -24,7 +24,7 @@ Global IterationsOfTurnSequence;
+replacing(from BasicInformKit) [ Main;
say__pc = say__pc | PARA_NORULEBOOKBREAKS;
rulebook_without_variables = ACTION_PROCESSING_RB;
FollowRulebook(STARTUP_RB);
Startup();
if (say__pc & PARA_NORULEBOOKBREAKS) say__pc = say__pc - PARA_NORULEBOOKBREAKS;
while (true) {
while (deadflag == false) {
@ -38,17 +38,14 @@ Global IterationsOfTurnSequence;
}
];
@h Virtual Machine Startup Rule.
We delegate to the appropriate VM-specific section of code for the real work.
@h Initial Whitespace Rule.
The printing of three blank lines at the start of play is traditional: on early
Z-machine interpreters such as InfoTaskForce and Zip it was a necessity because
of the way they buffered output. On modern windowed ones it still helps to
space the opening text better.
=
[ VIRTUAL_MACHINE_STARTUP_R;
CarryOutActivity(STARTING_VIRTUAL_MACHINE_ACT);
VM_Initialise();
[ INITIAL_WHITESPACE_R;
! It is now safe for the paragraph breaking between rules mechanism to work
if (say__pc & PARA_NORULEBOOKBREAKS) say__pc = say__pc - PARA_NORULEBOOKBREAKS;
print "^^^";
@ -71,13 +68,10 @@ reads, say, "Mrs Bridges is a woman. The player is Mrs Bridges."): in
other circumstances they are often correct, but this must not be relied on.
@h Initialise Memory Rule.
This rule amalgamates some minimal initialisations which all need to happen
before we can risk using some of the more exotic I7 kinds:
In addition to BasicInformKit's memory initialisation, this rule sets up
the initial situation:
(a) The language definition might call for initialisation, although the
default language of play (English) does not.
(b) A handful of variables are filled in. |I7_LOOKMODE| is a constant
A handful of variables are filled in. |I7_LOOKMODE| is a constant
created by the use options "use full-length room descriptions" or
"use abbreviated room descriptions", but otherwise not existing. It is
particularly important that |player| have the correct value, as the
@ -91,22 +85,14 @@ we know better. We do this so that the "update chronological records rule"
cannot see where the player is: see the Standard Rules for an explanation
of why this is, albeit perhaps dubiously, a good thing.
(c) We start the machinery needed to check that property accesses are
valid during play.
(d) And we initialise the memory allocation heap, and expand the literal
constants, as hinted above: these are called "block constants" since
they occupy blocks of memory.
The |not_yet_in_play| flag, which is cleared when the first command is
about to be read from the keyboard, suppresses the standard status line
text: thus, if there is some long text to read before the player finds out
where he is, the surprise will not be spoiled.
=
+replacing(from BasicInformKit) [ INITIALISE_MEMORY_R;
VM_PreInitialise();
LanguageInitialise();
+replacing(keeping)(from BasicInformKit) [ INITIALISE_MEMORY_R;
replaced`INITIALISE_MEMORY_R();
not_yet_in_play = true;
lookmode = WorldModelKit`ROOM_DESC_DETAIL_CFGV;
@ -115,10 +101,6 @@ where he is, the surprise will not be spoiled.
real_location = nothing;
location = nothing;
HeapInitialise(); ! Create a completely unused memory allocation heap
StackFramingInitialise(); ! Create an empty stack
CreateDynamicRelations(); ! Create relation structures on the heap
rfalse;
];

View file

@ -78,22 +78,93 @@ Be wary modifying these: rulebooks and activities must be defined in exactly
the right order, matching definitions both in the Inform 7 compiler and in the
template libraries. (Remember that creating an activity creates three rulebooks.)
These rules here are the ones which get the basic machinery working
to the point where it is safe to run arbitrary I7 source text. They necessarily
do very low-level things, and it is not guaranteed that I7 phrases will behave
to specification if executed before these early rules have finished. So it
is hazardous to obstruct or alter them.
(a) The "virtual machine startup rule" carries out necessary steps to
begin execution on the virtual machine in use: this entails relatively little
on the Z-machine versions 5 or 8, but can involve extensive work to get the
screen display working on Glulx or Z6.
(b) The "initialise memory rule" starts up the memory allocation heap,
if there is one, and sets some essential I6 variables. If there is any rule
not to meddle with, this is it.
(c) The "seed random number generator rule" seeds the RNG to a fixed value
if Inform has requested this (which it does in response to the |-rng| command
line switch, which is in turn used by the |intest| testing utility: it's a
way to make deterministic tests of programs which use random values).
(d) The "recover Glk objects rule" runs the object recovery process in order to
identify and pre-existing Glk objects after a restart.
(e) The "open built-in windows rule" opens the Glk windows; only after this
point is it safe to print anything.
=
Chapter - Startup
Startup rules is a rulebook.
The startup rulebook is accessible to Inter as "STARTUP_RB".
Startup rules have outcomes allow startup (success) and deny startup (failure).
Shutdown rules is a rulebook.
The shutdown rulebook is accessible to Inter as "SHUTDOWN_RB".
Starting the virtual machine (documented at act_startvm) is an activity.
The starting the virtual machine activity is accessible to Inter as "STARTING_VIRTUAL_MACHINE_ACT".
The final code startup rule is listed first in for starting the virtual machine.
The final code startup rule is defined by Inter as "FINAL_CODE_STARTUP_R".
The for starting the virtual machine rules have default no outcome.
First startup rule (this is the virtual machine startup rule):
carry out the starting the virtual machine activity.
Section - Startup A (for Glulx only)
The start capturing startup text rule is listed in the before starting the virtual machine rules.
The start capturing startup text rule translates into Inter as "CAPTURE_STARTUP_TEXT_R".
The enable Glulx acceleration rule is listed in the before starting the virtual machine rules.
The enable Glulx acceleration rule translates into Inter as "FINAL_CODE_STARTUP_R".
Section - Startup B
The initialise memory rule is listed in the before starting the virtual machine rules.
The initialise memory rule translates into Inter as "INITIALISE_MEMORY_R".
The seed random number generator rule is listed in the before starting the virtual machine rules.
The seed random number generator rule translates into Inter as "SEED_RANDOM_NUMBER_GENERATOR_R".
Section - Startup C (for Z-Machine only)
The final code startup rule is listed in the for starting the virtual machine rules.
The final code startup rule translates into Inter as "FINAL_CODE_STARTUP_R".
Section - Startup D (for Glulx only)
The recover Glk objects rule is listed in the before starting the virtual machine rules.
The recover Glk objects rule translates into Inter as "GGRecoverObjects".
@ These rules now set up the built in sound channels and windows.
=
The sound channel initialisation rule is listed in the for starting the virtual machine rules.
The sound channel initialisation rule translates into Inter as "SOUND_CHANNEL_INIT_R".
The open built-in windows rule is listed in the for starting the virtual machine rules.
The open built-in windows rule translates into Inter as "OPEN_BUILT_IN_WINDOWS_R".
The display captured startup text rule is listed in the for starting the virtual machine rules.
The display captured startup text rule translates into Inter as "END_CAPTURE_STARTUP_TEXT_R".
@ However, the two activities for printing names of objects are indeed
functional in Basic Inform.
=
Chapter - Printing activities
Printing the name of something (hidden in RULES command) (documented at act_pn) is an activity.
The printing the name activity is accessible to Inter as "PRINTING_THE_NAME_ACT".

View file

@ -328,7 +328,7 @@ A thing can be privately-named or publicly-named. A thing is usually publicly-na
A thing can be undescribed or described. A thing is usually described.
A thing can be marked for listing or unmarked for listing. A thing is usually
unmarked for listing.
A thing can be mentioned or unmentioned. A thing is usually mentioned.
A thing can be mentioned or unmentioned. A thing is usually unmentioned.
@ We now have a mixed bag of value properties, all descriptive -- it's an
interesting reflection on how qualitative English text usually is that the

View file

@ -481,35 +481,19 @@ Section 2 - The Standard Rules
The little-used do nothing rule is defined by Inter as "LITTLE_USED_DO_NOTHING_R".
@h Startup.
Every rulebook contains a (possibly empty) run of "first" rules, then
a (possibly empty) run of miscellaneous rules, then a (possibly empty)
run of "last" rules. It's unusual to have more than one rule anchored
to either end as "first" or "last", but entirely legal, and we make
use of that ability here.
These startup rules prepare the various world model specific systems.
The "first" rules here are the ones which get the basic machinery working
to the point where it is safe to run arbitrary I7 source text. They necessarily
do very low-level things, and it is not guaranteed that I7 phrases will behave
to specification if executed before these early rules have finished. So it
is hazardous to obstruct or alter them.
(a) The printing of three blank lines at the start of play is traditional: on early
Z-machine interpreters such as InfoTaskForce and Zip it was a necessity because
of the way they buffered output. On modern windowed ones it still helps to
space the opening text better.
(a) The "initialise memory rule" starts up the memory allocation heap,
if there is one, and sets some essential I6 variables. If there is any rule
not to meddle with, this is it.
(b) The "position player in model world rule" completes the initial
construction of the spatial model world.
(b) The "virtual machine startup rule" carries out necessary steps to
begin execution on the virtual machine in use: this entails relatively little
on the Z-machine versions 5 or 8, but can involve extensive work to get the
screen display working on Glulx or Z6. Before anything else happens, however,
the "starting the virtual machine" activity (see below) is carried out.
(c) The "seed random number generator rule" seeds the RNG to a fixed value
if Inform has requested this (which it does in response to the |-rng| command
line switch, which is in turn used by the |intest| testing utility: it's a
way to make deterministic tests of programs which use random values).
(d) The "update chronological records rule" is described in further detail
below, since it appears both here and also in the turn sequence rulebook.
(c) The "prepare chronological records rule" is described in further detail
below (where it's called the "update chronological records rule), since it appears
both here and also in the turn sequence rulebook.
Here it's providing us with a baseline of initial truths from which we can
later assess conditions such as "the marble door has been open". A subtle
and questionable point of the design is that this rule is placed at a time
@ -520,12 +504,7 @@ Dining Room for three turns". It's as if the player teleports into an
already-existing world, like some Star Trek crewman, just in time for the
first command.
(e) All items begin unmentioned, as might be expected.
(f) And the "position player in model world rule" completes the initial
construction of the spatial model world.
(g) The "start in the correct scenes rule" ensures that we start out
(d) The "start in the correct scenes rule" ensures that we start out
in the correct scenes. (This can't wait, because it's just conceivable
that somebody has written a rule with a preamble like "When play
begins during the Hunting Season...": it's also where the scene
@ -533,24 +512,24 @@ Entire Game begins.) That completes the necessary preliminaries before
ordinary I7 rules can be run.
=
The start in the correct scenes rule is listed first in the startup rulebook. [7th.]
The position player in model world rule is listed first in the startup rulebook. [6th.]
This is the declare everything initially unmentioned rule:
repeat with item running through things:
now the item is not mentioned.
The declare everything initially unmentioned rule is listed first in the startup rulebook. [5th]
The update chronological records rule is listed first in the startup rulebook. [4th.]
The seed random number generator rule is listed first in the startup rulebook. [3rd.]
The virtual machine startup rule is listed first in the startup rulebook. [2nd.]
The initialise memory rule is listed first in the startup rulebook. [1st.]
The initial whitespace rule is listed first in the after starting the virtual machine rules.
The initial whitespace rule translates into Inter as "INITIAL_WHITESPACE_R".
The virtual machine startup rule is defined by Inter as "VIRTUAL_MACHINE_STARTUP_R".
The initialise memory rule is defined by Inter as "INITIALISE_MEMORY_R".
The seed random number generator rule is defined by Inter as "SEED_RANDOM_NUMBER_GENERATOR_R".
The update chronological records rule is defined by Inter as "UPDATE_CHRONOLOGICAL_RECORDS_R".
The position player in model world rule is defined by Inter as "POSITION_PLAYER_IN_MODEL_R".
The position player in model world rule is listed in the after starting the virtual machine rules.
The position player in model world rule translates into Inter as "POSITION_PLAYER_IN_MODEL_R".
This is the start in the correct scenes rule: follow the scene changing rules.
The prepare chronological records rule is listed in the after starting the virtual machine rules.
The prepare chronological records rule translates into Inter as "PREPARE_CHRONOLOGICAL_RECORDS_R".
Include (-
! Awkward hack to get around rulebook basis issues that mean we can't simply include the one rule everywhere we need to
[ PREPARE_CHRONOLOGICAL_RECORDS_R;
UPDATE_CHRONOLOGICAL_RECORDS_R();
];
-).
After starting the virtual machine (this is the start in the correct scenes rule):
follow the scene changing rules.
@ The remaining rules, though, are fair game for alteration, and as if to
prove the point they are all written in standard I7 source text. Note that
@ -561,18 +540,17 @@ a change to be notified to the player as if it has happened through some
action.
=
The when play begins stage rule is listed in the startup rulebook.
The fix baseline scoring rule is listed in the startup rulebook.
The display banner rule is listed in the startup rulebook.
The initial room description rule is listed in the startup rulebook.
Startup rule (this is the when play begins stage rule):
follow the when play begins rulebook.
This is the when play begins stage rule: follow the when play begins rulebook.
Startup rule (this is the fix baseline scoring rule):
now the last notified score is the score.
This is the fix baseline scoring rule: now the last notified score is the score.
Startup rule (this is the display banner rule):
say "[banner text]".
This is the display banner rule: say "[banner text]".
This is the initial room description rule: try looking.
Startup rule (this is the initial room description rule):
try looking.
@h The turn sequence.
In each turn, a command is read and parsed from the keyboard, and any
@ -633,6 +611,9 @@ A first turn sequence rule (this is the every turn stage rule):
A first turn sequence rule (this is the early scene changing stage rule):
follow the scene changing rules. [4th.]
The generate action rule is listed first in the turn sequence rulebook. [3rd.]
This is the declare everything initially unmentioned rule:
repeat with item running through things:
now the item is not mentioned.
The declare everything initially unmentioned rule is listed first in the turn sequence rulebook. [2nd.]
The parse command rule is listed first in the turn sequence rulebook. [1st.]
@ -658,6 +639,7 @@ points", which are strategic moments during play, and this is one of them.
The timed events rule is listed in the turn sequence rulebook.
The advance time rule is listed in the turn sequence rulebook.
The update chronological records rule is listed in the turn sequence rulebook.
The update chronological records rule translates into Inter as "UPDATE_CHRONOLOGICAL_RECORDS_R".
@ We now come to the rules anchored at the end, using "last". This part of
the rulebook is reserved for book-keeping which has to happen positively