Monday, April 10, 2017

Apple II - Double Hi-Res From The Ground Up
Part 1: Hi-res Fundementals

Introduction:


In this series I'm going to explain how to code for the most difficult, colourful, and probably most poorly understood graphics mode on the Apple II: Double Hi-Res.  Make no mistake, the programs we will be developing are not toys.  They will be fast, flexible and useful for creating many types of games.  In addition they, along with the article text will give you a clear understanding of how they work.  Allowing you to develop programs of your own.  Since our goals include high performing code we will only have a few short examples in BASIC and by my next post we will have transitioned entirely to 6502 assembly.  

So what is it?:


Double hi-res is a special graphics mode available only on an Apple //e with an extended 80 column card and revision B motherboard (this includes the Apple //e card for the Macintosh), an Apple //c, or an Apple IIgs. It provides a resolution of 560 x 192 pixels on a monochrome display and 160 x 192 pixels in 16 colours on an RGB, composite monitor or TV set. To put this into perspective I've taken some emulator screenshots from two reference systems: The Tandy 1000 and the Macintosh which were both introduced around the same time the Apple //e was.


The image on the left is from an original Macintosh running System 5.  To make the one on the right I took the Macintosh screenshot, converted it to DHR and displayed on the Apple II.  As you can see in this mode the Apple II is actually capable of producing a Macintosh style UI.  It's major difference is a lack of vertical resolution, represented by the black bar at the bottom of the image.

On a colour display DHR provides 160 x 192 pixels in 16 colours.  Which suffers somewhat in comparison to the Tandy 1000 graphics whose 16 colour mode topped out at 300 x 200 pixels.  Below is a screenshot of the original Kings Quest, one of the first games to take advantage of both the Tandy video hardware amd the Apple //e's DHR mode.


Fact
While the Tandy 1000 did have a 320x200 mode. The AGI, the engine behind Kings Quest only understood one resolution 160x200. This was due to it being designed by Sierra under contract for IBM to promote the PCjr. A machine who's minimal 16 colour mode was 160x200.
The image on the left is the Tandy 1000 and while it's display is clearly superior the Apple //e is not that far off. It's worth noting that the Tandy 1000 and the Apple Macintosh were released in 1984, a full year after the Apple //e and given that DHR is actually a rather simple extension to the original Apple II hi-res graphics system it probably could have been added to the Apple II line much earlier. In fact, an identical mode was in the ill-fated Apple /// which was released in 1980!

Because of how deeply DHR is tied to many of the compromises Steve Wozniak made in his design of the Apple II. We're going to start our look into DHR by first getting an understanding of the original Apple II hi-res screen.

Hi-res Overview:


On all Apple II machines, with sufficient memory there are two hi-res screens they are called PAGE1 and PAGE2 respectively.  These exist as two 8KB areas of memory starting at $2000 (8192) for PAGE1 and $4000 (16384) for PAGE2. You can tell the hardware to display either page and since these are simply big blocks of memory you can update their contents simply by writing to them.

A common method of making smoother animation was to write to PAGE1 while telling the Apple to display PAGE2  Once your program is finished drawing it tells the Apple to display PAGE1 and begins drawing to PAGE2.

One other feature I'll mention is the ability to choose between having  four lines of text at the bottom of the hi-res screen - which we call MIXED mode - or the whole screen as hi-res graphics - which we call FULLSCREEN mode.

You can see all of these elements in action by doing the following.  From BASIC type the following:

HGR2 HGR POKE 8192,63 POKE 16384,21

You should now see a white bar at the top left corner of the screen.

How it works:


  • HGR2 - Clears hi-res PAGE2 and sets it up as the visible page in FULLSCREEN mode.  At which point you will no longer be able to see what you are typing.
  • HGR  - Clears PAGE1, sets it up as the visible page and sets the mode to be MIXED. You still might not see your cursor because these four lines of text are just a window into the bottom four lines of the text screen.  Hitting return a few times will bring the cursor down far enough for you to see it,  
  • POKE 8192,63 -  Puts the value 63 into memory location 8192.  Which if you remember is the beginning of the first hi-res page.  So you should have seen the white bar appear as soon as you hit the return key.
  • POKE 16384,21 - Puts 21 into location 16384 which is the top left corner of PAGE2.


But we can't see that page right now because the "HGR" command set the visible page to PAGE1!

So how do we see PAGE2 without erasing it?  All these modes and pages are controlled by a series of "softswitches'.  These are memory locations that don't contain real memory, instead they let you talk to the video hardware. $C054 (49236) tells the hardware to display PAGE1 and $C055 (49237) tells the hardware to display PAGE2.

So if we type:
POKE 49237,100
We will turn on PAGE2.  You will notice that the text at the bottom of the screen looks crazy.  This is because the PAGE2 softswitch also turns on text page 2 as well as hires page 2.  Text page 2 is generally never used even by the built-in BASIC firmware so all you see is whatever garbage was in that portion of memory.  You should also notice that there's a magenta coloured bar at the top left corner of the screen (or if you're using a monochrome monitor it will appear as a dotted line).

By the way, you don't need to put the exact value 100 into 49237 to activate PAGE2 any number will do.  You don't even need to store a value there.  You can just read that memory location by doing:

PRINT PEEK(49237)

Anything that "touches" that location flips the switch.  If you want to get back to PAGE1 just use:

POKE 49236,100


Apple II's crazy colour system:


The colour we see on the screen is, unsurprisingly related to the value we poked into memory.  To get a better understanding let's look at the binary representation of each number:

63 = 00111111
21 = 00010101

The relationship between bits and pixels on the screen is just a little odd.  On a  monochrome monitor the Apple II hi-res page appears to be made of pixels that are 1 bit wide.  So poking 21 into 8192 would produce a dotted line with three white pixels.  Each 1 in the binary representation of 27 turning on a single white pixel.   On an TV or composite monitor the Apple II video appears to be made of pixels 2 bits wide.  So that same set of bits looks like a solid magenta line.  It's worth pointing out that there are no colour/monochrome "modes" here.  It just depends what screen you're plugged into.

Each line on the display maps to a row of 40 bytes in memory.  That is from $2000 (8192)  - $2039 (8231) is the first line on PAGE1 and from $4000 (16384) - $4039 (16423) is the first line of PAGE2

Given that we would think that the Apple hi-res screen should be 8 bits * 40 bytes = 320 monochrome pixels OR 160 colour pixels.  Right?  Well not exactly....

You'll notice that he first bit in every byte (which we call the HIGH BIT) of our example POKE's is zero.  That's because the first bit doesn't represent a pixel it actually selects between one of two colour sets - which we call PALLETS.  You can see this by adding 128 (the value of the leftmost bit) to the value 21.  21+128=149

POKE 16384,149

By changing the value from 00010101 to 10010101. The line will turn from magenta to blue. So if we have two bits per pixel and two different pallets we must have eight different colours to choose from.  Right? Well not really...

Here are the colours when the high bit is zero:


Here are the colours when the high bit is one.


So there are TWO whites and TWO blacks leaving us with only six different hires colours.

Wonder what happens on a monochrome monitor?  Turning the high bit on shifts everything half a pixel to the right.  You can try this like so:
5 HGR
10 POKE 8192,21
20 POKE 8192,149
30 GOTO 10
RUN
On a colour screen you would see a three pixel wide line shift between magenta and blue and on a monochrome screen you would see a dotted line do a little half-pixel dance from left to right.

Another counter-intuitive thing about the hires display is the order of mapping bits to pixels.  Poking 21 into 8192 turns the first, second and third pixels magenta.  If you remember the binary representation of this is 00010101.  If you check the chart above you will see that magenta is represented by the bit pattern 01.  You should be able to see that the first, second and third pixels on the screen occupy the LOWEST six bits in that byte. We also know that the highest bit selects the pallet.  We can illustrate this by replacing 01 with MM to represent a magenta pixel and P to represent the pallet bit like so:

Notice something?  There's an extra zero right after the pallet bit? So what's this leftover bit do?  Well actually that's HALF of the fourth pixel's information. Which should make you wonder where the other half is. The answer of course is In the next byte!  If I draw another picture but this time use AA to represent the first pixel, BB to represent the second pixel, and so on... the first two bytes of hi-res page one would look like this:


Which means we have 7 pixels across 16 bits.  If you recall there are 40 bytes in a single line on the Apple II hires screen.  This means the horizontal resolution is 40*3.5 = 140 coloured pixels in six colours or 280 monochrome pixels.

Ok that's the short short version of the Apple II hires system.  Next post I'll get into the basics of bank switched memory and how to activate the DHR screen...

No comments:

Apple II - Double Hi-Res From The Ground Up<br />Part 8: Drawing lines with an 8-bit Bresenham algorithm 2

A big shoutout to the Folks at OpenApple for covering my humble blog. You can check out the episode here The story so far... After a lo...