• Please review our updated Terms and Rules here

Continuing my sound card support (IMF)

deathshadow

Veteran Member
Joined
Jan 4, 2011
Messages
1,378
Rather than go full threadjack on Trixter's thread about his way cool recovery of the long-lost dreadful-sounding IMF demo, I figured I'd start a new thread in programming answering some questions and relating where I'm at on trying to implement support.

Isn't the MFC just a Yamaha FB-01 on the other end of a MIDI controller card?
The thing is not all controller cards function even remotely the same. MPU-401 doesn't work how the Sound Blaster's does (at least not until DSP 2.0 added UART mode), neither of those work how the IMF's implementation works, etc, etc... It's not as simple as saying "use midi on this port" if the port implementations are wildly different.

For example:

MPU401 UART

Writing a data byte: wait for the the write flag (0x40) to be set on the command port (0x331), send the data byte on the data port (0x330).

Writing a command byte: Same as data, but write the command byte ot 0x331 instead of 0x330

"command ok" message: you have an outer loop (20 max) that first runs an inner loop (255 max) waiting for the read flag to be set at 0x331, then reads 0x330 to compare against commandOk (0xFE)

Resetting the device: Write MPUReset (0xFF) as a command, if commandOk set UART mode, otherwise MPU401 is not present and/or has a problem.

Sound Blaster "Normal" MIDI

First you need a "DSPWrite" command that accesses the DSP status (typically 0x022C, but could be one of any number of ports), loops until bit 7 is set. You then out the same port the DSP command.

Writing a data byte: for "dumb" mode you send DSPWrite(0x38 ) then DSPWrite(dataByte).

Reading a data byte: loop until 0x022E bit 7 is set, read 0x0226

Resetting the device: Same as you would for setting up for digital audio -- and is why SB digital Audio the same time as SB Midi is unreliable and quite often disastrous; at least on early models. Basically you out 0x226, 0x01 - then wait a LONG unspecified period of time (usually 256 loops of DEC is sufficient), out 0x226, 0 - then loop until 0x226 bit 7 goes high. If it fails to in say, 63356 loops, the DSP failed to initilize so bomb out with an error. If it succeeds you read 0x222 and throw away the result (!?!) then compare 0x226 with 0xAA, if true it worked, if it fails you keep looping.

PHEW.

IBM Music Feature

Assuming it's at 0x2A20 (could also be at 0x2A30)

Writing Command OR Data: You loop until 0x2A22 (PIU2) bit 1 (txReady) is set, if it's not set in a reasonable amount of time (256 loops) it's not there so bomb out. If it is there you out 0x2A28 either 0x10 for a command or 0x00 for Data. You then out that command or data to 0x2A21 (PUI1)

Reading Command Or Data: Again loop until 0x2A22 (PIU2) has a bit set, though in this case it's bit 3 (rxReady). Takes too long, bomb out... when/if it is set you pull bit 7 from that same port as if that's set it's a command response, if it isn't it's MIDI data. You then read 0x2A20 (PIU0) for the actual command or midi data.

Resetting the device: Send the reset command ($E5), wait an arbitrary amount of time (0x0400 loops of "in al, 0x61" should be good as always -- untested) for it to actually happen, then loop reads until the same command is sent back to us. Then to set it for Internal Synth or External port we send command 0xE0 followed by 0x00 for internal or 0x01 for external MIDI. This resets both the Internal Synth and the MIDI device.

They are all similar, but they are NOT the same.

The FB-01's documentation is reasonably clear.
... which that part I'm fine on. Once I get it sending MIDI, that's the easy-peasy part; I already had FB-01 support working on other MIDI port types (before mine went kaboom).

Again though, sending MIDI isn't as simple as say... Serial. Mostly due to needlessly and pointlessly convoluted implementations. Resetting and configuring the devices being the actual "hard" part.

Made harder by REALLY crappy documentation on IBM's part.

The official documentation is extremely clear. The card is capable of much more than you think it would be (for starters, it has an independent 8253 PIT, interrupts, and more), so naturally the documentation is extensive.
Extensive? Methinks we have a different definition of that word. The PAMPHLET you linked to with it's invalid assembly (since the port addresses are 16 bit you can't do "in al, PIU2"), broken and confusing "Engrish moist goodry", and "how few words can we put on a page" making it near impossible to follow the text... extensive is NOT a word that comes to mind.

If anything reading that thing I'm half tempted to knee-jerk Samuel L. Jackson style into "Ácweðan hwá ágéncuman! Ácweðan hwá ágéncuman! Ic néðe þú, Ic twifealde néðe þú! Englisc, modor-wyrter! Gedon eow cweþan hit!?!". That's called getting medieval on someones tuchas. -- It almost reads like Twitter generation bullshit; laughable since it was written decades before there even was such a thing.

It took me FOUR re-reads of it just to decipher that if bit 4 on the TCR is on you send IMF commands on the data port (PIU1), otherwise the data port is dumb midi writes... and laughably that's 90%+ of what I needed to know; Why the hell they couldn't just say that cleanly/clearly and instead insist on dicking around trying to pass that as a full word is beyond me. MUCH LESS IF you're going to pass it as a word, wouldn't it be better to pass 0x10 instead of 0x01 in the high word since *SHOCK* that's actually what's being used? You know, making all their 0x01BE be 0x10BE since that's what's ACTUALLY sent to the ports? :/

Of course, sending 0x10nn and recieving 0x80nn would be WAY too simple an answer. Documenting commands as commands and midi as MIDI would be way too simple... Putting stuff in a logical order instead of splitting it between chapters willy-nilly so you're constantly scanning over 2 pages worth of text spread out across 20 pages hunting for "what category do THEY consider this command to be part of" would have been WAY too simple for IBM to go and do.

Their code, their examples, and the documentation almost reads like whoever wrote it didn't understand the device or x86 machine language enough to do EITHER. I've not been this annoyed with documentation since the last time I was in the PCJr tech ref which to be frank is equally rubbish. I got more out of the badly coded and poorly documented CMS.ZIP unit for TP that's buried over on cdtextfiles.

The other 10% of what I needed just being how to send a reset, select thru vs. music, and set the pitch bender range; the last of which was far better explained in the FB01 manual...

I also disliked how their code-base left retry up to the caller; a delay on the check for say... 256 loops wouldn't be the end of the world to have included.

Which I THINK I've got down solid with this...

Assembly write commands:
Code:
IMFWrite:
; INPUT
;    BH = DATA
;    BL = Command (0x10) or MIDI (0x00)
; OUTPUT
;    DX = IMFPort + 1 (PIU1)
;    CX = Zero on failure, non-zero on success
; CORRUPTS
;    AX
	mov   ax, 0x0100
	mov   cx, ax
	mov   dx, IMFPort
	inc   dx
	inc   dx
	cli
.loop:
	in    al, dx
	test  al, ah
	loopz .loop
	jz    .done
	mov   al, bl
	add   dx, 6
	out   dx, al
	mov   al, bh
	sub   dx, 7
	out   dx, al
.done:
	sti
	ret
	
; procedure IMFWriteCommand(data:byte);
pProcArgs IMFWriteCommand
	mov  bl, 0x10
	mov  bh, [bp + 6]
	call IMFWrite
	pRet 2

; procedure IMFWriteMIDI(data:byte);	
pProcArgs IMFWriteMIDI
	xor  bl, bl
	mov  bh, [bp + 6]
	call IMFWrite
	pRet 2
	
; function IMFRead:integer;
pProcNoArgs IMFRead
	mov   dx, IMFPort
	inc   dx
	inc   dx
	mov   cx, 0x0100
	cli
.loop:
	in    al, dx
	test  al, 0x08
	loopz .loop
	jz    .failed
	and   al, 0x80
	mov   ah, al
	dec   dx
	dec   dx
	in    al, dx
	sti
	retf
.failed:
	mov   ax, -1
	sti
	retf

My setup for internal synth or external port being:
Code:
function IMFLocalSetup(mode:byte):boolean;
var
	i, c:integer;
begin
	IMFWriteCommand($E5); { reboot }
	InALx61Delay($0400);
	c := $0100;
	repeat
		i := IMFRead;
		dec(c);
	until (i = $80E5) or (i < 0) or (c = 0);
	if (i < 0) or (c = 0) then begin
		IMFLocalSetup := false;
	end else begin
		IMFWriteCommand($E0);
		IMFWriteCommand(mode);
		IMFLocalSetup := true;
	end;
end;

function IMFSynthSetup:boolean;
begin
	IMFPort := IMFMidiCard.cardPort;
	if (IMFLocalSetup($00)) then begin
		IMFSynthSetup := FB01Setup;
	end else IMFSynthSetup := false;
end;

function IMFExternalSetup:boolean;
begin
	IMFPort := IMFMidiCardExt.cardPort;
	IMFExternalSetup := IMFLocalSetup($01);
end;

Though I'm still a bit sketchy on decoding the nodes and instrument assignments, so the latter will likely go unused.

The MIDI module itself has some features that the MT-32 doesn't
Only one I can think of is channel aftertouch. Most of the rest of it is just there so you can try to brute-force implement things other synths already do. (like auto-determining voice to channel assignments)

such as extremely fine pitch adjustment.
VC_PITCH_FINE in the instrument parameters... or you could just restrict the pitch bender range. Setting it in the program/patch can actually be very useful as you can set multiple patches of the same timbre with different de-tune rates.

How are you testing then?
One of the things I've held onto is a 8 bit card I bought from Jameco a few decades ago called a "DiagnoSys 8x8" -- you can configure it through dip switches to sit at any base port, with it occupying 8 ports in a row. It provides latches for the last written values, a tiny breadboard mapped to the latches, and a series of LED's to show you what's currently there for values. I'm running PIU1's values to a cheap arduino nano knockoff so I can at least send the midi output stream as MIDI (right now rigged to my SC-7 since my FB01 is now a writeoff) and fake the proper return values for the setup routine.

Kinda wish I could find more of these boards... I've not seen another one in a decade or more.

I was actually thinking that it might be really cool if we could have a modern board that had pinouts for connecting something like a PI or a cubieBoard directly to the PC bus -- the Cubie might be better as it actually has a proper number of inputs (as opposed to the Pi being so anemic on inputs I usually have to put an AtMega on USB in front of it) you could have it hooked up to monitor the entire PC bus. It could then be used to emulate any number of possible devices from ROM, to mapped RAM, to disk controllers, to sound cards.

But that's probably a noodle-doodle pipe-dream idea.

In any case, I'll probably be uploading a test executable later today that should just play the pac man theme; It will probably be the full current monolithic package of sound card support which currently means:

Local Synths:
Adlib
CMS
Tandy/Jr
Speaker (arpeggio)

Midi Devices:
MPU401 UART
SoundBlaster proprietary MIDI (aka through the DSP, not via UART mode)
IMF external MIDI
IMF internal MIDI

With patch/maps and specific behaviors for the following MIDI synths:
MT-32
GM
FB-01

and will hopefully soon add:

Innovation SSI 2001 (Commodore SID on a PC card)
Covox Sound Master (AY8930/P)

I'm having fun with it in a nostalgic sort of way -- back in the day I wrote a LOT of software for systems that hadn't even been built yet; blind implementation is tons of fun.

Laugh is even in it's current state it's only around 18k of code total; a full 6k less than my previous implementation -- and it's far, FAR faster since I'm using procedural variables. I've been implementing pointered lists of constants (TP constants are funny, they ARE writable if you know how -- so I have next: pSoundCardInfo as nil inside tSoundCardInfo, with firstCard,lastCard, currentCard: pSoundCardInfo to build the list.) for adding devices by simply including their unit allowing me to scan for both command line parameters and for autodetection without hardcoding any of it.

The other approach would be to load them as TSR's (I'm pretty sure that's what Sierra did) but one of my objectives here is to have the new distribution be self-contained in a single executable. The ONLY external file is going to be the high score list.

BTW, does anyone have any hard docs on PS/1 audio? 3 Voice similar to Junior, but apart from one thread on Vogons my Google-fu is failing me.
 
... and here's that demo:
http://www.deathshadow.com/downloads/mustest.rar

For IMF testing just type:

theme /imf

Defaults to 2A20, if at 2A30 use

theme /imf:2A30

If you type

theme /?

It will show you all the options. I'd be very interested to know if it even does anything on the IMF, as well as what people's results are with things like the CMS support which I THINK I've finally nailed down solid. Also, any opinions on how the arpeggio version of the theme sounds?

I'm also using a different patch for the Adlib that sounds a LITTLE better, but really Yamaha FM is so weak and tinny I've NEVER heard anything out of it that wasn't total rubbish. As I've said a few places, I'd sooner have Jr/Tandy sound for music than Adlib in all but the rarest of cases.

I'd also be interested in hearing how autodetection works -- or doesn't. Not all cards can be autodetected, but Adlib and Tandy/Jr most certainly should work out of box without command line parameters. I also no longer have that pesky bug that won't let you say "CMS" if Tandy/Jr is autodetected. I also have much better code in place for handling "fallbacks" when/if a command line parameter or auto-detect fails.

I currently do NOT have the custom midi support in there, but that's coming soon.

So, PLEASE anyone out there with a sound card, please test and let me know how it goes -- in particular IMF and FB01 are the big ones as I'm basically flying blind on implementing those.

-- edit -- Oh, and most midi other than IMF defaults to MPU-401. If you want to test pre MPU-401 compatible UART soundblasters specify BOTH the synthesizer type and /sbmidi -- for example:

theme /gm /sbmidi

Would be a general midi synth on a soundblaster 2.0 or earlier.

theme /mt32 /imfext

Would be a MT-32 compatible on the external port of an IMF.

Though again, I have NO clue if the IMF code actually works.
 
The only such information I've ever seen is in the appendix of the PS/1 Technical Reference manual:

http://ps1stuff.wordpress.com/tag/techref/
Wow, that's SO helpful -- not. (though the effort is greatly appreciated!)

What was I saying about shoddy IBM documentation? Wow, couldn't even spare more than three sentences that say NOTHING more than "3 voices at port 205" -- 0x0205 at that, can't even be bothered to throw a letter h in there.

If this (and the DOSBox emulation) is to be believed:
http://www.vogons.org/viewtopic.php?f=32&t=18327

All I should need to do is remap my tandy support to port 205... Which I've done and it works... in DOSBox, no clue if it will fly on real hardware.

Anybody know of a way to accurately detect a PS/1? The check from PoP 2 looks interesting, but did all PS/1's include this capability? Anyone out there have a PS/1 they could test this on?

I've re-uploaded the archive to include said PS/1 support... which was WAY easier to add than I had expected; if it works.
 
Oops, code regression in the CMS code, fixed and uploaded new copy to same spot:
http://www.deathshadow.com/downloads/mustest.rar

What I get for mixing and matching :p

That said, this does in fact seem good for PS/1 detection:

Code:
; function PS1Detect:boolean;
pProcNoArgs PS1Detect
	mov  ah, 0xC0
	stc
	int  0x15
	jc   .failed
	mov  ax, [es:bx + 2]
	cmp  ax, 0x0BFC ; PS/1 LW-Type 44 or Model 2011
	je   .success
	cmp  al, 0xF8
	jne  .failed
	cmp  ah, 0x30 ; PS/1 Model 2121
	je   .success
	cmp  ah, 0x0E ; PS/1 486SX
	je   .success
	cmp  ah, 0x0F ; PS/1 486DX
	je   .success
.failed:
	xor  ax, ax
.success:
	retf
 
Sorry, but all I get is FAILED for both options, both with and without the KAPI driver loaded. You may want to hold off on IMF until you can get your hands on a card.
I can't see that any of the code sets up the 8255. It needs an 0xBCh sendt to IMF I/O base+3 or else the transfer-status bits won't be working. I also recomend keeping bit 6 of the total control register on and use the IRQs for transfer instead of timed data-polling.
 
Last edited:
You may want to hold off on IMF until you can get your hands on a card.
Well, with my finances where they're at that would mean no IMF support until 2040. Let's just say heating oil kicked my ass this year.

I can't see that any of the code sets up the 8255. It needs an 0xBCh sent to IMF I/O base+3 or else the transfer-status bits won't be working.
I can't find anything in the documentation, code examples or the allegedly working pascal code that does that or says I need to do that... where did you get that tidbit from?

Either that or I'm just not seeing it in the disorganized confusing mess of gibberish Engrish that IBM typically called "documentation" -- and as someone who used to WRITE documentation for products it really is shamefully bad!

I'll add that to the setup -- So basically after a reset it's not even setup to send / receive anything? Considering that even the reset needs the transfer bits working, does that have to be done BEFORE you can even reset it? Wouldn't/Shouldn't a "reset' be undoing that?!? (I'm assuming the two are treated separate and that "resetting the card" apparently doesn't reset the ENTIRE card?!?) That's annoyingly stupid.

I'm also wondering if I'm not waiting long enough after the reset since the Pascal demo code I found seems to shove it's thumb up it's backside for a full second before polling for the return byte.

Having found that here:
http://cd.textfiles.com/psl/pslv2nv12/PRGMMING/DOS/PASCAL2/MFC.ZIP

... and being a needlessly convoluted and hard to follow train wreck, but it's the closest I've found to a plain language implementation. I'm gonna go back through and re-read it's codebase again to at least try and make some sort of sense out of it.

I also recomend keeping bit 6 of the total control register on and use the IRQs for transfer instead of timed data-polling.
Uhm, ouch... particularly for the only operation I care about once it's configured which is writing since to be frank, txReady should rarely if ever report 0...

Isn't that the type of **** that is why the number of people who used MPU-401 intelligent mode can be counted on three fingers and why hardware implementors never bothered cloning same on other cards; as outputting MIDI should not be as if not more complicated than soundblaster digital audio given it's just an UNDERglorified 31250 baud serial port? Which is why the simplest output only MIDI is an optoisolator on a serial port -- see most Arduino implementations.

All I want is a dumb UART mode... If it can't do that or it's going to get into having another whole blasted set of IRQ's on top of the existing timer I already have in place for game control, I may just give it the middle finger and walk away. It's sad when direct to hardware SID implementations are simpler to initialize and control... which I should have working by the weekend.

I mean, if all I was using it for was music playback ISR driving it would be fine, but I'm also doing incidentals as it's ALSO the sound effects engine, where flat dumb UART is just more desirable. (since again, there should be no wait for TXready unless the card sucks even more than I'm thinking...)

I'm starting to think I know why the IMFC never caught on. Well, other than the ridiculous price and the FB-01 sounding like **** that makes the Orchestra 90 seem impressive.

*** sake how hard is it to just reset the device, choose music or thru, set the pitch bender range to 12, and give me dumb uart mode for MIDI messages?!? That's ALL I farking need! That I can't even find anything that simple in the documentation or any code samples? Herpafreakingderp.
 
Last edited:
I can't find anything in the documentation, code examples or the allegedly working pascal code that does that or says I need to do that... where did you get that tidbit from?

Either that or I'm just not seeing it in the disorganized confusing mess of gibberish Engrish that IBM typically called "documentation" -- and as someone who used to WRITE documentation for products it really is shamefully bad!

I'll add that to the setup -- So basically after a reset it's not even setup to send / receive anything? Considering that even the reset needs the transfer bits working, does that have to be done BEFORE you can even reset it? Wouldn't/Shouldn't a "reset' be undoing that?!? (I'm assuming the two are treated separate and that "resetting the card" apparently doesn't reset the ENTIRE card?!?) That's annoyingly stupid.
Page 2-8, under the PIU Command Register description. If you want to use the optional timers, they need to be initialized in a similar fashion as well (described on page 2-11).

Note that this initialization of the 8255 has to be sendt before you reset the card, or send any data over the card interface at all for that matter. When it's done once, it doesn't have to be done again untill after a cold reboot.

All I want is a dumb UART mode... If it can't do that or it's going to get into having another whole blasted set of IRQ's on top of the existing timer I already have in place for game control, I may just give it the middle finger and walk away. It's sad when direct to hardware SID implementations are simpler to initialize and control... which I should have working by the weekend.
It is possible to disable error handling and use it as a dumb UART, but for bulk reads it might risk the buffer filling up. As you mention, this is no problem for just writes.
 
Page 2-8, under the PIU Command Register description.
Wow, that's almost as crappy a documentation as their PS/1 Audio docs. Is one supposed to magically divine that from the four words on one bit in the port description? Do you have a crystal ball or something that deciphered that for you?

Were they in the habit of being this uselessly vague ALL the time? Oh wait, I've read the PCJr technical reference; nevermind.
 
Oh wait, I've read the PCJr technical reference

Penalty for unnecessary roughness! The PCjr tech ref was good enough for me to divine a new video mode for the machine, and write functional hardware VINT and music code, so come on, the docs aren't that bad. If you think all these docs are bad then I would guess you haven't read enough hardware docs. Just because it doesn't hold your hand doesn't mean it's bad.

(So which docs ARE so bad that they were unusable in their first draft? Sega 32x, Sega Saturn, Playstation 2, Creative Sound Blaster (first draft), and Gravis Ultrasound come to mind. The latter two expected everyone to just use the SDK, and it was only after RE'd docs appeared in the wild did they relent. To this day there is no official description of the Sound Blaster's ADPCM modes/format; if you want to use it, you must compress your samples with VOCEDIT2 and use whatever you get.)
 
Had a burst of ideas in my head, so I got them down into code and did some bugfixes. New version uploaded with Innova SSI-2001 support that at least works in DOSBox (lacking a real card, who knows what it does on real hardware), fixed a bad bug that was preventing port overrides on that last upload :/ -- and adds that PIU initialize and ups the "wait before polling for reset ok" to 19 IRQ 0 ticks.

Again, same place as before:
http://www.deathshadow.com/downloads/mustest.rar

Which is interesting, IMF may or may not be working, but I was able to add what should be working for PS/1 Audio and Innova SSI-2001 in about an hour of coding. (Ten minutes of that being the PS/1 support since it seems to JUST be jr/tandy sound at a different port)

I also added a "phantom' command line "/jr" switch. It's not reported in the help at the moment, but it's just an alias for /tandy should junior detection fail, that way Jr fans don't get their panties in a knot over typing /tandy on their junior or vice-versa. NOT that junior autodetection should fail... and I now have it set that tandy/jr trumps adlib detection since honestly, the Adlib doesn't sound as good for a game like this.

If anyone out there has an actual Innovation SSI-2001 or a PS/1, I'd love to find out if this works -- again it works in DOSBox, but that's not like that actually means anything.

Implementing the SID on PC was interesting since it's based on the PC's clock instead of the NTSC or PAL clock of the C64... Finally came up with this translation to turn hz into SID intervals:

67108864 * HZ / 3579545

Which I used to figure the fixed note interval table, but for realtime conversion I'm using:

(hz << 18) / 13982

Which is close enough for government work... Well, the ACTUAL code I'm using is:

Code:
; function SIDHzToInterval(hz:word):word;
pProcArgs SIDHzToInterval
	mov   dx, [bp + 6]
	xor   ax, ax
	shl   dx, 1
	shl   dx, 1
	mov   bx, 13982
	div   bx
	pRet  2

Which works pretty good. Wish I could lose the 'long' divide as that's gonna have a pretty hefty penalty on the siren... and I'm not sure I want to pre-calc that into a table since for all siren ranges that would be about 2k of values; but we'll see how it goes. Honestly the conversion used for PC Speaker (1193180 / hz) isn't any better and that part runs just fine so...

Be interesting to see how well all this implements once I go from just playing the theme to using all these for the sound effects.

Penalty for unnecessary roughness! The PCjr tech ref was good enough for me to divine a new video mode for the machine, and write functional hardware VINT and music code, so come on, the docs aren't that bad
I was ripping out my hair trying to implement sound until I found someone else's implementation that was actually easy enough to follow. Made me want to pimp-slap whoever wrote the Jr. docs.

I think I'm lucky I missed out on some of the ones you listed then -- as I was about to say that the docs that came with my el-cheapo Reveal FX (the original 8 bit one -- basically a rebadged thunderboard as opposed to later versions since they released a dozen different cards under the same name) was better than most of this stuff. Hell, that one was an actual BOOK that even included how to make your own breakout for the MIDI on the joystick port. Pretty impressive on a 'blaster knockoff that cost the same new as a fancy dinner for 2 at a posh joint in the late '80's.

Though I could just be spoiled by really good documentation like everything from when Tandy's were still TRS-80's, or my first foray into 8088's which was exclusively through Heathkits.

Of course I'm a New Englander, unnecessary roughness is what we do... or as Colbert pointed out, we are reserved in our displays of affection (4:40 mark -- these "free keene" jacktards are in my town and have more than once had their backsides introduced to my size 9 boot)

Basically, ripping things to shreds so you can build them back up stronger is how you make things better; the limp, soft, namby-pamby "don't you dare say anything bad" status quo FTMFW west coast metrosexual prius driving tofu eating bull? Keep it... after all, "Ya cahnt geht theyah frum heeyah" isn't a stereotype, it's a way of life. Or as our friends from Brooklyn would say, "What? Ya gat a freekin problem widdat?" :D
 
Last edited:
Which is close enough for government work... Well, the ACTUAL code I'm using is:
With the risk of being Captain Obvious, when dividing with a constant like this you can shr both numbers as long as no bits are lost. The result of the division will be the same.

13982 is an even number. This means you can lose one of the 'shl dx,1' instructions and divide with 6991 instead.
 
With the risk of being Captain Obvious
First time I've ever said this without sarcasm; Thank you Captain Obvious!

The extra shift was a leftover from the PAL divisor (which was not an even number). When I first wrote it things didn't sound right as I was using the wrong multiplier; on the C64 there are two different clock speeds being fed into the SID since the system clock is based on the video clock.

C64 NTSC -- 1.02272714 MHz
C64 PAL -- 0.98524851 MHz
SSI-2001 -- 0.89488625 MHz

On the C64 version I had code in place to detect NTSC or PAL, but due to speed restrictions switched which hardcoded routine I was calling. When I translated to PC I just blindly copied my PAL code, then adjusted the rate without optimizing. Optimization is in progress now; and I probably would have missed that simple detail if not for your pointing it out, so thanks for that.

Though if I shifted one more I could actually have more accuracy, but really restricting it to 0..8191 is kinda iffy for freq, even if that's the limits on Tandy/Jr and even Adlib... besides at that many decimal places being rounded down to a 16 bit integer, it's not like it REALLY matters.

One 'quirk' of the lower clock though? You lose 3 notes at the top end of the scale compared to a real SID, but you gain accuracy on the sub-100hz frequencies.

----------------------------

On the topic of the IMF though, I don't supposed anyone who has one and knows how to program it could give me a few simple ASM routines to call it, as without one in hand and the piss poor docs I'm ice-skating uphill. As mentioned before ALL I need is:

1) A reset/initialization routine, preferably one that returns "TRUE" (non-zero AX) if the device is present and initialized properly.

2) Ability to select Music (internal) or Thru (external)

3) Set the pitch bender on all voices to 12 (so +12/-12 steps)

4) dumb UART midi writes.

That's ALL I need for support. I'm sitting here disassembling Sierra's IMF.DRV and it contradicts the manual AND the example code I have (which don't agree with each-other either!) so I'm utterly and completely lost it is unlikely I will ever be found again. I'm half tempted to give it the finger and walk away, but at the same time I'm usually not one to give up so easily.

Really though I want to pimp slap whoever wrote the documentation as it's utter and complete gibberish... I've NEVER struggled so hard to implement something, even if I don't have the hardware in question in front of me. (That's NOT unfamiliar territory for me) and I think that's why it's pissing me off so much.
 
Last edited:
Ok, I think I found the missing piece, apparantly on startup ALL the masks for data are turned off; for some jacktarded reason they call the operation "Set paths" -- which I had assumed meant setting the paths/routing of data, NOT a bunch of masks of what's allowed to be passed on the different data paths. The IMF really does boot up in a completely and utterly useless configuration, so in addition to sending 0xBC to the PIUC and sending a device reset then waiting with your thumb up your ass, you ALSO have to call "set paths" sending five bytes of 0x011F masks to it before any MIDI messages are even allowed to be sent ANYWHERE.

Only figured this out by disassembling Sierra's IMF.DRV -- It WAS there in the Pascal sample code I had, but I thought it was something else and not part of the normal startup.

Also a major typo resulted in some of the operations not even pointing at the IMF's port :/ herpaderp on my part.

I should have a new test executable later tonight. I'm making some other changes as well to lower the memory footprint and make it easier to add new sound card support. I MAY have initial support for AY-3-8930 (original Covox Sound Master) support, depending on how that goes this afternoon. As with the SSI-2001 that support will be mostly theoretical and only really be DOSBox custom build supported. (YKHWONG's DAUM good as always)

As it is I've added port 380 override to Adlib, since Sound Master 2 cards can put their Yamaha FM at that address.

... and then I get to try integrating this new sound stack into the ongoing paku 2.0 codebase, as well as my as-yet unnamed space shooter.
 
... and I just deep-sixed both IMF support and FB-01 support for the time being. I figured setting the pitch-bender range would be a per voice or per channel operation like every other MIDI synth out there. (everything else pretty much using NRPN to set it) -- but no, it's PER PATCH; and you change the patch/program you are supposed to rewrite it using system exclusive EVERY BLASTED TIME?!? PLOW THAT.

Ok, so to hell with FB-01 and IMF. NOT going to be supported by me unless someone else wants to try and do it. NEVER seen a synth implementation so utterly and completely jacktarded in my three and a half decades of writing software. It's screwed up when simply setting the pitch bender range is more complex than accessing the display on a MT32 and more convoluted than initializing an adlib card. I really do see why for all intents and purposes the IMF was stillborn.

I'll probably leave the code in the directory, but just not link it into the builds.

Way to waste a week and a half... I've wasted more time on FB-01 and IMF than it took to write version 1.0 of Paku Paku, and twice the time it took for the total engine rewrite that was version 1.2
 
Last edited:
Back
Top