Let's see if we can pretend to be a ComputerEyes 2 (the version which goes in a Apple II slot). They were kind enough to give out Big Mac assembly source code for the capture routines which made things so much easier to figure out what's happening. That's found on digitalvision_computereyes2_sourcecode.dsk.

The ComputerEyes 2 will capture at 280x192 black and white, and 140x192 with 8 levels of gray (or with false color, which is an interesting effect). It will capture a full frame in about 6 seconds. (Since we're cheating it goes faster because we're not actually syncing to a real signal)

It captures one pixel in each column moving from top to bottom, with the column captures moving from left to right across the screen.

So let's make a hack to see something on screen.

We'll put it in slot 1 which means that its i/o addresses are at C090,C091 and C092. I don't want to go to the trouble of making a proper device so we'll hack up the c080_r and the c080_w routine to intercept accesses to these addresses.

First, add a couple of variables:

uint m_computereyescapturebuffer[192][280];  // [280][192] that is column major, we want [192][280]
int m_cex, m_cey;
int m_cer0,m_cer1,m_cer2;
int m_cethreshold_counter;


//  CER0 read bits
//  10 = sync
//  20 = test bit (used in detection), turned on by writing 80 to cer0
//  01 = capture data bit
// CER0 write bits
//  80 = test bit on (bit 7)
//  40 = monitor relay
//  0F = brightness  (guessing bits 0..3?)
// CER1 write bits
//  80 = interface on (bit 7)
//  0F = contrast (guessing bits 0..3?)

int retval=0;

        if (offset==0x10) // c090 SLOT 1
           {  if (m_cer0 & 0x80) {printf("cer0 returning 0x20\n");  return 0x20;}

                  if ( ((m_maincpu->pc() >= 0x84f3) && (m_maincpu->pc()<= 0x8505)) ||
                       ((m_maincpu->pc() >= 0x85a1) && (m_maincpu->pc()<= 0x85b3)) ||
                       ((m_maincpu->pc() >= 0x82c5) && (m_maincpu->pc()<= 0x82c5)) )
                     retval = (m_computereyescapturebuffer[std::min(m_cey,191)][std::min(m_cex,279)]<((7-m_cethreshold_counter) * 0x20))? 0x01:0;  // data in bit 0 
                     printf("cer0 retval = %x   pc= %x  m_cethreshold_counter=%d  m_cex=%x m_cey=%x\n",retval,m_maincpu->pc(),m_cethreshold_counter,m_cex,m_cey);
                     m_cethreshold_counter += 1;
                     int thresholdlimit = 7;
                     if (m_maincpu->pc() == 0x82c5) thresholdlimit=1;
                     int totalrows=191;
                     if ((m_maincpu->pc() >= 0x84f3) && (m_maincpu->pc()<= 0x8505)) totalrows=192; 
                     if (m_cethreshold_counter >= thresholdlimit)
                        {m_cey += 1;
                        if (m_cey>totalrows){m_cex += 1; m_cey=0;}
                     m_cethreshold_counter %= thresholdlimit; 
                   else if (m_maincpu->pc() == 0x8270) {retval = 0x10; m_cex=0;m_cey=0;}   // sync, expects a 1 to proceed within 256 loop cycles or error
                   else if (m_maincpu->pc() == 0x827f) retval = 0x0;                       // sync, expects a 0 to proceed within 256 loop cycles or error
                     return retval;
        else if ((offset==0x11)||(offset==0x12)) return 0;


and we'll hack on c080_w for the writes:


        int slot;

        offset &= 0x7F;
        slot = offset / 0x10;

        if (offset==0x10) // c090 SLOT 1
             printf("write cer0  data=%x\n",data);
             m_cer0 = data;
        else if (offset==0x11)
              printf("write cer1  data=%x\n",data);
              m_cer1 = data;
         else if (offset==0x12)
              printf("write cer2  data=%x\n",data);
              m_cer2 = data;
//              m_cex = 0;
//              m_cey = 0;

One of the things that threw me for awhile was that the DHR grey captures 192 rows while the DHR false color captures 193 rows, which I have to take into account.

Then we'll get lua to execute v4l2-ctl to capture us a frame, convert it and preview it with eog (eye of gnome, Ubuntu's default image viewer). If the lua code wants to pause on launching eog, just have an eog window already open.

Note that I'm converting it to 140x192 twice, once for the data to load, and another to generate a preview so I can see what's actually being passed through:
(Yes I named it frame280.jpg, that's before I figured out it was actually a 140 pixel capture).

If you pass imagemagick convert the -append parameter it will "concatenate" jpegs together for my preview jpeg.

[Linked Image]

The commands broken into separate lines:

v4l2-ctl --device /dev/video0 --stream-mmap --stream-to=frame.jpg --stream-count=1 --set-fmt-video=width=640,height=480,pixelformat=JPEG  --set-ctrl=brightness=175,contrast=255;
convert -resize 140x192\\! frame.jpg -modulate 200,0 -brightness-contrast 0x50 frame280.jpg;
convert -resize 140x192\\! frame.jpg -size 140x192 -depth 8 -modulate 200,0 -brightness-contrast 0x50 RGBA:myframe.data; 
convert -append frame.jpg frame280.jpg framepreview.jpg;
eog -w framepreview.jpg

ret=os.execute("v4l2-ctl --device /dev/video0 --stream-mmap --stream-to=frame.jpg --stream-count=1 --set-fmt-video=width=640,height=480,pixelformat=JPEG  --set-ctrl=brightness=175,contrast=255;convert -resize 140x192\\! frame.jpg -modulate 200,0 -brightness-contrast 0x50 frame280.jpg;convert -resize 140x192\\! frame.jpg -size 140x192 -depth 8 -modulate 200,0 -brightness-contrast 0x50 RGBA:myframe.data; convert -append frame.jpg frame280.jpg framepreview.jpg;echo hello;eog -w framepreview.jpg;echo hello")  print(ret) 

f=assert(io.open("myframe.data")) a=f:read("*a") f:close() print(#a) quick = emu.items()["Apple //e/:/0/m_computereyescapturebuffer"] print(quick)
counter=0 for row=0,192-1 do for x=0,139 do emu.item(quick):write(row*280+x,a:byte(1+counter*4)) counter = counter + 1 end end

[Linked Image] [Linked Image]
Note the black and white capture is 280x192 (and I've loaded only a 140x192 bitmap, the right side is from a previous load of a 280x192 bitmap)
[Linked Image] [Linked Image]
DHR grey
[Linked Image] [Linked Image]
DHR False Color
[Linked Image] [Linked Image]

According to the Digital Vision website, there's supposed to be a 12-second scan mode in addition to the 6-second scan, but the version of the software that I was able to find only seems to do the single scan only. I can do 8 levels of gray, where the article says 64 levels of gray. Perhaps it adjusts the brightness and contrast slightly, then does another 6-second scan and averages the results.

ComputerEyes/2 was still a black-and-white slow-scan device requiring six seconds to capture an image, but all of the gray levels were captured at once, and there were 64 of them! This resulted in images of excellent quality (for the day). A 12-second scan mode was also provided that produced images with even higher resolution. ComputerEyes/2 replaced the original ComputerEyes, which was the company's first product to become obsolete!


Last edited by Golden Child; 05/05/19 05:30 PM.