content/blog/2016/12/chip8-debugging-infrastructure.markdown @ ef47c27c6470

Start writing debugger entry
author Steve Losh <steve@stevelosh.com>
date Mon, 02 Jan 2017 12:55:34 +0000
parents 0f57fe590e90
children (none)
+++
title = "CHIP-8 in Common Lisp: Debugging Infrastructure"
snip = "What's happening inside this computer?"
date = 2016-12-31T18:50:00Z
draft = true

+++

Our [CHIP-8][] emulator in Common Lisp is coming along nicely.  It can play
games, and in the last post we added a disassembler so we can dump the code of
ROMs.

In this post we'll add some low-level debugging infrastructure so we can set
breakpoints and step through code.

The full series of posts so far:

1. [CHIP-8 in Common Lisp: The CPU](http://stevelosh.com/blog/2016/12/chip8-cpu/)
2. [CHIP-8 in Common Lisp: Graphics](http://stevelosh.com/blog/2016/12/chip8-graphics/)
3. [CHIP-8 in Common Lisp: Input](http://stevelosh.com/blog/2016/12/chip8-input/)
4. [CHIP-8 in Common Lisp: Sound](http://stevelosh.com/blog/2016/12/chip8-sound/)

The full emulator source is on [BitBucket][] and [GitHub][].

[CHIP-8]: https://en.wikipedia.org/wiki/CHIP-8
[BitBucket]: https://bitbucket.org/sjl/cl-chip8
[GitHub]: https://github.com/sjl/cl-chip8

<div id="toc"></div>

## Architecture

The overall goal will be to keep the debugging infrastructure as separate from
possible the rest of the emulation code.  Unfortunately the nature of debugging
will require us to weave it into some of the normal emulator code, but we'll try
to keep the pollution to a minimum.

All information about the state of the debugger (e.g. breakpoints, pause status,
etc) will be stored in a separate debugger data structure.  We'll define a small
API for interacting with this structure.  The `chip` struct will have
a `debugger` slot and will use the debugger API to interact with it.  Later on,
the graphical debugger UI will also use this API.

## The Debugger Data Structure

We'll start by creating a `debugger` struct and a `with-debugger` macro for it.
We'll be adding fields to this struct as we build the debugging infrastructure.

```lisp
(defstruct debugger
  ; ...
  )

(define-with-macro debugger
  ; ...
  )
```

We'll then add a `debugger` slot to our `chip` struct:

```lisp
(defstruct chip
  ; ...
  (debugger (make-debugger) :type debugger :read-only t))
```

## Pausing

The first thing we'll add is support for pausing and unpausing execution.  We'll
add a `paused` slot to the debugger:

```lisp
(defstruct debugger
  (paused nil :type boolean)
  ; ...
  )
```

Then we'll add some simple API functions so the emulator won't have to directly
work with the debugger's slots:

```lisp
(defun debugger-pause (debugger)
  (with-debugger (debugger)
    (setf paused t print-needed t)))

(defun debugger-unpause (debugger)
  (with-debugger (debugger)
    (setf paused nil print-needed nil)))

(defun debugger-toggle-pause (debugger)
  (if (debugger-paused debugger)
    (debugger-unpause debugger)
    (debugger-pause debugger)))

(defun debugger-paused-p (debugger)
  (debugger-paused debugger))
```

Now we'll modify `emulate-cycle` to check if the debugger is paused before
actually running an instruction:

```lisp
(defun emulate-cycle (chip)
  (with-chip (chip)
    (if (debugger-paused-p debugger)      ; NEW
      (sleep 10/1000)                     ; NEW
      (let ((instruction (cat-bytes
                           (aref memory program-counter)
                           (aref memory (1+ program-counter)))))
        (zapf program-counter (chop 12 (+ % 2)))
        (dispatch-instruction chip instruction)))
    nil))
```

The timer thread will need to skip the timer decrements whenever the debugger is
paused:

```lisp
(defun run-timers (chip)
  (with-chip (chip)
    (iterate
      (while running)
      (when (not (debugger-paused-p debugger)) ; NEW
        (decrement-timers chip))
      (sleep 1/60))))
```

Next we'll modify the sound thread to be silent when paused, otherwise pausing
during a buzz would result in perpetual buzzing:

```lisp
(defun run-sound (chip)
  (portaudio:with-audio
    (portaudio:with-default-audio-stream
        (audio-stream 0 1
                      :sample-format :float
                      :sample-rate +sample-rate+
                      :frames-per-buffer +audio-buffer-size+)
      (with-chip (chip)
        (iterate (with buffer = (make-audio-buffer))
                 (with angle = 0.0)
                 (with rate = (audio-rate 440))
                 (while running)
                 (if (and (plusp sound-timer)
                          (not (debugger-paused-p debugger)))  ; NEW
                   (progn
                     (setf angle (funcall (audio-buffer-filler chip)
                                          buffer rate angle))
                     (portaudio:write-stream audio-stream buffer))
                   (sleep +audio-buffer-time+))))))
  nil)
```

We'll also need a way to actually pause and unpause the debugger.  We could do
it through NREPL or SLIME, but it'll be much easier if we just add a key for it
over on the Qt side of things:

```lisp
(define-override (screen key-release-event) (ev)
  (let* ((key (q+:key ev))
         (pad-key (pad-key-for key)))
    (if pad-key
      (chip8::keyup chip pad-key)
      (qtenumcase key
        ((q+:qt.key_escape) (die screen))
        ((q+:qt.key_f1)     (chip8::reset chip))
        ((q+:qt.key_space)                                     ; NEW
         (chip8::debugger-toggle-pause (chip8::debugger chip)) ; NEW
        (t (pr :unknown-key (format nil "~X" key))))))
  (stop-overriding))
```

Now we can pause and unpause the emulator with the space bar, which is a handy
feature to have all on its own.

## Stepping

## Printing

## Breakpoints

## UI

## Result

## Future


```lisp
```

```lisp
```

```lisp
```

```lisp
```

```lisp
```

```lisp
```