The following document was created by Werner Terreblanche in South Africa.
He was kind enough to take our source code and PDF-format schematic and then
produce this document, which contains everything you need to make a simple
4-channel digital scope using a regular analog scope.  Quite clever, we
thought, to use text characters to make the schematic.

- Lance Walley, Parallax  (lwalley@parallaxinc.com)


;******************************************************************************
;*                 ;   CIRCUIT CELLAR DIGITAL SCOPE PROJECT                   *
;******************************************************************************
;
;   Copied from a Circuit Cellar article.  All that was changed from the
;   original source code is the addition of this Ascii version of the
;   circuit diagram do by Werner Terreblanche ( email wterreb@iacces.za).
;
;
;                     Vcc    PIC16C55
;                      |  +------------+
;                      |  |            |
;                 /-TR-+--|1         28|                     20MHZ
;                |     \--|2         27|-------------------+--------+
;                |      --|3         26|------/\/\/--------+--I[]I--+
;          GND---|--------|4         25|--A7----\          |        |
;                |      --|5         24|--A6----\|        ===      === 22pF
;                |/---D0--|6         23|--A5----\|         |        |
;                |/---D1--|7         22|--A4----\|         +---+----+
;                |/---D2--|8         21|--A3----\|             |
;                |/---D3--|9         20|--A2----\|            GND
;                |/--ZIP--|10        19|--A1----\|
;                |/---WE--|11        18|--A0----\|      +----------- OUTPUT
;                |/--CAS--|12        17|------+  |      |  +-+       TO SCOPE
;                |/--RAS--|13        16|----+ |  |      +-1| |
;                |/---TR--|14        15|--+ | |  |/--A7---2| |
;                |        +------------+  | | |  |/--A6---3| |  9 Way resistor
;                |                        | | |  |/--A5---4| |  flatpack
;                |           +-----RECORD-+ | |  |/--A4---5| |  forms A/D conv.
;                |           | +---LEFT-----+ |  |/--A3---6| |  9x10K
; TRIG------TR--/|  VCC      | | +-RIGHT------+  |/--A2---7| |
;       30K      |   |   30K | | |   __          |/--A1---8| |
; CH1--/\/\-D0--\|   +--/\/\-+-|-|---||---+      |/--A0---9| |
; CH2--/\/\-D1--\|   |   30K   | |   __   |      |     +-10| |
; CH3--/\/\-D2--\|   +--/\/\---+-|---||---+      |     |   +-+
; CH4--/\/\-D3--\|   |   30K     |   __   |      |    GND
;                |   +--/\/\-----+---||---+      |
;                |                        |      |
;                |                       GND     |
;                |          +--------+           |
;                |          |        |           |
;                |\--D0----2|D0    A0|14---A0---/|
;                |\--D1----3|D1    A1|13---A1---/|
;                |\--D2---15|D2    A2|12---A2---/|
;                |\--D3-----|D3    A3|11---A3---/|
;                |\--RAS---5|RAS   A4|8----A4---/|
;                |\--CAS--16|CAS   A5|7----A5---/|
;                |       +-1|OE    A6|6----A6---/|
;                 \--WE--|-4|WE    A7|10---A7---/
;                        |  |        |
;                       GND +--------+
;                             53C464 RAM
;
;********************************************************
;*                   *
;*    PIC-Based DIGITAL SCOPE       *
;*                   *
;*     - Records 4 channels x 64K at 357 KHz    *
;*     - Displays recorded waveforms on oscilloscope  *
;*     - Straight or edge-triggered recording      *
;*                   *
;*    Designed by Parallax, Inc.    *
;*    Last updated 11/15/91         *
;*                   *
;********************************************************
;
      device   pic16c55,hs_osc,wdt_off,protect_off
      id checksum    ;device's id word = checksum
      reset power_up    ;jmp to power_up on reset
;
;
; RA is a 4-bit bidirectional data bus connecting the PIC, DRAM, and channels
; RB contains control signals - some pins are used as both inputs and outputs
; RC is an 8-bit output bus for DRAM address and DAC data
;
zip      =  rb.0        ;can output a low to cancel dac
we       =  rb.1        ;dram write enable bit
cas      =  rb.2        ;dram column address select bit
ras      =  rb.3        ;dram row address select bit
tr       =  rb.4        ;can output low to stop noise
record   =  rb.5        ;record button input
left     =  rb.6        ;left button input
right    =  rb.7        ;right button input

display_on  =  11100001b      ;directions for rb
display_off =  11110000b      ;0=output, 1=input

toggle      =  pa0         ;bit variables
bit         =  pa1         ;(unused bits in status reg)
direction   =  pa2
;
;
; General register assignments (08h-1fh)
;
      org   08h         ;set org to general registers

trace_buffer   ds 8        ;trace buffer holds 40h bits
address_low    ds 1        ;dram address low
address_high   ds 1        ;dram address high
channel        ds 1        ;channel number
trace          ds 1        ;trace position (x axis)
level          ds 1        ;level of line (y axis)
temp           ds 1        ;temporary usage register

      org   0h       ;reset origin for rom code
;
;
;************************
;* SUBROUTINES *
;************************
;
;
; Clear memory from address to end
;
clear_to_end
      clr   ra              ;make ra output lows
      mov   !ra,#0000b      ;to be written into dram
      mov   w,#regular_loop ;load w with address
      clrb  bit             ;clear bit
      jmp   write_entry     ;write to end of memory
;
;
; Record pressed - get final record command and read 64K samples into dram
;
handle_record  call  clear_address     ;reset address
      clrb  bit         ;clear bit to flag left/right

:debounce   mov   temp,#100      ;100 cycles of record debounce
:wait    sb left        ;if left button, direction=0
      clrb  direction
      sb right       ;if right button, direction=1
      setb  direction
      snb   left        ;if either button, set bit
      sb right
      setb  bit
      jnb   record,:debounce  ;if record, reload debounce
      djnz  temp,:wait     ;record not pressed, 100 yet?

      mov   w,#101000b     ;get pos or neg edge for option
      snb   direction      ;according to direction
      mov   w,#111000b
      mov   option,w    ;load option with w

      mov   w,#regular_loop      ;if left or right button is
      snb   left        ;still pressed, then do per-
      sb right       ;sample triggering
      mov   w,#trigger_loop

write_entry mov   temp,w         ;save loop address in temp
      clrb  we       ;clear we to enable dram writes
      jnb   bit,regular_loop  ;if no button, regular

trigger_loop   clr   rtcc        ;reset rtcc
:wait    jnb   record,clear_to_end  ;if record button, abort
      jnb   rtcc.0,:wait      ;wait for trigger edge

regular_loop   mov   rc,address_low ;2 cyc   ;write row address
      clrb  ras      ;1 ;latch into dram
      mov   rc,address_high   ;2 ;write column address
      clrb  cas      ;1 ;latch into dram, write
      setb  cas      ;1 ;end write cycle
      setb  ras      ;1 ;return ras high, too
      mov   w,temp      ;1 ;get loop address in w
      inc   address_low ;1 ;inc low address
      snz         ;1/2s ;skip if not 0
      incsz address_high   ;1/2s ;inc high address, skip if 0
      jmp   w     ;2 ;jmp to loop (14 cycles/loop)

      setb  we       ;done, return we high
      jmp   make_ra_input     ;compensate for write_entry
;
;
; Handle panning - perform pan if left and/or right buttons are pressed
;
handle_panning mov   w,#01h         ;w=01h in case of left or right
      jb left,:not_left    ;check for left button pressed
      jnb   right,:both    ;check for both buttons pressed
      clrb  direction      ;just left, clear direction bit
      jmp   step_left      ;step left 01h

:not_left   jb right,:neither    ;check for right button pressed
      setb  direction      ;just right, set direction bit
      jmp   step_right     ;step right 01h

:both    mov   w,#40h         ;both buttons pressed
      jnb   direction,step_left  ;step either left or right 40h
      jmp   step_right     ;depending on direction bit

:neither ret            ;neither button, exit
;
;
; Step left w positions - don't go below address 0
;
step_left   sub   address_low,w     ;subtract w from address_low
      jc :done       ;if c=1 (no underflow), done
      dec   address_high      ;decrement address_high
      movsz w,++address_high  ;skip if address_high was 0
:done    ret

clear_address  clr   address_low    ;clear address word
      clr   address_high
      ret
;
;
; Step right w positions - don't go above address 0ffc0h
;
step_right  mov   temp,w         ;store w in temp
:loop    mov   w,++address_high  ;check if address_high = 0ffh
      jnz   :step       ;if not, do step
      cje   address_low,#0c0h,:done ;if address = 0ffc0h, done
:step    inc   address_low    ;inc address_low
      snz            ;skip if not 0
      inc   address_high      ;inc address_high
      djnz  temp,:loop     ;loop if more steps
:done    ret
;
;
; Refresh - cycle dram through 256 row accesses
;
refresh     clr   rc       ;clear rc for row addresses
:loop    clrb  ras         ;latch row address to refresh
      setb  ras         ;return ras high
      ijnz  rc,:loop    ;inc row address, loop if not 0
      ret
;
;
; Get buffer pointer - fsr points to a trace_buffer register and w holds mask
;
get_buffer_ptr mov   w,>>trace      ;get trace/2 in w
      mov   fsr,w       ;store in fsr
      rr fsr         ;rotate right fsr
      rr fsr         ;rotate right fsr
      clrb  fsr.4       ;fsr = 08h-0fh (trace_buffer)
      mov   w,trace        ;get trace in w
      and   w,#07h         ;isolate lower 3 bits (0h-07h)
lookup_mask jmp   pc+w        ;lookup bit mask according to w
      retw  01h,02h,04h,08h,10h,20h,40h,80h  ;w holds mask after ret
;
;
; Trigger scope - make ra and tr output lows, generate sync pulse, start rtcc
;  All I/O pins which connect to input channels or the trigger will output lows
;  in order to reduce ac noise on the dac output during trace generation
;
trigger_scope  clr   ra       ;ra=gnd to reduce dac noise
      mov   rb,#00001110b     ;tr=gnd to reduce dac noise
      mov   rc,#0ffh    ;dac will go high when enabled
      mov   !ra,#0000b     ;make ra output
      mov   !rb,#display_on      ;make tr output and enable dac
      mov   !rc,#00000000b    ;make rc output (already was)
      mov   option,#001000b      ;rtcc counts instruction cycles
      clr   rtcc        ;clear rtcc for fresh count
      clr   trace       ;clear trace for caller's use
      ret
;
;
; Bit sync output - wait for rtcc's msb to go high, then output w
;
bit_sync_level add   w,level        ;on entry, w must hold height

bit_sync jnb   rtcc.7,$    ;loop until rtcc's msb high
      mov   rc,w        ;output w to dac
      clrb  rtcc.7         ;clear msb (low bits unchanged)
      ret
;
;
; Bit sync output - wait for rtcc's msb to go high, then output w with slewing
; this routine was added at the last minute so that traces look like functions
;
bit_sync_level2   add   w,level        ;on entry, w must hold height

      mov   1eh,w
      mov   1fh,rc

      jnb   rtcc.7,$    ;loop until rtcc's msb high
      clrb  rtcc.7         ;clear msb (low bits unchanged)

      cje   1eh,1fh,:exit

      cjb   1eh,1fh,:a

:b    mov   1dh,#0ch
:blp     inc   rc
      djnz  1dh,:blp
      jmp   :exit

:a    mov   1dh,#0ch
:alp     dec   rc
      djnz  1dh,:alp
      jmp   :exit

:exit    ret
;
;
; Show levels
;
show_levels call  :lookup
      mov   level,w
      clr   w
      snb   toggle
      mov   w,#0ch
      jmp   bit_sync_level

:lookup     add   pc,channel     ;jump to pc+channel
      retw  0b8h,98h,78h,58h  ;levels for 4 channels
;
;
; Trace done - disable dac output, make ra and tr inputs
;
trace_done  clr   w        ;ready to output low on dac
      call  bit_sync    ;wait for current bit done
      clrb  zip         ;ready to disable dac
      mov   !rb,#display_off  ;disable dac and make tr input
make_ra_input  mov   !ra,#1111b     ;make ra input
      ret
;
;
;************************
;* MAIN     *
;************************
;
;
; Power up
;
power_up call  trigger_scope     ;call trigger_scope to init i/o

      call  clear_address     ;clear memory
      call  clear_to_end
;
;
; Draw 4 channel traces
;
display     clr   channel        ;reset channel counter

:trace      call  refresh        ;refresh dram

      clr   trace       ;get trace_buffer from dram
:z0      mov   rc,address_low    ;write low address byte to rc
      clrb  ras         ;latch row address (refreshes)
      mov   rc,address_high      ;write high address byte to rc
      clrb  cas         ;latch column address (rd/wr)
      mov   w,channel      ;get channel in w
      call  lookup_mask    ;translate w to bit mask
      and   w,ra        ;test bit in ra with mask
      movb  bit,/z         ;move not z into bit
      setb  cas         ;return cas high
      setb  ras         ;return ras high
      inc   address_low    ;inc low address
      snz            ;skip if not 0
      inc   address_high      ;inc high address
      call  get_buffer_ptr    ;get fsr and mask in w
      not   w        ;not w mask
      and   indirect,w     ;clear bit in trace_buffer
      not   w        ;return mask to normal
      snb   bit         ;skip if bit not set
      or indirect,w     ;set bit in trace_buffer
      inc   trace       ;inc trace
      jnb   trace.6,:z0    ;if trace not 40h yet, loop
      sub   address_low,#40h  ;restore address by subtracting
      subb  address_high,c    ;40h from address word

      call  trigger_scope     ;do sync pulse
      call  show_levels    ;show levels

:sample     call  get_buffer_ptr
      and   w,indirect
      mov   w,#0
      sz
      mov   w,#0ch
      test  trace
      jnz   :slow
      call  bit_sync_level
      jmp   :cont
:slow    call  bit_sync_level2
:cont
      inc   trace       ;inc trace position
      jnb   trace.6,:sample      ;if not 40h, show next sample

      call  show_levels
      call  trace_done
      inc   channel        ;inc channel
      jnb   channel.2,:trace  ;if not 4h, show next trace
;
;
; Show guide trace
;
guide_trace call  refresh        ;refresh dram
      call  refresh        ;do it a couple of times to
      call  refresh        ;avoid triggering the scope
      call  refresh        ;prematurely on the guide trace

      call  trigger_scope     ;trigger scope
      mov   level,#30h     ;draw to the guide level
      call  bit_sync    ;w holds 30h

:loop    mov   w,>>address_high  ;check for global indicator
      mov   temp,w         ;get address_high/4 into w
      mov   w,>>temp
      and   w,#3fh
      xor   w,trace        ;xor w with trace, z=1 if same
      mov   w,#10h         ;(doesn't affect z)
      jz :show       ;show global indicator if z=1
      mov   w,address_low     ;check for guide indicator
      add   w,trace
      and   w,#3fh
      mov   w,#08h
      sz
      clr   w        ;nothing, just line
:show    call  bit_sync_level    ;draw bit
      inc   trace       ;increment trace
      jnb   trace.6,:loop     ;if trace not 40h yet, loop

      call  bit_sync_level    ;draw last segment (w=0)
      call  trace_done     ;end trace
;
;
; Handle any button inputs
;
      sb record
      call  handle_record
      call  handle_panning
      xor   status,#20h    ;toggle 'toggle' bit in status
      jmp   display        ;loop to display