Projektmodule und vollständiges TBC-Kompilat umsetzen und Change archivieren

This commit is contained in:
2026-09-05 23:06:56 +02:00
parent 58b1f620ea
commit 815825dde7
40 changed files with 4177 additions and 555 deletions

View File

@@ -70,6 +70,8 @@ struct Frame {
/// Instruktionsindex der zuletzt begonnenen Anweisung (`Stmt`).
last_stmt_pc: usize,
line: u32,
source: u32,
column: u32,
/// Gesetzt, wenn dieser Frame der Handler eines Ereignis-Traps ist.
/// Er läuft im Modulrumpf und **teilt dessen Locals** — ein eigener
/// Satz würde dem Handler leere Modulvariablen zeigen. Sein `RETURN`
@@ -117,14 +119,14 @@ pub struct Vm {
erl: u32,
/// Zuletzt durchlaufene numerische Zeilennummer (0 = keine).
zeile_nr: u32,
module_handler: Handler,
module_handlers: Vec<Handler>,
in_handler: bool,
resume_pc: usize,
// Steuerung
flags: u32,
/// Zählt Anweisungsgrenzen für die regelmäßige Ereignisabholung.
tick_zaehler: u32,
breakpoints: HashSet<u32>,
breakpoints: HashSet<(u16, u32)>,
data_ptr: usize,
start_pc: Option<usize>,
pub forms: FormsModel,
@@ -170,7 +172,15 @@ impl Vm {
.iter()
.map(|t| default_value(t, &module.udts))
.collect();
let forms = FormsModel::new(module.objects.clone(), 80, 25);
let mut forms = FormsModel::new(module.objects.clone(), 80, 25);
for initial in &module.form_initial {
initial
.apply(&mut forms)
.expect("validierte Forms-Anfangsdaten");
}
if let Some(form) = module.startup_form {
forms.show(form, false).expect("validiertes Startformular");
}
let mut vm = Vm {
globals,
locals: Vec::new(),
@@ -180,7 +190,7 @@ impl Vm {
err: 0,
erl: 0,
zeile_nr: 0,
module_handler: Handler::None,
module_handlers: vec![Handler::None; module.modules.len()],
in_handler: false,
resume_pc: 0,
flags: 0,
@@ -202,7 +212,12 @@ impl Vm {
let target = self.module.procs[0]
.code
.iter()
.position(|instruction| matches!(instruction, Instr::Stmt(found) if *found == line));
.position(|instruction| matches!(instruction, Instr::SetErl(found) if *found == line))
.or_else(|| {
self.module.procs[0].code.iter().position(
|instruction| matches!(instruction, Instr::Stmt(found) if *found == line),
)
});
let target = target.ok_or(RuntimeError(8))?;
if self.module.procs[0]
.code
@@ -229,6 +244,8 @@ impl Vm {
}
let stack_base = self.stack.len();
self.frames.push(Frame {
source: 0,
column: 0,
proc,
pc: 0,
locals_base,
@@ -361,7 +378,12 @@ impl Vm {
if !matches!(self.module.procs[proc].code.as_slice(), [Instr::RetProc]) {
return Ok(false);
}
let name = self.module.procs[proc].name.to_ascii_uppercase();
let name = self.module.procs[proc]
.name
.rsplit('!')
.next()
.unwrap()
.to_ascii_uppercase();
if !matches!(
name.as_str(),
"CMNDLGREGISTER"
@@ -767,6 +789,8 @@ impl Vm {
let locals_base = self.frames[0].locals_base;
let stack_base = self.stack.len();
self.frames.push(Frame {
source: 0,
column: 0,
proc: 0,
pc: ziel as usize,
locals_base,
@@ -821,17 +845,54 @@ impl Vm {
}
pub fn add_breakpoint(&mut self, line: u32) {
self.breakpoints.insert(line);
self.breakpoints.insert((0, line));
self.flags |= F_BREAK;
}
pub fn remove_breakpoint(&mut self, line: u32) {
self.breakpoints.remove(&line);
self.breakpoints.remove(&(0, line));
if self.breakpoints.is_empty() {
self.flags &= !F_BREAK;
}
}
pub fn add_module_breakpoint(&mut self, module: u16, line: u32) {
self.breakpoints.insert((module, line));
self.flags |= F_BREAK;
}
pub fn remove_module_breakpoint(&mut self, module: u16, line: u32) {
self.breakpoints.remove(&(module, line));
if self.breakpoints.is_empty() {
self.flags &= !F_BREAK;
}
}
pub fn current_source_pos(&self) -> tb_frontend::SourcePos {
self.frames
.last()
.map(|f| tb_frontend::SourcePos {
source: f.source,
line: f.line,
column: f.column,
})
.unwrap_or_default()
}
pub fn current_module(&self) -> u16 {
self.module
.sources
.get(self.current_source_pos().source as usize)
.map_or(0, |s| s.module)
}
pub fn current_file(&self) -> &str {
self.module
.sources
.get(self.current_source_pos().source as usize)
.map_or(&self.module.name, |s| &s.path)
}
pub fn current_line(&self) -> u32 {
self.frames.last().map(|f| f.line).unwrap_or(0)
}
@@ -843,26 +904,58 @@ impl Vm {
.unwrap_or("")
}
/// Variableninspektion (Debugger): erst Locals des obersten Frames
/// (Referenzen werden aufgelöst), dann Modulvariablen. Ein
/// Typ-Suffix (`n%`, `s$`) wird toleriert — Slots tragen Basisnamen.
/// Variableninspektion: lokale Namen vor Modulvariablen, explizite
/// Typ-Suffixe vor einer eindeutigen Suche nach dem Basisnamen.
pub fn inspect(&self, name: &str) -> Option<Value> {
let name = name.trim_end_matches(['%', '&', '!', '#', '$', '@']);
fn matches(stored: &str, requested: &str) -> bool {
let suffixes = ['%', '&', '!', '#', '$', '@'];
stored.eq_ignore_ascii_case(requested)
|| (!(stored.ends_with(suffixes) && requested.ends_with(suffixes))
&& stored
.trim_end_matches(suffixes)
.eq_ignore_ascii_case(requested.trim_end_matches(suffixes)))
}
fn unique(mut ids: impl Iterator<Item = usize>) -> Option<usize> {
let first = ids.next()?;
ids.next().is_none().then_some(first)
}
if let Some(f) = self.frames.last() {
let p = &self.module.procs[f.proc];
for (i, n) in p.local_names.iter().enumerate() {
if n.eq_ignore_ascii_case(name) {
let v = self.locals[f.locals_base + i].clone();
return Some(self.deref_for_inspect(v));
}
if let Some(i) = unique(
p.local_names
.iter()
.enumerate()
.filter(|(_, n)| matches(n, name))
.map(|(i, _)| i),
) {
return Some(self.deref_for_inspect(self.locals[f.locals_base + i].clone()));
}
}
for (i, n) in self.module.global_names.iter().enumerate() {
if n.eq_ignore_ascii_case(name) {
return Some(self.deref_for_inspect(self.globals[i].clone()));
}
let qualified = format!(
"{}!{name}",
self.module.modules[self.current_module() as usize].0
);
if let Some(id) = unique(
self.module
.global_names
.iter()
.enumerate()
.filter(|(_, n)| matches(n, name) || matches(n, &qualified))
.map(|(i, _)| i),
) {
return Some(self.globals[id].clone());
}
None
let id = unique(
self.module
.global_names
.iter()
.enumerate()
.filter(|(_, n)| {
matches(n.split_once('!').map_or(n.as_str(), |(_, name)| name), name)
})
.map(|(i, _)| i),
)?;
Some(self.globals[id].clone())
}
/// Arrayelement inspizieren.
@@ -993,8 +1086,15 @@ impl Vm {
break;
}
}
if target.is_none() && self.module_handler != Handler::None {
target = Some((0, self.module_handler));
if target.is_none() {
for frame in self.frames.iter().rev() {
let module = self.module.sources[frame.source as usize].module as usize;
let handler = self.module_handlers[module];
if handler != Handler::None {
target = Some((0, handler));
break;
}
}
}
let Some((depth, handler)) = target else {
return Some(self.error_event(code, line));
@@ -1039,7 +1139,7 @@ impl Vm {
let code = &self.module.procs[proc].code;
let mut i = from + 1;
while i < code.len() {
if matches!(code[i], Instr::Stmt(_)) {
if matches!(code[i], Instr::Stmt(_) | Instr::InitStmt(_)) {
return i;
}
i += 1;
@@ -1189,7 +1289,7 @@ impl Vm {
match cell {
Value::Arr(a) => Ok(a.clone()),
Value::Empty => {
let lo = self.module.option_base as i32;
let lo = self.module.modules[self.current_module() as usize].1 as i32;
let bounds = vec![(lo, 10); dims as usize];
let arr = ArrayObj::new(elem.clone(), bounds, &self.module.udts)?;
let handle = Rc::new(std::cell::RefCell::new(arr));
@@ -1223,7 +1323,9 @@ impl Vm {
fn exec(&mut self, instr: Instr, pc: usize, host: &mut dyn Host) -> Result<Flow, RuntimeError> {
use Instr as I;
match instr {
I::Stmt(line) => {
I::Source(_, _) => Ok(Flow::Normal),
I::Stmt(line) | I::InitStmt(line) => {
let initializing = matches!(instr, I::InitStmt(_));
if line == 0 {
if let Some(target) = self.start_pc.take() {
self.frames[0].pc = target;
@@ -1231,6 +1333,13 @@ impl Vm {
}
let f = self.frames.last_mut().unwrap();
f.line = line;
if let Some(I::Source(source, column)) = pc
.checked_sub(1)
.and_then(|pc| self.module.procs[f.proc].code.get(pc))
{
f.source = *source;
f.column = *column;
}
f.last_stmt_pc = pc;
// Zustellpunkt: anzeigen, wenn sich der Bildschirm geändert
// hat, und regelmäßig Ereignisse abholen. Das ist keine
@@ -1238,20 +1347,27 @@ impl Vm {
// Größenänderungen kämen nie an.
self.tick_zaehler = self.tick_zaehler.wrapping_add(1);
self.forms.render(&mut self.rt.screen);
if self.rt.screen.ist_veraendert() || self.tick_zaehler.is_multiple_of(1024) {
if !initializing
&& (self.rt.screen.ist_veraendert() || self.tick_zaehler.is_multiple_of(1024))
{
self.tick(host);
self.rt.screen.veraenderung_quittieren();
}
let erster_eintritt =
std::mem::take(&mut self.frames.last_mut().unwrap().handler_start);
if !erster_eintritt && self.zustellen(host, Zustellpunkt::Anweisung) {
if !initializing
&& !erster_eintritt
&& self.zustellen(host, Zustellpunkt::Anweisung)
{
return Ok(Flow::Normal);
}
if self.flags != 0 {
if self.flags & F_STEP != 0 {
return Ok(Flow::Event(RunEvent::Stepped { line }));
}
if self.flags & F_BREAK != 0 && self.breakpoints.contains(&line) {
if self.flags & F_BREAK != 0
&& self.breakpoints.contains(&(self.current_module(), line))
{
return Ok(Flow::Event(RunEvent::Breakpoint { line }));
}
if self.flags & F_POLL != 0 && self.rt.abbruch {
@@ -1328,6 +1444,10 @@ impl Vm {
self.push(Value::Lng(v));
Ok(Flow::Normal)
}
I::PushUdtId(id) => {
self.push(Value::Lng(id as i32));
Ok(Flow::Normal)
}
I::PushSng(v) => {
self.push(Value::Sng(v));
Ok(Flow::Normal)
@@ -1716,13 +1836,25 @@ impl Vm {
a.data[flat as usize] = v;
Ok(Flow::Normal)
}
I::DimArr(global, slot, dims, elem) => {
I::DimArr(global, slot, dims, ref elem)
| I::CommonArr(global, slot, dims, ref elem) => {
let common = matches!(instr, I::CommonArr(..));
let bounds = self.pop_bounds(dims)?;
let cell = self.slot_value(global, slot);
if common {
if let Value::Arr(array) = cell {
let array = array.borrow();
return if &array.elem == elem && array.dims == bounds {
Ok(Flow::Normal)
} else {
Err(RuntimeError::TYPE_MISMATCH)
};
}
}
if !matches!(cell, Value::Empty) {
return Err(RuntimeError::DUPLICATE_DEFINITION);
}
let arr = ArrayObj::new(elem, bounds, &self.module.udts)?;
let arr = ArrayObj::new(elem.clone(), bounds, &self.module.udts)?;
*self.slot_value(global, slot) = Value::Arr(Rc::new(std::cell::RefCell::new(arr)));
Ok(Flow::Normal)
}
@@ -2293,7 +2425,8 @@ impl Vm {
// ---- Fehlerbehandlung ----
I::OnErrorGoto(t) => {
self.module_handler = Handler::Goto(t);
let module = self.current_module() as usize;
self.module_handlers[module] = Handler::Goto(t);
Ok(Flow::Normal)
}
I::OnErrorLocal(t) => {
@@ -2301,7 +2434,8 @@ impl Vm {
Ok(Flow::Normal)
}
I::OnErrorDisable => {
self.module_handler = Handler::None;
let module = self.current_module() as usize;
self.module_handlers[module] = Handler::None;
Ok(Flow::Normal)
}
I::OnErrorLocalDisable => {
@@ -2312,7 +2446,8 @@ impl Vm {
if local {
self.frames.last_mut().unwrap().local_handler = Handler::ResumeNext;
} else {
self.module_handler = Handler::ResumeNext;
let module = self.current_module() as usize;
self.module_handlers[module] = Handler::ResumeNext;
}
Ok(Flow::Normal)
}