media loop ok, add jumpr assembly, remove old arts

This commit is contained in:
Anthony Rabine 2023-05-24 18:06:38 +02:00
parent c2d8ccdde9
commit 0112c935d2
24 changed files with 404 additions and 72 deletions

BIN
art/hat-only-mini.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
art/hat-only.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

52
art/hat-only.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

BIN
art/logo-color2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

78
art/logo-color2.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

BIN
art/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

88
art/logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -55,7 +55,7 @@ static const uint32_t NbRegs = sizeof(AllRegs) / sizeof(AllRegs[0]);
// Keep same order than the opcodes list!! // Keep same order than the opcodes list!!
static const std::string Mnemonics[] = { static const std::string Mnemonics[] = {
"nop", "halt", "syscall", "lcons", "mov", "push", "pop", "store", "load", "add", "sub", "mul", "div", "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; static OpCode OpCodes[] = OPCODES_LIST;
@ -196,6 +196,7 @@ bool Assembler::CompileMnemonicArguments(Instr &instr)
case OP_SKIPZ: case OP_SKIPZ:
case OP_SKIPNZ: case OP_SKIPNZ:
case OP_CALL: case OP_CALL:
case OP_JUMPR:
GET_REG(instr.args[0], ra); GET_REG(instr.args[0], ra);
instr.compiledArgs.push_back(ra); instr.compiledArgs.push_back(ra);
break; break;

View file

@ -354,6 +354,13 @@ chip32_result_t chip32_step(chip32_ctx_t *ctx)
ctx->registers[PC] = _NEXT_SHORT - 1; ctx->registers[PC] = _NEXT_SHORT - 1;
break; 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_SKIPZ:
case OP_SKIPNZ: case OP_SKIPNZ:
{ {

View file

@ -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_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_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_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_JUMPR = 23, ///< jump to address contained in a register, e.g.: jumpr t9
OP_SKIPNZ = 24, ///< skip next instruction if not zero, e.g.: skipnz r2 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 INSTRUCTION_COUNT
} chip32_instruction_t; } 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_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_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_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 Whole memory is 64KB

View file

@ -8,6 +8,7 @@
; t1: increment 1 ; t1: increment 1
; t2: increment 4 ; t2: increment 4
; t3: current media address ; t3: current media address
; t4: where to jump when OK button is pressed
.media_loop_start: .media_loop_start:
load t0, @r0, 4 ; Le premier élément est le nombre de choix possibles, t0 = 3 (exemple) load t0, @r0, 4 ; Le premier élément est le nombre de choix possibles, t0 = 3 (exemple)
@ -18,12 +19,27 @@
add t3, t2 ; @++ add t3, t2 ; @++
; ------- On appelle un autre media node ; --------- We call a media transition node
push r0 ; save r0 push r0 ; save r0
load r0, @t3, 4 ; r0 = content in ram at address in T4 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 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-- sub t0, t1 ; i--
skipnz t0 ; if (r0) goto start_loop; skipnz t0 ; if (r0) goto start_loop;

View file

@ -243,6 +243,7 @@ MainWindow::MainWindow()
if ((m_dbg.run_result == VM_WAIT_EVENT) && (m_dbg.running)) if ((m_dbg.run_result == VM_WAIT_EVENT) && (m_dbg.running))
{ {
m_dbg.run_result = VM_OK;
// Continue execution of node // Continue execution of node
m_runTimer->start(100); m_runTimer->start(100);
} }
@ -478,7 +479,8 @@ bool MainWindow::event(QEvent *event)
{ {
// Result event is in R1 // Result event is in R1
m_chip32_ctx.registers[R1] = 0x01; 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; qDebug() << "SYSCALL: " << (int)code;
// Media // Media
if (code == 1) if (code == 1) // Execute media
{
if (m_chip32_ctx.registers[R0] != 0)
{ {
// image file name address is in R0 // image file name address is in R0
QString imageFile = m_model.BuildFullImagePath(GetFileNameFromMemory(m_chip32_ctx.registers[R0])); QString imageFile = m_model.BuildFullImagePath(GetFileNameFromMemory(m_chip32_ctx.registers[R0]));
qDebug() << "Image: " << imageFile;
m_ostHmiDock->SetImage(imageFile);
}
else
{
m_ostHmiDock->ClearImage();
}
if (m_chip32_ctx.registers[R1] != 0)
{
// sound file name address is in R1 // sound file name address is in R1
QString soundFile = m_model.BuildFullSoundPath(GetFileNameFromMemory(m_chip32_ctx.registers[R1])); QString soundFile = m_model.BuildFullSoundPath(GetFileNameFromMemory(m_chip32_ctx.registers[R1]));
qDebug() << ", Sound: " << soundFile;
qDebug() << "Image: " << imageFile << ", Sound: " << soundFile;
m_ostHmiDock->SetImage(imageFile);
m_model.PlaySound(soundFile); m_model.PlaySound(soundFile);
}
retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause
} }
// WAIT EVENT bits: // WAIT EVENT bits:
@ -513,7 +526,7 @@ uint8_t MainWindow::Syscall(uint8_t code)
// 3: pause button // 3: pause button
// 4: rotary left // 4: rotary left
// 5: rotary right // 5: rotary right
else if (code == 2) else if (code == 2) // Wait for event
{ {
// Event mask is located in R0 // Event mask is located in R0
// optional timeout is located in R1 // optional timeout is located in R1

View file

@ -64,6 +64,22 @@ MediaNodeModel::MediaNodeModel(StoryGraphModel &model)
}; };
m_mediaData.merge_patch(StoryNodeBase::ToJson()); 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 QString MediaNodeModel::caption() const
@ -131,29 +147,65 @@ std::string MediaNodeModel::GenerateConstants()
s += StoryProject::FileToConstant(sound); s += StoryProject::FileToConstant(sound);
} }
// FIXME: Generate choice table if needed (out ports > 1) int nb_out_conns = ComputeOutputConnections();
if (nb_out_conns > 1)
{
// 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()); std::unordered_set<ConnectionId> conns = m_model.allConnectionIds(getNodeId());
int i = 0;
int nb_out_ports = 0;
for (auto & c : conns) for (auto & c : conns)
{ {
if (c.outNodeId > 0) 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))
{ {
nb_out_ports++; ss << ", ";
} }
else
{
ss << "\n";
}
i++;
}
s += ss.str();
} }
return s; 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::string MediaNodeModel::Build()
{ {
std::stringstream ss; 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 image = StoryProject::RemoveFileExtension(m_mediaData["image"].get<std::string>());
std::string sound = StoryProject::RemoveFileExtension(m_mediaData["sound"].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) if (image.size() > 0)
{ {
ss << "lcons r0, $" << image << "\n"; ss << "lcons r0, $" << image << "\n";
@ -174,53 +226,54 @@ std::string MediaNodeModel::Build()
// Call the media executor (image, sound) // Call the media executor (image, sound)
ss << "syscall 1\n"; ss << "syscall 1\n";
NodeId id = getNodeId(); // Check output connections number
std::unordered_set<ConnectionId> conns = m_model.allConnectionIds(id); // == 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; if (nb_out_conns == 0) // End node
for (auto & c : conns)
{
if (c.inNodeId == id)
{
nb_out_ports++;
}
}
if (nb_out_ports == 0)
{ {
ss << "halt\n"; 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(); 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->setValue(count);
// m_ui.spinBox->blockSignals(true);
} }
unsigned int MediaNodeModel::nPorts(PortType portType) const 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); Q_EMIT dataUpdated(0);
} }
std::string MediaNodeModel::EntryLabel() const
{
std::stringstream ss;
ss << ".mediaEntry" << std::setw(4) << std::setfill('0') << getNodeId();
return ss.str();
}

View file

@ -63,6 +63,7 @@ public:
bool resizable() const override { return true; } bool resizable() const override { return true; }
virtual std::string EntryLabel() const override;
protected: protected:
bool eventFilter(QObject *object, QEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override;
@ -80,4 +81,6 @@ private:
nlohmann::json m_mediaData; nlohmann::json m_mediaData;
void setImage(const QString &fileName); void setImage(const QString &fileName);
int ComputeOutputConnections();
std::string ChoiceLabel() const;
}; };

View file

@ -15,3 +15,8 @@ void OstHmiDock::SetImage(const QString &fileName)
{ {
m_uiOstDisplay.display->setPixmap(QPixmap(fileName)); m_uiOstDisplay.display->setPixmap(QPixmap(fileName));
} }
void OstHmiDock::ClearImage()
{
m_uiOstDisplay.display->clear();
}

View file

@ -11,6 +11,7 @@ public:
OstHmiDock(); OstHmiDock();
void SetImage(const QString &fileName); void SetImage(const QString &fileName);
void ClearImage();
signals: signals:
void sigOkButton(); void sigOkButton();

View file

@ -523,7 +523,7 @@ std::string StoryGraphModel::Build()
NodeId firstNode = FindFirstNode(); NodeId firstNode = FindFirstNode();
chip32 << "\tjump .entry\r\n"; chip32 << "\tjump " << GetNodeEntryLabel(firstNode) << "\r\n";
// First generate all constants // First generate all constants
for (auto const nodeId : allNodeIds()) for (auto const nodeId : allNodeIds())
@ -535,27 +535,30 @@ std::string StoryGraphModel::Build()
} }
} }
nlohmann::json nodesJsonArray; nlohmann::json nodesJsonArray;
for (auto const nodeId : allNodeIds()) for (auto const nodeId : allNodeIds())
{ {
auto it = _models.find(nodeId); auto it = _models.find(nodeId);
if (it == _models.end()) if (it != _models.end())
return "";
auto &model = it->second;
if (model->getNodeId() == firstNode)
{ {
chip32 << ".entry:\r\n"; chip32 << it->second->Build() << "\n";
} }
chip32 << model->Build() << "\n";
} }
return chip32.str(); 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) void StoryGraphModel::addPort(NodeId nodeId, PortType portType, PortIndex portIndex)
{ {
// STAGE 1. // STAGE 1.

View file

@ -176,6 +176,8 @@ public:
std::string Build(); std::string Build();
///< Returns a node entry name
std::string GetNodeEntryLabel(NodeId nodeId) const;
// Centralized for wide usage // Centralized for wide usage
void PlaySound(const QString &fileName); void PlaySound(const QString &fileName);

View file

@ -60,6 +60,8 @@ public:
return ""; return "";
} }
virtual std::string EntryLabel() const = 0;
NodeGeometryData &geometryData() { return m_geometryData; } NodeGeometryData &geometryData() { return m_geometryData; }
std::string GetNodeTitle() const { return m_nodeTitle; } std::string GetNodeTitle() const { return m_nodeTitle; }