|
Joined: Feb 2014
Posts: 1,173 Likes: 214
Very Senior Member
|
Very Senior Member
Joined: Feb 2014
Posts: 1,173 Likes: 214 |
Hi Anoid, I thought I'd give the HERO woz a spin and sure enough, it doesn't work properly, dropping dynamite every time you land (until you run out of dynamite, of course). According to the atari 2600 manual for hero, when you pull the stick down its supposed to put down some dynamite. So that must be what's happening. When I was playing with the joystick calibration values, I would just put some different values in and with trial and error and much recompiling I came up with some good values. Waiting for the compile and link became intolerable. So then I thought, what if I could change the calibration "on the fly" while mame was running. I thought a slider would be cool, but that would mean messing about in the sliders with ui.cpp. WARNING: HORRIBLE HACK ALERT! I did a terrible hack of luaengine.cpp and the apple2.cpp, a terrible terrible hack but it seems to work. (For proof of concept) So I added m_x_calibration, and m_y_calibration to the save_item list:
save_item(NAME(m_an0));
save_item(NAME(m_an1));
save_item(NAME(m_an2));
save_item(NAME(m_an3));
save_item(NAME(m_anykeydown));
save_item(NAME(m_x_calibration));
save_item(NAME(m_y_calibration));
And then did a horrible hack of emu.item, adding readasdouble and writeasdouble:
*
* emu.item(item_index)
* item.size - size of the raw data type
* item.count - number of entries
* item:read(offset) - read entry value by index
* item:read_block(offset, count) - read a block of entry values as a string (byte addressing)
* item:write(offset, value) - write entry value by index
*/
emu.new_usertype<save_item>("item", sol::call_constructor, sol::initializers([this](save_item &item, int index) {
if(!machine().save().indexed_item(index, item.base, item.size, item.count))
{
item.base = nullptr;
item.size = 0;
item.count= 0;
}
}),
"size", sol::readonly(&save_item::size),
"count", sol::readonly(&save_item::count),
"read", [this](save_item &item, int offset) -> sol::object {
uint64_t ret = 0;
if(!item.base || (offset > item.count))
return sol::make_object(sol(), sol::nil);
switch(item.size)
{
case 1:
default:
ret = ((uint8_t *)item.base)[offset];
break;
case 2:
ret = ((uint16_t *)item.base)[offset];
break;
case 4:
ret = ((uint32_t *)item.base)[offset];
break;
case 8:
ret = ((uint64_t *)item.base)[offset];
break;
}
return sol::make_object(sol(), ret);
},
"readasdouble", [this](save_item &item, int offset) -> sol::object {
uint64_t ret = 0;
double retfloat = 0;
if(!item.base || (offset > item.count))
return sol::make_object(sol(), sol::nil);
switch(item.size)
{
case 1:
default:
ret = ((uint8_t *)item.base)[offset];
break;
case 2:
ret = ((uint16_t *)item.base)[offset];
break;
case 4:
ret = ((uint32_t *)item.base)[offset];
break;
case 8:
ret = ((uint64_t *)item.base)[offset];
break;
}
retfloat = (*(double *) &ret); // get the return value as a float
return sol::make_object(sol(), retfloat);
},
"read_block", [](save_item &item, int offset, sol::buffer *buff) {
if(!item.base || ((offset + buff->get_len()) > (item.size * item.count)))
buff->set_len(0);
else
memcpy(buff->get_ptr(), (uint8_t *)item.base + offset, buff->get_len());
return buff;
},
"write", [](save_item &item, int offset, uint64_t value) {
if(!item.base || (offset > item.count))
return;
switch(item.size)
{
case 1:
default:
((uint8_t *)item.base)[offset] = (uint8_t)value;
break;
case 2:
((uint16_t *)item.base)[offset] = (uint16_t)value;
break;
case 4:
((uint32_t *)item.base)[offset] = (uint32_t)value;
break;
case 8:
((uint64_t *)item.base)[offset] = (uint64_t)value;
break;
}
},
"writeasdouble", [](save_item &item, int offset, double value) {
if(!item.base || (offset > item.count))
return;
switch(item.size)
{
case 1:
default:
((uint8_t *)item.base)[offset] = (uint8_t)value;
break;
case 2:
((uint16_t *)item.base)[offset] = (uint16_t)value;
break;
case 4:
((uint32_t *)item.base)[offset] = (uint32_t)value;
break;
case 8:
((double *)item.base)[offset] = value; // write the double value
break;
}
});
and I also made a new device method to return all of items in the item list: allitems Why did I do that? Because the items in the apple2 state are inaccessible. I can see them in the Memory Window in the debugger, but not with lua. If I execute this lua one-liner, my x and y calibration don't show up in the device list. for devname,devdevice in pairs(manager:machine().devices) do print (devname,devdevice) for i,j in pairs(devdevice.items) do print(i,j) end end But device.items[] iterates through the whole list, checking the devicetag to match, why not just make a variation called allitems that will just add them all. So it you look below, it just has an if (1).
/* machine.devices[device_tag]
* device:name() - device long name
* device:shortname() - device short name
* device:tag() - device tree tag
* device:owner() - device parent tag
* device:debug() - debug interface, cpus only
* device.spaces[] - device address spaces table
* device.state[] - device state entries table
* device.items[] - device save state items table (item name is key, item index is value)
*/
sol().registry().new_usertype<device_t>("device", "new", sol::no_constructor,
"name", &device_t::name,
"shortname", &device_t::shortname,
"tag", &device_t::tag,
"owner", &device_t::owner,
"debug", [this](device_t &dev) -> sol::object {
if(!(dev.machine().debug_flags & DEBUG_FLAG_ENABLED) || !dynamic_cast<cpu_device *>(&dev)) // debugger not enabled or not cpu
return sol::make_object(sol(), sol::nil);
return sol::make_object(sol(), dev.debug());
},
"spaces", sol::property([this](device_t &dev) {
device_memory_interface *memdev = dynamic_cast<device_memory_interface *>(&dev);
sol::table sp_table = sol().create_table();
if(!memdev)
return sp_table;
for(int sp = 0; sp < memdev->max_space_count(); ++sp)
{
if(memdev->has_space(sp))
sp_table[memdev->space(sp).name()] = addr_space(memdev->space(sp), *memdev);
}
return sp_table;
}),
"state", sol::property([this](device_t &dev) {
sol::table st_table = sol().create_table();
if(!dynamic_cast<device_state_interface *>(&dev))
return st_table;
// XXX: refrain from exporting non-visible entries?
for(auto &s : dev.state().state_entries())
st_table[s->symbol()] = s.get();
return st_table;
}),
"items", sol::property([this](device_t &dev) {
sol::table table = sol().create_table();
std::string tag = dev.tag();
// 10000 is enough?
for(int i = 0; i < 10000; i++)
{
std::string name;
const char *item;
unsigned int size, count;
void *base;
item = dev.machine().save().indexed_item(i, base, size, count);
if(!item)
break;
name = &(strchr(item, '/')[1]);
if(name.substr(0, name.find("/")) == tag)
{
name = name.substr(name.find("/") + 1, std::string::npos);
table[name] = i;
}
}
return table;
}),
"allitems", sol::property([this](device_t &dev) {
sol::table table = sol().create_table();
std::string tag = dev.tag();
// 10000 is enough?
for(int i = 0; i < 10000; i++)
{
std::string name;
const char *item;
unsigned int size, count;
void *base;
item = dev.machine().save().indexed_item(i, base, size, count);
if(!item)
break;
name = &(strchr(item, '/')[1]);
//if(name.substr(0, name.find("/")) == tag)
if(1)
{
name = name.substr(name.find("/") + 1, std::string::npos);
table[name] = i;
}
}
return table;
})
);
so once that's done I can do this: emu.item(manager:machine().devices[":maincpu"].allitems["0/m_y_calibration"]):writeasdouble(0,9800e-9) emu.item(manager:machine().devices[":maincpu"].allitems["0/m_y_calibration"]):writeasdouble(0,9800e-9) If you read it as an integer, it is nonsense. print(emu.item(manager:machine().devices[":maincpu"].allitems["0/m_x_calibration"]):read(0)) 4533201175231652948 You want to use my new function readasdouble: print(emu.item(manager:machine().devices[":maincpu"].allitems["0/m_x_calibration"]):readasdouble(0)) 1.2e-05 [MAME]> function printt(a) for i,j in pairs(a) do print(i,j) end end [MAME]> [MAME]> printt(manager:machine().devices[":maincpu"].allitems) m_lastcoin 610 10/m_stored_vector 351 0/TMP 319 0/m_last_update_time.attoseconds 264 31/m_stored_vector 456 21/m_stored_vector 405 41/m_stored_vector 507 2E/m_curstate 442 1E/m_curstate 391 0/m_y_calibration 234 0/m_totalcycles 341 0/m_step_samples 308 ... etc Okay, so back to HERO: Just trying some different values I can get the joystick to work properly and not drop the dynamite: 9800 nsec seems to work: [MAME]> print(emu.item(manager:machine().devices[":maincpu"].allitems["0/m_x_calibration"]):writeasdouble(0,9800e-9)) [MAME]> print(emu.item(manager:machine().devices[":maincpu"].allitems["0/m_y_calibration"]):writeasdouble(0,9800e-9)) [MAME]> print(emu.item(manager:machine().devices[":maincpu"].allitems["0/m_x_calibration"]):readasdouble(0)) 9.8e-06 [MAME]> print(emu.item(manager:machine().devices[":maincpu"].allitems["0/m_y_calibration"]):readasdouble(0)) 9.8e-06 Actually, 10800 nsec works too (I forgot that I was using the old 12 msec values to start with) print(emu.item(manager:machine().devices[":maincpu"].allitems["0/m_x_calibration"]):writeasdouble(0,10800e-9)) print(emu.item(manager:machine().devices[":maincpu"].allitems["0/m_y_calibration"]):writeasdouble(0,10800e-9))
Last edited by Golden Child; 02/17/19 04:01 AM.
|
|
|
|
Joined: Feb 2014
Posts: 1,173 Likes: 214
Very Senior Member
|
Very Senior Member
Joined: Feb 2014
Posts: 1,173 Likes: 214 |
Horrible hack part #2 - finding the optimum value for the Apple IIGS and Stellar 7 on fast cpu mode. I added the m_x_calibration and m_y_calibration to the save item list in the apple2gs.cpp driver. So I located the joystick read routine for Stellar 7 by putting a watchpoint on C064 (analog paddle 0 read) ![[Linked Image from i.imgur.com]](https://i.imgur.com/vebgmjj.png) then I put some breakpoints on every RTS from the read joystick subroutine that would print out the joystick return values: wp c064,1,r bp 7ca1,1,{printf "x=%02x y=%02x",x,y;g} bp 7cb2,1,{printf "x=%02x y=%02x",x,y;g} bp 7cc3,1,{printf "x=%02x y=%02x",x,y;g} wpdis 1 ![[Linked Image from i.imgur.com]](https://i.imgur.com/qVcptHJ.png) then I could fine-tune my joystick calibration values with some lua commands in real time: print(emu.item(manager:machine().devices[":maincpu"].allitems["0/m_x_calibration"]):writeasdouble(0,2500e-9)) -- maximum is 3E print(emu.item(manager:machine().devices[":maincpu"].allitems["0/m_x_calibration"]):writeasdouble(0,4800e-9)) -- maximum is 7e, half 3c print(emu.item(manager:machine().devices[":maincpu"].allitems["0/m_x_calibration"]):writeasdouble(0,5150e-9)) -- maximum is 7F, half is 40 print(emu.item(manager:machine().devices[":maincpu"].allitems["0/m_y_calibration"]):writeasdouble(0,5150e-9)) So it looks like the "perfect" calibration for Stellar 7 running in fast mode is 5150 nsec.
Last edited by Golden Child; 02/17/19 04:12 AM.
|
|
|
|
Joined: Jul 2007
Posts: 43 Likes: 4
Member
|
Member
Joined: Jul 2007
Posts: 43 Likes: 4 |
Golden Child,
Thanks for the information. I guess that the fix Arbee did earlier may actually fix the problem in HERO by changing to 10800 nsec.
I do remember manually centering the joysticks on the Apple II back in the day...
A-Noid
|
|
|
|
Joined: Feb 2014
Posts: 1,173 Likes: 214
Very Senior Member
|
Very Senior Member
Joined: Feb 2014
Posts: 1,173 Likes: 214 |
Horrible hack part #3: I wanted to see if I could create a function under the emu table that would give me all of the items:
// New function emu["items"]
emu["items"] = [this](){ sol::table table = sol().create_table();
// 10000 is enough?
for(int i = 0; i < 10000; i++)
{
std::string name;
const char *item;
unsigned int size, count;
void *base;
item = machine().save().indexed_item(i, base, size, count);
if(!item) break;
//name = &(strchr(item, '/')[1]);
name = item;
table[name] = i;
}
return table;
};
Now I can do this: print(emu.item(emu.items()["Apple //e/:/0/m_y_calibration"]):readasdouble(0))
1.3e-05 If I uncomment the //name = &(strchr(item, '/')[1]); it will strip off the leading part up to the first / but I kind of like having the full name. It also matches the names that show up in the Debug: Memory View. function printt(a) for i,j in pairs(a) do print(i,j) end end function printto(a) local b={} for i,j in pairs(a) do b[j]=i end printt(b) end [MAME]> printto(emu.items()) 1 5.25" single density floppy drive/:sl6:diskiing:0:525/0/m_clock 2 5.25" single density floppy drive/:sl6:diskiing:0:525/0/m_clock_scale ... 142 Apple //e/:/0/m_x_calibration 143 Apple //e/:/0/m_xirq 144 Apple //e/:/0/m_xy 145 Apple //e/:/0/m_y0 146 Apple //e/:/0/m_y0edge 147 Apple //e/:/0/m_y1 148 Apple //e/:/0/m_y_calibration 149 Apple //e/:/0/m_yirq ... 828 timer/sound_manager::update/0/m_start.attoseconds 829 timer/sound_manager::update/0/m_start.seconds
|
|
|
|
Joined: Feb 2014
Posts: 1,173 Likes: 214
Very Senior Member
|
Very Senior Member
Joined: Feb 2014
Posts: 1,173 Likes: 214 |
More hacking on luaengine: why not just directly get the item? If I create this function (it's horribly inefficient but proof of concept:) I can just go ahead and get an item directly from a string:
emu["getitem"] = [this](const char* itemname){ sol::table table = sol().create_table();
// 10000 is enough?
int index = -1;
save_item return_item;
if (!(itemname == 0))
for(int i = 0; i < 10000; i++)
{
std::string name;
const char *item;
unsigned int size, count;
void *base;
item = machine().save().indexed_item(i, base, size, count);
if(!item) break;
//name = &(strchr(item, '/')[1]);
name = item;
table[name] = i;
if (itemname == name) index = i;
}
if (index == -1 || !machine().save().indexed_item(index, return_item.base, return_item.size, return_item.count))
{ return_item.base = nullptr;
return_item.size = 0;
return_item.count= 0;
};
return return_item;
};
[MAME]> print(emu.item(emu.items()["Apple //e/:/0/m_y_calibration"]):readasdouble(0))
1.3e-05
[MAME]> print(emu.getitem("Apple //e/:/0/m_y_calibration"):readasdouble(0))
1.3e-05
[MAME]> print(emu.getitem("Apple //e/:/0/m_x_calibration"):readasdouble(0))
1.2e-05
[MAME]> print(emu.getitem("Apple misspelled"):readasdouble(0))
nil
[MAME]> print(emu.getitem("doesnt exist"):readasdouble(0))
nil
[MAME]> print(emu.getitem():readasdouble(0))
nil
|
|
|
|
Joined: Jun 2014
Posts: 95 Likes: 3
Member
|
Member
Joined: Jun 2014
Posts: 95 Likes: 3 |
It's funny about Joust. I never tested it in MAME, apparently. I've known about Serpentine for ages. I thought that I submitted the fix for it, but clearly not. A couple of other games had the same requirement. I think that Pooyan and Lady Tut also rely on uninitialised memory, but the effects aren't detrimental.
Yes, Hero is very sensitive to the joystick calibration.
|
|
|
|
Joined: Jul 2007
Posts: 43 Likes: 4
Member
|
Member
Joined: Jul 2007
Posts: 43 Likes: 4 |
A couple more...
Congo Bongo (WOZ) won't boot in MAME, but the crack works from the software list.
I've noticed longer delays in Drol (WOZ) when loading the 3rd level (snakes) and winning cut scene after 3rd level. Levels 1 and 2 seem to have load times similar to cracked version.
A-Noid
|
|
|
|
Joined: Mar 2001
Posts: 17,258 Likes: 267
Very Senior Member
|
Very Senior Member
Joined: Mar 2001
Posts: 17,258 Likes: 267 |
Regarding load times, originals oftentimes will take longer because they're doing a protection check instead of just loading the data.
Peter: does Congo Bongo use weak bits?
|
|
|
|
Joined: Jun 2014
Posts: 95 Likes: 3
Member
|
Member
Joined: Jun 2014
Posts: 95 Likes: 3 |
RB: yes, along with Bruce Lee and Mr. Do.
|
|
|
|
Joined: Jun 2014
Posts: 95 Likes: 3
Member
|
Member
Joined: Jun 2014
Posts: 95 Likes: 3 |
Regarding Drol, those two sections are huge compared to the other two, and the protection is alternating quarter-track, so it's very slow to load. Try our Lode Runner crack vs the original for extreme speed difference! :-)
|
|
|
1 members (Heihachi_73),
111
guests, and
4
robots. |
Key:
Admin,
Global Mod,
Mod
|
|
Forums9
Topics9,354
Posts122,409
Members5,082
|
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!
|
|
|
|