diff --git a/art/hat-only-mini.png b/art/hat-only-mini.png
new file mode 100644
index 0000000..6a3322e
Binary files /dev/null and b/art/hat-only-mini.png differ
diff --git a/art/hat-only.png b/art/hat-only.png
new file mode 100644
index 0000000..5ba39e3
Binary files /dev/null and b/art/hat-only.png differ
diff --git a/art/hat-only.svg b/art/hat-only.svg
new file mode 100644
index 0000000..f0a958b
--- /dev/null
+++ b/art/hat-only.svg
@@ -0,0 +1,52 @@
+
+
+
+
diff --git a/art/header.xcf b/art/header.xcf
deleted file mode 100644
index 91c9f63..0000000
Binary files a/art/header.xcf and /dev/null differ
diff --git a/art/header_1400x500.png b/art/header_1400x500.png
deleted file mode 100644
index 196dac7..0000000
Binary files a/art/header_1400x500.png and /dev/null differ
diff --git a/art/logo-color2.png b/art/logo-color2.png
new file mode 100644
index 0000000..5427504
Binary files /dev/null and b/art/logo-color2.png differ
diff --git a/art/logo-color2.svg b/art/logo-color2.svg
new file mode 100644
index 0000000..50ac19a
--- /dev/null
+++ b/art/logo-color2.svg
@@ -0,0 +1,78 @@
+
+
+
+
diff --git a/art/logo.png b/art/logo.png
new file mode 100644
index 0000000..200d4a8
Binary files /dev/null and b/art/logo.png differ
diff --git a/art/logo.svg b/art/logo.svg
new file mode 100644
index 0000000..7531e7c
--- /dev/null
+++ b/art/logo.svg
@@ -0,0 +1,88 @@
+
+
+
+
diff --git a/art/osb-logo-scale320x240.png b/art/osb-logo-scale320x240.png
deleted file mode 100644
index aafca90..0000000
Binary files a/art/osb-logo-scale320x240.png and /dev/null differ
diff --git a/art/osb-logo.kra b/art/osb-logo.kra
deleted file mode 100644
index fd4e835..0000000
Binary files a/art/osb-logo.kra and /dev/null differ
diff --git a/art/osb-logo.png b/art/osb-logo.png
deleted file mode 100644
index e74c29f..0000000
Binary files a/art/osb-logo.png and /dev/null differ
diff --git a/software/chip32/chip32_assembler.cpp b/software/chip32/chip32_assembler.cpp
index 3ef4a75..1021720 100644
--- a/software/chip32/chip32_assembler.cpp
+++ b/software/chip32/chip32_assembler.cpp
@@ -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;
diff --git a/software/chip32/chip32_vm.c b/software/chip32/chip32_vm.c
index 374beb6..ef2aabc 100644
--- a/software/chip32/chip32_vm.c
+++ b/software/chip32/chip32_vm.c
@@ -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:
{
diff --git a/software/chip32/chip32_vm.h b/software/chip32/chip32_vm.h
index 98c8dd0..cc97d77 100644
--- a/software/chip32/chip32_vm.h
+++ b/software/chip32/chip32_vm.h
@@ -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
diff --git a/story-editor/scripts/media.asm b/story-editor/scripts/media.asm
index 74d7c86..112e139 100644
--- a/story-editor/scripts/media.asm
+++ b/story-editor/scripts/media.asm
@@ -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;
diff --git a/story-editor/src/main_window.cpp b/story-editor/src/main_window.cpp
index 5e14569..7e53a8d 100644
--- a/story-editor/src/main_window.cpp
+++ b/story-editor/src/main_window.cpp
@@ -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
diff --git a/story-editor/src/media_node_model.cpp b/story-editor/src/media_node_model.cpp
index 9b4ed37..9c6d30e 100644
--- a/story-editor/src/media_node_model.cpp
+++ b/story-editor/src/media_node_model.cpp
@@ -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 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 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 sound = StoryProject::RemoveFileExtension(m_mediaData["sound"].get());
+
+ // 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 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 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 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, 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();
+}
diff --git a/story-editor/src/media_node_model.h b/story-editor/src/media_node_model.h
index d31cb3c..6c9eab4 100644
--- a/story-editor/src/media_node_model.h
+++ b/story-editor/src/media_node_model.h
@@ -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;
};
diff --git a/story-editor/src/osthmi_dock.cpp b/story-editor/src/osthmi_dock.cpp
index 54b589f..9680954 100644
--- a/story-editor/src/osthmi_dock.cpp
+++ b/story-editor/src/osthmi_dock.cpp
@@ -15,3 +15,8 @@ void OstHmiDock::SetImage(const QString &fileName)
{
m_uiOstDisplay.display->setPixmap(QPixmap(fileName));
}
+
+void OstHmiDock::ClearImage()
+{
+ m_uiOstDisplay.display->clear();
+}
diff --git a/story-editor/src/osthmi_dock.h b/story-editor/src/osthmi_dock.h
index 1a8ac3f..74238ce 100644
--- a/story-editor/src/osthmi_dock.h
+++ b/story-editor/src/osthmi_dock.h
@@ -11,6 +11,7 @@ public:
OstHmiDock();
void SetImage(const QString &fileName);
+ void ClearImage();
signals:
void sigOkButton();
diff --git a/story-editor/src/story_graph_model.cpp b/story-editor/src/story_graph_model.cpp
index b9b1abd..0d2600c 100644
--- a/story-editor/src/story_graph_model.cpp
+++ b/story-editor/src/story_graph_model.cpp
@@ -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.
diff --git a/story-editor/src/story_graph_model.h b/story-editor/src/story_graph_model.h
index 7dd669e..e26ead2 100644
--- a/story-editor/src/story_graph_model.h
+++ b/story-editor/src/story_graph_model.h
@@ -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);
diff --git a/story-editor/src/story_node_base.h b/story-editor/src/story_node_base.h
index b0c465b..11c6e0f 100644
--- a/story-editor/src/story_node_base.h
+++ b/story-editor/src/story_node_base.h
@@ -60,6 +60,8 @@ public:
return "";
}
+ virtual std::string EntryLabel() const = 0;
+
NodeGeometryData &geometryData() { return m_geometryData; }
std::string GetNodeTitle() const { return m_nodeTitle; }