New Project: DoubleVision SE/30 Card

(How are you so fast?)

Have you considered a passthrough PDS slot and a physical card ID selection? Stacking SE/30 cards is very handy and the more cards with passthrough, the easier it is to customize (of course, you can't really get past a few cards or power and signaling issues crop up). Along with that you can have ID conflicts with multiple boards so it's common to provide jumpers or DIP switch so you can adjust before inserting.
 
Indeed there is no support for hardware cursor in 6/7/8. I also have the hardware cursor (albeit smaller at IIRC 32x32, a clone of what available in Brooktree DACs in SPARCstations, also with a limited number of colors) but it's only useful in X11.
At that size, if the sprite is full-color (same depth and CLUT as the display), then it could replace the SW implementation.

Even if the sprite is too shallow, you could limit it to black-and-white cursors — which is most of them. A more significant limitation would be the lack of XOR mode in the blitter, used in the I-beam cursor for example.

Maybe it could be used by hijacking some of the QD functions - but it's likely going to be quite difficult to implement.

It's not out of the question. Here's a proof of concept for how it could work:


QuickDraw per se is untouched, and no traps are patched — only some jump vectors in low memory are rerouted. That is, in fact, how it works in my reimplementation of classic Mac OS — though just how closely it resembles the real thing is a topic I haven't investigated.
 
Even if the sprite is too shallow, you could limit it to black-and-white cursors — which is most of them. A more significant limitation would be the lack of XOR mode in the blitter, used in the I-beam cursor for example.
"True" hardware cursor, in the style of Brooktree, don't rely on a blitter - do you use what I would describe as an "offscreen cursor" ? (stored in additional VRAM beyond the picture, perhaps alongside a copy of the area it replaces?)

In the Brooktree style, the cursor bitmap is stored directly in the DAC, alongside its coordinate. When the HW cursor is active, inside the cursor area the DAC simply ignores the data coming from the framebuffer and use those from the cursor bitmap instead wherever the cursor bitmap is not set to "transparent". The content of the framebuffer underneath the cursor is never changed at all. It's very efficient.

It's not out of the question. Here's a proof of concept for how it could work:
QuickDraw per se is untouched, and no traps are patched — only some jump vectors in low memory are rerouted. That is, in fact, how it works in my reimplementation of classic Mac OS — though just how closely it resembles the real thing is a topic I haven't investigated.
Interesting. Does your reimplementation only cover the original QD, or the entire Color QD ? It appears to be mostly C/C++ (with a tiny bit of 68k ASM), and it makes me wonder whether some of it could be used as the basis for acceleration code. I limited myself to low-level variant(s) of BitBlit because the upper level stuff was too complex to reimplement, but if you already have done the work it might bear revisiting...
 
Indeed there is no support for hardware cursor in 6/7/8. I also have the hardware cursor (albeit smaller at IIRC 32x32, a clone of what available in Brooktree DACs in SPARCstations, also with a limited number of colors) but it's only useful in X11.
At that size, if the sprite is full-color (same depth and CLUT as the display), then it could replace the SW implementation. Maybe it could be used by hijacking some of the QD functions - but it's likely going to be quite difficult to implement.

As I said, the hardware function exists, and perhaps it will come in handy at some point. ;) Same as with the P-Vision, I will publish a complete register description of the DoubleVision. The mouse sprite is 3 colors and has its own independent palette, so it has constant appearance no matter if a CLUT or RGB color mode is displayed.

What does that mean and what is it used for on Amigas, practically?

On Amiga, any application can open its idividual screen with its own display parameters. That by itself is nothing new, but the UI concepts of Intuition (that is how the window system is called) include the concept of draggable screens.

So each application has, similar to the MAC.. err..... Macintosh(tm) ;) , a menu bar on top (even when it is invisible), and with the mouse, you can select the menu bar, click and hold the left mouse button, and then you can move the screen up- and downwards, like a curtain.

And the Amiga display hardware can do this by just updating a so-called "Display List", which is a definition of screen regions, consisting of display memory pointers, palette CLUT entries, display attributes, etc. For that, it has its own display processor called the "Copper", which is executing a program, while the display hardware draws the screen.

I'll show you a short example here.

Here, you can see a Directory / File manager application running on its own screen:

Screenshot 2026-01-22 090903.png

And by clicking and holding the menu bar, i can "pull" the application down, revealing the underlying desktop screen (Workbench):

Screenshot 2026-01-22 090930.png


AFAIK, it's a UI concept which is pretty much unique to the Amiga. Which is why my graphics core also supports this feature. ;)

You basically can handle multiple surfaces on one display by just updating a few parameters in display memory, and it is very smooth. An 68020-based Amiga has no trouble moving screens at 60fps.

You can debate whether that's a useful feature. For me personally, I cherish it, because it's easier to go "in-between" applications, than dragging windows around on a single desktop. I guess it's a matter of personal preference.

Thinking of screens, plural, on Mac, QD basically define a massive 64Ki*64Ki (16-bits signed values) area, and each display is just a small subset of that - obviously the primary screen is anchored at (0,0). They can have their own resolution and depth independently. It's very convenient for the user but as mentioned previously, makes it less than obvious what's going on if you don't go through QD.

Of course, that is supported on my graphics core. I can even define, for each display slice, a "virtual screen", and apply a per-pixel panning (scrolling) to move over this area. This is also implemented in Amiga graphics library - example:

Paint program opening a 640x480 8-bit CLUT screen:

Screenshot 2026-01-22 095510.png

Next, I "drag" the screen down, revealing the workbench at 1280x720 Hi-Color - but displayed at 640x480 display resolution.

Screenshot 2026-01-22 095610.png

To reach other regions of the desktop, I can smoothly scroll over the virtual 1280x720 surface witout changing the display for either of the 2 screens.

Screenshot 2026-01-22 095625.png


And this all being accomplished WITHOUT moving a single pixel byte in memory. ;)

I'm not suggesting that these features are applicable in MacOS, it's basically what this graphics card is going to get "for free", since the core has already been developed.
 
"True" hardware cursor, in the style of Brooktree, don't rely on a blitter - do you use what I would describe as an "offscreen cursor" ? (stored in additional VRAM beyond the picture, perhaps alongside a copy of the area it replaces?)

That's how the software cursor works. A copy of the cursor image is kept around offscreen, as is a save buffer for pixels overwritten by the onscreen cursor. (Whether either image is cached in VRAM is an implementation detail that's opaque to me as a Mac OS user and irrelevant to my emulator — since emulated memory is all the same speed.)

The benefit of the software cursor is that it does get rendered into the framebuffer, so it just works everywhere, and on any desktop-grade system it's plenty fast. It's not as good when it's a Raspberry Pi 1 running a non-JITted 68K emulation. :-) Also, if you're recording a screencast, the software cursor loses information about the cursor and generates otherwise superfluous screen updates instead of simpler cursor movements (or other changes).

In the Brooktree style, the cursor bitmap is stored directly in the DAC, alongside its coordinate. When the HW cursor is active, inside the cursor area the DAC simply ignores the data coming from the framebuffer and use those from the cursor bitmap instead wherever the cursor bitmap is not set to "transparent". The content of the framebuffer underneath the cursor is never changed at all. It's very efficient.

In stark contrast to projects that strictly emulate hardware, my goal is to make applications run. The distinction between "hardware" and "software" can get fuzzy (because in truth, it's all software). I tend to think of 68K code as "software" and anything else as "hardware".

My OpenGL-based front ends use two `glLogicOp()` calls to blit the cursor:


Neither the virtual framebuffer (in 68K address space), the transit framebuffer (in host-CPU shared memory), nor the framebuffer texture (on the GPU) are modified for "hardware" cursor changes.

Interesting. Does your reimplementation only cover the original QD, or the entire Color QD ?

Modest support for multibit displays (cursors and cursor/menu save buffering) has been publicly merged, while the vastly larger effort to implement Color QuickDraw remains a work in progress in private branches. Prince of Persia, System's Twilight, TaskMaker, and Tetris are basically playable in color, despite a few noticeable performance and palette issues.

It appears to be mostly C/C++ (with a tiny bit of 68k ASM), and it makes me wonder whether some of it could be used as the basis for acceleration code.

I would imagine so. The first step would be moving the code of interest into library projects and building them for other architectures with modern tools.

I limited myself to low-level variant(s) of BitBlit because the upper level stuff was too complex to reimplement, but if you already have done the work it might bear revisiting...

The sheer scale of the complexity difference between BitBlit and CopyBits can induce vertigo. :-)

Some of the work is done already, and I'm open to doing some more.
 
Most PCI PowerMacs have a hardware cursor, and I believe MacOS *does* use it. @joevt or @dingusdev would know more though.
There was support added, as documented in "Designing PCI Cards and Drivers for Power Macintosh Computers" (p513). However, I have never seen the relevant status/control codes called on any 68K version of the system I've tried :-(

On Amiga, any application can open its idividual screen with its own display parameters
Similar to X11; the Mac is much more simple - an application can change the screen settings if it really wants to (usually, games), but everything is displayed with whichever parameters are currently set on the display(s) they are on. So a large enough window could spread over three displays and have a part displayed in B&W, a part in 4-bits and a part in 8-bits... provided it was written entirely to Apple's specifications. Some applications (again - usually, games) would take shortcuts and don't play so nice on multi-display.

(...) you can select the menu bar, click and hold the left mouse button, and then you can move the screen up- and downwards, like a curtain.
Thanks for the explanations.

And this all being accomplished WITHOUT moving a single pixel byte in memory. ;)
Which was a distinct benefits at the time; however, it probably didn't help with multiple displays where you have no choice but to copy data from one to the other. Multi-display/multi-screen was a big selling point of NuBus Macintoshes in some markets that were critical for Apple at the time. Workstations with X11 could do multiple displays, but before Xinerama (1998) they couldn't move from one to another easily. It was new when on a '87 Mac II you could just drag a window from one display to another, and even leave it in the middle with parts on both displays.


I'm not suggesting that these features are applicable in MacOS, it's basically what this graphics card is going to get "for free", since the core has already been developed.
Yes, I think that one you can save some area in the FPGA :-) 68k Mac just need a dumb framebuffer, and maybe some acceleration if it can be done.
The only feature I added by request (liked the challenge!) in the *FPGA was the ability to change the hardware resolution; my original design only did one 'true' resolution (usually, but not necessarily, Full HD), and everything else was windowboxed - in the middle of the display, surrounded by black borders. But for some games, an user (maybe I should say the user, really, not that many *FPGA out there :-) ) asked for "real" resolutions - to get full screen 640x480 instead of a small area in the middle of a huge screen. Reconfiguring the FPGA PLL was the big challenge. To this day, there's an issue when changing resolution or depth, where the screen comes back a bit offset as for reason unknown the DMA engine and the Raster engine goes out of sync. I've never been able to fix that f*****g issue :-( (and it really annoys me, I rewrote the hardware reset code like a dozen times). Fortunately it can be "fixes" by ... changing again until it works (yuck!).
You probably want that ability to change depth and resolution ; lots of early color games only worked in 4 and/or 16 colors (later games added, then required, 256), or only worked at 512x384 (the Mac LC 12" screen resolution) or 640x480 (most early NuBus card and early onboard video, including early color Powerbooks like the 180c). Later games become more tolerant, but that was more in the PowerPC era.
 
Last edited:
Most PCI PowerMacs have a hardware cursor, and I believe MacOS *does* use it. @joevt or @dingusdev would know more though.
True. DingusPPC has hardware cursor emulation for ATI graphics cards, and built-in graphics controllers (control and platinum) used by Power Macs. sixty6 doesn't use a hardware cursor. Other graphics controllers aren't implemented yet.
 
So, first prototype assembled….

IMG_2504.jpeg

And it already makes a great addition to my soon to be completed SE/30 restpration project. ;)

IMG_2505.jpeg

To be continued….
 
So, first prototype assembled….

View attachment 95274

And it already makes a great addition to my soon to be completed SE/30 restpration project. ;)

View attachment 95275

To be continued….
Nice looking card - the internal HDMI port will be handy for in a few more years when all the CRTs are dead due to failed flybacks and worn out tubes and we're all fitting LCDs under curved perspex bowls :LOL:

Out of curiosity - how are you finding it writing software targeting the Mac OS, as in user facing software? I'm an absolute amateur, but have always found it a weird mix of super easy and supper obstructive. Things like GUI tools to lay out a dialogue box, but then you have to faff about converting things into Pascal format to pass to toolbox functions and it is technically the application's responsibility to run the Mac GUI.
 
I'd love to finally get my hands on a greyscale board for my SE/30. I'll soon have the time and room to play with mine. How would I use an ethernet board at the same time as this?
 
Still need to figure this out, as I am completely new to the Macintosh, and get my SE/30 restored first (still, one part missing).

It is a "Revision 0" prototype, and I do consider to modify it, depending on how well this design works. What I strongly intend to retain is the small, handy form factor, because, same as my Amiga projects, people should be able to build for themselves with relatively small effort and price.

From an I/O ressource POV, I want to keep this design jumper free (after all, this is Macintosh, not IBM XT). So I guess it depends which "virtual" NuBUS Slot range I am using, right? This is, btw, something I can keep configuratble by using the FPGA flash.

Have you tried the WLAN option of BlueSCSI? I heard it works quite well, maybe this is an alternative, in case I don't find a general solution.

If somebody knows maybe a place where all SE30 PDS cards are listed, with ressource information, I will definetely consider this.
 
Last edited:
Nice looking card - the internal HDMI port will be handy for in a few more years when all the CRTs are dead due to failed flybacks and worn out tubes and we're all fitting LCDs under curved perspex bowls :LOL:

Thanks! :-) That is indeed an intended use-case, especially since the card will be able to mirror the output of the internal video on the HDMI.

Out of curiosity - how are you finding it writing software targeting the Mac OS, as in user facing software? I'm an absolute amateur, but have always found it a weird mix of super easy and supper obstructive. Things like GUI tools to lay out a dialogue box, but then you have to faff about converting things into Pascal format to pass to toolbox functions and it is technically the application's responsibility to run the Mac GUI.

Actually, I like the experience. Compared to AmigaOS (sorry guys), MacOS architecture looks very old-fashioned (I was shocked to discover that a lot of essential resources are represented as a global state).

But once you adapt to it, and this is the big advantage to the Amiga, the development tools and modular approach to designing a GUI-based application are quite versatile,and fun to use. :)

In that sense, developing the software on an actual MAC is relatively comfortable. And there is actually A LOT of documentation available, once you sort it out for you. So far, this has been much easier than I anticipated. And I even started to get my brain wrapped around the concept and architecture of the Declaration Rom.

I will, btw, publish all the sources of my drivers and software. I think that's only fair, given the great help and feedback from this community. :-)
 
This project is groovy! Hyped to see where this goes. Thank you for everything you do for the retro computing community 🙏 🍎🍏
You're welcome, it's really fun doing something different besides my Amiga projects. :-)

I am, btw, already preparing my graphics core to be adapted to this board, while at the same time, I am finishing the Amiga version.

Even thought it might be not trivial to add hardware accelerated blitting to a classic Mac, the DoubleVisionSE/30 retains the same, blazingly fast Blitter from the Amiga variant of this product.

I will publish all register documentation, and the source of the Declaration Rom, so that somebody, who is more experienced in the intrinsics of MacOS / QuickDraw as me, can have a go at it. I will personally support this effort from my side.
 
Last edited:
So I guess it depends which "virtual" NuBUS Slot range I am using, right? This is, btw, something I can keep configuratble by using the FPGA flash.

Beware the pseudo-slot range is "associated" to an IRQ (loosely, but it's easier to do the SW when respecting it), as those you're supposed to use are connected to a VIA - you're not supposed to use /IPL[2..0] directly, only IRQ[3..1]. The unused one should be high-Z to play nice with other cards/functions. You can't skip the VBL interrupt it's required for the mouse to function. You can share the IRQ between multiple functions in the same range if you want to (example in my DeclRom).

If you don't have onboard switches, then yes you'll likely need a different bitstream for each of the three possibilities (hardwired), but that should be trivial as except for the IRQ pin and which address range to match, nothing else will change if the SW is correctly written. On NuBus you can do this dynamically as NuBus slots have a few pins that are either floating or pulled down as "geographical address" so the hardware/ROM can easily identify where they're supposed to be, rather than hardwiring anything.
 
Beware the pseudo-slot range is "associated" to an IRQ (loosely, but it's easier to do the SW when respecting it), as those you're supposed to use are connected to a VIA - you're not supposed to use /IPL[2..0] directly, only IRQ[3..1]. The unused one should be high-Z to play nice with other cards/functions. You can't skip the VBL interrupt it's required for the mouse to function. You can share the IRQ between multiple functions in the same range if you want to (example in my DeclRom).

Thanks, I thought that this might be the case, which is why I added a "solder blob" jumper on the PCB for experiments.

Does a video declaration ROM need a dedicated IRQ at all?

My graphics core can actually sync to a different update rate by making use of the display processor I have integrated - you can have multiple display contexts, which are automatically swapped, either mid-frame, or when the beam has entered VBLANK, to completely eliminate "tearing" or other unpleasant artefacts.

Therefore, if something calls, for instance, a switch in framebuffer attributes like color mode or start address, I can modify the parameters of the unused context, set a new context start address, and the display processor will smoothly update all the register changes when VBLANK is hit.

If you don't have onboard switches, then yes you'll likely need a different bitstream for each of the three possibilities (hardwired)

Even easier, the FPGA has a specific 32K area where I can store code and data. So when I have a cold or warmstart, a state-machine fetches the parameters and updates the registers. Works very well on my sound card to store mixer settings.

I intend to use this feature also to define and store custom resolutions.

, but that should be trivial as except for the IRQ pin and which address range to match, nothing else will change if the SW is correctly written. On NuBus you can do this dynamically as NuBus slots have a few pins that are either floating or pulled down as "geographical address" so the hardware/ROM can easily identify where they're supposed to be, rather than hardwiring anything.

Where would you recommend to put the 32MB framebuffer? I was considering to use a location outside of the NuBUS range (e.g. 0xfc000000 - 0xfdffffff), only putting the declaration rom and hardware registers in the NuBUS area.

Since I can snoop pretty much every memory access from the 68030 on the FPGA, that might be another method to decide where I map my ressources.

Thanks for your help!
:)
 
Last edited:
Does a video declaration ROM need a dedicated IRQ at all?

The older systems are not very well protected or architectured, so ultimately you can do almost anything... but it's a lot easier and safer (in terms of SW compatibility) to stick to Apple's rules.

The "normal" way is to use the memory range(s) and associated IRQ, as were originally defined for NuBus. You get 16 MiB usable in the standard slot space ($Fx00_0000 to $FxFF_FFFF), and another 256 MiB in the super slot space ($x000_0000 to $xFFF_FFFF), where x is the slot number (from $9 to $E) Pseudo-slots work exactly the same, but you have to "hardwire" them as there is no geographical addressing (and just the one PDS slot at the most). In the SE/30 and IIsi motherboard slot, it happens that additional IRQ are wired, and most PDS (other than the IIfx) you can usually use any slot as a pseudo-slot, provided it's not physically present/in use and either the appropriate IRQ is present or not needed.

A framebuffer needs an IRQ for the mouse as mentioned; normally, it is the IRQ dedicated to that (pseudo-)slot (for NuBus one unique IRQ line is hardwired in the physical slot, one or more of those are connected to the PDS instead on machine with PDS). You can share the IRQ between multiple devices in the same (pseudo-)slot reasonably easily.

As mentioned - you can step out of those rules, but then it makes the SW a lot bigger issue.

Even easier, the FPGA has a specific 32K area where I can store code and data. So when I have a cold or warmstart, a state-machine fetches the parameters and updates the registers. Works very well on my sound card to store mixer settings.

The non-volatile RAM on the motherboard (known as Parameter RAM or PRAM in old Macs) is where boot-time resolution/depth is meant to be stored. You can probably store it elsewhere, but IIRC the Mac system needs to be told as well so the appropriate resolution/depth is selected when opening the Monitor (or Monitor&Sound, depending on the version) control panel.

Where would you recommend to put the 32MB framebuffer? I was considering to use a location outside of the NuBUS range (e.g. 0xfc000000 - 0xfdffffff), only putting the declaration rom and hardware registers in the NuBUS area.

If you need the full 32 MiB to be contiguous, then that's what the super slot space is for - but it's unusual for it to be used (very few applications needed that much range back then), so there may be some compatibility issue with exotic software (as usual, read: mostly games!). You could also double-map in both spaces, so the actual framebuffer (8 MiB is enough for 1920x1024/32-bits) is mapped in the standard slot space, and everything is accessible for off-screen use in the super slot space.

In the *FPGA, I map 8 MiB in the standard slot space and use the other 8 MiB for control registers, interface to the acceleration engine, etc. The other 248 MiB of memory can be unmapped (IIRC that's what happen for my DMA-based RAM disk), mapped in the super slot range (direct-access RAM disk), or in the IIsiFPGA mapped into the RAM range for memory expansion by a ROM patch. I have on my TODO list to patch the Q650 ROM for the same purpose, but I've yet to finish that job (and the Quadra PDS connector is entirely unavailable by now unless buying 200 of them at once, so unfortunately that has very limited interest).

... all that is for 32-bits addressing. 24-bits addressing is probably useless with a design like this (24-bits addressing limit you to 1 MiB per slot!).

Also there's some machine-specific ranges below $9000_0000 that are occasionally described as "slot space" by Apple, beware those can have "interesting" behavior (e.g. the IIfx uses some of that range in the PDS for 40 MHz accesses while the normal pseudo-slot $E is accessed via buffering at only 20 MHz...).

Since I can snoop pretty much every memory access from the 68030 on the FPGA, that might be another method to decide where I map my ressources.

At least at first for bring-up, I recommend sticking to Apple's specification. It helps to play nice with other installed hardware and the full range of software (system and user).
 
Back
Top