|
Joined: Feb 2014
Posts: 1,102 Likes: 173
Very Senior Member
|
Very Senior Member
Joined: Feb 2014
Posts: 1,102 Likes: 173 |
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:
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:
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:
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:
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
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.
|
|
|
|
Joined: Mar 2001
Posts: 17,217 Likes: 234
Very Senior Member
|
Very Senior Member
Joined: Mar 2001
Posts: 17,217 Likes: 234 |
|
|
|
|
Joined: Feb 2014
Posts: 1,102 Likes: 173
Very Senior Member
|
Very Senior Member
Joined: Feb 2014
Posts: 1,102 Likes: 173 |
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. 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. 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. 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. b+w double hi-res color double hi-res
Last edited by Golden Child; 04/29/19 02:21 PM.
|
|
|
|
Joined: Mar 2001
Posts: 17,217 Likes: 234
Very Senior Member
|
Very Senior Member
Joined: Mar 2001
Posts: 17,217 Likes: 234 |
Yeah, that return should be inside the if (m_ioudis) {} block, not just after it. Good catch!
|
|
|
|
Joined: Feb 2014
Posts: 1,102 Likes: 173
Very Senior Member
|
Very Senior Member
Joined: Feb 2014
Posts: 1,102 Likes: 173 |
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. 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: 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, 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: 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: 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? 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: 1,102 Likes: 173
Very Senior Member
|
Very Senior Member
Joined: Feb 2014
Posts: 1,102 Likes: 173 |
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)
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:
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.
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: 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. 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:
convert -resize 320x200\! frame.jpg -size 320x200 -depth 8 -modulate 200,0 -brightness-contrast 0x50 RGBA:myframe.data
Last edited by Golden Child; 05/02/19 04:31 AM.
|
|
|
|
Joined: Mar 2001
Posts: 17,217 Likes: 234
Very Senior Member
|
Very Senior Member
Joined: Mar 2001
Posts: 17,217 Likes: 234 |
That's great! Original software for Apple II clones is a bit hard to find these days
|
|
|
|
Joined: Jan 2012
Posts: 1,179 Likes: 17
Very Senior Member
|
OP
Very Senior Member
Joined: Jan 2012
Posts: 1,179 Likes: 17 |
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: 1,102 Likes: 173
Very Senior Member
|
Very Senior Member
Joined: Feb 2014
Posts: 1,102 Likes: 173 |
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; 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:
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. 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
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) DHR grey DHR False Color 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! http://www.digital-vision-inc.com/productCEAppleII2.htm
Last edited by Golden Child; 05/05/19 05:30 PM.
|
|
|
3 members (AJR, Dorando, 1 invisible),
346
guests, and
3
robots. |
Key:
Admin,
Global Mod,
Mod
|
|
Forums9
Topics9,320
Posts121,944
Members5,074
|
Most Online1,283 Dec 21st, 2022
|
|
These forums are sponsored by Superior Solitaire, an ad-free card game collection for macOS and iOS. Download it today!
|
|
|
|