Skip to content

Commit

Permalink
add about, fix crash on brk, add ctrlc, clean up command syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
pm100 committed Jan 25, 2024
1 parent 3e57816 commit b3a828d
Show file tree
Hide file tree
Showing 12 changed files with 401 additions and 96 deletions.
50 changes: 50 additions & 0 deletions src/about.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use std::collections::HashMap;

use crate::log::say;

pub struct About {
db: HashMap<String, String>,
aliases: HashMap<String, String>,
}
impl About {
pub fn new() -> Self {
let text = include_str!("about.txt");
let mut s = Self {
db: HashMap::new(),
aliases: HashMap::new(),
};
let mut dummy = String::new();
let mut top_text: &mut String = &mut dummy;
for line in text.lines() {
if let Some(topic) = line.strip_prefix('=') {
let names = topic
.split(',')
.map(|s| s.to_string())
.collect::<Vec<String>>();
let mut name_iter = names.iter();
let topic = name_iter.next().unwrap().clone();
s.db.insert(topic.clone(), String::new());
for name in name_iter {
// say(&format!("{} is an alias for {}", name, topic));
s.aliases.insert(name.to_string(), topic.to_string());
}
top_text = s.db.get_mut(&topic).unwrap();
} else {
top_text.push_str(line);
top_text.push('\n');
}
}
s
}
pub fn get_topic(&self, topic: &str) -> &str {
let topic = if let Some(t) = self.aliases.get(topic) {
t
} else {
topic
};
self.db
.get(topic)
.map(|s| s.as_str())
.unwrap_or("Unknown topic")
}
}
205 changes: 205 additions & 0 deletions src/about.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
=topics
symbols,s - information about symbols, names ,uses..
breakpoint,b - information about break and watch points
traps,t - information about traps (invalid writes, reads..)
watch,w - watchpoints
ccode,c - working with c source code
expressions,ex - expression evaluator
stop - what happens when code execution is interrupted

about accepts the long or short topic name ('about s' for symbols)
=symbols,s
Symbols are used to identify locations in memory, either code or data.
They can also be simple values.

A symbol has a name that consists of module.symbol, in most cases
a symbol is unique throughout the entire program , in which case
just the name is needed. If it is ambigous the use the full name.
db65 will tell you if a symbol is ambigous when you try to use it.

To see what symbols are loaded use the 'list_symbols' (lsy) command.
You can add a string that will be used to match symbols. For example

>> lsy reg
0x0006 [.]regbanksize (equ)
0x1645 [regswap2.]regswap2 (lab)
0x0014 [zeropage.]regbank (lab)
0x0004 [zeropage.]regsave (lab)
0x0002 [zeropage.]sreg (lab)

This shows all symbols that contain the substring 'reg'

'regbanksize' is a simple numeric value (equ)
'regswap2' is a code address (lab)
'regbank' is a data address (lab)

=breakpoint,b
Breakpoints are used to stop code execution at a specific point.
The address can be specified by anything that can be resolved to an address
- an explicit address 'b $350'
- a symbol 'b main'
- a source line 'b myprog.c:12'
- an expression (see 'expression' topic) 'b =_printf+2'
in the case of a source line db65 will find the next line that has code
associated with it.

Breakpoints can be listed using 'list_breakpoints' (lbp) command
and deleted using 'delete_breakpoint' (dbp)

>> b main
>> b filetest.c:3
>> b =__printf+20
>> lbp
#2 0x0229 (filetest.c:3)
#3 0x0565 ()
#1 0x0256 (main)
>> dbp 2
>> lbp
#3 0x0565 ()
#1 0x0256 (main)
>>

=traps,t
db65 detects several errors. These traps are turned off by default
they can be turned on using the 'enable_trap' command

segment errors
- reading memory that is valid (assigned by the linker)
- writing to memory that is not valid (as above) or writing to ro memory

heap errors
- writing outside an allocated block (basically runtime segment check)
- failing to free memory (reported at exit)
- invalid calls to free or realloc (pointers to invalid addresses)

hardware stack errors
- unbalanced returns, a call that returns before it has popped its data

uninitialized reads
- reading from ram that has not been written

Not detected (yet)
- reading or writing outside the current c stack frame
- reading or writing outside the bounds of a c variable
- unbalanced c stack

=watch,w
Watchpoints are like breakpoints except they watch for read or writes to
specific addresses


=ccode,c
db65 can work with c code, assembler source or raw binary.
When it detects that it is working with c code (either from
the loaded program or from the cc65 runtime portions in c) it
will try to show the source rather than the assembly.
(This can be turned off using the 'TBD' command)

To use c source debugging with the runtime library db65 needs
to be able to find the 'libsrc' tree. The linker indicates the
location of this tree relative to the compiled binary so if you
have the source code installed db65 will probably find it. If
not then full assembly level debugging is still available.

Compiling

The full feature set requires that

- debug info is generated by cc65 using the -g option
- debug info is generated by ld65 using --dbgfile

db65 will look for a .dbg file with the same name as the binary
and load it automatically. If it has a different name it can be explicitly
loaded with the 'load_symbols' command
(Note that this is *not* the VICE symbols file)

example

cc65 -r -g -t sim6502 filetest.c -o filetest.s
ca65 -g -t sim6502 filetest.s -o filetest.o
ld65 --dbgfile filetest.dbg -t sim6502 filetest.o -o filetest sim6502.lib

then

db65 sim6502 debugger 0.2.1 (16024e9)
>> load filetest
Loaded 6533 bytes
Loading debug info from "filetest.dbg"
files: 117
modules: 82
segments: 9
symbols: 1602
load source file common/_scanf.c
load source file common/fgetc.c
load source file common/fgets.c
load source file common/perror.c
load source file filetest.c
>>

Displaying Variables:

db65 does not know the size or type of variables (yet) but it does know
their location

when paused in a c stack frame the local stack variables are available.
They can be displayed using 'print' but the type must be specified

example

>> b filetest.c:12
>> run
bp #1 filetest.c:12
filetest.c:13 register int len=12;
>> ns
filetest.c:14 char buf[10]="yo";
>> ns
filetest.c:17 FILE *foo = fopen("foo.txt", "w");
>> p -i len
12
>> p -s buf
yo

TODO
- no way to use locals in expressions
- no 'nice' change variable command, write byte has to be used
- watch points set on locals will fire when that stack memory is reused

=expression,ex
Expressions can be used anywhere an address is required. Expr command
can be used to test an expression and also to inspect values.

Examples:
expr =0x20 evaluates to 0x20 (redundant)
expr =.xr the xr register
expr =.xr+1 the xr register plus 1
dis =.pc-6 disassemble from pc-6
m =.xr+0x20 display memory at xr+0x20
m ptr display memory at pointer (raw symbols just work anyway)
m =@(ptr) dereference a pointer
m =@(ptr+0x20) do math on a pointer
m =@(p1+(2*.yr)) more math
p -s =@(sreg) print a string pointed to by sreg, sreg+1

@ is the dereference operator for a word
@b is the dereference operator for a byte

Note if there are spaces in the expression, you must quote it:
mem '=@(ptr + 0x20)'
=stop
When execution stops for any reason other than program exit the current
code location is displayed, preceded by the reason for the stop.

Commands to resume execution:

- ns, next statement (in c code mode), will not enter function calls
- ss, step statment , like ns but will enter function calls
- ni, next instruction, will execute on instruction but skip jsr
- si, step instruction, like ni but will follow jsr
- go, will resume execution
- fin, will run until the next return / rts
- run, will start from the beginning (not the same as 'go'!)

TODO
- execute n statments or instructions
- trace, ie execute one line or instruction, report state and then continue
- resume at a different address (doable via changing pc using reg command)
5 changes: 3 additions & 2 deletions src/db/debugdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use evalexpr::Value;
//pub const NO_PARAMS: = [];
use crate::db::util::Extract;
use crate::debugger::debugger::{HLSym, SegChunk, Segment, Symbol, SymbolType};
use crate::log::say;
use rusqlite::{
params,
types::{Null, Value as SqlValue},
Expand Down Expand Up @@ -171,7 +172,7 @@ impl DebugData {
file_table.insert(id, sf);
// println!("found file {}", p.display());
} else {
println!("can't find file {}", name);
say(&format!("can't find file {}", name));
}
}
Ok(())
Expand Down Expand Up @@ -457,7 +458,7 @@ impl DebugData {
size: size,
});
} else {
println!("bad segid {}", segid);
bail!("bad segid {}", segid);
}
}
Ok(())
Expand Down
20 changes: 8 additions & 12 deletions src/db/parsedb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use anyhow::{anyhow, bail, Result};
type StringRecord = Vec<String>;
//pub const NO_PARAMS: = [];
use crate::db::util::Extract;
use crate::log::say;
use crate::{db::debugdb::DebugData, debugger::debugger::SegmentType};
use rusqlite::params;
use std::{
Expand Down Expand Up @@ -120,8 +121,8 @@ impl DebugData {
let record = Self::csv_parse(&line)?;
let hdr = record.get(0).ok_or(anyhow!("bad file"))?;
match Self::parse_eq(hdr)?.0.as_str() {
"version\tmajor" => println!("version"),
"info\tcsym" => println!("information"),
"version\tmajor" => {}
"info\tcsym" => {}
"csym\tid" => {
ccount += 1;
let csym = Self::parse_csym(&record)?;
Expand All @@ -148,7 +149,7 @@ impl DebugData {
params![file.id, file.name, file.size, file.mod_time],
)?;
}
"lib\tid" => println!("lib"),
"lib\tid" => {}
"line\tid" => {
let line = Self::parse_line(&record)?;
lcount += 1;
Expand Down Expand Up @@ -312,17 +313,12 @@ impl DebugData {
};
}

println!("csyms: {}", ccount);
println!("files: {}", fcount);
println!("lines: {}", lcount);
println!("modules: {}", mcount);
println!("segments: {}", segcount);
println!("spans: {}", spcount);
println!("scopes: {}", scount);
println!("symbols: {}", symcount);
say(&format!("files: {}", fcount));
say(&format!("modules: {}", mcount));
say(&format!("segments: {}", segcount));
say(&format!("symbols: {}", ccount + symcount));
let end = SystemTime::now();
let duration = end.duration_since(start).unwrap();
println!("it took {} milli seconds", duration.as_millis());
tx.commit()?;
self.merge_csymbols(symcount)?;
Ok(())
Expand Down
6 changes: 3 additions & 3 deletions src/debugger/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ static mut THECPU: Cpu = Cpu {
exit_code: 0,
memcheck: MemCheck::None,
arg_array: Vec::new(),
memhits: [(false, 0); 6],
memhits: [(false, 0); 8],
memhitcount: 0,
paracall: false,
tainted_ac: false,
Expand All @@ -58,7 +58,7 @@ pub struct Cpu {
sp65_addr: u8, // the location of the cc65 'stack' pointer
memcheck: MemCheck, // the address of the last memcheck failure
arg_array: Vec<String>, // the command line arguments
memhits: [(bool, u16); 6], // used for data watches
memhits: [(bool, u16); 8], // used for data watches
memhitcount: u8, // entry count in hit array for this instruction
pub paracall: bool, // we just did a pv call
tainted_ac: bool, // the ac is tainted
Expand Down Expand Up @@ -230,7 +230,7 @@ impl Cpu {
pub fn get_memhitcount() -> u8 {
unsafe { THECPU.memhitcount }
}
pub fn get_memhits() -> [(bool, u16); 6] {
pub fn get_memhits() -> [(bool, u16); 8] {
unsafe { THECPU.memhits }
}
pub fn get_arg_count() -> u8 {
Expand Down
Loading

0 comments on commit b3a828d

Please sign in to comment.