mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
Add comparison chip32 instructions, add debugger, add CPU window
This commit is contained in:
parent
5ed9233778
commit
fcc3562ecd
24 changed files with 399 additions and 86 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "story-editor/libs/SDL_image"]
|
||||
path = story-editor/libs/SDL_image
|
||||
url = git@github.com:libsdl-org/SDL_image.git
|
||||
21
firmware/chip32/chip32-syntax/LICENSE
Normal file
21
firmware/chip32/chip32-syntax/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Anthony Rabine
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
BIN
firmware/chip32/chip32-syntax/chip32-syntax-0.0.1.vsix
Normal file
BIN
firmware/chip32/chip32-syntax/chip32-syntax-0.0.1.vsix
Normal file
Binary file not shown.
29
firmware/chip32/chip32-syntax/package.json
Normal file
29
firmware/chip32/chip32-syntax/package.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "chip32-syntax",
|
||||
"displayName": "Chip32 Syntax Highlighting",
|
||||
"description": "Syntax highlighting for Chip32 assembly language",
|
||||
"version": "0.0.1",
|
||||
"publisher": "Anthony Rabine",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/arabine/open-story-teller"},
|
||||
"engines": {
|
||||
"vscode": "^1.0.0"
|
||||
},
|
||||
"categories": [
|
||||
"Programming Languages"
|
||||
],
|
||||
"contributes": {
|
||||
"languages": [{
|
||||
"id": "chip32",
|
||||
"extensions": [".chip32"],
|
||||
"aliases": ["Chip32", "chip32"]
|
||||
}],
|
||||
"grammars": [{
|
||||
"language": "chip32",
|
||||
"scopeName": "source.assembly",
|
||||
"path": "./syntaxes/chip32.tmLanguage.json"
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
||||
"name": "Chip32",
|
||||
"patterns": [
|
||||
{
|
||||
"match": "\\b(nop|halt|syscall|lcons|mov|push|pop|store|load|add|sub|mul|div|shiftl|shiftr|ishiftr|and|or|xor|not|call|ret|jump|jumpr|skipz|skipnz|eq|gt|lt)\\b",
|
||||
"name": "keyword.control.assembly"
|
||||
},
|
||||
{
|
||||
"match": "\\b(r[0-9]+|t[0-9]+|pc|sp|ra)\\b",
|
||||
"name": "variable.parameter.register.assembly"
|
||||
},
|
||||
{
|
||||
"match": "@",
|
||||
"name": "keyword.operator.assembly"
|
||||
},
|
||||
{
|
||||
"match": "\\b(0x[0-9a-fA-F]+)\\b",
|
||||
"name": "constant.numeric.hex.assembly"
|
||||
},
|
||||
{
|
||||
"match": ";.*",
|
||||
"name": "comment.line.assembly"
|
||||
}
|
||||
],
|
||||
"scopeName": "source.assembly"
|
||||
}
|
||||
|
|
@ -55,7 +55,8 @@ 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", "jumpr", "skipz", "skipnz"
|
||||
"shiftl", "shiftr", "ishiftr", "and", "or", "xor", "not", "call", "ret", "jump", "jumpr", "skipz", "skipnz",
|
||||
"eq", "gt", "lt"
|
||||
};
|
||||
|
||||
static OpCode OpCodes[] = OPCODES_LIST;
|
||||
|
|
@ -166,7 +167,7 @@ bool Assembler::GetRegisterName(uint8_t reg, std::string ®Name)
|
|||
|
||||
bool Assembler::CompileMnemonicArguments(Instr &instr)
|
||||
{
|
||||
uint8_t ra, rb;
|
||||
uint8_t ra, rb, rc;
|
||||
|
||||
switch(instr.code.opcode)
|
||||
{
|
||||
|
|
@ -239,6 +240,16 @@ bool Assembler::CompileMnemonicArguments(Instr &instr)
|
|||
instr.compiledArgs.push_back(rb);
|
||||
instr.compiledArgs.push_back(static_cast<uint32_t>(strtol(instr.args[2].c_str(), NULL, 0)));
|
||||
break;
|
||||
case OP_CMP_EQ:
|
||||
case OP_CMP_GT:
|
||||
case OP_CMP_LT:
|
||||
GET_REG(instr.args[0], ra);
|
||||
GET_REG(instr.args[1], rb);
|
||||
GET_REG(instr.args[2], rc);
|
||||
instr.compiledArgs.push_back(ra);
|
||||
instr.compiledArgs.push_back(rb);
|
||||
instr.compiledArgs.push_back(rc);
|
||||
break;
|
||||
default:
|
||||
CHIP32_CHECK(instr, false, "Unsupported mnemonic: " + instr.mnemonic);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -85,6 +85,18 @@ static inline uint32_t _NEXT_INT (chip32_ctx_t *ctx)
|
|||
static const OpCode OpCodes[] = OPCODES_LIST;
|
||||
static const uint16_t OpCodesSize = sizeof(OpCodes) / sizeof(OpCodes[0]);
|
||||
|
||||
|
||||
static void push(chip32_ctx_t *ctx, uint32_t val) {
|
||||
ctx->registers[SP] -= 4;
|
||||
ctx->ram.mem[ctx->registers[SP]] = val;
|
||||
}
|
||||
|
||||
static uint32_t pop(chip32_ctx_t *ctx) {
|
||||
uint32_t val = ctx->ram.mem[ctx->registers[SP]];
|
||||
ctx->registers[SP] += 4;
|
||||
return val;
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
// FUNCTIONS
|
||||
// =======================================================================================
|
||||
|
|
@ -162,8 +174,7 @@ chip32_result_t chip32_step(chip32_ctx_t *ctx)
|
|||
const uint8_t reg = _NEXT_BYTE;
|
||||
_CHECK_REGISTER_VALID(reg)
|
||||
_CHECK_CAN_PUSH(1)
|
||||
ctx->registers[SP] -= 4;
|
||||
memcpy(&ctx->ram.mem[ctx->registers[SP]], &ctx->registers[reg], sizeof(uint32_t));
|
||||
push(ctx, ctx->registers[reg]);
|
||||
break;
|
||||
}
|
||||
case OP_POP:
|
||||
|
|
@ -171,8 +182,7 @@ chip32_result_t chip32_step(chip32_ctx_t *ctx)
|
|||
const uint8_t reg = _NEXT_BYTE;
|
||||
_CHECK_REGISTER_VALID(reg)
|
||||
_CHECK_CAN_POP(1)
|
||||
memcpy(&ctx->registers[reg], &ctx->ram.mem[ctx->registers[SP]], sizeof(uint32_t));
|
||||
ctx->registers[SP] += 4;
|
||||
ctx->registers[reg] = pop(ctx);
|
||||
break;
|
||||
}
|
||||
case OP_CALL:
|
||||
|
|
@ -181,11 +191,23 @@ chip32_result_t chip32_step(chip32_ctx_t *ctx)
|
|||
const uint8_t reg = _NEXT_BYTE;
|
||||
_CHECK_REGISTER_VALID(reg)
|
||||
ctx->registers[PC] = ctx->registers[reg] - 1;
|
||||
|
||||
// Save Tx registers on stack
|
||||
_CHECK_CAN_POP(10)
|
||||
for (int i = 0; i < 10; i++) {
|
||||
push(ctx, ctx->registers[T0 + i]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case OP_RET:
|
||||
{
|
||||
ctx->registers[PC] = ctx->registers[RA] - 1;
|
||||
|
||||
// restore Tx registers from stack
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ctx->registers[T9 - i] = pop(ctx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_STORE:
|
||||
|
|
@ -353,6 +375,40 @@ chip32_result_t chip32_step(chip32_ctx_t *ctx)
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_CMP_EQ:
|
||||
{
|
||||
const uint8_t reg1 = _NEXT_BYTE;
|
||||
const uint8_t reg2 = _NEXT_BYTE;
|
||||
const uint8_t reg3 = _NEXT_BYTE;
|
||||
_CHECK_REGISTER_VALID(reg1)
|
||||
_CHECK_REGISTER_VALID(reg2)
|
||||
_CHECK_REGISTER_VALID(reg3)
|
||||
ctx->registers[reg1] = ctx->registers[reg2] == ctx->registers[reg3] ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
case OP_CMP_GT:
|
||||
{
|
||||
const uint8_t reg1 = _NEXT_BYTE;
|
||||
const uint8_t reg2 = _NEXT_BYTE;
|
||||
const uint8_t reg3 = _NEXT_BYTE;
|
||||
_CHECK_REGISTER_VALID(reg1)
|
||||
_CHECK_REGISTER_VALID(reg2)
|
||||
_CHECK_REGISTER_VALID(reg3)
|
||||
ctx->registers[reg1] = ctx->registers[reg2] > ctx->registers[reg3] ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
case OP_CMP_LT:
|
||||
{
|
||||
const uint8_t reg1 = _NEXT_BYTE;
|
||||
const uint8_t reg2 = _NEXT_BYTE;
|
||||
const uint8_t reg3 = _NEXT_BYTE;
|
||||
_CHECK_REGISTER_VALID(reg1)
|
||||
_CHECK_REGISTER_VALID(reg2)
|
||||
_CHECK_REGISTER_VALID(reg3)
|
||||
ctx->registers[reg1] = ctx->registers[reg2] < ctx->registers[reg3] ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->registers[PC]++;
|
||||
|
|
|
|||
|
|
@ -80,6 +80,11 @@ typedef enum
|
|||
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
|
||||
|
||||
// Comparison
|
||||
OP_CMP_EQ = 26, ///< compare two registers for equality, result in first e.g.: cmp_eq r4, r0, r1 (r4 = (r0 == r1 ? 1 : 0)
|
||||
OP_CMP_GT = 27, ///< compare if first register is greater than the second, result in first e.g.: cmp_gt r4, r0, r1
|
||||
OP_CMP_LT = 28, ///< compare if first register is less than the second, result in first e.g.: cmp_lt r4, r0, r1
|
||||
|
||||
INSTRUCTION_COUNT
|
||||
} chip32_instruction_t;
|
||||
|
||||
|
|
@ -152,10 +157,11 @@ typedef struct {
|
|||
|
||||
#define OPCODES_LIST { { OP_NOP, 0, 0 }, { OP_HALT, 0, 0 }, { OP_SYSCALL, 1, 1 }, { OP_LCONS, 2, 5 }, \
|
||||
{ OP_MOV, 2, 2 }, { OP_PUSH, 1, 1 }, {OP_POP, 1, 1 }, \
|
||||
{ OP_STORE, 3, 4 }, { OP_LOAD, 3, 4 }, { OP_ADD, 2, 2 }, { OP_SUB, 2, 2 }, { OP_MUL, 2, 2 }, \
|
||||
{ OP_STORE, 3, 3 }, { OP_LOAD, 3, 3 }, { 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_JUMPR, 1, 1 }, { OP_SKIPZ, 1, 1 }, { OP_SKIPNZ, 1, 1 } }
|
||||
{ OP_JUMP, 1, 2 }, { OP_JUMPR, 1, 1 }, { OP_SKIPZ, 1, 1 }, { OP_SKIPNZ, 1, 1 }, \
|
||||
{ OP_CMP_EQ, 3, 3 }, { OP_CMP_GT, 3, 3 }, { OP_CMP_LT, 3, 3 } }
|
||||
|
||||
/**
|
||||
Whole memory is 64KB
|
||||
|
|
|
|||
|
|
@ -380,7 +380,7 @@ bool StoryProject::GenerateScript(std::string &codeStr)
|
|||
// Add our utility functions
|
||||
std::string buffer;
|
||||
|
||||
std::ifstream f("scripts/media.asm");
|
||||
std::ifstream f("scripts/media.chip32");
|
||||
f.seekg(0, std::ios::end);
|
||||
buffer.resize(f.tellg());
|
||||
f.seekg(0);
|
||||
|
|
@ -580,7 +580,7 @@ std::string StoryProject::BuildFullAssetsPath(const std::string_view fileName) c
|
|||
std::string StoryProject::FileToConstant(const std::string &FileName, const std::string &extension)
|
||||
{
|
||||
std::string f = SysLib::RemoveFileExtension(FileName);
|
||||
return "$" + f + " DC8 \"" + f + "." + extension + "\", 8\r\n";
|
||||
return "$" + FileName + " DC8 \"" + FileName + "\", 8\r\n";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -207,6 +207,9 @@ set(SRCS
|
|||
src/properties_window.cpp
|
||||
src/properties_window.h
|
||||
|
||||
src/cpu_window.cpp
|
||||
src/cpu_window.h
|
||||
|
||||
src/gui.h
|
||||
src/gui.cpp
|
||||
|
||||
|
|
|
|||
|
|
@ -953,7 +953,7 @@ void TextEditor::Render()
|
|||
if (mBreakpoints.count(lineNo + 1) != 0)
|
||||
{
|
||||
auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y);
|
||||
drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::Breakpoint]);
|
||||
drawList->AddCircleFilled(ImVec2(start.x + 8, start.y + 8), 5.0f, IM_COL32(255, 0, 0, 255), 12);
|
||||
}
|
||||
|
||||
// Draw error markers
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 25e816f614dbe4aa9fdaf89c248ae283f20038f9
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
; Generic media choice manager
|
||||
.media:
|
||||
; Les adresses des différents medias sont dans la stack
|
||||
; Arguments:
|
||||
; r0: address d'une structure de type "media choice"
|
||||
; Local:
|
||||
; t0: loop counter
|
||||
; t1: increment 1
|
||||
; t2: increment 4
|
||||
; t3: current media address
|
||||
; t4: where to jump when OK button is pressed
|
||||
; t5: address of the "choice" array
|
||||
mov t5, r0 ; we save R0
|
||||
|
||||
.media_loop_start:
|
||||
load t0, @t5, 4 ; Le premier élément est le nombre de choix possibles, t0 = 3 (exemple)
|
||||
lcons t1, 1
|
||||
lcons t2, 4
|
||||
mov t3, t5
|
||||
.media_loop:
|
||||
add t3, t2 ; @++
|
||||
|
||||
; --------- We call a media transition node
|
||||
load r0, @t3, 4 ; r0 = content in ram at address in T3
|
||||
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
|
||||
|
||||
; 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;
|
||||
jump .media_loop_start
|
||||
jump .media_loop
|
||||
|
||||
70
story-editor/scripts/media.chip32
Normal file
70
story-editor/scripts/media.chip32
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
; Generic media choice manager
|
||||
.media:
|
||||
; Les adresses des différents medias sont dans la stack
|
||||
; Arguments:
|
||||
; r0: address d'une structure de type "media choice"
|
||||
; Local:
|
||||
; t0: current media address
|
||||
; t1: increment 4
|
||||
; t3: address of the first element in the choice array
|
||||
; t4: address of the last element in the choice array
|
||||
; t5: where to jump when OK button is pressed
|
||||
|
||||
mov t3, r0 ; on sauvegarde R0 (rx non préservés)
|
||||
load r0, @t3, 4 ; Le premier élément est le nombre de choix possibles, ex: r0 = 12
|
||||
lcons t1, 4
|
||||
mul r0, t1 ; on calcule l'offset: r0 = nb_elements * 4 = 48
|
||||
mov t4, t3 ; t4 = t3
|
||||
add t4, r0 ; t4 pointe maintenant sur le dernier élément de la structure
|
||||
add t3, t1 ; t3 pointe maintenant sur le premier élément
|
||||
mov t0, t3 ; on commence sur le premier élément
|
||||
|
||||
.media_loop:
|
||||
|
||||
; --------- We call a media transition node
|
||||
load r0, @t0, 4 ; Get the address located at memory T0
|
||||
call r0 ; call subroutine
|
||||
; Return argument in R0: the address of the node to call whe OK is pressed
|
||||
mov t5, r0 ; save it
|
||||
|
||||
; wait for event (OK or wheel)
|
||||
syscall 2
|
||||
; Event is stored in R0
|
||||
; 1 = ok
|
||||
; 2 = previous
|
||||
; 4 = next
|
||||
; 8 = audio finished
|
||||
|
||||
; ----- 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 t5 ; we do not plan to return here, so a jump is enough
|
||||
|
||||
; test previous event
|
||||
lcons r1, 2 ; mask for previous button
|
||||
and r1, r0 ; r1 = r1 AND r0
|
||||
skipz r1 ; not OK, skip jump
|
||||
jump .media_previous
|
||||
|
||||
; all other events mean: next node
|
||||
eq r0, t0, t4 ; t4 est le dernier élément
|
||||
skipz r0 ; zéro, on peut incrémenter l'adresse
|
||||
jump .media_set_first
|
||||
add t0, t1 ; t0 += 4
|
||||
jump .media_loop
|
||||
|
||||
.media_set_first: ; on reboucle sur le premier élément de la structure
|
||||
mov t0, t3
|
||||
jump .media_loop
|
||||
|
||||
.media_previous:
|
||||
eq r0, t0, t3 ; on teste si on est au premier élément
|
||||
skipz r0 ; zéro, on peut décrémenter l'adresse
|
||||
jump .media_set_last
|
||||
sub t0, t1 ; t0 += 4
|
||||
jump .media_loop
|
||||
|
||||
.media_set_last: ; on reboucle sur le dernier élément de la structure
|
||||
mov t0, t4
|
||||
jump .media_loop
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#include "code_editor.h"
|
||||
|
||||
#include "IconsMaterialDesignIcons.h"
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
|
|
@ -55,8 +55,6 @@ void CodeEditor::Draw()
|
|||
|
||||
auto cpos = mEditor.GetCursorPosition();
|
||||
|
||||
|
||||
|
||||
ImGui::SetWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
|
||||
if (ImGui::BeginMenuBar())
|
||||
{
|
||||
|
|
@ -119,17 +117,26 @@ void CodeEditor::Draw()
|
|||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Toggle breakpoint")) {
|
||||
if (m_breakPoints.contains(cpos.mLine))
|
||||
if (m_breakPoints.contains(cpos.mLine + 1))
|
||||
{
|
||||
m_breakPoints.erase(cpos.mLine);
|
||||
m_breakPoints.erase(cpos.mLine + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_breakPoints.insert(cpos.mLine);
|
||||
m_breakPoints.insert(cpos.mLine + 1);
|
||||
}
|
||||
mEditor.SetBreakpoints(m_breakPoints);
|
||||
|
||||
m_storyManager.ToggleBreakpoint(cpos.mLine);
|
||||
m_storyManager.ToggleBreakpoint(cpos.mLine + 1);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(ICON_MDI_SKIP_NEXT "##step_instruction")) {
|
||||
m_storyManager.Step();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(ICON_MDI_PLAY "##run")) {
|
||||
m_storyManager.Run();
|
||||
}
|
||||
|
||||
mEditor.Render("TextEditor");
|
||||
|
|
|
|||
63
story-editor/src/cpu_window.cpp
Normal file
63
story-editor/src/cpu_window.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#include "cpu_window.h"
|
||||
#include "gui.h"
|
||||
#include "ImGuiFileDialog.h"
|
||||
#include "IconsMaterialDesignIcons.h"
|
||||
#include "chip32_vm.h"
|
||||
|
||||
static const char* CpuRegNames[] = {
|
||||
"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7",
|
||||
"R8", "R9", "T0", "T1", "T2", "T3", "T4", "T5",
|
||||
"T6", "T7", "T8", "T9", "PC", "SP", "RA"
|
||||
};
|
||||
|
||||
CpuWindow::CpuWindow(IStoryManager &proj)
|
||||
: WindowBase("CPU")
|
||||
, m_story(proj)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CpuWindow::Initialize()
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void CpuWindow::Draw()
|
||||
{
|
||||
static ImVec2 size(320.0f, 240.0f);
|
||||
|
||||
|
||||
WindowBase::BeginDraw();
|
||||
ImGui::SetWindowSize(ImVec2(626, 744), ImGuiCond_FirstUseEver);
|
||||
|
||||
static ImGuiTableFlags tableFlags =
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable
|
||||
| ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti
|
||||
| ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody
|
||||
| ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY
|
||||
| ImGuiTableFlags_SizingFixedFit;
|
||||
|
||||
|
||||
if (ImGui::BeginTable("cpu_table", 2, tableFlags))
|
||||
{
|
||||
ImGui::TableSetupColumn("Register", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed);
|
||||
|
||||
for (int i = 0; i < REGISTER_COUNT; i++)
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("%s",CpuRegNames[i]);
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("0x%04X", m_story.GetRegister(i));
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
WindowBase::EndDraw();
|
||||
}
|
||||
19
story-editor/src/cpu_window.h
Normal file
19
story-editor/src/cpu_window.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "window_base.h"
|
||||
#include "i_story_manager.h"
|
||||
#include "gui.h"
|
||||
|
||||
class CpuWindow : public WindowBase
|
||||
{
|
||||
public:
|
||||
CpuWindow(IStoryManager &proj);
|
||||
|
||||
void Initialize();
|
||||
virtual void Draw() override;
|
||||
|
||||
private:
|
||||
IStoryManager &m_story;
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -54,8 +54,11 @@ public:
|
|||
virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(const std::string &nodeId) = 0;
|
||||
virtual void LoadBinaryStory(const std::string &filename) = 0;
|
||||
virtual void ToggleBreakpoint(int line) = 0;
|
||||
virtual uint32_t GetRegister(int reg) = 0;
|
||||
|
||||
virtual void Play() = 0;
|
||||
virtual void Step() = 0;
|
||||
virtual void Run() = 0;
|
||||
virtual void Ok() = 0;
|
||||
virtual void Stop() = 0;
|
||||
virtual void Pause() = 0;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ MainWindow::MainWindow()
|
|||
: m_libraryManager(*this)
|
||||
, m_emulatorWindow(*this)
|
||||
, m_codeEditorWindow(*this)
|
||||
, m_cpuWindow(*this)
|
||||
, m_resourcesWindow(*this)
|
||||
, m_nodeEditorWindow(*this)
|
||||
, m_libraryWindow(*this, m_libraryManager)
|
||||
|
|
@ -77,11 +78,22 @@ void MainWindow::Play()
|
|||
|
||||
}
|
||||
|
||||
void MainWindow::Step()
|
||||
{
|
||||
m_eventQueue.push({VmEventType::EvStep});
|
||||
}
|
||||
|
||||
void MainWindow::Run()
|
||||
{
|
||||
m_eventQueue.push({VmEventType::EvRun});
|
||||
}
|
||||
|
||||
void MainWindow::Ok()
|
||||
{
|
||||
m_eventQueue.push({VmEventType::EvOkButton});
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::Stop()
|
||||
{
|
||||
m_eventQueue.push({VmEventType::EvOkButton});
|
||||
|
|
@ -160,6 +172,12 @@ void MainWindow::ProcessStory()
|
|||
{
|
||||
if (event.type == VmEventType::EvStep)
|
||||
{
|
||||
StepInstruction();
|
||||
m_dbg.run_result = VM_OK;
|
||||
}
|
||||
else if (event.type == VmEventType::EvRun)
|
||||
{
|
||||
m_dbg.free_run = true;
|
||||
m_dbg.run_result = VM_OK;
|
||||
}
|
||||
else if (event.type == VmEventType::EvOkButton)
|
||||
|
|
@ -191,14 +209,18 @@ void MainWindow::ProcessStory()
|
|||
|
||||
if (m_dbg.run_result == VM_OK)
|
||||
{
|
||||
if (m_dbg.m_breakpoints.contains(m_dbg.line + 1))
|
||||
if (m_dbg.m_breakpoints.contains(m_dbg.line))
|
||||
{
|
||||
// Log("Breakpoint on line: " + std::to_string(m_dbg.line + 1));
|
||||
m_dbg.run_result = VM_WAIT_EVENT;
|
||||
m_dbg.free_run = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_dbg.free_run)
|
||||
{
|
||||
StepInstruction();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_dbg.run_result == VM_FINISHED)
|
||||
{
|
||||
|
|
@ -729,6 +751,7 @@ void MainWindow::OpenProject(const std::string &uuid)
|
|||
m_codeEditorWindow.Enable();
|
||||
m_resourcesWindow.Enable();
|
||||
m_PropertiesWindow.Enable();
|
||||
m_cpuWindow.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -780,6 +803,7 @@ void MainWindow::CloseProject()
|
|||
m_codeEditorWindow.Disable();
|
||||
m_resourcesWindow.Disable();
|
||||
m_PropertiesWindow.Disable();
|
||||
m_cpuWindow.Disable();
|
||||
|
||||
RefreshProjectInformation();
|
||||
}
|
||||
|
|
@ -859,6 +883,7 @@ void MainWindow::Loop()
|
|||
m_codeEditorWindow.Draw();
|
||||
m_resourcesWindow.Draw();
|
||||
m_nodeEditorWindow.Draw();
|
||||
m_cpuWindow.Draw();
|
||||
|
||||
|
||||
m_PropertiesWindow.SetSelectedNode(m_nodeEditorWindow.GetSelectedNode());
|
||||
|
|
@ -982,6 +1007,18 @@ void MainWindow::ToggleBreakpoint(int line)
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t MainWindow::GetRegister(int reg)
|
||||
{
|
||||
uint32_t regVal = 0;
|
||||
|
||||
if (reg >= 0 && reg < REGISTER_COUNT)
|
||||
{
|
||||
regVal = m_chip32_ctx.registers[reg];
|
||||
}
|
||||
|
||||
return regVal;
|
||||
}
|
||||
|
||||
void MainWindow::BuildNodes(bool compileonly)
|
||||
{
|
||||
if (m_story->GenerateScript(m_currentCode))
|
||||
|
|
@ -1060,11 +1097,10 @@ void MainWindow::UpdateVmView()
|
|||
// Highlight next line in the test editor
|
||||
uint32_t pcVal = m_chip32_ctx.registers[PC];
|
||||
|
||||
uint32_t line = 1;
|
||||
if (m_story->GetAssemblyLine(pcVal, line))
|
||||
if (m_story->GetAssemblyLine(pcVal, m_dbg.line))
|
||||
{
|
||||
m_dbg.line = (line - 1);
|
||||
m_codeEditorWindow.HighlightLine(m_dbg.line);
|
||||
std::cout << "Executing line: " << m_dbg.line << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,13 +21,14 @@
|
|||
#include "audio_player.h"
|
||||
#include "library_manager.h"
|
||||
#include "library_window.h"
|
||||
#include "cpu_window.h"
|
||||
|
||||
struct DebugContext
|
||||
{
|
||||
uint32_t event_mask{0};
|
||||
bool wait_event{0};
|
||||
bool free_run{false};
|
||||
int line{-1};
|
||||
uint32_t line{0};
|
||||
chip32_result_t run_result{VM_FINISHED};
|
||||
|
||||
std::set<int> m_breakpoints;
|
||||
|
|
@ -73,7 +74,7 @@ public:
|
|||
void Loop();
|
||||
|
||||
private:
|
||||
enum VmEventType { EvNoEvent, EvStep, EvOkButton, EvPreviousButton, EvNextButton, EvAudioFinished, EvStop};
|
||||
enum VmEventType { EvNoEvent, EvStep, EvRun, EvOkButton, EvPreviousButton, EvNextButton, EvAudioFinished, EvStop};
|
||||
|
||||
std::shared_ptr<StoryProject> m_story;
|
||||
|
||||
|
|
@ -98,6 +99,7 @@ private:
|
|||
EmulatorWindow m_emulatorWindow;
|
||||
ConsoleWindow m_consoleWindow;
|
||||
CodeEditor m_codeEditorWindow;
|
||||
CpuWindow m_cpuWindow;
|
||||
|
||||
char m_project_name[256] = "";
|
||||
|
||||
|
|
@ -140,8 +142,11 @@ private:
|
|||
virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(const std::string &nodeId) override;
|
||||
virtual void LoadBinaryStory(const std::string &filename) override;
|
||||
virtual void ToggleBreakpoint(int line) override;
|
||||
virtual uint32_t GetRegister(int reg) override;
|
||||
|
||||
virtual void Play() override;
|
||||
virtual void Step() override;
|
||||
virtual void Run() override;
|
||||
virtual void Ok() override;
|
||||
virtual void Stop() override;
|
||||
virtual void Pause() override;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public:
|
|||
void SetId(const std::string &id) { m_uuid = id; }
|
||||
std::string GetId() const { return m_uuid; }
|
||||
|
||||
void SeTitle(const std::string &title) { m_title = title; }
|
||||
void SetTitle(const std::string &title) { m_title = title; }
|
||||
std::string GetTitle() const { return m_title; }
|
||||
|
||||
void FromJson(const nlohmann::json &);
|
||||
|
|
|
|||
|
|
@ -46,8 +46,6 @@ std::string MediaNode::GenerateConstants(IStoryProject &story, int nb_out_conns)
|
|||
s += StoryProject::FileToConstant(m_sound, story.SoundExtension(m_sound)); // FIXME: Generate the extension setup in user option of output format
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Generate choice table if needed (out ports > 1)
|
||||
std::stringstream ss;
|
||||
std::string label = ChoiceLabel(GetId());
|
||||
|
|
@ -121,7 +119,7 @@ std::string MediaNode::Build(IStoryProject &story, int nb_out_conns)
|
|||
|
||||
if (img.size() > 0)
|
||||
{
|
||||
ss << "lcons r0, $" << img << "\n";
|
||||
ss << "lcons r0, $" << m_image << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -130,7 +128,7 @@ std::string MediaNode::Build(IStoryProject &story, int nb_out_conns)
|
|||
|
||||
if (snd.size() > 0)
|
||||
{
|
||||
ss << "lcons r1, $" << snd << "\n";
|
||||
ss << "lcons r1, $" << m_sound << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,10 +21,20 @@ void PropertiesWindow::Draw()
|
|||
|
||||
ImGui::SeparatorText("Selected node");
|
||||
|
||||
|
||||
static std::shared_ptr<BaseNodeWidget> prev;
|
||||
if (m_selectedNode)
|
||||
{
|
||||
static char buf1[32] = ""; ImGui::InputText("Title", buf1, 32);
|
||||
static char buf1[100] = "";
|
||||
|
||||
if (prev != m_selectedNode)
|
||||
{
|
||||
prev = m_selectedNode;
|
||||
// auto t = m_selectedNode->Base()->GetTitle();
|
||||
strncpy (buf1, m_selectedNode->Base()->GetTitle().data(), sizeof(buf1)) ;
|
||||
}
|
||||
|
||||
ImGui::InputText("Title", buf1, 32);
|
||||
m_selectedNode->Base()->SetTitle(buf1);
|
||||
ImGui::Text("Node ID: %s", m_selectedNode->Base()->GetId().data());
|
||||
m_selectedNode->DrawProperties();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ ResourcesWindow::~ResourcesWindow()
|
|||
void ResourcesWindow::ChooseFile()
|
||||
{
|
||||
static const char * soundFormats = ".wav,.mp3,.ogg,.flac";
|
||||
static const char * imagesFormats = ".bmp *.png *.jpg";
|
||||
static const char * imagesFormats = ".bmp,.png,.jpg";
|
||||
|
||||
if (m_showImportDialog)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue