Previous Thread
Next Thread
Print Thread
Page 36 of 78 1 2 34 35 36 37 38 77 78
Joined: Feb 2014
Posts: 828
Likes: 37
G
Senior Member
Offline
Senior Member
G
Joined: Feb 2014
Posts: 828
Likes: 37
I'm on a roll now, let's see if we can get the atari 800 computer eyes to show something.

I found a file with v1.3 and v2.0 and the plain 800 driver wouldn't load it, but the 800xl driver would boot them properly.

So let's make some changes to atari400.cpp:

We'll add our capture buffer, plus a couple of functions to pass-through the pia read and write:

Code
uint m_computereyescapturebuffer[192][320];  // resolution of atari 800 capture is 320x192
int  m_cex, m_cey;
int m_portb_write;

uint8_t mypiaread_alt(offs_t offset){

int retval = m_pia->read_alt(offset);

if (offset==0) {

int threshold = ((m_portb_write & (1<<3)) ? 128 : 0) +      // bit 3 = threshold bit 7
                ((m_portb_write & (1<<2)) ? 64  : 0) +      // bit 2 = threshold bit 6
                ((m_portb_write & (1<<1)) ? 32  : 0) ;      // bit 1 = threshold bit 5

retval = m_computereyescapturebuffer[std::min(m_cey,191)][std::min(m_cex,319)]>threshold ? 0x40:0;  // return capture in bit 6, sync is bit 7

// find address of pc
std::string mycontext = machine().describe_context();
int myaddr = stoi(mycontext.substr(mycontext.find("(")+1,4),nullptr,16);
if (myaddr==0x1eb7) // only increment our position if we are reading $D300 at address 0xc101
{
  m_cey=m_cey+1;
  if(m_cey>191) {m_cey=0; m_cex=m_cex+1;}
}
}
//printf("read pia offset=%x return=%x\n",offset,retval);
return retval;
}


void   mypiawrite_alt(offs_t offset, uint8_t data) {

if (offset==0) {  // reset the counters on any port write
  m_cex=0;
  m_cey=0;
  printf("set cex,cey to zero pia write to D300 \n");
  m_portb_write = data;
}
;printf("write pia offset=%x  data=%x\n",offset,data); m_pia->write_alt(offset,data); }

};



put my pass through handlers into the memory map in place of the pia:
Code
void a400_state::a800xl_mem(address_map &map)
{
        map(0x0000, 0xcfff).rw(FUNC(a400_state::a800xl_low_r), FUNC(a400_state::a800xl_low_w));
        map(0xd000, 0xd0ff).rw(m_gtia, FUNC(gtia_device::read), FUNC(gtia_device::write));
        map(0xd100, 0xd1ff).noprw();
        map(0xd200, 0xd2ff).rw("pokey", FUNC(pokey_device::read), FUNC(pokey_device::write));
//      map(0xd300, 0xd3ff).rw(m_pia, FUNC(mypiaread_alt), FUNC(pia6821_device::write_alt));
// put in my pia functions
        map(0xd300, 0xd3ff).rw(FUNC(a400_state::mypiaread_alt), FUNC(a400_state::mypiawrite_alt));
        map(0xd400, 0xd4ff).rw(m_antic, FUNC(antic_device::read), FUNC(antic_device::write));
        map(0xd500, 0xd7ff).noprw();
        map(0xd800, 0xffff).rw(FUNC(a400_state::a800xl_high_r), FUNC(a400_state::a800xl_high_w));
}


add our save item to a800xl:
Code
MACHINE_START_MEMBER( a400_state, a800xl )
{
        m_mmu = 0xfd;
        m_ext_bank = 0x03;  // only used by a130xe
        setup_cart(m_cart);

        save_item(NAME(m_cart_disabled));
        save_item(NAME(m_cart_helper));
        save_item(NAME(m_last_offs));
        save_item(NAME(m_mmu));
        save_item(NAME(m_ext_bank));

 save_item(NAME(m_computereyescapturebuffer));

}



Making a picture in Gimp that's 320x192.

and our lua code to load the picture from a disk file:
Code
f=assert(io.open("ATARI800.data"))
a=f:read("*a") f:close() print(#a) 
quick = emu.items()["Atari 800XL (NTSC)/:/0/m_computereyescapturebuffer"] print(quick)
for i=0,320*200-1 do emu.item(quick):write(i,a:byte(1+i*4)) end  -- just grab the red component to make it simple


[Linked Image][Linked Image][Linked Image][Linked Image]


What's different about the Atari 800 is that you get two new options: High contrast and Low contrast which gives you a 4 level grayscale capture, which I like a little better than the dithering.

[Linked Image][Linked Image]

Joined: Mar 2001
Posts: 16,914
Likes: 56
R
Very Senior Member
Online Content
Very Senior Member
R
Joined: Mar 2001
Posts: 16,914
Likes: 56
Very, very cool!

Joined: Feb 2014
Posts: 828
Likes: 37
G
Senior Member
Offline
Senior Member
G
Joined: Feb 2014
Posts: 828
Likes: 37
Thanks, RB!

I found a disk image of the ComputerEyes that would capture in Double hi-res, so I did some hacking on the apple2e driver to make it work:


I did run into a couple of interesting problems.

The ioudis was preventing the annunciators from getting modified.

In the function do_io the ioudis keeps the bottommost switch from getting reached due to a return.


Code
void apple2e_state::do_io(address_space &space, int offset, bool is_iic)
{
        if(machine().side_effects_disabled()) return;

        // Handle C058-C05F according to IOUDIS
        if ((offset & 0x58) == 0x58)
        {

          if (m_ioudis)
                {       printf("m_ioudis <> 0\n");
                        switch (offset)
                        {
                                case 0x5e:  // SETDHIRES
                                        m_screen->update_now();
                                        m_video->m_dhires = true;
                                        break;

                                case 0x5f:  // CLRDHIRES
                                        m_screen->update_now();
                                        m_video->m_dhires = false;
                                        break;
                        }
                }

                return;           // always hits return here
        }

      switch (offset)      // switch here never gets reached for cases 0x58-0x5f
        {
                case 0x20:
                        if (m_cassette)
                        {



so my hack code never gets reached.

Code
                case 0x58: // AN0 off
                         m_cex=0;
                         m_cey=0;
                         printf("set cex,cey to zero AN0 %x \n",0);
                        
                         m_an0 = false; break;


Everything's made right by copying the switch statement and putting the relevant parts into an else.

Code
               else
                        switch (offset)
                        {

               case 0x58: // AN0 off
                        m_an0 = false; break;

                case 0x59: // AN0 on
                        m_an0 = true; break;

                case 0x5a: // AN1 off
                        m_an1 = false; break;

                case 0x5b: // AN1 on
                        m_an1 = true; break;

                case 0x5c: // AN2 off
                        m_an2 = false; break;

                case 0x5d: // AN2 on
                        m_an2 = true; break;

                case 0x5e: // AN3 off
                        m_an3 = false; break;

                case 0x5f: // AN3 on
                        m_an3 = true; break;
                        }

                return;         



And I noticed that ioudis is 1 on bootup, so after setting ioudis to 0 manually with the debugger memory window, things started working. Yay!

The dithering is a bit better (no pun intended) at the higher resolution of 560x192.

[Linked Image][Linked Image]
b+w double hi-res [Linked Image] color double hi-res [Linked Image]

Last edited by Golden Child; 04/29/19 02:21 PM.
Joined: Mar 2001
Posts: 16,914
Likes: 56
R
Very Senior Member
Online Content
Very Senior Member
R
Joined: Mar 2001
Posts: 16,914
Likes: 56
Yeah, that return should be inside the if (m_ioudis) {} block, not just after it. Good catch!

Joined: Feb 2014
Posts: 828
Likes: 37
G
Senior Member
Offline
Senior Member
G
Joined: Feb 2014
Posts: 828
Likes: 37
The Computereyes double hires capture program is really baffling to me because it never seems to touch the ioudis softswitches at C07E and C07F to turn ioudis off and on.

According to Jon Relay's page, C07E and C07F are the important switches.

Code
C07E 49278 IOUDISON      EC  W    Disable IOU
           RDIOUDIS      EC   R7  Status of IOU Disabling
C07F 49279 IOUDISOFF     EC  W    Enable IOU
           RDDHIRES      EC   R7  Status of Double HiRes


and ioudis is supposed to control access to the annunciators:

Code
C058 49240 CLRAN0       OE G WR   If IOUDIS off: Annunciator 0 Off
           DISXY          C  WR   If IOUDIS on: Mask X0/Y0 Move Interrupts
C059 49241 SETAN0       OE G WR   If IOUDIS off: Annunciator 0 On
           ENBXY          C  WR   If IOUDIS on: Allow X0/Y0 Move Interrupts
C05A 49242 CLRAN1       OE G WR   If IOUDIS off: Annunciator 1 Off
           DISVBL         C  WR   If IOUDIS on: Disable VBL Interrupts
C05B 49243 SETAN1       OE G WR   If IOUDIS off: Annunciator 1 On
           ENVBL          C  WR   If IOUDIS on: Enable VBL Interrupts
C05C 49244 CLRAN2       OE G WR   If IOUDIS off: Annunciator 2 Off
           X0EDGE         C  WR   If IOUDIS on: Interrupt on X0 Rising
C05D 49245 SETAN2       OE G WR   If IOUDIS off: Annunciator 2 On
           X0EDGE         C  WR   If IOUDIS on: Interrupt on X0 Falling
C05E 49246 CLRAN3       OE G WR   If IOUDIS off: Annunciator 3 Off
           Y0EDGE         C  WR   If IOUDIS on: Interrupt on Y0 Rising
           DHIRESON      ECG WR   In 80-Column Mode: Double Width Graphics
C05F 49247 SETAN3       OE G WR   If IOUDIS off: Annunciator 3 On
           Y0EDGE         C  WR   If IOUDIS on: Interrupt on Y0 Falling
           DHIRESOFF     ECG WR   In 80-Column Mode: Single Width Graphics


and according to the Apple IIe Technical reference manual, appendix F: frequently used tables,

Code
IOUDIS W $C07E On: disable IOU access for addresses $C058 to $C05F; enable access to DHIRES switch
IOUDIS W $C07F Off: enable IOU access for addresses $C058 to $C05F; disable access to DHIRES switch


and lower down on the page it says:

Code
 The firmware normally leaves IOUDIS on.


The thing is, I can't find anywhere that touches these softswitches:

Running the apple2e driver with digitalvision_computereyes_dhgr_v2.4.dsk (v2.4 which was found inside of digitalvision_computereyes.zip)
and another version in AIIComputerEyesDHR.DSK (v.1.0 of the capture program)

and setting a watchpoint in the debugger before the driver boots:

Code
wpset c07e,2,rw


and it never hits the watchpoint at all in the capture program.

So it must be still able to modify the Annunciators even with IOUDIS on?

Code
812A: AD 5E C0 lda DHIRESON
812D: 66 01    ror $01
812F: 90 03    bcc $8134
8131: AD 5F C0 lda DHIRESOFF
8134: AD 5A C0 lda CLRAN1
8137: 66 01    ror $01
8139: 90 03    bcc $813e
813B: AD 5B C0 lda SETAN1
813E: AD 5C C0 lda CLRAN2
8141: 66 01    ror $01
8143: 90 03    bcc $8148
8145: AD 5D C0 lda SETAN2
8148: AD 59 C0 lda SETAN0

Joined: Feb 2014
Posts: 828
Likes: 37
G
Senior Member
Offline
Senior Member
G
Joined: Feb 2014
Posts: 828
Likes: 37
Let's see if we can capture a "real" picture from our webcam and get it to load:

Using v4l2 on ubuntu with my LifeCam VX-6000:

lsusb reports:
Bus 001 Device 014: ID 045e:00f4 Microsoft Corp. LifeCam VX-6000 (SN9C20x + OV9650)

Code
v4l2-ctl --device /dev/video0 --stream-mmap --stream-to=frame.jpg --stream-count=1 --set-fmt-video=width=640,height=480,pixelformat=JPEG

and then convert frame.jpg to a raw RGBA using imagemagick:

Code
convert -resize 280x192\! frame.jpg -size 280x192 -depth 8 -modulate 200,0 -brightness-contrast 0x50 RGBA:myframe.data


I had to put the exclamation mark after the resize or imagemagick would set it to the original aspect ratio and I'd get a 256x192 image instead of 280x192.

The modulate takes away all of the color and I thought I'd ramp up the contrast a bit:

and then load it into the buffer, though it's a good idea to do this once the computer has booted, since it treats the controller button 2 as one of the open apple keys
and it goes into a self test.

Code
f=assert(io.open("myframe.data"))
a=f:read("*a") f:close() print(#a) 
quick = emu.items()["Apple //e/:/0/m_computereyescapturebuffer"] print(quick)
for i=0,280*192-1 do emu.item(quick):write(i,a:byte(1+i*4)) end  -- just grab the red component to make it simple


Taking a picture of something handy nearby and then passing it to the Apple //e double-hi res capture program:

[Linked Image][Linked Image]


I also found a c64 computereyes capture program that will do greys (5 levels of grey though only 2 levels per 8x8 block)

computereyes32doodle-sb168180-Page-ZimmermanSet2.d64

It will do a LOW-CONT capture or a HIGH-CONT capture in addition to the regular B/W dithering.

But it puts its routines in a different location so my hacky code that checks the PC needs modifying, The accesses for $DD01 that are important use the BIT opcode so we'll look for a BIT opcode then. Hacky, but it seems to work.

Code
READ8_MEMBER( c64_state::cia2_pb_r )
{

int threshold = ((m_portb_write & (1<<2)) ? 128 : 0) +      // bit 2 = threshold bit 7
                ((m_portb_write & (1<<1)) ? 64  : 0) +      // bit 1 = threshold bit 6
                ((m_portb_write & (1<<0)) ? 32  : 0) ;      // bit 0 = threshold bit 5

int valtoreturn = m_computereyescapturebuffer[std::min(m_cey,199)][std::min(m_cex,319)]>threshold ? 0x40:0;  // return capture in bit 6, sync is bit 7
if (m_ram->pointer()[m_maincpu->pc()]==0x2c)  // 0x2c == BIT opcode, looking for BIT $DD01 
{
m_cey=m_cey+1;
if(m_cey>199) {m_cey=0; m_cex=m_cex+1;}
}

return valtoreturn;  


and we just convert it to 320x200 with imagemagick:
Code
convert -resize 320x200\! frame.jpg -size 320x200 -depth 8 -modulate 200,0 -brightness-contrast 0x50 RGBA:myframe.data

[Linked Image][Linked Image]


Last edited by Golden Child; 05/02/19 04:31 AM.
Joined: Jan 2012
Posts: 1,160
Likes: 6
rfka01 Offline OP
Very Senior Member
OP Offline
Very Senior Member
Joined: Jan 2012
Posts: 1,160
Likes: 6
Guy over at the German vintage computer forum uploaded an archive with Basis 108 disk images ... I've uploaded them to the FTP just in case.

https://forum.classic-computing.de/forum/index.php?thread/16546-disketten-sw-f%C3%BCr-basis-108/


NCR DMV- DEC Rainbow- Siemens PCD- ITT 3030-Oly People- Acorn A5000- Olivetti M20
Joined: Mar 2001
Posts: 16,914
Likes: 56
R
Very Senior Member
Online Content
Very Senior Member
R
Joined: Mar 2001
Posts: 16,914
Likes: 56
That's great! Original software for Apple II clones is a bit hard to find these days smile

Joined: Jan 2012
Posts: 1,160
Likes: 6
rfka01 Offline OP
Very Senior Member
OP Offline
Very Senior Member
Joined: Jan 2012
Posts: 1,160
Likes: 6
I'm not sure if they're original, but at least they're marked as being for the Basis 108.


NCR DMV- DEC Rainbow- Siemens PCD- ITT 3030-Oly People- Acorn A5000- Olivetti M20
Joined: Feb 2014
Posts: 828
Likes: 37
G
Senior Member
Offline
Senior Member
G
Joined: Feb 2014
Posts: 828
Likes: 37
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:

Code
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;


Code
READ8_MEMBER(apple2e_state::c080_r)
{

//  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;}
              else

                  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;

        if(!machine().side_effects_disabled())
...


and we'll hack on c080_w for the writes:

Code

WRITE8_MEMBER(apple2e_state::c080_w)
{
        int slot;

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

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




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:

Code
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


Code
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.
Quote

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!


http://www.digital-vision-inc.com/productCEAppleII2.htm

Last edited by Golden Child; 05/05/19 05:30 PM.
Page 36 of 78 1 2 34 35 36 37 38 77 78

Link Copied to Clipboard
Who's Online Now
3 members (Revenant, Reznor007, mixmaster), 21 guests, and 1 robot.
Key: Admin, Global Mod, Mod
ShoutChat
Comment Guidelines: Do post respectful and insightful comments. Don't flame, hate, spam.
Forum Statistics
Forums9
Topics9,089
Posts119,122
Members5,014
Most Online890
Jan 17th, 2020
Our Sponsor
These forums are sponsored by Superior Solitaire, an ad-free card game collection for macOS and iOS. Download it today!

Superior Solitaire
Forum hosted by www.retrogamesformac.com