media loop ok, add jumpr assembly, remove old arts
BIN
art/hat-only-mini.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
art/hat-only.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
52
art/hat-only.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
art/header.xcf
|
Before Width: | Height: | Size: 197 KiB |
BIN
art/logo-color2.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
78
art/logo-color2.svg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
art/logo.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
88
art/logo.svg
Normal file
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 18 KiB |
BIN
art/osb-logo.kra
BIN
art/osb-logo.png
|
Before Width: | Height: | Size: 1.6 KiB |
|
|
@ -55,7 +55,7 @@ static const uint32_t NbRegs = sizeof(AllRegs) / sizeof(AllRegs[0]);
|
|||
// Keep same order than the opcodes list!!
|
||||
static const std::string Mnemonics[] = {
|
||||
"nop", "halt", "syscall", "lcons", "mov", "push", "pop", "store", "load", "add", "sub", "mul", "div",
|
||||
"shiftl", "shiftr", "ishiftr", "and", "or", "xor", "not", "call", "ret", "jump", "skipz", "skipnz"
|
||||
"shiftl", "shiftr", "ishiftr", "and", "or", "xor", "not", "call", "ret", "jump", "jumpr", "skipz", "skipnz"
|
||||
};
|
||||
|
||||
static OpCode OpCodes[] = OPCODES_LIST;
|
||||
|
|
@ -196,6 +196,7 @@ bool Assembler::CompileMnemonicArguments(Instr &instr)
|
|||
case OP_SKIPZ:
|
||||
case OP_SKIPNZ:
|
||||
case OP_CALL:
|
||||
case OP_JUMPR:
|
||||
GET_REG(instr.args[0], ra);
|
||||
instr.compiledArgs.push_back(ra);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -354,6 +354,13 @@ chip32_result_t chip32_step(chip32_ctx_t *ctx)
|
|||
ctx->registers[PC] = _NEXT_SHORT - 1;
|
||||
break;
|
||||
}
|
||||
case OP_JUMPR:
|
||||
{
|
||||
const uint8_t reg = _NEXT_BYTE;
|
||||
_CHECK_REGISTER_VALID(reg)
|
||||
ctx->registers[PC] = ctx->registers[reg] - 1;
|
||||
break;
|
||||
}
|
||||
case OP_SKIPZ:
|
||||
case OP_SKIPNZ:
|
||||
{
|
||||
|
|
|
|||
|
|
@ -76,8 +76,9 @@ typedef enum
|
|||
OP_CALL = 20, ///< set register RA to the next instruction and jump to subroutine, e.g.: call 0x10 0x00
|
||||
OP_RET = 21, ///< return to the address of last callee (RA), e.g.: ret
|
||||
OP_JUMP = 22, ///< jump to address (can use label or address), e.g.: jump .my_label
|
||||
OP_SKIPZ = 23, ///< skip next instruction if zero, e.g.: skipz r0
|
||||
OP_SKIPNZ = 24, ///< skip next instruction if not zero, e.g.: skipnz r2
|
||||
OP_JUMPR = 23, ///< jump to address contained in a register, e.g.: jumpr t9
|
||||
OP_SKIPZ = 24, ///< skip next instruction if zero, e.g.: skipz r0
|
||||
OP_SKIPNZ = 25, ///< skip next instruction if not zero, e.g.: skipnz r2
|
||||
|
||||
INSTRUCTION_COUNT
|
||||
} chip32_instruction_t;
|
||||
|
|
@ -153,7 +154,7 @@ typedef struct {
|
|||
{ OP_STORE, 3, 4 }, { OP_LOAD, 3, 4 }, { OP_ADD, 2, 2 }, { OP_SUB, 2, 2 }, { OP_MUL, 2, 2 }, \
|
||||
{ OP_DIV, 2, 2 }, { OP_SHL, 2, 2 }, { OP_SHR, 2, 2 }, { OP_ISHR, 2, 2 }, { OP_AND, 2, 2 }, \
|
||||
{ OP_OR, 2, 2 }, { OP_XOR, 2, 2 }, { OP_NOT, 1, 1 }, { OP_CALL, 1, 1 }, { OP_RET, 0, 0 }, \
|
||||
{ OP_JUMP, 1, 2 }, { OP_SKIPZ, 1, 1 }, { OP_SKIPNZ, 1, 1 } }
|
||||
{ OP_JUMP, 1, 2 }, { OP_JUMPR, 1, 1 }, { OP_SKIPZ, 1, 1 }, { OP_SKIPNZ, 1, 1 } }
|
||||
|
||||
/**
|
||||
Whole memory is 64KB
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
; t1: increment 1
|
||||
; t2: increment 4
|
||||
; t3: current media address
|
||||
; t4: where to jump when OK button is pressed
|
||||
|
||||
.media_loop_start:
|
||||
load t0, @r0, 4 ; Le premier élément est le nombre de choix possibles, t0 = 3 (exemple)
|
||||
|
|
@ -18,12 +19,27 @@
|
|||
add t3, t2 ; @++
|
||||
|
||||
|
||||
; ------- On appelle un autre media node
|
||||
; --------- We call a media transition node
|
||||
push r0 ; save r0
|
||||
load r0, @t3, 4 ; r0 = content in ram at address in T4
|
||||
call r0
|
||||
call r0 ; call subroutine
|
||||
|
||||
; Return argument in R0: the address of the node to call whe OK is pressed
|
||||
; save it in t4
|
||||
mov t4, r0
|
||||
pop r0
|
||||
; TODO: wait for event
|
||||
|
||||
; wait for event (OK or wheel)
|
||||
syscall 2
|
||||
; Event is stored in R0
|
||||
|
||||
; ----- Test if event is OK button
|
||||
lcons r1, 1 ; mask for OK button
|
||||
and r1, r0 ; r1 = r1 AND r0
|
||||
skipz r1 ; not OK, skip jump
|
||||
jumpr t4 ; we do not plan to return here, so a jump is enough
|
||||
|
||||
; all other events mean: next node
|
||||
|
||||
sub t0, t1 ; i--
|
||||
skipnz t0 ; if (r0) goto start_loop;
|
||||
|
|
|
|||
|
|
@ -243,6 +243,7 @@ MainWindow::MainWindow()
|
|||
|
||||
if ((m_dbg.run_result == VM_WAIT_EVENT) && (m_dbg.running))
|
||||
{
|
||||
m_dbg.run_result = VM_OK;
|
||||
// Continue execution of node
|
||||
m_runTimer->start(100);
|
||||
}
|
||||
|
|
@ -478,7 +479,8 @@ bool MainWindow::event(QEvent *event)
|
|||
{
|
||||
// Result event is in R1
|
||||
m_chip32_ctx.registers[R1] = 0x01;
|
||||
stepInstruction();
|
||||
m_dbg.run_result = VM_OK;
|
||||
m_runTimer->start(100);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -494,16 +496,27 @@ uint8_t MainWindow::Syscall(uint8_t code)
|
|||
qDebug() << "SYSCALL: " << (int)code;
|
||||
|
||||
// Media
|
||||
if (code == 1)
|
||||
if (code == 1) // Execute media
|
||||
{
|
||||
// image file name address is in R0
|
||||
QString imageFile = m_model.BuildFullImagePath(GetFileNameFromMemory(m_chip32_ctx.registers[R0]));
|
||||
// sound file name address is in R1
|
||||
QString soundFile = m_model.BuildFullSoundPath(GetFileNameFromMemory(m_chip32_ctx.registers[R1]));
|
||||
if (m_chip32_ctx.registers[R0] != 0)
|
||||
{
|
||||
// image file name address is in R0
|
||||
QString imageFile = m_model.BuildFullImagePath(GetFileNameFromMemory(m_chip32_ctx.registers[R0]));
|
||||
qDebug() << "Image: " << imageFile;
|
||||
m_ostHmiDock->SetImage(imageFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ostHmiDock->ClearImage();
|
||||
}
|
||||
|
||||
qDebug() << "Image: " << imageFile << ", Sound: " << soundFile;
|
||||
m_ostHmiDock->SetImage(imageFile);
|
||||
m_model.PlaySound(soundFile);
|
||||
if (m_chip32_ctx.registers[R1] != 0)
|
||||
{
|
||||
// sound file name address is in R1
|
||||
QString soundFile = m_model.BuildFullSoundPath(GetFileNameFromMemory(m_chip32_ctx.registers[R1]));
|
||||
qDebug() << ", Sound: " << soundFile;
|
||||
m_model.PlaySound(soundFile);
|
||||
}
|
||||
retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause
|
||||
}
|
||||
// WAIT EVENT bits:
|
||||
|
|
@ -513,7 +526,7 @@ uint8_t MainWindow::Syscall(uint8_t code)
|
|||
// 3: pause button
|
||||
// 4: rotary left
|
||||
// 5: rotary right
|
||||
else if (code == 2)
|
||||
else if (code == 2) // Wait for event
|
||||
{
|
||||
// Event mask is located in R0
|
||||
// optional timeout is located in R1
|
||||
|
|
|
|||
|
|
@ -64,6 +64,22 @@ MediaNodeModel::MediaNodeModel(StoryGraphModel &model)
|
|||
};
|
||||
|
||||
m_mediaData.merge_patch(StoryNodeBase::ToJson());
|
||||
|
||||
QtNodes::NodeStyle _nodeStyle;
|
||||
|
||||
QColor bgColor = QColor(94, 94, 94);
|
||||
_nodeStyle.GradientColor0 = bgColor;
|
||||
_nodeStyle.GradientColor1 = bgColor;
|
||||
_nodeStyle.GradientColor2 = bgColor;
|
||||
_nodeStyle.GradientColor3 = bgColor;
|
||||
_nodeStyle.NormalBoundaryColor = bgColor;
|
||||
_nodeStyle.FontColor = QColor(206, 206, 206);
|
||||
_nodeStyle.FontColorFaded = QColor(125, 125, 125);
|
||||
_nodeStyle.ShadowColor = QColor(20, 20, 20);
|
||||
_nodeStyle.ConnectionPointColor = QColor(125, 125, 125);
|
||||
_nodeStyle.FilledConnectionPointColor = QColor(206, 206, 206);
|
||||
|
||||
setNodeStyle(_nodeStyle);
|
||||
}
|
||||
|
||||
QString MediaNodeModel::caption() const
|
||||
|
|
@ -131,29 +147,65 @@ std::string MediaNodeModel::GenerateConstants()
|
|||
s += StoryProject::FileToConstant(sound);
|
||||
}
|
||||
|
||||
// FIXME: Generate choice table if needed (out ports > 1)
|
||||
std::unordered_set<ConnectionId> conns = m_model.allConnectionIds(getNodeId());
|
||||
|
||||
int nb_out_ports = 0;
|
||||
|
||||
for (auto & c : conns)
|
||||
int nb_out_conns = ComputeOutputConnections();
|
||||
if (nb_out_conns > 1)
|
||||
{
|
||||
if (c.outNodeId > 0)
|
||||
// Generate choice table if needed (out ports > 1)
|
||||
std::stringstream ss;
|
||||
std::string label = ChoiceLabel();
|
||||
ss << "$" << label
|
||||
<< " DC32, "
|
||||
<< nb_out_conns << ", ";
|
||||
|
||||
std::unordered_set<ConnectionId> conns = m_model.allConnectionIds(getNodeId());
|
||||
int i = 0;
|
||||
for (auto & c : conns)
|
||||
{
|
||||
nb_out_ports++;
|
||||
std::stringstream ssChoice;
|
||||
|
||||
// On va chercher le label d'entrée du noeud connecté à l'autre bout
|
||||
ss << m_model.GetNodeEntryLabel(c.inNodeId);
|
||||
if (i < (nb_out_conns - 1))
|
||||
{
|
||||
ss << ", ";
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << "\n";
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
s += ss.str();
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string MediaNodeModel::ChoiceLabel() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "mediaChoice" << std::setw(4) << std::setfill('0') << getNodeId();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string MediaNodeModel::Build()
|
||||
{
|
||||
std::stringstream ss;
|
||||
int nb_out_conns = ComputeOutputConnections();
|
||||
|
||||
ss << R"(; ---------------- )" << GetNodeTitle() << "\n";
|
||||
ss << R"(; ---------------------------- )"
|
||||
<< GetNodeTitle()
|
||||
<< " Type: "
|
||||
<< (nb_out_conns == 0 ? "End" : nb_out_conns == 1 ? "Transition" : "Choice")
|
||||
<< "\n";
|
||||
std::string image = StoryProject::RemoveFileExtension(m_mediaData["image"].get<std::string>());
|
||||
std::string sound = StoryProject::RemoveFileExtension(m_mediaData["sound"].get<std::string>());
|
||||
|
||||
// Le label de ce noeud est généré de la façon suivante :
|
||||
// "media" + Node ID + id du noeud parent. Si pas de noeud parent, alors rien
|
||||
ss << EntryLabel() << ":\n";
|
||||
|
||||
if (image.size() > 0)
|
||||
{
|
||||
ss << "lcons r0, $" << image << "\n";
|
||||
|
|
@ -174,53 +226,54 @@ std::string MediaNodeModel::Build()
|
|||
// Call the media executor (image, sound)
|
||||
ss << "syscall 1\n";
|
||||
|
||||
NodeId id = getNodeId();
|
||||
std::unordered_set<ConnectionId> conns = m_model.allConnectionIds(id);
|
||||
// Check output connections number
|
||||
// == 0: end node : generate halt
|
||||
// == 1: transition node : image + sound on demand, jump directly to the other node when OK
|
||||
// > 1 : choice node : call the node choice manager
|
||||
|
||||
int nb_out_ports = 0;
|
||||
|
||||
for (auto & c : conns)
|
||||
{
|
||||
if (c.inNodeId == id)
|
||||
{
|
||||
nb_out_ports++;
|
||||
}
|
||||
}
|
||||
|
||||
if (nb_out_ports == 0)
|
||||
if (nb_out_conns == 0) // End node
|
||||
{
|
||||
ss << "halt\n";
|
||||
}
|
||||
else
|
||||
else if (nb_out_conns == 1) // Transition node
|
||||
{
|
||||
std::unordered_set<ConnectionId> conns = m_model.allConnectionIds(getNodeId());
|
||||
|
||||
auto it = conns.begin();
|
||||
++it;
|
||||
// On place dans R0 le prochain noeud à exécuter en cas de OK
|
||||
ss << "lcons r0, "
|
||||
<< m_model.GetNodeEntryLabel(it->inNodeId) << "\n"
|
||||
<< "ret\n";
|
||||
}
|
||||
else // Choice node
|
||||
{
|
||||
ss << "lcons r0, $" << ChoiceLabel() << "\n"
|
||||
<< "jump .media ; no return possible, so a jump is enough";
|
||||
}
|
||||
|
||||
// Check output connections number
|
||||
// == 0: end, generate halt
|
||||
// == 1: jump directly to the other node
|
||||
// > 1 : call the node choice manager
|
||||
|
||||
// lcons r0, $ChoiceObject
|
||||
// jump .media ; no return possible, so a jump is enough
|
||||
|
||||
|
||||
/*
|
||||
|
||||
|
||||
|
||||
syscall 1
|
||||
lcons r0, $ChoiceObject
|
||||
jump .media ; no return possible, so a jump is enough
|
||||
*/
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void MediaNodeModel::SetOutPortCount(int count) {
|
||||
int MediaNodeModel::ComputeOutputConnections()
|
||||
{
|
||||
NodeId id = getNodeId();
|
||||
std::unordered_set<ConnectionId> conns = m_model.allConnectionIds(id);
|
||||
|
||||
// m_ui.spinBox->blockSignals(true);
|
||||
int nb_out_conns = 0;
|
||||
|
||||
for (auto & c : conns)
|
||||
{
|
||||
if (c.outNodeId == id)
|
||||
{
|
||||
nb_out_conns++;
|
||||
}
|
||||
}
|
||||
return nb_out_conns;
|
||||
}
|
||||
|
||||
void MediaNodeModel::SetOutPortCount(int count)
|
||||
{
|
||||
m_ui.spinBox->setValue(count);
|
||||
// m_ui.spinBox->blockSignals(true);
|
||||
}
|
||||
|
||||
unsigned int MediaNodeModel::nPorts(PortType portType) const
|
||||
|
|
@ -286,3 +339,10 @@ void MediaNodeModel::setInData(std::shared_ptr<NodeData> nodeData, PortIndex con
|
|||
|
||||
Q_EMIT dataUpdated(0);
|
||||
}
|
||||
|
||||
std::string MediaNodeModel::EntryLabel() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << ".mediaEntry" << std::setw(4) << std::setfill('0') << getNodeId();
|
||||
return ss.str();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ public:
|
|||
|
||||
bool resizable() const override { return true; }
|
||||
|
||||
virtual std::string EntryLabel() const override;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
|
|
@ -80,4 +81,6 @@ private:
|
|||
|
||||
nlohmann::json m_mediaData;
|
||||
void setImage(const QString &fileName);
|
||||
int ComputeOutputConnections();
|
||||
std::string ChoiceLabel() const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,3 +15,8 @@ void OstHmiDock::SetImage(const QString &fileName)
|
|||
{
|
||||
m_uiOstDisplay.display->setPixmap(QPixmap(fileName));
|
||||
}
|
||||
|
||||
void OstHmiDock::ClearImage()
|
||||
{
|
||||
m_uiOstDisplay.display->clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ public:
|
|||
OstHmiDock();
|
||||
|
||||
void SetImage(const QString &fileName);
|
||||
void ClearImage();
|
||||
|
||||
signals:
|
||||
void sigOkButton();
|
||||
|
|
|
|||
|
|
@ -523,7 +523,7 @@ std::string StoryGraphModel::Build()
|
|||
|
||||
NodeId firstNode = FindFirstNode();
|
||||
|
||||
chip32 << "\tjump .entry\r\n";
|
||||
chip32 << "\tjump " << GetNodeEntryLabel(firstNode) << "\r\n";
|
||||
|
||||
// First generate all constants
|
||||
for (auto const nodeId : allNodeIds())
|
||||
|
|
@ -535,27 +535,30 @@ std::string StoryGraphModel::Build()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
nlohmann::json nodesJsonArray;
|
||||
for (auto const nodeId : allNodeIds())
|
||||
{
|
||||
auto it = _models.find(nodeId);
|
||||
if (it == _models.end())
|
||||
return "";
|
||||
|
||||
auto &model = it->second;
|
||||
if (model->getNodeId() == firstNode)
|
||||
if (it != _models.end())
|
||||
{
|
||||
chip32 << ".entry:\r\n";
|
||||
chip32 << it->second->Build() << "\n";
|
||||
}
|
||||
|
||||
chip32 << model->Build() << "\n";
|
||||
}
|
||||
|
||||
return chip32.str();
|
||||
}
|
||||
|
||||
std::string StoryGraphModel::GetNodeEntryLabel(NodeId nodeId) const
|
||||
{
|
||||
std::string label;
|
||||
auto it = _models.find(nodeId);
|
||||
if (it != _models.end())
|
||||
{
|
||||
label = it->second->EntryLabel();
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
void StoryGraphModel::addPort(NodeId nodeId, PortType portType, PortIndex portIndex)
|
||||
{
|
||||
// STAGE 1.
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ public:
|
|||
|
||||
std::string Build();
|
||||
|
||||
///< Returns a node entry name
|
||||
std::string GetNodeEntryLabel(NodeId nodeId) const;
|
||||
|
||||
// Centralized for wide usage
|
||||
void PlaySound(const QString &fileName);
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ public:
|
|||
return "";
|
||||
}
|
||||
|
||||
virtual std::string EntryLabel() const = 0;
|
||||
|
||||
NodeGeometryData &geometryData() { return m_geometryData; }
|
||||
|
||||
std::string GetNodeTitle() const { return m_nodeTitle; }
|
||||
|
|
|
|||