I'll post some code once I get player controls implemented, but I'll go over the changes in a rough summary now.
First and foremost, the splitscreen mode marks the game as being in netplay mode with 2 nodes and 2 players, which handles the majority of the changes automatically. However, it disables various functions related to receiving network packets and suppresses a warning message about the game desyncing over the network.
As for the software renderer, it's like 99% untouched. I haven't touched anything related to the rendering of levels, sprites, etc.
The function which calls Renderer->RenderView() (well, actually the function which calls that function) alternates back and forth between player 1 and player 2, essentially dedicating half of the framerate to one player and the other half of the framerate to the other player. The actual class which handles the Direct3D calls (specifically, D3D9's present() function) has a new variable which indicates whether or not the current player being handled is the first or second player. Depending on the value of that variable, the present() function uses a different bounding box for the draw region on the window. (That's why Fraps doesn't record it correctly.)
StatusBar->AttachToPlayer() and StatusBar->Tick() are also called to prevent the need for two StatusBar instances. For this hack to work, stat interpolation (like the health counter in Heretic) had to be disabled. It also does some funky things to the preview of the player's head in DooM 1 and 2, so I'm disabling the player head preview in the status bar for now.