Getting an image on screen

A project log for Reverse Engineering The Weather STAR 4000

The Weather STAR 4000: A Journey of reverse engineering the hardware to an iconic machine of the 1980s/90s.

techknighttechknight 04/28/2021 at 22:520 Comments

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
				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
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
	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)

	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!