|
Joined: Feb 2014
Posts: 1,135 Likes: 198
Very Senior Member
|
Very Senior Member
Joined: Feb 2014
Posts: 1,135 Likes: 198 |
One of the great mysteries to me was why calling write on an io port was ineffective. I wanted to set the Apple II's configuration for the Composite monitor type and it wouldn't work at all. 0 = color 1 = bw 2 = green 3 = amber Calling manager:machine():ioport().ports[":a2_config"]:write(1) would seemingly have no effect, I could then print it out and see that there was no change:
[MAME]> print(manager:machine():ioport().ports[":a2_config"]:read())
0
[MAME]> print(manager:machine():ioport().ports[":a2_config"]:write(1))
[MAME]> print(manager:machine():ioport().ports[":a2_config"]:read())
0
and the trying of the field's set_value behaved strangely: only by passing it values of 1 or 0 would get it to "cycle" through the possible values. manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"]:set_value(0) manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"]:set_value(1) This made no sense to me at all. But looking at the code to set the ioport_fields in ioport.cpp gave some clues:
void ioport_field::set_value(ioport_value value)
{
m_digital_value = value != 0;
}
So here the m_digital value is only getting a value of 1 or 0. So I thought, why not set the live value directly: Adding a set_value function to the ioport_field_live in ioport.h // internal live state of an input field
struct ioport_field_live
{
// construction/destruction
ioport_field_live(ioport_field &field, analog_field *analog);
void set_value(ioport_value newvalue) {value = newvalue;};
and adding set_value and value to the ioport_field_live usertype in luaengine.cpp /* field.live
*/
sol().registry().new_usertype<ioport_field_live>("ioport_field_live", "new", sol::no_constructor,
"set_value", &ioport_field_live::set_value, // added set_value function
"name", &ioport_field_live::name,
"value", &ioport_field_live::value); // added access to member value
Now I can set the value directly: [MAME]> manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live:set_value(0)
[MAME]> manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live:set_value(1)
[MAME]> manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live:set_value(2)
[MAME]> manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live:set_value(3)
[MAME]> manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live.value=2
[MAME]> manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live.value=0
[MAME]> print(manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live.value)
0
[MAME]> manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live.value=3
[MAME]> print(manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live.value)
3
[MAME]> print(manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live:set_value(5))
[MAME]> print(manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live.value)
5
[MAME]> print(manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live:set_value(2))
[MAME]> print(manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live.value)
2
[MAME]> print(manager:machine():ioport().ports[":a2_config"].fields["Shift key mod"].live.value)
4
[MAME]> print(manager:machine():ioport().ports[":a2_config"]:read())
6
[MAME]> manager:machine():ioport().ports[":a2_config"].fields["Shift key mod"].live.value=0
[MAME]> print(manager:machine():ioport().ports[":a2_config"].fields["Shift key mod"].live.value)
0
[MAME]> print(manager:machine():ioport().ports[":a2_config"]:read())
2
If I set the value "out of bounds" like setting the monitor type to 5 the mame control panel will complain INVALID, but it doesn't seem to hurt anything since it gets masked off. One thing I realize now is that .live.value=x and .live:set_value(x) are equivalent, so the set_value function probably isn't needed. Another thing that is a bit perplexing is that you don't want to mix these two accesses: This is the field set_value:
manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].set_value(1)
and this is the field.live.value or field.live.set_value()
manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live.value=1
manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"].live.set_value(1)
When you use the field set_value, it causes strange behaviors when you use the live set_value, like inverting the field bits. So use the live one 8-) ![[Linked Image from i.imgur.com]](https://i.imgur.com/BZcAfxg.png) And one last bit: here's a print table function where you pass a lambda function that will print the .live.value for each field in the table: It's pretty handy to show you all of the values of each field.
[MAME]> function printtf(a,f) for i,j in pairs(a) do print(i,f(j)) end end
[MAME]> printtf(manager:machine():ioport().ports[":a2_config"].fields, function(b) return b.live.value end)
Composite monitor type 2
Shift key mod 0
And if you want to get tricky with lambda functions, this will print all of the values of the ports and fields:
[MAME]> function dopairs(a,f) for i,j in pairs(a) do f(i,j) end end -- calls f on each pair i,j in the table
[MAME]> dopairs(manager:machine():ioport().ports,function(a,b) print("ports[\""..a.."\"]:read()="..b:read()) dopairs(b.fields,function(i,j) print("ports[\""..a.."\"].".."fields[\""..i.."\"]"..".live.value="..j.live.value) end) end)
ports[":a2_config"]:read()=2
ports[":a2_config"].fields["Composite monitor type"].live.value=2
ports[":a2_config"].fields["Shift key mod"].live.value=0
ports[":sl2:agraphtablet:a2mse_x"]:read()=64608
ports[":sl2:agraphtablet:a2mse_x"].fields["Mouse X 3"].live.value=0
... etc
Last edited by Golden Child; 04/21/19 04:27 PM.
|
|
|
|
Joined: Feb 2004
Posts: 2,608 Likes: 315
Very Senior Member
|
Very Senior Member
Joined: Feb 2004
Posts: 2,608 Likes: 315 |
The write method on an I/O port isn't for setting the value of input fields. I/O ports can have output fields as well, with handlers supplied by a device. That's what write is for.
|
|
|
|
Joined: Feb 2014
Posts: 1,135 Likes: 198
Very Senior Member
|
Very Senior Member
Joined: Feb 2014
Posts: 1,135 Likes: 198 |
So why not get the tablet to do some turtlegraphics? ![[Linked Image from i.imgur.com]](https://i.imgur.com/yEegxIk.png) function hgrclr() for i=16384,16384+8192-1 do manager:machine().devices[":maincpu"].spaces["program"]:write_u8(i,0) end end
function pu() manager:machine():ioport().ports[":sl2:agraphtablet:joystick_3_buttons"].fields["P5 Button 2"].live.value = 0x0 end
function pd() manager:machine():ioport().ports[":sl2:agraphtablet:joystick_3_buttons"].fields["P5 Button 2"].live.value = 0x20 end
function setxy(x,y) manager:machine():ioport().ports[":sl2:agraphtablet:joystick_3_x"].fields["P3 Joystick X"].live:set_analog_value(math.floor((x)*1024),math.floor((x)*1024),0) manager:machine():ioport().ports[":sl2:agraphtablet:joystick_3_y"].fields["P3 Joystick Y"].live:set_analog_value(math.floor((y)*1024),math.floor((y)*1024),0) end
function fd(dist) turtlex = turtlex + math.cos(turtledir/360.0*2*math.pi)*dist turtley=turtley-math.sin(turtledir/360.0*2*math.pi)*dist setxy(turtlex,turtley) fr() end
function fd(dist) pieces=10 dist=dist/pieces for i=0,9 do turtlex = turtlex + math.cos(turtledir/360.0*2*math.pi)*dist turtley=turtley-math.sin(turtledir/360.0*2*math.pi)*dist setxy(turtlex,turtley) fr() end end
function bk(dist) fd(-dist) end
function rt(tang) turtledir = (turtledir - tang) % 360.0 end
function lt(tang) rt(-tang) end
function fr(f) f = f or 1 emu.wait(f/60+.001) end
function home() hgrclr() turtlex=0 turtley=0 turtledir=0 pu() fr() setxy(turtlex,turtley) fr() end
function docircle() home() pd() for j=0,35 do rt(10) fr() print(j,turtledir) for i=0,35 do fd(8) fr() rt(10) end end end
function doco(f,...) co1=coroutine.create(f) ok,err = coroutine.resume(co1,...) print("coroutine.resume -> ",ok,err) end
-- doco = do coroutine
--doco(docircle)
manager:machine():options().entries["snapsize"]:value("560x384")
function drawsquiral() home() for i=0,63 do pu() fr(8) pd() fr(8) fd(i/.7+3) fr(8) rt(88) end print("DONE") end
-- much smoother if you keep the pen down
function drawsquiral() home() pd() for i=0,63 do fr(8) fd(i/.7+3) fr(8) rt(88) end print("DONE") pu() fr() end
doco(drawsquiral)
-- function clickat(x,y) pu() fr(3) setxy(x-128,y-128) fr(3) pd() fr(3) end
-- applesoft can't keep up with "quick" pendowns and penups, so give it plenty of time to react
function clickat(x,y) pu() fr(3) setxy(x-128,y-128) print("click at ",x,y) fr(15) pd() fr(20) pu() fr(25) setxy(0,0) fr(2) end
function clickb(x,y,size) buttonsize=size/22 clickat(x*buttonsize+buttonsize/2+13,4) end
doco(clickb,9,4,194) -- click on pencolor button
doco(clickb,0,4,194) -- click on reset button
And I can click on the tablet menu overlays manually: doco(clickb,15,4,194) will click on the "CATALOG" button.
|
|
|
|
Joined: Feb 2014
Posts: 1,135 Likes: 198
Very Senior Member
|
Very Senior Member
Joined: Feb 2014
Posts: 1,135 Likes: 198 |
I don't have a real graphics tablet to calibrate against, however there is calibration information on the various software disks available. The tablet calibration file is stored in TAB.INFORMATION and it has a really simple format: slot #, upper left x1,y1 and lower right x2,y2 490 PRINT D$;"OPEN TAB.INFORMATION" 500 PRINT D$;"WRITE TAB.INFORMATION" 510 PRINT SL: PRINT X1: PRINT Y1: PRINT X2: PRINT Y2 520 PRINT D$;"CLOSE TAB.INFORMATION" 525 PRINT D$;"LOCK TAB.INFORMATION" The scale is set to 2 in the MENU ALIGNMENT calibration program so those numbers should be multiplied by 2 to get the actual results. [MAME]> loaddisk("../../APPLE GRAPHICS TABLET pristine.dsk")
[MAME]> hexdump(getfileraw("TAB.INFORMATION"))
(11,0f) 00 *A 006 HELLO TS List=(13,0f)
(11,0f) 01 *B 022 GRAPHICS TABLET LOGO TS List=(14,0f)
(11,0f) 02 *A 012 MENU ALIGNMENT TS List=(15,0f)
(11,0f) 03 *T 002 GRAPHICS TABLET SOFTWARE TS List=(16,0f)
(11,0f) 04 *A 00a QUICK-DRAW TS List=(17,0f)
(11,0f) 05 *B 005 UTILITIES TS List=(18,0f)
(11,0e) 01 *T 002 TAB.INFORMATION TS List=(20,0f)
matched "TAB.INFORMATION" with "TAB.INFORMATION "
00 b2 8d b2 b9 b3 8d b3 b2 | b6 8d b3 b0 b9 b0 8d b3 | 2.293.326.3090.3
10 b1 b1 b8 8d 00 00 00 00 | 00 00 00 00 00 00 00 00 | 118.............
20 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................
30 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................
40 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................
50 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................
60 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................
[MAME]> loaddisk("../../Apple Graphics Tablet - Applesoft BASIC.dsk")
[MAME]> hexdump(getfileraw("TAB.INFORMATION"))
(11,0f) 00 *A 006 HELLO TS List=(12,0f)
(11,0f) 01 *B 022 GRAPHICS TABLET LOGO TS List=(13,0f)
(11,0f) 02 *A 012 MENU ALIGNMENT TS List=(16,0f)
(11,0f) 03 *A 002 DOCUMENT FILE LENGTHS TS List=(0c,0f)
(11,0f) 04 *B 005 UTILITIES TS List=(0a,0d)
(11,0f) 05 *T 002 TAB.INFORMATION TS List=(09,0f)
matched "TAB.INFORMATION" with "TAB.INFORMATION "
00 b5 8d b2 b8 b5 8d b2 b7 | b1 8d b3 b0 b7 b1 8d b3 | 5.285.271.3071.3
10 b0 b5 b6 8d 00 00 00 00 | 00 00 00 00 00 00 00 00 | 056.............
20 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................
30 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................
40 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................
50 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | ................ So it looks like the numbers are pretty consistent, around 290 and 3090. X and Y are approx the same so we'll assume they will be the same. Multiplying by 2 we get around 580 for upper left and 6200 for lower right. If we divide those numbers by 3 we get approx 193 and 2066 for our tablet calibration. [MAME]> print(580/3) 193.33333333333 [MAME]> print(6200/3) 2066.6666666667 [MAME]> and we'll change those sliders to match: Some fine tuning of the values gives us 2064 and 193. ![[Linked Image from i.imgur.com]](https://i.imgur.com/nSIoD2g.png) Now the tablet is pretty well calibrated for the graphics software, [MAME]> setxy(-127,0) -- puts cursor on the far left edge [MAME]> setxy(127,0) -- puts cursor on the far right edge [MAME]> setxy(-128,0) -- puts cursor off the screen [MAME]> setxy(128,0) The tablet software seems to work better with the ability to have the cursor off the screen at the edges. Computing the position of the buttons is a lot easier with the possible joystick range from -128 to 128 going from screen edge to screen edge. function clickat(x,y) pu() fr(3) setxy(x-128,y-128) print("click at ",x,y) fr(15) pd() fr(20) pu() fr(25) setxy(0,0) fr(2) end
function clickb(x,y,size,offset) size = size or 255 offset = offset or 0 buttonsize=size/22 clickat(x*buttonsize+buttonsize/2+offset,4) end -- 22 buttons across
function doco(f,...) co1=coroutine.create(f) ok,err = coroutine.resume(co1,...) print("coroutine.resume -> ",ok,err) end
doco(clickb,9) -- clicks on Pen Color
doco(clickb,3) -- click on BG Color
and I can do my own Jackson Pollock. ![[Linked Image from i.imgur.com]](https://i.imgur.com/3T1D8g6.png)
|
|
|
|
Joined: Jul 2007
Posts: 43 Likes: 4
Member
|
Member
Joined: Jul 2007
Posts: 43 Likes: 4 |
FYI - The latest WOZ disks for Adventureland and Pirate Adventure do not appear to work in MAME once you get to the actual game. Could this be more weak bit issues from the copy protection? 4AM similar crack infoVideo below is a playthrough of Adventureland for reference: Playthrough of AdventurelandThanks, A-Noid
|
|
|
|
Joined: Mar 2001
Posts: 17,239 Likes: 263
Very Senior Member
|
Very Senior Member
Joined: Mar 2001
Posts: 17,239 Likes: 263 |
Those don't have weak bits from what I've read of the protection, it's a deeper issue. Probably the same one that prevents us from implementing the weak bits. I'm gonna have to write my own damn floppy subsystem at this rate.
|
|
|
|
Joined: Jul 2007
Posts: 43 Likes: 4
Member
|
Member
Joined: Jul 2007
Posts: 43 Likes: 4 |
Thanks Arbee! Impossible Mission WOZ was released today and has it's own, but different difficulties.
It will be nice to play these with graphics. Also, has Votrax speech!
A-Noid
|
|
|
|
Joined: Feb 2014
Posts: 1,135 Likes: 198
Very Senior Member
|
Very Senior Member
Joined: Feb 2014
Posts: 1,135 Likes: 198 |
![[Linked Image from i.imgur.com]](https://i.imgur.com/H7oaXtW.png) Managed to figure out enough about layouts to make a row of buttons. Launching mame with -override_artwork my_art_directory works pretty well. I have to be careful with this XML stuff, if there's any errors mame segfaults on startup. Segfault! (sung to the Freak out song) Segfault! xmllint is pretty darn useful in this case to zero in on the erroneous xml. It's so easy to lose a quote here and there.
Segmentation fault (core dumped)
$ xmllint artwork/segfault_apple2p/default.lay
artwork/segfault_apple2p/default.lay:165: parser error : Unescaped '<' not allowed in attributes values
"menu~menunum~" inputtag=" sl2:agraphtablet:joystick_3_buttons" inputmask="0x10>
^
artwork/segfault_apple2p/default.lay:165: parser error : attributes construct error
"menu~menunum~" inputtag=" sl2:agraphtablet:joystick_3_buttons" inputmask="0x10>
^
|
|
|
|
Joined: Feb 2004
Posts: 2,608 Likes: 315
Very Senior Member
|
Very Senior Member
Joined: Feb 2004
Posts: 2,608 Likes: 315 |
You realise the format is documented, and we include a script that validates a lot of stuff in XML layouts, right?
|
|
|
|
Joined: Feb 2014
Posts: 1,135 Likes: 198
Very Senior Member
|
Very Senior Member
Joined: Feb 2014
Posts: 1,135 Likes: 198 |
Hi Vas, I read the page at https://docs.mamedev.org/techspecs/layout_files.html which is a lot to digest for a noob. and now I see there's a section on error handling that says: Error handling
For internal (developer-supplied) layout files, errors detected by the complay.py script result in a build failure. MAME will stop loading a layout file if a syntax error is encountered. No views from the layout will be available. Examples of syntax errors include undefined element or group references, invalid bounds, invalid colours, recursively nested groups, and redefined generator parameters. When loading a layout file, if a view references a non-existent screen, MAME will print a warning message and continue. Views referencing non-existent screens are considered unviable and not available to the user. Oh now I see the part about complay.py: Using complay.py
The MAME source contains a Python script called complay.py, found in the scripts/build subdirectory. This script is used as part of MAME’s build process to reduce the size of data for internal layouts and convert it to a form that can be built into the executable. However, it can also detect many common layout file format errors, and generally provides better error messages than MAME does when loading a layout file. Note that it doesn’t actually run the whole layout engine, so it can’t detect errors like undefined element references when parameters are used, or recursively nested groups. The complay.py script is compatible with both Python 2.7 and Python 3 interpreters.
To check a layout file for common errors, run the script with the path to the file no check and no output file name or base variable name. For example:
python scripts/build/complay.py artwork/dino/default.lay And complay.py will tell me the line and the character position of the malformed token. Thanks for the pointer. I learn something new about mame every day.
$ python scripts/build/complay.py artwork/segfault_apple2p/default.lay
fatal error: artwork/segfault_apple2p/default.lay:164:98: not well-formed (invalid token)
Last edited by Golden Child; 04/24/19 05:08 PM.
|
|
|
0 members (),
58
guests, and
5
robots. |
Key:
Admin,
Global Mod,
Mod
|
|
Forums9
Topics9,331
Posts122,197
Members5,077
|
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!
|
|
|
|