jCLogicAnalyzer : simple console-based logic analyzer

Download jclogicanalyzer.zip

Synopsis:

App.cpp
App.h
AppWindow.cpp
AppWindow.h
BaseThread.h
BaseWindow.h
ByteBuffer.h
common.h
ConsoleBaseWindow.cpp
ConsoleBaseWindow.h
ConsoleWindow.cpp
ConsoleWindow.h
FtException.h
jCLogicAnalyzer.cpp
KeyboardThread.cpp
KeyboardThread.h
Tracer.h
UsbBoard.cpp
UsbBoard.h
UsbThread.cpp
UsbThread.h


App.cpp

Synopsis
#include "App.h"
#include "BaseWindow.h"
#include "ConsoleBaseWindow.h"
#include "ConsoleWindow.h"

#include "UsbThread.h"
#include "AppWindow.h"
#include "ByteBuffer.h"

class App::Private
  {
  public:
    Private()
      : usb(::GetCurrentThreadId())
      {
      //
      }

    AppWindow win;
    UsbThread usb;
    ByteBuffer buf;
  } ;

//-------------------------
App::App()
: impl(* new App::Private)
  {
  impl.win.SetMessage("Press 'f' to freeze");
  }

App::~App()
  {
  impl.usb.End();
  delete &impl;
  }

byte* App::Buffer()
  {
  return impl.buf.in;
  }

void App::Show()
  {
  impl.win.Init(*this);
  impl.usb.Start();
  impl.win.Show(); 
  }

int App::ReturnCode()
  {
  return impl.win.ReturnCode();
  }

void App::ProcessByte(byte v)
  {
  impl.buf.Prepend(v);
  impl.win.Redraw();
  }

void App::Freeze()
  {
  impl.usb.Freeze(); 
  impl.win.SetMessage("Press 'u' to unfreeze");
  }

void App::Unfreeze()
  {
  impl.usb.Unfreeze(); 
  impl.win.SetMessage("Press 'f' to freeze");
  }

App.h

Synopsis
#pragma once

#include "common.h"
class App
  {
  public:
    App();
    ~App();
    void Show();
    int ReturnCode();

    void ProcessByte(byte v);
    byte* Buffer();
    void Freeze();
    void Unfreeze();

  private:
    class Private;
    Private& impl;
  } ;

AppWindow.cpp

Synopsis
#include "AppWindow.h"
#include "App.h"
#include "UsbThread.h"

//-------------------------
void AppWindow::Init(App& a)
  {
  mApp = &a;
  Register(UsbThread::WM_BYTE, (TranslateFunc) &AppWindow::OnByte);
  }

void AppWindow::OnByte(WPARAM wparam, LPARAM lparam)
  {
  mApp->ProcessByte((byte) wparam);
  }

//override OnChar
void AppWindow::OnChar(WPARAM wparam, LPARAM lparam)
  {
  switch ((char) wparam)
    {
    case 'f': mApp->Freeze(); break;
    case 'u': mApp->Unfreeze(); break;
    case 'q': PostQuitMessage(0); break;
    }
  }

//override OnPaint
void AppWindow::OnPaint(WPARAM wparam, LPARAM lparam)
  {
  Draw(mApp->Buffer());
  }

AppWindow.h

Synopsis
#pragma once

#include "ConsoleWindow.h"

class App;
class AppWindow : public ConsoleWindow
  {
  public:
    AppWindow() : mApp(0)
      {
      //nothing
      }
    void Init(App& c);
    void OnByte(WPARAM wparam, LPARAM lparam);
    void OnChar(WPARAM wparam, LPARAM lparam);
    void OnPaint(WPARAM wparam, LPARAM lparam);

  private:
    App* mApp;
  } ;

BaseThread.h

Synopsis
#pragma once

//-------------------------
#include <wtypes.h>

class BaseThread
  {
  public:
    //----
    BaseThread()
      {
      }

    //----
    void Start()
      {
      ::CreateThread(0, 0, theThread, (LPVOID) this, 0, &mTid);
      }

    virtual long Run() = 0;


  private:
    //----
    static DWORD WINAPI theThread(LPVOID parm)
      {
      return ((BaseThread*) parm)->Run();
      }

  private:
    DWORD mTid;
  } ;

BaseWindow.h

Synopsis
#pragma once

#include <wtypes.h>
#include <map>

class BaseWindow
  {
  public:
    typedef void (BaseWindow::* TranslateFunc) (WPARAM, LPARAM);
    int ReturnCode()
      {
      return mReturnCode;
      }

    void Register(UINT msg, TranslateFunc f)
      {
      //only one handler per message id
      //todo: multiple handlers
      mMap[msg] = f;
      }

    virtual void Redraw()
      {
      PostMessage(0, WM_PAINT, 0, 666);
      }

    //overridable
    //return true to skip message handling and wait for next message
    virtual bool PreHandler(UINT id, WPARAM wparam, LPARAM lparam)
      {
      //do nothing
      return false;
      }
    //overridable:
    //handle unknown messages
    virtual void UnknownHandler(UINT id, WPARAM wparam, LPARAM lparam)
      {
      //do nothing
      }

    //default handlers
    virtual void OnChar(WPARAM wparam, LPARAM lparam)
      {
      //do nothing
      }
    virtual void OnQuit(WPARAM wparam, LPARAM lparam)
      {
      mReturnCode = 0;
      }
    virtual void OnPaint(WPARAM wparam, LPARAM lparam)
      {
      //do nothing
      }

    virtual void OnDestroy(WPARAM wparam, LPARAM lparam)
      {
      PostQuitMessage(0);
      }

    void Show()
      {
      //load remaining handlers
      LoadMap();

      MSG msg;
      while (::GetMessage(&msg, 0, 0, 0))
        {
        ::TranslateMessage(&msg);
        this->Dispatch(msg.hwnd, msg.message, msg.wParam, msg.lParam);
        }
      }

    LRESULT Dispatch(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
      {
      if (PreHandler(message, wParam, lParam))
        return 0;

      TranslateMap::iterator it = mMap.find(message);
      if (it == mMap.end())
        return ::DefWindowProc(hwnd, message, wParam, lParam);

      TranslateFunc f = (*it).second;
      (this->*f)(wParam, lParam);
      return ::DefWindowProc(hwnd, message, wParam, lParam);
      }

  private:
    void LoadMap()
      {
      Register(WM_CHAR,  &BaseWindow::OnChar);
      Register(WM_QUIT,  &BaseWindow::OnQuit);
      Register(WM_PAINT, &BaseWindow::OnPaint);
      Register(WM_DESTROY, &BaseWindow::OnDestroy);
      }

  private:
    typedef std::map<UINT, TranslateFunc> TranslateMap;
    TranslateMap mMap;
    int mReturnCode;
  } ;

ByteBuffer.h

Synopsis
#pragma once

//-------------------------
class ByteBuffer
  {
  public:
    ByteBuffer()
      {
      memset(in, 0x0, sizeof(in));
      }

    void Prepend(byte v)
      {
      for (int j = 19; j > 0; --j)
        {
        in[j] = in[j-1];
        }
      in[0] = v;
      }

    byte in[20];
  } ;

common.h

Synopsis
#pragma once

typedef unsigned char byte;

ConsoleBaseWindow.cpp

Synopsis
#include <conio.h>

#include <string>
#include <iostream>
#include <iomanip>
using namespace std;

#include "ConsoleBaseWindow.h"

ConsoleBaseWindow::ConsoleBaseWindow()
: mKB(::GetCurrentThreadId())
  {
  mKB.Start();
  outh = ::GetStdHandle(STD_OUTPUT_HANDLE);
  Clear();
  }
void ConsoleBaseWindow::Clear()
  {
  CONSOLE_SCREEN_BUFFER_INFO info;
  ::GetConsoleScreenBufferInfo(outh, &info);
  int cols = info.srWindow.Right;
  int rows = info.srWindow.Bottom;
  Home();
  string blank;
  blank.resize(cols, ' ');
  for(int r = 0; r < rows; ++r)
    cout << blank << endl;
  }
void ConsoleBaseWindow::Home()
  {
  COORD coord;
  coord.X = 0;
  coord.Y = 0;
  ::SetConsoleCursorPosition(outh, coord);
  }
void ConsoleBaseWindow::Write(int c)
  {
  ::putch(c);
  }
void ConsoleBaseWindow::Writeln()
  {
  cout << endl;
  }
void ConsoleBaseWindow::Write(const std::string& msg)
  {
  cout << msg;
  }
void ConsoleBaseWindow::Writeln(const std::string& msg)
  {
  Write(msg);
  Writeln();
  }
void ConsoleBaseWindow::WriteHexByte(unsigned char b)
  {
  //todo: save the format flags
  cout << uppercase << hex << setfill('0') << setw(2) << (int) b;
  //todo: reset the format flags
  }


ConsoleBaseWindow.h

Synopsis
#pragma once

#include <string>
#include "BaseWindow.h"
#include "KeyboardThread.h"

class ConsoleBaseWindow : public BaseWindow
  {
  public:
    ConsoleBaseWindow();
    void Clear();
    void Home();
    void Write(int c);
    void Writeln();
    void Write(const std::string& msg);
    void Writeln(const std::string& msg);
    void WriteHexByte(unsigned char b);

  private:
    HANDLE outh;
    KeyboardThread mKB;
  } ;


ConsoleWindow.cpp

Synopsis
#include <string>
using namespace std;

#include "ConsoleWindow.h"

void ConsoleWindow::SetMessage(const string& s)
  {
  msg = s;
  msg.resize(80, ' ');
  this->Redraw();
  }

//redraws the screen
void ConsoleWindow::Draw(byte* buf)
  {
  Home();

  HeaderRow(buf);
  for (int r = 0; r < 8; ++r)
    {
    byte mask = (byte) (0x80 >> r);
    RowStart();
    Row(buf, mask);
    NextLine();
    }
  MessageRow();
  }

void ConsoleWindow::Row(byte* in, byte mask)
  {
  bool lastbit = ((in[0] & mask) == 0) ? false : true;

  for (int c = 0; c < 20; ++c)
    {
    bool bit = ((in[c] & mask) == 0) ? false : true;
    if (lastbit && bit)
      putHi();
    else if (!lastbit && !bit)
      putLo();
    else if (lastbit && !bit)
      putHi2Lo();
    else // !lastbit && bit
      putLo2Hi();

    lastbit = bit;
    }
  }

void ConsoleWindow::RowStart()
  {
  Write('[');
  }
void ConsoleWindow::HeaderRow(byte* in)
  {
  Write(' ');
  for (int c = 0; c < 20; ++c)
    {
    WriteHexByte(in[c]);
    Write(' ');
    }
  Writeln();
  }
void ConsoleWindow::MessageRow()
  {
  Writeln(msg);
  }
void ConsoleWindow::NextLine()
  {
  Writeln();
  Writeln();
  }
//waveform : a segment of the wave is 3 characters wide
//show the segment was hi 
void ConsoleWindow::putHi()
  {
  writeHi();
  writeHi();
  writeHi();
  }
//show the segment was lo 
void ConsoleWindow::putLo()
  {
  writeLo();
  writeLo();
  writeLo();
  }
//show the segment transitioned from hi to lo
void ConsoleWindow::putHi2Lo()
  {
  writeHi();
  writeLo();
  writeLo();
  }
//show the segment transitioned from lo to hi
void ConsoleWindow::putLo2Hi()
  {
  writeLo();
  writeHi();
  writeHi();
  }
//a lo is the underbar
void ConsoleWindow::writeLo()
  {
  Write('_');
  }
//a hi is a graphic character similar to an underbar
void ConsoleWindow::writeHi()
  {
  Write(0xC4);
  }


ConsoleWindow.h

Synopsis
#pragma once

#include <string>
#include "ConsoleBaseWindow.h"

class ConsoleWindow : public ConsoleBaseWindow
  {
  public:
    void SetMessage(const std::string& s);
    void Draw(byte* buf);

  private:
    void Row(byte* in, byte mask);
    void RowStart();
    void HeaderRow(byte* in);
    void MessageRow();
    void NextLine();
    void putHi();
    void putLo();
    void putHi2Lo();
    void putLo2Hi();
    void writeLo();
    void writeHi();

  private:
    std::string msg;
  };


FtException.h

Synopsis
#pragma once

#include <string>
#include <wtypes.h>

class FT_Exception
  {
  public:
    FT_Exception(const std::string& m, ULONG rc)
      : Msg(m), RC(rc)
      {
      }
    std::string Msg;
    ULONG RC;
  } ;

jCLogicAnalyzer.cpp

Synopsis
//uncomment to turn on tracing
//#define TRACEON
#include "Tracer.h"
#include "App.h"

//-------------------------
int main()
  {
  App app;
  app.Show();
  return app.ReturnCode();
  }

KeyboardThread.cpp

Synopsis
#include <conio.h>
#include "KeyboardThread.h"

KeyboardThread::KeyboardThread(DWORD tid)
: mParentThreadId(tid)
  {
  }

//----
long KeyboardThread::Run()
  {
  for (;;)
    {
    Sleep(100); //check every 100 ms for a keyboard hit
    if (! ::kbhit())
      continue;

    PostThreadMessage(mParentThreadId, WM_CHAR, getch(), 0);
    }
  }

KeyboardThread.h

Synopsis
#pragma once

//-------------------------
#include "BaseThread.h"

class KeyboardThread : public BaseThread
  {
  public:
    KeyboardThread(DWORD tid);
    long Run();

  private:
    DWORD mParentThreadId;
  } ;

Tracer.h

Synopsis
#pragma once

#ifdef TRACEON
#include <fstream>
#include <string>
class Tracer
  {
  public:
    Tracer()
      {
      mFile.open("out.txt", ios::out);
      }
    void Write(const std::string& s)
      {
      mFile.write(s.c_str(), (std::streamsize) s.size());
      mFile.write("\n", 1);
      mFile.flush();
      }
    ~Tracer()
      {
      mFile.close();
      }
  private:
    std::fstream mFile;
  } ;

const std::string toString(long i)
  {
  char buf[20];
  sprintf(buf, "%ld", i);
  return buf;
  }

const std::string toString(HWND i)
  {
  char buf[20];
  sprintf(buf, "0x%8.8X", (long) i);
  return buf;
  }

Tracer gTrace;
#define TRACE(s) gTrace.Write(s)
#else
#define TRACE(s)
#endif

UsbBoard.cpp

Synopsis
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <string>
using std::string;

#pragma comment (lib, "FTD2XX.lib")
#include "FTD2XX.H"

#include "common.h"

#include "FtException.h"
#include "UsbBoard.h"

class UsbBoard::Private
  {
  public:
    Private() : mHandle(0)
      {
      }
    FT_HANDLE mHandle;
  } ;

//-------------------------
UsbBoard::UsbBoard()
: impl(* new UsbBoard::Private)
  {
  }

UsbBoard::~UsbBoard()
  {
  delete &impl;
  }

//----
int UsbBoard::NumDevs()
  {
  DWORD numDevs = 0;
  FT_STATUS ftStatus = FT_ListDevices(&numDevs, 0, FT_LIST_NUMBER_ONLY);
  if (ftStatus != FT_OK)
    throw new FT_Exception("FAILED: FT_ListDevices", ftStatus);
  //cout << "NumDevs: " << numDevs << "\n";
  return numDevs;
  }
//----
string UsbBoard::ConvertRC(ULONG rc)
  {
  switch (rc)
    {
    case 0: return "OK";
    case 1: return "Invalid Handle";
    case 2: return "Device Not Found";
    default: 
      char buf[50];
      sprintf(buf, "status=", rc);
      return buf;
    }
  }
//----
void UsbBoard::Open(int portnum)
  {
  if (impl.mHandle != 0)
    Close();

  FT_STATUS ftStatus = FT_Open(portnum, &impl.mHandle);
  if (ftStatus != FT_OK)
    {
    impl.mHandle = 0;
    throw new FT_Exception("FAILED: FT_Open", ftStatus);
    }
  }
//----
void UsbBoard::Close()
  {
  if (impl.mHandle == 0) return;
  FT_STATUS ftStatus = FT_Close(impl.mHandle);
  impl.mHandle = 0;
  if (ftStatus != FT_OK)
    throw new FT_Exception("FAILED: FT_Close", ftStatus);
  }
//----
void UsbBoard::Write()
  {
  DWORD byteswritten;
  DWORD len;
  byte buf[20];
  buf[0] = 0x01;
  buf[1] = 0x65;
  buf[2] = 0x64;

  len = 3;
  FT_STATUS ftStatus = FT_Write(impl.mHandle, buf, len, &byteswritten);
  if (ftStatus != FT_OK)
    throw new FT_Exception("FAILED: FT_Write", ftStatus);
  }

//----
byte UsbBoard::Read()
  {
  byte buf[20];
  DWORD bytestoread = 2;
  DWORD bytesread;
  FT_STATUS ftStatus = FT_Read(impl.mHandle, buf, bytestoread, &bytesread);
  if (ftStatus != FT_OK)
    throw new FT_Exception("FAILED: FT_Read", ftStatus);
  if (buf[0] != 0x55)
    throw new FT_Exception("FAILED: FT_Read bad LRC", buf[0]);
  if (bytesread != 2)
    throw new FT_Exception("FAILED: FT_Read bytesread!=2", bytesread);

  return buf[1];
  }

UsbBoard.h

Synopsis
#pragma once

#include <wtypes.h>
#include <string>

//-------------------------
class UsbBoard
  {
  public:
    UsbBoard();
    ~UsbBoard();
    int NumDevs();
    static std::string ConvertRC(ULONG rc);
    void Open(int portnum);
    void Close();
    void Write();
    byte Read();

  private:
    class Private;
    Private& impl;
  } ;

UsbThread.cpp

Synopsis
#include <iostream>
#include <iomanip>
using namespace std;

#include "FtException.h"
#include "UsbThread.h"
#include "UsbBoard.h"

//--------------------
UsbThread::UsbThread(DWORD tid)
: mDone(false), mFrozen(false), mParentTid(tid)
  {
  }

//--------------------
long UsbThread::Run()
  {
  try
    {
    UsbBoard usb;

    int num = usb.NumDevs();
    if (num < 1)
      return 1;

    usb.Open(0);

    while (!mDone)
      {
      if (!mFrozen)
        {
        usb.Write();
        Send(WM_BYTE, usb.Read());
        }
      Sleep(100);
      }

    usb.Close();
    }
  catch (FT_Exception* ex)
    {
    cout << "FAILED: (" << ex->RC << ") " << ex->Msg << endl;
    }
  return 0;
  }

//----
void UsbThread::End()
  {
  mDone = true;
  }

//----
void UsbThread::Freeze()
  {
  mFrozen = true;
  }
//----
void UsbThread::Unfreeze()
  {
  mFrozen = false;
  }

void UsbThread::Send(UINT msg, byte v)
  {
  ::PostThreadMessage(mParentTid, msg, (WPARAM) v, 0);
  }

UsbThread.h

Synopsis
#pragma once

#include <wtypes.h>
#include "BaseThread.h"

class UsbThread : public BaseThread
  {
  public:
    enum { WM_BYTE = WM_USER+1, } ;

    UsbThread(DWORD tid);
    long Run();
    void End();
    void Freeze();
    void Unfreeze();

  private:
    void Send(UINT msg, byte v);

  private:
    DWORD mParentTid;
    bool mDone;
    bool mFrozen;
  } ;






Contact me about content on this page using john_web-at-arrizza-dot-com
For Web Master or site problems contact: webadmin-at-arrizza-dot-com
Copyright John Arrizza (c) 2001-2010