mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
279 lines
8.1 KiB
C++
279 lines
8.1 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#ifndef MTROPOLIS_COROUTINES_H
|
|
#define MTROPOLIS_COROUTINES_H
|
|
|
|
#include "mtropolis/coroutine_protos.h"
|
|
#include "mtropolis/miniscript_protos.h"
|
|
#include "mtropolis/vthread.h"
|
|
|
|
namespace MTropolis {
|
|
|
|
struct CoroExecInstr;
|
|
struct CoroutineRuntimeState;
|
|
|
|
typedef VThreadState (*CoroutineFragmentFunction_t)(CoroutineRuntimeState &coroState);
|
|
|
|
struct CoroutineRuntimeState {
|
|
CoroutineRuntimeState(VThread *vthread, CoroutineStackFrame2 *frame);
|
|
|
|
VThread *_vthread;
|
|
CoroutineStackFrame2 *_frame;
|
|
bool _condition;
|
|
MiniscriptInstructionOutcome _miniscriptOutcome;
|
|
|
|
private:
|
|
CoroutineRuntimeState() = delete;
|
|
CoroutineRuntimeState(const CoroutineRuntimeState &) = delete;
|
|
CoroutineRuntimeState &operator=(const CoroutineRuntimeState&) = delete;
|
|
};
|
|
|
|
struct CompiledCoroutine {
|
|
CompiledCoroutine();
|
|
~CompiledCoroutine();
|
|
|
|
CoroutineFrameConstructor_t _frameConstructor;
|
|
CoroutineGetFrameParametersFunction_t _getFrameParameters;
|
|
bool _isVoidReturn;
|
|
|
|
CoroExecInstr *_instructions;
|
|
uint _numInstructions;
|
|
|
|
private:
|
|
CompiledCoroutine(const CompiledCoroutine &) = delete;
|
|
CompiledCoroutine &operator =(const CompiledCoroutine &) = delete;
|
|
};
|
|
|
|
struct CoroutineStackFrame2 : public VThreadTaskData {
|
|
explicit CoroutineStackFrame2(const CompiledCoroutine *compiledCoro);
|
|
virtual ~CoroutineStackFrame2();
|
|
|
|
VThreadState execute(VThread *thread) override;
|
|
|
|
const CompiledCoroutine *getCompiledCoroutine() const;
|
|
|
|
private:
|
|
CoroutineStackFrame2() = delete;
|
|
CoroutineStackFrame2(const CoroutineStackFrame2&) = delete;
|
|
|
|
const CompiledCoroutine *_compiledCoro;
|
|
uint _nextInstr;
|
|
};
|
|
|
|
enum class CoroOps {
|
|
Invalid = 0,
|
|
|
|
BeginFunction,
|
|
EndFunction,
|
|
|
|
IfCond,
|
|
IfBody,
|
|
Else,
|
|
ElseIfCond,
|
|
ElseIfBody,
|
|
EndIf,
|
|
|
|
WhileCond,
|
|
WhileBody,
|
|
EndWhile,
|
|
|
|
ForNext,
|
|
ForCond,
|
|
ForBody,
|
|
EndFor,
|
|
|
|
Do,
|
|
DoWhile,
|
|
DoWhileCond,
|
|
|
|
Return,
|
|
|
|
Error,
|
|
|
|
YieldToFunction,
|
|
|
|
CheckMiniscript,
|
|
|
|
Code,
|
|
};
|
|
|
|
struct ICoroutineCompiler {
|
|
virtual ~ICoroutineCompiler();
|
|
|
|
virtual void defineFunction(CoroutineFrameConstructor_t frameConstructor, CoroutineGetFrameParametersFunction_t frameGetParams) = 0;
|
|
virtual void addOp(CoroOps op, CoroutineFragmentFunction_t fragmentFunc) = 0;
|
|
};
|
|
|
|
#define CORO_START_CODE_BLOCK(op) \
|
|
compiler->addOp(op, [](CoroutineRuntimeState &coroRuntime) -> VThreadState { \
|
|
Params *params = &static_cast<CoroStackFrame *>(coroRuntime._frame)->_params; \
|
|
Locals *locals = &static_cast<CoroStackFrame *>(coroRuntime._frame)->_locals; \
|
|
CoroutineReturnValueRef<ReturnValue_t> coroReturnValueRef = (static_cast<CoroStackFrame *>(coroRuntime._frame)->_rvRef);
|
|
|
|
#define CORO_AWAIT_PUSHED_TASK \
|
|
return kVThreadReturn
|
|
|
|
#define CORO_DISUSE_CODE_BLOCK_VARS \
|
|
(void)params; \
|
|
(void)locals; \
|
|
(void)coroReturnValueRef;
|
|
|
|
#define CORO_END_CODE_BLOCK \
|
|
CORO_DISUSE_CODE_BLOCK_VARS \
|
|
return kVThreadReturn; \
|
|
});
|
|
|
|
#define CORO_BEGIN_DEFINITION(type) \
|
|
CompiledCoroutine *type::ms_compiledCoro = nullptr;\
|
|
void type::compileCoroutine(ICoroutineCompiler *compiler) {
|
|
|
|
#define CORO_END_DEFINITION \
|
|
}
|
|
|
|
#define CORO_BEGIN_FUNCTION \
|
|
struct CoroStackFrame : public CoroutineStackFrame2 {\
|
|
Params _params;\
|
|
Locals _locals;\
|
|
CoroutineReturnValueRef<ReturnValue_t> _rvRef;\
|
|
explicit CoroStackFrame(const CompiledCoroutine *compiledCoro, const Params ¶ms, const CoroutineReturnValueRef<ReturnValue_t> &rvRef)\
|
|
: CoroutineStackFrame2(compiledCoro), _params(params), _rvRef(rvRef) {\
|
|
}\
|
|
static CoroutineStackFrame2 *constructFrame(void *ptr, const CompiledCoroutine *compiledCoro, const CoroutineParamsBase ¶ms, const CoroutineReturnValueRefBase &returnValueRef) {\
|
|
return new (ptr) CoroStackFrame(compiledCoro, static_cast<const Params &>(params), static_cast<const CoroutineReturnValueRef<ReturnValue_t>&>(returnValueRef));\
|
|
}\
|
|
static void getFrameParameters(size_t &outSize, size_t &outAlignment) { \
|
|
outSize = sizeof(CoroStackFrame); \
|
|
outAlignment = alignof(CoroStackFrame); \
|
|
}\
|
|
};\
|
|
compiler->defineFunction(CoroStackFrame::constructFrame, CoroStackFrame::getFrameParameters); \
|
|
CORO_START_CODE_BLOCK(CoroOps::BeginFunction)
|
|
|
|
#define CORO_END_FUNCTION \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::EndFunction) \
|
|
CORO_END_CODE_BLOCK
|
|
|
|
#define CORO_AWAIT(expr) \
|
|
(expr); \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::YieldToFunction) \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::Code)
|
|
|
|
#define CORO_AWAIT_MINISCRIPT(expr) \
|
|
coroRuntime._miniscriptOutcome = ((expr)); \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::CheckMiniscript) \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::Code)
|
|
|
|
#define CORO_IF(expr) \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::IfCond) \
|
|
coroRuntime._condition = !!(expr); \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::IfBody)
|
|
|
|
#define CORO_ELSE \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::Else)
|
|
|
|
#define CORO_ELSE_IF(expr) \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::ElseIfCond) \
|
|
coroRuntime._condition = !!(expr); \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::ElseIfBody)
|
|
|
|
#define CORO_END_IF \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::EndIf)
|
|
|
|
#define CORO_WHILE(expr) \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::WhileCond) \
|
|
coroRuntime._condition = !!(expr); \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::WhileBody)
|
|
|
|
#define CORO_END_WHILE \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::EndWhile)
|
|
|
|
#define CORO_DO \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::Do)
|
|
|
|
#define CORO_DO_WHILE(expr) \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::DoWhileCond) \
|
|
coroCondition = !(expr); \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::DoWhile)
|
|
|
|
#define CORO_FOR(initExpr, condExpr, nextExpr) \
|
|
(initExpr); \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::ForNext) \
|
|
(nextExpr); \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::ForCond) \
|
|
coroRuntime._condition = !!(condExpr); \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::ForBody)
|
|
|
|
#define CORO_END_FOR \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::EndFor)
|
|
|
|
#define CORO_RETURN_VALUE(expr) \
|
|
coroReturnValueRef.set((expr)); \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::Return)
|
|
|
|
#define CORO_ERROR \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::Error)
|
|
|
|
|
|
#define CORO_RETURN \
|
|
coroReturnValueRef.voidSet(); \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::Return)
|
|
|
|
#define CORO_RETURN_CALL(func, ...) \
|
|
coroFrame->pushFrame<CoroAutoFrame<func> >(coroReturnValueRef._returnValue, __VA_ARGS__); \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::YieldToFunction) \
|
|
CORO_END_CODE_BLOCK \
|
|
CORO_START_CODE_BLOCK(CoroOps::Return)
|
|
|
|
#define CORO_SET_CALL(dest, func, ...) \
|
|
CORO_AWAIT(coroRuntime._vthread->pushCoroutineWithReturn<func>(&(dest), __VA_ARGS__))
|
|
|
|
#define CORO_CALL(func, ...) \
|
|
CORO_AWAIT(coroRuntime._vthread->pushCoroutine<func>(__VA_ARGS__))
|
|
|
|
} // Namespace MTropolis
|
|
|
|
#endif
|