Пример #1
0
        }/* z_check_arg_count */

        /*
         * z_jump, jump unconditionally to the given address.
         *
         *	zargs[0] = PC relative address
         *
         */

        internal static void z_jump()
        {
            long pc;

            FastMem.GET_PC(out pc);

            pc += (short)zargs[0] - 2; // TODO This actually counts on an overflow to work

            if (pc >= main.story_size)
            {
                Err.runtime_error(ErrorCodes.ERR_ILL_JUMP_ADDR);
            }

            FastMem.SET_PC(pc);
        }/* z_jump */
Пример #2
0
        }/* ret */

        /*
         * branch
         *
         * Take a jump after an instruction based on the flag, either true or
         * false. The branch can be short or long; it is encoded in one or two
         * bytes respectively. When bit 7 of the first byte is set, the jump
         * takes place if the flag is true; otherwise it is taken if the flag
         * is false. When bit 6 of the first byte is set, the branch is short;
         * otherwise it is long. The offset occupies the bottom 6 bits of the
         * first byte plus all the bits in the second byte for long branches.
         * Uniquely, an offset of 0 means return false, and an offset of 1 is
         * return true.
         *
         */

        internal static void branch(bool flag)
        {
            long  pc;
            zword offset;
            zbyte specifier;
            zbyte off1;
            zbyte off2;

            FastMem.CODE_BYTE(out specifier);

            off1 = (zbyte)(specifier & 0x3f);

            if (!flag)
            {
                specifier ^= 0x80;
            }

            if ((specifier & 0x40) == 0)
            {                          // if (!(specifier & 0x40)) {		/* it's a long branch */
                if ((off1 & 0x20) > 0) /* propagate sign bit */
                {
                    off1 |= 0xc0;
                }

                FastMem.CODE_BYTE(out off2);

                offset = (zword)((off1 << 8) | off2);
            }
            else
            {
                offset = off1;          /* it's a short branch */
            }
            if ((specifier & 0x80) > 0)
            {
                if (offset > 1)
                {               /* normal branch */
                    FastMem.GET_PC(out pc);
                    pc += (short)offset - 2;
                    FastMem.SET_PC(pc);
                }
                else
                {
                    ret(offset);                /* special case, return 0 or 1 */
                }
            }
        }/* branch */
Пример #3
0
        }/* storew */

        /*
         * z_restart, re-load dynamic area, clear the stack and set the PC.
         *
         *  no zargs used
         *
         */

        internal static void z_restart()
        {
            Buffer.flush_buffer();

            os_.restart_game(ZMachine.RESTART_BEGIN);

            Random.seed_random(0);

            if (!first_restart)
            {
                story_fp.Position = init_fp_pos;

                int read = story_fp.Read(ZMData, 0, main.h_dynamic_size);
                if (read != main.h_dynamic_size)
                {
                    os_.fatal("Story file read error");
                }
            }
            else
            {
                first_restart = false;
            }

            restart_header();
            Screen.restart_screen();

            main.sp = main.fp = General.STACK_SIZE; // TODO Critical to make sure this logic works; sp = fp = stack + STACK_SIZE;

            main.frame_count = 0;

            if (main.h_version != ZMachine.V6)
            {
                zword pc = main.h_start_pc;
                FastMem.SET_PC((int)pc);
            }
            else
            {
                Process.call(main.h_start_pc, 0, 0, 0);
            }

            os_.restart_game(ZMachine.RESTART_END);
        }/* z_restart */
Пример #4
0
        }/* call */

        /*
         * ret
         *
         * Return from the current subroutine and restore the previous stack
         * frame. The result may be stored (0), thrown away (1) or pushed on
         * the stack (2). In the latter case a direct call has been finished
         * and we must exit the interpreter loop.
         *
         */

        internal static void ret(zword value)
        {
            long pc;
            int  ct;

            if (main.sp > main.fp)
            {
                Err.runtime_error(ErrorCodes.ERR_STK_UNDF);
            }

            main.sp = main.fp;

            DebugState.Output("Removing Frame: {0}", main.frame_count);

            ct = main.stack[main.sp++] >> (main.option_save_quetzal == true ? 12 : 8);
            main.frame_count--;
            main.fp = 1 + main.stack[main.sp++];              // fp = stack + 1 + *sp++;
            pc      = main.stack[main.sp++];
            pc      = (main.stack[main.sp++] << 9) | (int)pc; // TODO Really don't trust casting PC to int

            FastMem.SET_PC(pc);

            /* Handle resulting value */

            if (ct == 0)
            {
                store(value);
            }
            if (ct == 2)
            {
                main.stack[--main.sp] = value;
            }

            /* Stop main loop for direct calls */

            if (ct == 2)
            {
                finished++;
            }
        }/* ret */
Пример #5
0
        }/* interpret */

        /*
         * call
         *
         * Call a subroutine. Save PC and FP then load new PC and initialise
         * new stack frame. Note that the caller may legally provide less or
         * more arguments than the function actually has. The call type "ct"
         * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call).
         *
         */

        internal static void call(zword routine, int argc, int args_offset, int ct)
        {
            long  pc;
            zword value;
            zbyte count;
            int   i;

            if (main.sp < 4)//if (sp - stack < 4)
            {
                Err.runtime_error(ErrorCodes.ERR_STK_OVF);
            }

            FastMem.GET_PC(out pc);

            main.stack[--main.sp] = (zword)(pc >> 9);
            main.stack[--main.sp] = (zword)(pc & 0x1ff);
            main.stack[--main.sp] = (zword)(main.fp - 1); // *--sp = (zword) (fp - stack - 1);
            main.stack[--main.sp] = (zword)(argc | (ct << (main.option_save_quetzal == true ? 12 : 8)));

            main.fp = main.sp;
            main.frame_count++;

            DebugState.Output("Added Frame: {0} -> {1}:{2}:{3}:{4}",
                              main.frame_count,
                              main.stack[main.sp + 0],
                              main.stack[main.sp + 1],
                              main.stack[main.sp + 2],
                              main.stack[main.sp + 3]);

            /* Calculate byte address of routine */

            if (main.h_version <= ZMachine.V3)
            {
                pc = (long)routine << 1;
            }
            else if (main.h_version <= ZMachine.V5)
            {
                pc = (long)routine << 2;
            }
            else if (main.h_version <= ZMachine.V7)
            {
                pc = ((long)routine << 2) + ((long)main.h_functions_offset << 3);
            }
            else /* (h_version <= V8) */
            {
                pc = (long)routine << 3;
            }

            if (pc >= main.story_size)
            {
                Err.runtime_error(ErrorCodes.ERR_ILL_CALL_ADDR);
            }

            FastMem.SET_PC(pc);

            /* Initialise local variables */

            FastMem.CODE_BYTE(out count);

            if (count > 15)
            {
                Err.runtime_error(ErrorCodes.ERR_CALL_NON_RTN);
            }
            if (main.sp < count)
            {
                Err.runtime_error(ErrorCodes.ERR_STK_OVF);
            }

            if (main.option_save_quetzal == true)
            {
                main.stack[main.fp] |= (zword)(count << 8);     /* Save local var count for Quetzal. */
            }
            value = 0;

            for (i = 0; i < count; i++)
            {
                if (main.h_version <= ZMachine.V4)              /* V1 to V4 games provide default */
                {
                    FastMem.CODE_WORD(out value);               /* values for all local variables */
                }
                main.stack[--main.sp] = (zword)((argc-- > 0) ? zargs[args_offset + i] : value);
                //*--sp = (zword) ((argc-- > 0) ? args[i] : value);
            }

            /* Start main loop for direct calls */

            if (ct == 2)
            {
                interpret();
            }
        }/* call */
Пример #6
0
        /*
         * Restore a saved game using Quetzal format. Return 2 if OK, 0 if an error
         * occurred before any damage was done, -1 on a fatal error.
         */

        internal static zword restore_quetzal(FileStream svf, System.IO.Stream stf)
        {
            zlong ifzslen, currlen, tmpl;
            zlong pc;
            zword i, tmpw;
            zword fatal = 0;    /* Set to -1 when errors must be fatal. */
            zbyte skip, progress = GOT_NONE;
            int   x, y;

            /* Check it's really an `IFZS' file. */


            if (!read_long(svf, out tmpl) ||
                !read_long(svf, out ifzslen) ||
                !read_long(svf, out currlen))
            {
                return(0);
            }
            if (tmpl != ID_FORM || currlen != ID_IFZS)
            {
                Text.print_string("This is not a saved game file!\n");
                return(0);
            }
            if (((ifzslen & 1) > 0) || ifzslen < 4) /* Sanity checks. */ return {
                (0);
            }
            ifzslen -= 4;

            /* Read each chunk and process it. */
            while (ifzslen > 0)
            {
                /* Read chunk header. */
                if (ifzslen < 8) /* Couldn't contain a chunk. */ return {
                    (0);
                }
                if (!read_long(svf, out tmpl) ||
                    !read_long(svf, out currlen))
                {
                    return(0);
                }
                ifzslen -= 8;   /* Reduce remaining by size of header. */

                /* Handle chunk body. */
                if (ifzslen < currlen) /* Chunk goes past EOF?! */ return {
                    (0);
                }
                skip     = (byte)(currlen & 1);
                ifzslen -= currlen + (zlong)skip;

                switch (tmpl)
                {
                /* `IFhd' header chunk; must be first in file. */
                case 1229351012:     // IFhd
                    if ((progress & GOT_HEADER) > 0)
                    {
                        Text.print_string("Save file has two IFZS chunks!\n");
                        return(fatal);
                    }
                    progress |= GOT_HEADER;
                    if (currlen < 13 ||
                        !read_word(svf, out tmpw))
                    {
                        return(fatal);
                    }
                    if (tmpw != main.h_release)
                    {
                        progress = GOT_ERROR;
                    }

                    for (i = ZMachine.H_SERIAL; i < ZMachine.H_SERIAL + 6; ++i)
                    {
                        if ((x = svf.ReadByte()) == -1)
                        {
                            return(fatal);
                        }
                        if (x != FastMem.ZMData[FastMem.zmp + i])
                        {
                            progress = GOT_ERROR;
                        }
                    }

                    if (!read_word(svf, out tmpw))
                    {
                        return(fatal);
                    }
                    if (tmpw != main.h_checksum)
                    {
                        progress = GOT_ERROR;
                    }

                    if ((progress & GOT_ERROR) > 0)
                    {
                        Text.print_string("File was not saved from this story!\n");
                        return(fatal);
                    }
                    if ((x = svf.ReadByte()) == -1)
                    {
                        return(fatal);
                    }
                    pc = (zlong)x << 16;
                    if ((x = svf.ReadByte()) == -1)
                    {
                        return(fatal);
                    }
                    pc |= (zlong)x << 8;
                    if ((x = svf.ReadByte()) == -1)
                    {
                        return(fatal);
                    }
                    pc   |= (zlong)x;
                    fatal = zword.MaxValue; /* Setting PC means errors must be fatal. */     // TODO make sure this works
                    FastMem.SET_PC(pc);

                    for (i = 13; i < currlen; ++i)
                    {
                        svf.ReadByte();         /* Skip rest of chunk. */
                    }
                    break;

                /* `Stks' stacks chunk; restoring this is quite complex. ;) */
                case 1400138611:     // ID_Stks:
                    if ((progress & GOT_STACK) > 0)
                    {
                        Text.print_string("File contains two stack chunks!\n");
                        break;
                    }
                    progress |= GOT_STACK;

                    fatal = zword.MaxValue;;            /* Setting SP means errors must be fatal. */
                    // sp = stack + General.STACK_SIZE;
                    main.sp = main.stack.Length;

                    /*
                     * All versions other than V6 may use evaluation stack outside
                     * any function context. As a result a faked function context
                     * will be present in the file here. We skip this context, but
                     * load the associated stack onto the stack proper...
                     */
                    if (main.h_version != ZMachine.V6)
                    {
                        if (currlen < 8)
                        {
                            return(fatal);
                        }
                        for (i = 0; i < 6; ++i)
                        {
                            if (svf.ReadByte() != 0)
                            {
                                return(fatal);
                            }
                        }
                        if (!read_word(svf, out tmpw))
                        {
                            return(fatal);
                        }
                        if (tmpw > General.STACK_SIZE)
                        {
                            Text.print_string("Save-file has too much stack (and I can't cope).\n");
                            return(fatal);
                        }
                        currlen -= 8;
                        if (currlen < tmpw * 2)
                        {
                            return(fatal);
                        }
                        for (i = 0; i < tmpw; ++i)
                        {
                            // if (!read_word(svf, --sp)) return fatal;
                            if (!read_word(svf, out main.stack[--main.sp]))
                            {
                                return(fatal);
                            }
                        }
                        currlen -= (zword)(tmpw * 2);
                    }

                    /* We now proceed to load the main block of stack frames. */
                    for (main.fp = main.stack.Length, main.frame_count = 0;
                         currlen > 0;
                         currlen -= 8, ++main.frame_count)
                    {
                        if (currlen < 8)
                        {
                            return(fatal);
                        }
                        if (main.sp < 4)        /* No space for frame. */
                        {
                            Text.print_string("Save-file has too much stack (and I can't cope).\n");
                            return(fatal);
                        }

                        /* Read PC, procedure flag and formal param count. */
                        if (!read_long(svf, out tmpl))
                        {
                            return(fatal);
                        }
                        y    = (int)(tmpl & 0x0F);      /* Number of formals. */
                        tmpw = (zword)(y << 8);

                        /* Read result variable. */
                        if ((x = svf.ReadByte()) == -1)
                        {
                            return(fatal);
                        }

                        /* Check the procedure flag... */
                        if ((tmpl & 0x10) > 0)
                        {
                            tmpw  |= 0x1000;    /* It's a procedure. */
                            tmpl >>= 8;         /* Shift to get PC value. */
                        }
                        else
                        {
                            /* Functions have type 0, so no need to or anything. */
                            tmpl >>= 8;         /* Shift to get PC value. */
                            --tmpl;             /* Point at result byte. */
                            /* Sanity check on result variable... */
                            if (FastMem.ZMData[FastMem.zmp + tmpl] != (zbyte)x)
                            {
                                Text.print_string("Save-file has wrong variable number on stack (possibly wrong game version?)\n");
                                return(fatal);
                            }
                        }

                        main.stack[--main.sp] = (zword)(tmpl >> 9);     /* High part of PC */
                        main.stack[--main.sp] = (zword)(tmpl & 0x1FF);  /* Low part of PC */
                        main.stack[--main.sp] = (zword)(main.fp - 1);   /* FP */

                        /* Read and process argument mask. */
                        if ((x = svf.ReadByte()) == -1)
                        {
                            return(fatal);
                        }
                        ++x;            /* Should now be a power of 2 */
                        for (i = 0; i < 8; ++i)
                        {
                            if ((x & (1 << i)) > 0)
                            {
                                break;
                            }
                        }
                        if ((x ^ (1 << i)) > 0)         /* Not a power of 2 */
                        {
                            Text.print_string("Save-file uses incomplete argument lists (which I can't handle)\n");
                            return(fatal);
                        }
                        // *--sp = tmpw | i;
                        main.stack[--main.sp] = (zword)(tmpw | i);
                        main.fp = main.sp;      /* FP for next frame. */

                        /* Read amount of eval stack used. */
                        if (!read_word(svf, out tmpw))
                        {
                            return(fatal);
                        }

                        tmpw += (zword)y;       /* Amount of stack + number of locals. */
                        // if (sp - stack <= tmpw) {
                        if (main.sp <= tmpw)
                        {
                            Text.print_string("Save-file has too much stack (and I can't cope).\n");
                            return(fatal);
                        }
                        if (currlen < tmpw * 2)
                        {
                            return(fatal);
                        }
                        for (i = 0; i < tmpw; ++i)
                        {
                            if (!read_word(svf, out main.stack[--main.sp]))
                            {
                                return(fatal);
                            }
                        }
                        currlen -= (zword)(tmpw * 2);
                    }
                    /* End of `Stks' processing... */
                    break;

                /* Any more special chunk types must go in HERE or ABOVE. */
                /* `CMem' compressed memory chunk; uncompress it. */
                case 1129145709:                      // CMem
                    if ((progress & GOT_MEMORY) == 0) /* Don't complain if two. */
                    {
                        stf.Position = 0;             // (void) fseek (stf, 0, SEEK_SET);
                        i            = 0;             /* Bytes written to data area. */
                        for (; currlen > 0; --currlen)
                        {
                            if ((x = svf.ReadByte()) == -1)
                            {
                                return(fatal);
                            }
                            if (x == 0)         /* Start run. */
                            {
                                /* Check for bogus run. */
                                if (currlen < 2)
                                {
                                    Text.print_string("File contains bogus `CMem' chunk.\n");
                                    for (; currlen > 0; --currlen)
                                    {
                                        svf.ReadByte();         /* Skip rest. */
                                    }
                                    currlen = 1;
                                    i       = 0xFFFF;
                                    break;     /* Keep going; may be a `UMem' too. */
                                }
                                /* Copy story file to memory during the run. */
                                --currlen;
                                if ((x = svf.ReadByte()) == -1)
                                {
                                    return(fatal);
                                }
                                for (; x >= 0 && i < main.h_dynamic_size; --x, ++i)
                                {
                                    if ((y = stf.ReadByte()) == -1)
                                    {
                                        return(fatal);
                                    }
                                    else
                                    {
                                        FastMem.ZMData[FastMem.zmp + i] = (zbyte)y;
                                    }
                                }
                            }
                            else        /* Not a run. */
                            {
                                if ((y = stf.ReadByte()) == -1)
                                {
                                    return(fatal);
                                }
                                FastMem.ZMData[FastMem.zmp + i] = (zbyte)(x ^ y);
                                ++i;
                            }
                            /* Make sure we don't load too much. */
                            if (i > main.h_dynamic_size)
                            {
                                Text.print_string("warning: `CMem' chunk too long!\n");
                                for (; currlen > 1; --currlen)
                                {
                                    svf.ReadByte(); /* Skip rest. */
                                }
                                break;              /* Keep going; there may be a `UMem' too. */
                            }
                        }
                        /* If chunk is short, assume a run. */
                        for (; i < main.h_dynamic_size; ++i)
                        {
                            if ((y = stf.ReadByte()) == -1)
                            {
                                return(fatal);
                            }
                            else
                            {
                                FastMem.ZMData[FastMem.zmp + i] = (zbyte)y;
                            }
                        }
                        if (currlen == 0)
                        {
                            progress |= GOT_MEMORY;     /* Only if succeeded. */
                        }
                        break;
                    }
                    goto default;

                /* Fall right thru (to default) if already GOT_MEMORY */
                /* `UMem' uncompressed memory chunk; load it. */
                case 1431135597:                      // ID_UMem:
                    if ((progress & GOT_MEMORY) == 0) /* Don't complain if two. */
                    {
                        /* Must be exactly the right size. */
                        if (currlen == main.h_dynamic_size)
                        {
                            byte[] buffer = new byte[currlen];
                            int    read   = svf.Read(FastMem.ZMData, (int)FastMem.zmp, (int)currlen);
                            if (read == currlen)
                            {
                                progress |= GOT_MEMORY;         /* Only on success. */
                                break;
                            }
                            //if (fread(zmp, currlen, 1, svf) == 1) {
                            //}
                        }
                        else
                        {
                            Text.print_string("`UMem' chunk wrong size!\n");
                        }
                        /* Fall into default action (skip chunk) on errors. */
                    }
                    goto default;

                /* Fall thru (to default) if already GOT_MEMORY */
                /* Unrecognised chunk type; skip it. */
                default:
                    // (void) fseek (svf, currlen, SEEK_CUR);	/* Skip chunk. */
                    svf.Position += currlen;
                    break;
                }
                if (skip > 0)
                {
                    svf.ReadByte();     /* Skip pad byte. */
                }
            }

            /*
             * We've reached the end of the file. For the restoration to have been a
             * success, we must have had one of each of the required chunks.
             */
            if ((progress & GOT_HEADER) == 0)
            {
                Text.print_string("error: no valid header (`IFhd') chunk in file.\n");
            }
            if ((progress & GOT_STACK) == 0)
            {
                Text.print_string("error: no valid stack (`Stks') chunk in file.\n");
            }
            if ((progress & GOT_MEMORY) == 0)
            {
                Text.print_string("error: no valid memory (`CMem' or `UMem') chunk in file.\n");
            }

            return((ushort)(progress == GOT_ALL ? 2 : fatal));
        }