Its been awhile since the last post because I caught COVID-19 at the beginning of april and its been a fight for my life. Even included a trip to the ER at one point. Finally things are starting to subside outside of a lagging cough. So hopefully things get better from here. Nasty stuff. But I digress...
Now that we have some form of framebuffer knowledge such as drawing text on the screen and other little bits and pieces, we need to see what else we can do.
At this point, lets see if we can get some graphics on this thing!
In the very beginning, i just simply took static images generated by the WS4000 Simulator over at Taiganet, and then converted it into a RAW indexed-color bitmap to be displayed on the screen.
The other part to this is the color palette table. Since the image is indexed color, it will have its own palette assigned to this. all GIFs work like this. and in some cases, each "frame" in an AGIF will have its own palette.
So this makes it easy to bring over to the 4000 framebuffer to see how it looks.
I wrote this little routine here:
Sub LoadStandardImage(Filename As String, RGB666 As Boolean, StandardImagePalette() As String) 'Load Standard Memory Dim img As Image = fx.LoadImage(File.DirAssets, Filename) Dim buffer() As Byte = GetPixels(img) Dim width As Int = img.Width Dim height As Int = img.Height For x = 0 To width - 1 For y = 0 To height - 1 Dim i As Int = y * width * 4 + x * 4 Dim b As Int = Bit.And(0xFF, buffer(i)) Dim g As Int = Bit.And(0xFF, buffer(i + 1)) Dim r As Int = Bit.And(0xFF, buffer(i + 2)) Dim a As Int = Bit.And(0xFF, buffer(i + 3)) Dim MatchHex As String If RGB666 = True Then MatchHex = Rgb2Hex(r/4, g/4, b/4).SubString2(0,6).ToUpperCase 'Get Hex value, while converting to RGB666 Else MatchHex = Rgb2Hex(r, g, b).SubString2(0,6).ToUpperCase End If 'Dim PaletteVal As Int For I = 0 To StandardImagePalette.Length-1 'Find our color in the table If StandardImagePalette(i).SubString2(0,6) = MatchHex Then 'We found the color, Exit with our index. StandardImage(x, y) = i 'Store the index value Exit 'Kill our loop End If If I = StandardImagePalette.Length-1 Then 'If we made it here without finding our color, Were not gonna find it. StandardImage(x, y) = 0 'Replace it with index 0 Log("Color Not Found: " & MatchHex) End If Next Next Next End Sub
This routine basically opens a GIF file, and reads it pixel-by-pixel, and it also looks at a palette file that you pass in as well. This will build an index table with the proper color index as long as the pixel of the GIF matches the color that's in the palette. Sure there are different ways to doing this, but for testing, this scenario worked perfect.
The other point I want to make, is the R/4, G/4, and B/4 formula above. Since the Graphics card has a BT471, we know that this chip is only capable of 6-bit RGB, or RGB666. so everything has to be converted to this convention. To convert 8-bit RGB or RGB888 like we use today over to 6 bit. we have to drop off 2 bits. easiest way to do this is to divide the 8 bit RGB value by 4. Now you end up with RGB666 with the loss of the maximum number of potential colors.
At this point, I haven't moved into the Palette code on the framebuffer control ROM yet, but i wanted to see if i can draw this on the framebuffer.
As we know from the previous conversation, the framebuffer is 768x480. So we need to make sure the image has been sized to the correct dimensions.
Transmitting/drawing the image byte-by-byte to framebuffer memory yields this:
Perfect! we can get an image into the framebuffer. So at least that experiment worked. I started drawing at $0x400000 in RAM with that image, and voila.
Ignore all the hex on the left, thats from experimentation of raw commands to the framebuffer control CPU, etc.... one of those commands transmits the palette. Which brings us to the next part.
The Palette. As explained above, all values sent to the framebuffer control CPU must be in RGB666 format. So you could leave the palette on the computer as 888 and do the conversion on the fly, or, you can convert the file as it is and send it straight. But the latter of the two options makes it hard to edit the file using a color picker tool. So i opted to keep all my file formats as RGB888, and when the palette is being transmitted to the unit, it performs the integer division at that point in time.
Sub LoadPalette(Filename As String, RGB666 As Boolean) As String() 'Load Palette Dim ImagePalette(256) As String Dim PaletteString As String = File.ReadString(File.DirAssets, Filename) ImagePalette = Regex.Split("#", PaletteString.ToUpperCase) If RGB666 = True Then 'Convert our Palette into RGB666 from RGB888. Dim PaletteR As Int Dim PaletteG As Int Dim PaletteB As Int For I = 0 To ImagePalette.Length-1 PaletteR = Bit.ParseInt(ImagePalette(i).SubString2(0,2), 16) PaletteG = Bit.ParseInt(ImagePalette(i).SubString2(2,4), 16) PaletteB = Bit.ParseInt(ImagePalette(i).SubString2(4,6), 16) If PaletteR > 0 Then PaletteR = PaletteR / 4 If PaletteG > 0 Then PaletteG = PaletteG / 4 If PaletteB > 0 Then PaletteB = PaletteB / 4 ImagePalette(i) = Rgb2Hex(PaletteR, PaletteG, PaletteB).ToUpperCase Next End If ' Dim Longpalettestring As String ' For I = 0 To ImagePalette.Length-1 ' Longpalettestring = Longpalettestring & ImagePalette(i).SubString2(0,6) ' Next ' File.WriteString(File.DirApp, "palette.txt", Longpalettestring) Return ImagePalette End Sub
Code snippet of my palette file loading tool.
Dim ImagePalette() As String = LoadPalette("Extended_Forecast.pal", True) Dim Longpalettestring As String For I = 0 To ImagePalette.Length-1 Longpalettestring = Longpalettestring & ImagePalette(i).SubString2(0,6) Next astream.Write(Convert.HexToBytes("00150301000301" & Longpalettestring.ToUpperCase & "024F5F0803601C83F0000003F0000003F0")) End Sub
This simply reads the palette file using the above subroutine, and then sends it over the arduino to the FIFO for the framebuffer control CPU. This is the command that sets up the framebuffer options with te correct palette.
Given any luck, we end up with this:
Now we have the correct colors! Sort-of. There is a problem here. so... uh oh.
On to the next part!