MSX UNAPI specification

By konamiman

Paragon (1211)

konamiman's picture

14-06-2010, 08:46

This is a follow-up of the following forum thread, which initially discussed about EXTBIO device identifiers but ended up as a discussion about the MSX UNAPI specification design. I think it is a good idea to have a separate forum thread with a more appropriate title.

www.msx.org/forumtopic9422p30.html

Login or register to post comments

By konamiman

Paragon (1211)

konamiman's picture

14-06-2010, 09:18

Two days without touching a computer and you have almost redesigned the UNAPI specification! Smile Let's try to clarify things a little.

List all the UNAPI extensions connected to a MSX. Kind of a "dir" of the extensions.
This is not possible since implementations will only react when its own spec identifier is queried and will remain completely silent and undetected otherwise. At most, you could develop an utility that takes a list of known spec identifiers (for example from a configuration file) and lists all the implementations for all the identifiers in sequence.

Request an extension to unload itself. ROM extensions would just remove themselves from the chain. This can be used to easily solve eventual conflicts.
Installed UNAPI implementations do not act as a "chain", except in the discovery procedure. You either invoke the routines of one implementation or of another, so I don't understand what kind of "conflicts" could arise (could you explain which conflicts you envision?)

I would like that at least the page-2 could be used to, for a good reason (IMO): I'm interested on making a single ROM containing both the MSX-Audio BIOS and the Universal OPL driver. But that would require the Universal OPL driver to reside on the page-2, because the MSX-Audio BIOS already takes the entire page-1 for itself and leaves little room for relocation.
I can think of a few options for this:

  • Use one single entry point in page 1 for both Audio and OPL driver code. When having to invoke a page 2 routine, simply use a CALSLT from your page 1 routine.
  • Place the entry point for page 2 routines somewhere in page 3, and do a CALSLT to your slot's page 2 routines from here.
  • Use a mapped ROM and put each piece of code on a different ROM page (you need to place the entry point the main page and do an inter-page call in this case).
  • Violate the standard and put your entry point in page 2 anyway. I promise I won't sue you. Smile As long as your client application developers are aware (which implies that you must clearly document this spec violation), it should all work fine.

Note anyway that if you have two entry points, you effectively have two specs and two implementations that are in principle independent of each other. If one depends on another, again you should clearly document that.

That's sure a heavy burden on the poor Z80A for each OPLL write.
The RAM helper, as its name says, is intended to be a help for implementation and client developer applications, but it is not mandatory to use it. You can perfeclty use the same code of your current routine, except that instead of "call WRTOPLL", you would use something like "ld a,WRTOPLL : call ENTRYPOINT". You should document this in your specification documentation so that client application developers are aware.

And of course, there is for sure room for improvement in the RAM helper itself; I am not a perfect programmer. Any suggestion for optimization will be welcome.

Does the RAM helper installation really needs to go to BASIC and return to DOS?
I haven't found a better way to allocate RAM on page 3. If you discover one, I'll be more that happy to use it. Smile

The users tend to hate this, because it disrupts any BAT file, including the AUTOEXEC. This means that two programs that work like this (MEMMAN and a UNAPI driver, i.e.) can never be placed on AUTOEXEC and at least one of them will always need to be loaded manually.
If you are using DOS 2, you can pass a command to the RAM helper installed, that will be executed after DOS is reloaded. So you could have the following in AUTOEXEC.BAT:

ramhelpr i autoex2.bat

...and have your real autoexec commands in autoex2.bat.

But again, if the RAM helper is a problem for you, and client applications are not expected to use it anyway, break the standard and go ahead with your implementation installation even if no RAM helper is installed. I repeat, assuming of course that you clearly document all of this in your specification document. Maybe APILIST will stop working then (I'm not sure) but that's a minor problem.

Note anyway that the RAM helper is not only useful for client applications, but for the implementations themselves as well. It allows you to perform a inters-slot+inter-segment call with 5 bytes of code, which means that you can patch any hook to directly call your implementation without having to bother about page 3 memory allocation. If no RAM helper is available, you need to do that by hand (and you always need to patch at least the EXTBIO hook).

The UNAPI specification don't allow APIs to offer their interface at page-2. But, because of (2), I believe I can "bend" this rule, as the limit is imposed by the RAM-helper itself.
Nope, it is quite the opposite. The page 1 limit is imposed by the core UNAPI specification. The RAM helper simply takes profit of the limitation and invokes code trhough page 1 only to simplify code.

Anyway I can understand that you want to invoke page 2 code when you are in ROM, but it makes no sense when you load your code in RAM. You can simply allocate two segments and invoke both via page 1. DOS 2 even provides an inter-segment call routine that makes this task trivial.

All of this said, I will revisit the UNAPI spec to see if I can easily add support for page 2 without breaking currently implemented software. Anyway I never expected anyone willing to do such thing, since MSX ROM software is usually executed in page 1 only.

I hope to have helped.

By sd_snatcher

Prophet (3675)

sd_snatcher's picture

15-06-2010, 00:16

Two days without touching a computer and you have almost redesigned the UNAPI specification! Smile Let's try to clarify things a little.

Ok, but please don't take the the suggestions as negative critics. I'm only trying to improve what already looks like a good spec to me. It would probably be easier for me just to build it following my own standards, but I really liked the UNIAPI philosophy above all. This is why I'm hoping to made viable for the Universal OPL driver to have an UNAPI interface.

List all the UNAPI extensions connected to a MSX. Kind of a "dir" of the extensions.
This is not possible since implementations will only react when its own spec identifier is queried and will remain completely silent and undetected otherwise. At most, you could develop an utility that takes a list of known spec identifiers (for example from a configuration file) and lists all the implementations for all the identifiers in sequence.

Yep, I noticed that limitation. This is why I required the spec to try to include such a feature. It shouldn't be hard to implement on the driver side (just a broadcast function for each driver to spit it's signatures). But OTOH, implementing it on the client side requires manual effort either from the user or from a centralized signature maintainer.

Request an extension to unload itself. ROM extensions would just remove themselves from the chain. This can be used to easily solve eventual conflicts.
Installed UNAPI implementations do not act as a "chain", except in the discovery procedure. You either invoke the routines of one implementation or of another, so I don't understand what kind of "conflicts" could arise (could you explain which conflicts you envision?)

The "chain" I mentioned is the EXTBIO chain formed by a sequence of installed extensions. On install each extension copies the old EXTBIO to an internal place and places their own EXTBIO call. This forms a chain of extensions, but the only way to release a node is requesting the extension itself (because there's no standard for the place where the previous EXTBIO was copied) to release it.

Upon release request, extensions should release the RAM they allocated and get themselves out of EXTBIO chain too. Currently, there's no way to do it on the UNAPI standard.

The situations I see for the release (or unload) feature are:

1) Many MSX are very limited on RAM (think of 128KB or 256KB models).
1.1) The user may have loaded some extensions, then don't have enough RAM to load a new one he desires.
1.2) The user would then list all extension installed (with the resources used), and choose one of them to be uninstalled. He uninstall the extension and now have enough free memory for loading the new one

2) Conflicts: Since we're dealing with software, unexpected bugs and collateral effects can arise when multiple distinct softwares are loaded on an unprotected system like the MSX-DOS2. There's no way to know previously what kind of conflicts will happen, but by experience we just know they'll happen. It can be some subslot operation an extension does before a second extension runs, and so on. Remember when the Turbo-R came into market and it's new type of Kanji driver conflicted with MEMMAN? That's the kind of situation I'm talking about.

Currently the only way to deal with both situations mentioned above is to reset the MSX and do not load some extensions. Not very aligned with the MSX-DOS2 philosophy: Usually DOS2 don't require a reset to free resources or for reconfiguration.

I would like that at least the page-2 could be used to, for a good reason (IMO): I'm interested on making a single ROM containing both the MSX-Audio BIOS and the Universal OPL driver. But that would require the Universal OPL driver to reside on the page-2, because the MSX-Audio BIOS already takes the entire page-1 for itself and leaves little room for relocation.
I can think of a few options for this:

  • Use one single entry point in page 1 for both Audio and OPL driver code. When having to invoke a page 2 routine, simply use a CALSLT from your page 1 routine.

    Unfortunately, that's not a viable option, because of the high CPU usage. All that slotswitching would require more CPU time than the translation algorithm itself.

  • Place the entry point for page 2 routines somewhere in page 3, and do a CALSLT to your slot's page 2 routines from here.

    Not a viable option too. Because one of the main possibilities of the driver is to (1) TurboFix old games and (2) Allow OPL1/2/3/4 support on them (stereo included).

    Those old games requires the BDOS to be placed as high as possible. This is why many of my HDD versions require DOS2.41 or even the RUNDOS tool. Loading RAM routines on page-3 would lower the RAM top even more, making it impossible for such old games to run.

  • Use a mapped ROM and put each piece of code on a different ROM page (you need to place the entry point the main page and do an inter-page call in this case).

    This would be only viable for new hardware. But one of the ideas is to make a ROM version of the Universal OPL driver for both the Philips and Toshiba Music Modules. Those don't have rom mapping, but do support 64KB of linear ROM.

  • Violate the standard and put your entry point in page 2 anyway. I promise I won't sue you. Smile As long as your client application developers are aware (which implies that you must clearly document this spec violation), it should all work fine.

Yep, that's the path I'm following. But unfortunately making an incompatible extension would throw away all the advantages of the standard, nearly nullifying the time invested to port it to the API. My idea is to try to negotiate with you an upgrade for the UNAPI to v1.1 if possible. This v1.1 would allow page-2 and other modifications I'll detail below, that are needed for the OPL driver to run efficiently.


Note anyway that if you have two entry points, you effectively have two specs and two implementations that are in principle independent of each other. If one depends on another, again you should clearly document that.

Well, to be sincere it currently already has three implementations, and a fourth one may come into light too. Smile
It will be documented, since the last two implementations are highlevel and depend on the 1st one, the lowlevel.

That's sure a heavy burden on the poor Z80A for each OPLL write.
The RAM helper, as its name says, is intended to be a help for implementation and client developer applications, but it is not mandatory to use it. You can perfeclty use the same code of your current routine, except that instead of "call WRTOPLL", you would use something like "ld a,WRTOPLL : call ENTRYPOINT". You should document this in your specification documentation so that client application developers are aware.

And of course, there is for sure room for improvement in the RAM helper itself; I am not a perfect programmer. Any suggestion for optimization will be welcome.


Quote:
Does the RAM helper installation really needs to go to BASIC and return to DOS?
I haven't found a better way to allocate RAM on page 3. If you discover one, I'll be more that happy to use it. Smile

I never dealt with this matter, so unfortunately I don't have an answer for that yet... Smile

The users tend to hate this, because it disrupts any BAT file, including the AUTOEXEC. This means that two programs that work like this (MEMMAN and a UNAPI driver, i.e.) can never be placed on AUTOEXEC and at least one of them will always need to be loaded manually.
If you are using DOS 2, you can pass a command to the RAM helper installed, that will be executed after DOS is reloaded. So you could have the following in AUTOEXEC.BAT:

ramhelpr i autoex2.bat

...and have your real autoexec commands in autoex2.bat.

Ok, but imagine the case of a user with 1MB of RAM on its MSX and a CF card. With that much RAM and a fast disk, it would be more comfortable just to load all needed extensions and even MEMMAN. But that would require AUTOEXEC.BAT, AUTOEX2.BAT, AUTOEX3.BAT and REBOOT.BAT. Quite a nightmare to manage. Smile

But again, if the RAM helper is a problem for you, and client applications are not expected to use it anyway, break the standard and go ahead with your implementation installation even if no RAM helper is installed. I repeat, assuming of course that you clearly document all of this in your specification document. Maybe APILIST will stop working then (I'm not sure) but that's a minor problem.

Yep, that's the idea also. But again the incompatibility with default tools problem arise and the main benefits of porting the OPL driver to UNAPI are nullified.

Note anyway that the RAM helper is not only useful for client applications, but for the implementations themselves as well. It allows you to perform a inters-slot+inter-segment call with 5 bytes of code, which means that you can patch any hook to directly call your implementation without having to bother about page 3 memory allocation. If no RAM helper is available, you need to do that by hand (and you always need to patch at least the EXTBIO hook).

You're right. For performance reasons I would need to impose an aditional restriction on my extension: It needs to be loaded on the main memory mapper. This way I can use the DOS2 mapper routines for dealing with this. The ROM version can use RST30h without any problem.

The UNAPI specification don't allow APIs to offer their interface at page-2. But, because of (2), I believe I can "bend" this rule, as the limit is imposed by the RAM-helper itself.

Nope, it is quite the opposite. The page 1 limit is imposed by the core UNAPI specification. The RAM helper simply takes profit of the limitation and invokes code trhough page 1 only to simplify code.

I do agree. I didn't expressed the idea properly: I meant the only *software* limitation. I was thinking on the compatibility perspective. Smile

Anyway I can understand that you want to invoke page 2 code when you are in ROM, but it makes no sense when you load your code in RAM. You can simply allocate two segments and invoke both via page 1. DOS 2 even provides an inter-segment call routine that makes this task trivial.

Same deal here, the problem is performance. Can you imagine doing all this extra slot/mapperswitching on each OPL register write? We'll need an aditional Z80A just for that! Smile

All of this said, I will revisit the UNAPI spec to see if I can easily add support for page 2 without breaking currently implemented software. Anyway I never expected anyone willing to do such thing, since MSX ROM software is usually executed in page 1 only.

Whoa, hold your horses! Smile Let me try it and see the limitations I'll encounter first. I don't even know if it's really viable, and I don't want to waste your time on something that is so uncertain yet. I'll try making a port, see if it is viable (performance/resources needed) when compared to the current interface. After that I'll come back to discuss it.

I needed to know if you were open to discuss improvements to the UNAPI to upgrade the specs to v1.1.

I'll post my proposition on a post below (I'm afraid this post got too big and the CMS might not handle this well)

By sd_snatcher

Prophet (3675)

sd_snatcher's picture

15-06-2010, 01:33

I'm finishing the Universal OPL driver port to the UNAPI interface. For now, I had adopt the violations of the 1.0 standard listed below. But I always kept in mind that UNAPI1.0 extensions must be compatible with the eventual UNAPI1.1 that will emerge if those violations are integrated on the new spec. I'm on the testing stage, so those modifications might still vary. I posted them here as a work-in-progress report:

This is a summary of the violations and the reasons I needed to do so:

1) Entry point at page-2

Required because it allows ROM versions for the Philips and Toshiba Music Modules. I plan that the HDD-version games I release will include patches to support the Universal OPL driver. This way, upgraded Philips and Toshiba Music Modules will work straight without requiring any extra RAM.
There's a second reason for the requirement: The vast majority of ROM games have their INT handler entirely contained on page-1. If the Universal OPL driver also resides on page-1, it will need to disable the interrupts on every register-write request it receives. This can cause glitches on games that use splitscreen, for example. By residing on page-2 the Universal OPL driver avoids this situation and the interrupts can still be enabled.

2) Modify the 2nd step of the API discovery response to return IX="UNAPI call table address", like this:

A = Slot where the implementation code is placed
B = RAM segment where the implementation code is placed
(#FF if not in mapped RAM)
HL = Routines entry point address
(if a page 3 address, A and B are meaningless)
IX = UNAPI call table address
DE is preserved. F and C are corrupted.

Reason: Currently, UNAPI only supports the indirect routine calling, which is an optimized version of the RS-232C EXTBIO method, but still too slow because of the demultiplexing needed. This "IX=table address" spec upgrade would allow direct calling of the UNAPI routines just like the MSX-DOS2 mapper routines. Much more CPU efficient. The IX register is very appropriate for returning that pointer, since the client (game i.e.) can quickly get the needed pointer using (IX+d) and place it on a RST30 (or CALLS, from DOS2) routine residing on page-3 to call the desired UNAPI routines.

3) Drop the RAM-helper loading requirement (rule 2.7)

The RAM-helper is very useful, but should be available as a separate UNAPI extension. Only those (extensions or clients) needing its services should have the burden of loading it on the 1st time they need. The RAM-Helper should have the possibility to be unloaded too, to release page-3 RAM when needed.

Reason: RAM on page-3 is a precious resource. Extensions that don't need it should not have the burden of loading it because, on my case, it causes a lot of games not to be able to run under DOS2 because the ramtop is lowered too much.

By konamiman

Paragon (1211)

konamiman's picture

15-06-2010, 17:19

Ok, but please don't take the the suggestions as negative critics.
I have never done such thing. Improvement suggestions are always welcome.

Yep, I noticed that limitation. This is why I required the spec to try to include such a feature. It shouldn't be hard to implement on the driver side (just a broadcast function for each driver to spit it's signatures).
Yes, some kind of broadcast call would do the trick. But anyway, I don't think this feature is essential. After all, implementations are installed by the user, and the user should be aware of what he has installed. I don't think anyone will install 20 UNAPI related programs or so. :-)

Upon release request, extensions should release the RAM they allocated and get themselves out of EXTBIO chain too. Currently, there's no way to do it on the UNAPI standard.
This is not a new problem on the MSX system. If you install a TSR program A, which patches a hook, and then install another TSR program B which patches the same hook, you can't uninstall A if you don't uninstall B first. And this happens regardless of being UNAPIfied or not.

Currently the only way to deal with both situations mentioned above is to reset the MSX and do not load some extensions. Not very aligned with the MSX-DOS2 philosophy: Usually DOS2 don't require a reset to free resources or for reconfiguration.
I understand your point: a design that does not force to reset is cleaner. But we are speaking about MSX here. This is not a workstation with some dozens of programs open, nor a server that must be up and running 24/7. If you need to reset, go and reset, the reboot takes just a few seconds. That's the big advantage of an obsolete system. Tongue

Loading RAM routines on page-3 would lower the RAM top even more, making it impossible for such old games to run.
You just need four bytes for a CALSLT to your entry point. Maybe you can even use the area for your slot at SLTWRK for this.

Yep, that's the path I'm following. But unfortunately making an incompatible extension would throw away all the advantages of the standard, nearly nullifying the time invested to port it to the API.
I don't think it is a so big problem. The reason of existence of an UNAPI implementation is to provide services to client applications. If the client applications developers are aware of any possible deviation from the standard, there should be no problems.

My idea is to try to negotiate with you an upgrade for the UNAPI to v1.1 if possible. This v1.1 would allow page-2 and other modifications I'll detail below, that are needed for the OPL driver to run efficiently.
Negotiate? Hummm, how much will you pay to me? Tongue Just kidding, of course I appreciate your suggestions and I will use them to revisit the UNAPI standard.

Ok, but imagine the case of a user with 1MB of RAM on its MSX and a CF card. With that much RAM and a fast disk, it would be more comfortable just to load all needed extensions and even MEMMAN. But that would require AUTOEXEC.BAT, AUTOEX2.BAT, AUTOEX3.BAT and REBOOT.BAT. Quite a nightmare to manage.
That's true. DOS is really lacking a standard mechanism to allocate memory on page 3. That's why I included the slot+segment hooking capability on the RAM helper, to at least minimize the problem.

But again the incompatibility with default tools problem arise and the main benefits of porting the OPL driver to UNAPI are nullified.
The only "default tool" currently is APILIST. Anything else are client applications, which are aware of the standard violation.

By the way, I just took a look at APILIST and I see that it only uses RDSLT and CALSLT to access implementations. Therefore it will work fine with page 2 entry points.

Anyway I can understand that you want to invoke page 2 code when you are in ROM, but it makes no sense when you load your code in RAM. You can simply allocate two segments and invoke both via page 1. DOS 2 even provides an inter-segment call routine that makes this task trivial.
Same deal here, the problem is performance. Can you imagine doing all this extra slot/mapperswitching on each OPL register write? We'll need an aditional Z80A just for that!

I understand that you don't want to deal with slot switching for performance reasons. But don't worry about segment switching. It is just an OUT instruction (plus a couple of LDs on DOS 2), it should be fast enough.

I needed to know if you were open to discuss improvements to the UNAPI to upgrade the specs to v1.1.
Of course I am.

I'll post my proposition on a post below
These seem quite reasonable. Don't wait for me, go ahead and implement them in your software. I will study them for a future version of the UNAPI spec as soon as I have some time.

By konamiman

Paragon (1211)

konamiman's picture

16-06-2010, 09:02

I have been thinking and have found a way in which you can offer a jump table, even in page 2, without breaking the UNAPI standard.

It's as easy as providing the following function in your specification (the explanation is provided as an example of how it could be documented):

* Get the routines jump table address
Input: A = 1 (or whatever other number)
Output: HL = Address of the jump table (possibly in page 2)
A = Segment of the jump table (#FF if ROM)

The routines of the XXXX specification are intended to be called intensively, therefore being performance a concern. For this reason, a jump table is provided so that all the routines can be invoked directly in addition to using the standard UNAPI entry point. Routines are ordered by their UNAPI routine number, so that routine 0 is at (HL), routine 1 is at (HL+3), and so on.

If you have more than one jump table, use BC and DE for returning data as well, or create other similar functions.

That way, you have your jump table and your page 2 support, and now the only UNAPI violation is ignoring the presence of the RAM helper at install time. Smile