Oldskooler Ramblings

the unlikely child born of the home computer wars

Upcoming Trixter Sighting + Retroprogramming

Posted by Trixter on May 15, 2013


June 14-16 you should be able to see me at @party in Boston.  (If anyone is looking to share a hotel room, drop me a line!)  I am scheduled to give a 30-minute version of the PCjr presentation I had worked on for NOTACON, and hopefully enter a compo or two with some oldskool hardware I will be shipping to arrive ahead of me.

Speaking of entering a compo: I really, really dig retroprogramming.  The cool part is, after 10 years of retroprogramming in spare time, my kung fu is getting advanced enough where I not only know how to do what I want in assembly, but I know the fastest possible method to getting it done on the target hardware. For example, I recently implemented a vertical-retrace interrupt in software because the hardware one wasn’t good enough. It’s sick that I know how to do that, but sicker that I know why I need to do that.

I still get a kick out of impressing Jim-of-20-years-ago.

9 Responses to “Upcoming Trixter Sighting + Retroprogramming”

  1. reenigne said

    Okay, I’ll bite: a vertical retrace interrupt in software? How do you do that then?

    • Trixter said

      Wha?? It’s YOUR calculations I’m using! (14.31818MHz / 12 / ((912*262) / 12) = 59.92 Hz so if I plug ((912*262) / 12)=19912 in as the PIT interval, the system timer will fire on the same scanline every time. So, reprogram Interrupt 8 (IRQ0) to fire with an interval of 19912 right when vertical retrace starts, et voila, you have a vertical retrace interrupt in software.

      PCjr has a real hardware one! Why am I not using it? I know you know the answer :-) but for those not familiar with what we’re doing: Because I want slightly more time to do things before the screen starts drawing. I programmed PCjr’s VINT (IRQ5) to change the border color so I could see where it was firing and I saw nothing, which means it really is firing right as the beam is going back up the screen. However, there is a border around the drawable area, so by making my own interrupt that fires right after the last scanline is drawn, I get some extra time to do things “invisibly” as long as I’m not changing the border color itself.

      Code for timing when to start the interrupt:

      
      Procedure InitChannelCGAVINT(channel,accessMode,mode:byte;divisor:word);
      const
        chan0base=$40;
      var
        modecmd,lobyte,hibyte,chanport:byte;
      begin
        {check for valid input allowed:
          only channels 0 and 2 (1 is for DRAM REFRESH, do NOT touch!)
          only accessmodes 1 through 3 (0 is not an access mode)}
      	if not (channel in [0,2]) or not (accessMode in [1..3]) then exit;
        {precalc how we're going to set the channel, so we don't tie up too much
        time with interrupts turned off}
        modecmd:=(channel shl 6) + (accessMode shl 4) + ((mode AND $7) shl 1); {bit 0 always 0 for 16-bit mode}
        lobyte:=lo(divisor);
        hibyte:=hi(divisor);
        chanport:=chan0base+channel;
        {must make these changes atomic, so disable interrupts before starting}
        asm
          {get to end of display cycle}
          mov   dx,m6845_status
          mov   bl,c_vertical_sync
          mov   bh,c_display_enable or c_vertical_sync
          mov   ah,c_display_enable
          mov   cx,199
          pushf
          cli
        @WDR:
          in    al,dx
          test  al,bl
          jz    @WDR  {wait if not vertically retracing}
          {Now we are tracing back up the screen.  Wait until first scanline.}
        @hor1:
          in    al,dx
          test  al,bh
          jnz   @hor1 {wait until not horizontally or vertically retracing}
          {Now we are drawing our first scanline.}
        @hor2:
          in    al,dx
          test  al,ah
          jz    @hor2 {wait until horizontally retracing}
          loop  @hor1 {loop 199 more times}
      
          {set new rate}
          mov     al,modecmd
          out     CTCModeCommandReg,al
          xor     dx,dx
          mov     dl,chanport
          mov     al,lobyte
          out     dx,al
          mov     al,hibyte
          out     dx,al
          popf
        end;
      end;
      
      
      • reenigne said

        Ah, okay – I thought it must be something like that but I was confused because I didn’t know (or had forgotten) that the PCjr has a hardware vertical retrace interrupt, and couldn’t think of a machine that did and which this trick would work on. Also, I wouldn’t describe that as “implemented in software” – it’s still a hardware interrupt, it just comes from a different piece of hardware. But I’m nitpicking – I look forward to see what effects you make with this!

        • Trixter said

          Well, it’s a “video hardware vertical retrace interrupt” simulated in software, how’s that? :-)

          Unfortunately, I discovered last night that the PCjr palette register mechanism will output random garbage while the palette change is in progress, which is likely why they built a hardware vertical retrace interrupt into the system — so that you could change the entire palette without affecting the picture. So they traded one type of “CGA snow” for another…! And your tricks for getting the system into lockstep don’t work on PCjr, it has a completely different DRAM refresh mechanism that is handled by the video circuitry. Finally, I found that something — I’m not sure what yet — can actually halt the system timer for short periods of time on PCjr (likely something to do with the NMI). I had a border change built into the interrupt so I could see where it was firing, and at some point after some keyboard and joystick handling, it started firing in the middle of the screen!

          In other words, I’m not sure what kinds of raster effects are possible with these glitches, but I’ll experiment.

          The PCjr is likely easier to get into lockstep *because* it doesn’t have DMA — everything is handled by the CPU. But I’m going to go back to PC+CGA programming before I try to get that deep into PCjr.

          • reenigne said

            Ah, that snow is annoying. I guess lockstep is the way to go for that then. Hope the PCjr’s wait states are as cooperative as the CGA’s were.

            The apparent system timer halt is very strange – I just looked at the schematic and the system timer clock depends only on signals which are coming straight from the 8284, so NMI shouldn’t be able to affect it. Are you by any chance doing 2 interrupts per frame (9956 count) and getting a skipped interrupt due to running with interrupts off for ~8ms? Otherwise it sounds like a faulty 8253, which is sometimes going into the wrong phase. If you’re using mode 3, it might work better in mode 2.

            • Trixter said

              I’m definitely able to reproduce it now, and it’s always by pressing a key, and the PCjr doesn’t have a keyboard controller (CPU does everything) so I think your theory has merit. I will also try to reproduce it in mode 2 (I am indeed setting it to mode 3).

  2. Peter Lund said

    You don’t need a vertical retrace interrupt for this trick. You can poll the status register and synch “manually” at first and then use a timer value that is a little to short so you start your interrupt handler with a little loop to wait for the actual blanking interval start.

    • Trixter said

      That’s exactly how I coded the software vertical retrace interrupt in IntroJR (I wanted a little extra raster time; the hardware VINT, while a cool addition on that platform, fires only at the start of vertical retrace)

Leave a Reply to reenigne Cancel reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

 
%d bloggers like this: