Close

First signs of life

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 03/27/2021 at 20:520 Comments

So now that we have basic framebuffer commands out of the way, Next thing we need to do is figure out the Memory-to-Pixel layout of the framebuffer. 

I will spare you the experimentation process to get to where I have, plus its been a couple of years so I don't remember all the details anyways. 

But the jest of it is this: 

So now that we have this information, the next thing to do is figure out how the colors work. 

So basically, each pixel is 1 byte. Since it is a 256-color system, this makes sense. However, 0 is reserved as the key color. So if there is a video signal coming into the unit, 0 gets replaced with 1 pixel of video information instead of a color. 

All the other index values of 1 to 255 are physical colors. This color information is sent from the framebuffer control CPU to the RAMDAC. 

There is a default palette as explained earlier, but you can also send a custom palette. We arnt quite there yet. 

For now, lets see if we can get something into the framebuffer, regardless what it is. Before we can figure out exact details on some of the framebuffer control commands, we need have something on screen to use as a base of reference for experimentation. 

So we have already played with colors, lines, etc... But we need to move on from that. We need to draw text and images. 

But alas, all this stuff has to be recreated from scratch because none of it exists anymore. Except: the Font. For now, I was able to extract the font from the Weather STAR Jr. The font is almost 100% identical with the exception of the lower case W. it is not the same. So its from the same family, but not the same exact flavor. I am not a font/typeface expert so i dont know what all the little differences mean. but, hey. at least now we have something to start with. 

There are public recreations of the WS4000 fonts, but they are far from accurate, and, they are true-type and the modern graphics suites have issues properly rendering fonts at low resolutions for bitmap use. so I abandoned that idea. Looks like garbage. 

First of all we need to get the font in a format that the graphics card is going to work with. For testing, it was simple enough to just to convert the font into a simple bitmap with different values representing the different colors to be displayed on screen. Yes, this takes up a lot of memory. because now you are using 1 byte per pixel in the font table. But its good for testing. 

So now I needed to write a simple program to shove data out of the UART to the Arduino for sending images/fonts. 

Once I got a rough skeleton setup something like this: 

	'Load 18x36 Font into Pixel Memory.
	Dim img As Image = fx.LoadImage(File.DirAssets, "font2.gif")
	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))
			If A = 255 And R = 127 And g = 127 And b = 127 Then 'This is a Gray color (We key this out with whatever is in the background, So save as a 0)
				PixelArray(x, y) = 0
			else if A = 255 And R = 0 And b = 0 And G = 0 Then 'We have a black color. So we make this black, or 1.
				PixelArray(x, y) = 1
			Else if A = 255 And R = 255 And G = 255 And B = 255 Then 'We have a white color. So this is the typeface color. or, 2.
				PixelArray(x, y) = 2
			End If
		Next
	Next
	
	'Load 18x18 Font into Pixel Memory.
	Dim img As Image = fx.LoadImage(File.DirAssets, "font3.gif")
	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))
			If A = 255 And R = 127 And g = 127 And b = 127 Then 'This is a Gray color (We key this out with whatever is in the background, So save as a 0)
				PixelArray2(x, y) = 0
			else if A = 255 And R = 0 And b = 0 And G = 0 Then 'We have a black color. So we make this black, or 1.
				PixelArray2(x, y) = 1
			Else if A = 255 And R = 255 And G = 255 And B = 255 Then 'We have a white color. So this is the typeface color. or, 2.
				PixelArray2(x, y) = 2
			End If
		Next
	Next

	'Turn 18x36 Pixel Map into a Character Array.
	Dim Characterbyte() As Byte
	Dim newI, b, A, c, newx, newy As Int
	For newI = 0 To 95 Step 1
		A = 0
		B = (newI / 16) + 1
		C = newI Mod 16
		Characterbyte = GetCharacter(c, b) 'Get character pixel data for ASCII Code I
		For newy = 0 To 35 Step 1
			For newx = 0 To 17 Step 1
				'Log("I" & newI & "X" & newx & "Y" & newy)
				ASCIIArray(newI, newx, newy) = Characterbyte(a)
				A = A + 1
			Next
		Next
	Next
	
	'Turn 18x18 Pixel Map into a Character Array.
	Dim Characterbyte() As Byte
	Dim newI, b, A, c, newx, newy As Int
	For newI = 0 To 95 Step 1
		A = 0
		B = (newI / 16) + 1
		C = newI Mod 16
		Characterbyte = GetCharacter2(c, b) 'Get character pixel data for ASCII Code I
		For newy = 0 To 17 Step 1
			For newx = 0 To 17 Step 1
				'Log("I" & newI & "X" & newx & "Y" & newy)
				ASCIIArray2(newI, newx, newy) = Characterbyte(a)
				A = A + 1
			Next
		Next
	Next

The above code basically loads up my Font Table which is a single image. Into memory. I laid out the font as a single indexed color GIF, and this program brings it into a bitmap font table. This is a Java/B4J program for ease of use. its very VB6-like. 

At this point, I can write a simple piece of code to write a random character glyph into the framebuffer and see what happens.

The subroutine I wrote which draws a character into the framebuffer, like so: 

Sub DrawCharacter(Text As String, Start As Int, Line As Int, StartAddress As Int, Background As Int)	
	'Write a character to the screen:
	Dim Fontval As Int
	Dim CMD(6) As Byte
	Dim AddressBytes() As Byte
	'Beginning of framebuffer memory
	'Dim Address As Int = 0x402716
	Dim Address As Int = StartAddress
	Dim Columnaddress, Lineaddress, I, A, B As Int
	Dim Textbuff As String
	
	'Arduino Packet format for Byte-Wide Memory Writes:
	'02 = Write Byte
	'XX, XX, XX, XX = Address (Little Endian)
	'Data Byte = Little Endian.
	
	'Arduino Packet format for Word-Wide Memory Writes:
	'03 = Write Word
	'XX, XX, XX, XX = Address (Little Endian)
	'XX, XX = Dataword (little Endian)
	
	
	'Calculate a new address based on the Specified start character position, and line. 
	A = (Line * 36) * 768 'Calculate new character line position
	B = Start * 18 'Calculate new character position
	Address = Address + A + B 'Create the new address
	
	Dim Characterbytes() As Byte = Text.GetBytes("UTF8") 'Get our character array. 
	For I = 0 To Characterbytes.Length-1 'Loop through each byte in the character line. 
		If Characterbytes(I) >= 32 Then 
			Fontval = Characterbytes(i) - 32'Re-align ASCII table to exclude non-printable characters (Doesnt exist in this font)
		Else
			Fontval = 0
		End If
		
		Textbuff = ""
		Lineaddress = Address
		For Y = 0 To 35
			Columnaddress = Lineaddress
			For X = 0 To 18
				'CMD(6) = ASCIIArray(Fontval, x, y) 'Load in left pixel
				'x = x + 1
				CMD(5) = ASCIIArray(Fontval, x, y) 'Load in right pixel
				AddressBytes = Convert.HexToBytes(Bit.ToHexString(Columnaddress)) 'Get the byte values
				CMD(0) = 2
				CMD(1) = AddressBytes(2)
				CMD(2) = AddressBytes(1)
				CMD(3) = AddressBytes(0)
				CMD(4) = 0
				If CMD(5) = 1 Then CMD(5) = 127
				If CMD(5) = 2 Then CMD(5) = 12
				If CMD(5) = 0 Then CMD(5) = Background
				'If CMD(6) = 1 Then CMD(6) = 15
				'If CMD(6) = 2 Then CMD(6) = 1
				'If CMD(6) = 0 Then CMD(6) = Background
				astream.Write(CMD)
				Columnaddress = Columnaddress + 1 'Advance to the next pixel
			Next
			Lineaddress = Lineaddress + 768 'Move to the next line
			Log(Textbuff)
			Textbuff = ""
		Next
		Address = Address + 18 'Advance to next character position
	Next
End Sub

 Great. This should do the trick. (All the debugging has been done, I dont have my old old code from this time period)

So in theory, if I draw a P into framebuffer, we can do it like this: 

DrawCharacter("P", 0, 0, 0x400195, 9)

Run the above subroutine, and boom:

Its not perfect, you can see the characters aren't properly aligned, this is due to some weird bug in my Arduino 68K emulation code. its doing some weirdness with that.  

Now lets see if we can draw out a full string: 

DrawCharacter("This is a test of the Characters.       ", 0, 0, 0x400000, 9)
DrawCharacter("This would be the next line.            ", 0, 1, 0x400000, 9)
DrawCharacter("then of course, the line under that.... ", 0, 2, 0x400000, 9)
DrawCharacter("      This is being displayed on:       ", 0, 3, 0x400000, 9)
DrawCharacter("        The Weather STAR 4000!          ", 0, 4, 0x400000, 9)

Boom! we now have the the primitives of the framebuffer figured out! 

Lets move onto the next thing....  

Discussions