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.

Similar projects worth following
Anyone in the USA who grew up in the 90s watching The Weather Channel, whether you got into meteorology, or technology, or just was an enthusiast of weather. Likely remembers this machine.

For those of you who don't know, This machine powered "The Local on the 8s" in the end of the 80s through the early 2000s, the system finally went offline and was replaced with newer systems by the 2nd half of 2014. This information in detail can be found at This machine was considered iconic during the time period.

However, the software was volatile and was lost with time. Therefore, it requires reverse engineering to make it run again as it once did. Whether it be nostalgia, or novelty, or both! As a retro-computing and weather enthusiast, I might as well get it working again. right?

This project is basically a Blog that will go into detail on what I went through to achieve the glory of reverse engineering the system to make it run again.

We bring this 1980s machine into the modern world as an IoT device to receive data and render presentations like it used to, once again... Bridging the digital divide between a 68K and the internet :-)

  • Designing a new data card

    techknight3 days ago 0 comments

    In order to bring this project to the 21st century and make it as easy as possible to interface to modern data standards, we need to design a new data card. 

    When i made the schematic for the original data card, i yeeted the audio circuitry because honestly I did not care as I didn't need as sophisticated of a circuit for the audio stages. 

    At this point in time, the audio and warning circuits weren't even in my mind. The most important thing is how do I interface with the bus, and how/what do I use to get data into the system? 

    The venerable Raspberry Pi has entered the chat... 

    Yeah, that's right. yet another raspberry pi project. BUT... it is the most convenient tool to interface with the data card, and act as a host. downloading the system binaries, graphics assets, and data on the fly in one swoop during startup. It makes the most sense to me logically. 

    So with our control host chosen, we need to design the data card around this concept. 

    First thing I need to do is pick the design. Physically, it will be just like the original one as far as shape/size. But the electronics for interfacing to the bus are going to be interesting. 

    However, i figured why not use the original stuff they already had? Thing is... unlike the I/O card dumpster fire, the data card must have been designed by a different engineer. it actually uses gated logic to interface to the 68K bus and its cycles, reading and writing out of latches and a FIFO. so CPU intervention isn't necessary. 

    So, to start off the new design, lets just "copy and paste" the original bus logic. 

    I kept the bus logic so original, that i even kept the original PALs! So you can just move the PALs over from the old data card onto the new prototype card. 

    As you can see, I simply kept the same logic design as they had, I even pay this as a tribute to the original data card kind of as a nice send-off. 

    We do need to make some modifications. First, I replaced the old FIFO with a much bigger one. I can load up to 4K chunks and then have the 68K read out that entire FIFO. This allows me to send data across the bus faster when i break large binary files up into 4K chunks versus 512byte chunks. alot less bus activity and back/forth negotiation. 

    Also, the key here was to figure out how to interface this logic to the Pi. My thought process here was to eliminate the old 8044 MCS51 CPU and replace it with the Pi instead. 

    However, anyone that has worked with a Pi, knows the GPIO is 3.3V. this is a 5V card so we need to design in some logic shifting circuitry:

    Which we have done here. Then, we just wire up the Pi's GPIO header into the logic level shifters which then wires up to the same spots the old 8051 used to be. 

    Presto! we have now successfully wired up a raspberry pi GPIO into the old bus interface logic borrowed from the original card. In theory, this should work... 

    This offers many benefits as in I dont have to design new logic circuitry from the ground up, but also it can allow a pi in direct control with the main CPU card via interrupts. This logic cannot access the bus directly, it can only load up the registers or FIFO and then issue a manually vectored interrupt back to the main 68K CPU. so the 68K itself has to handle whats going on with the data card, and its logic. 

    So even with this, we still need some minimal bootstrap code on the 68K ROM itself to handle whats going on with the data card. 

    With the data/control side out of the way, that leaves just the audio part. For the prototype data card, I did not design any of the audio circuitry on the card since I did not quite know yet exactly how to approach it as I wanted to simplify the original audio circuit into a single audio path plus warning alert path. All the extras the system did originally, did not matter here. 

    I did wire up the original latch logic they had, but I left them unconnected here. 

    So for now, I will forgo the audio circuitry...

    Read more »

  • Analyzing the data card

    techknight3 days ago 0 comments

    That leaves the last piece to this puzzle. Now that we have the I/O card, CPU card, and graphics card figured out, that leaves behind the data card. 

    The data card serves a few purposes in the machine, it also contains the audio circuitry. it contains the FM demods, the audio switching network, output amplifier and the warning tone generator. 

    previously in the project we had already netlisted this board and created a schematic. 

    This board is originally designed to demodulate the subcarriers from the satellite transponder. 

    Once again, I decide to take a look at the ROM to figure out potentially what is going on with this card, and there is a patent available for the STAR 4000 which explains the data packet format, what all the different types are, and etc. the ROM pretty much follows the patent for the most part. 

    Other interesting thing is, there are some stuff in the bottom of the ROM which im not sure what does, but its interesting: 

    Some text values there used for something. the Data card has no serial in/out so it must be something that gets sent back to the 68K at some point over the FIFO. 

    The architecture of the data card's design, basically has a single byte latch register for the 68K to be able to read, as well as one to be able to write. there is also a FIFO that is read-only by the 68K. 

    so therefore, the 68K has two data registers it can read from the data card, and one to write back. There is also two audio registers the 68K can also write to, which can control the audio matrix. 

    The other problem is, some of the audio stuff is also controlled by the data CPU. so the two have to work in tandem to control the audio stage. This is painful. THIS... is also why when a 4000 would go into alert mode, the warning tone appeared to have some "breakup"  in it. It is due to the fact the data card's CPU has to go off and do some other stuff handling incoming data which would cause the audio latch to de-assert in short bursts. Even though the 68K is keeping the alert tone enable line asserted during the entire duration. 

    The data format at which it uses, they used the old legacy STAR 3000/Jr for text-based products at which the scripts would use and format to display on screen in a graphical manner, you can see this here in ROM with the data parser I had commented:

    So this machine did, in-fact, use the old 37-byte Teletext frame format for controlling its products and parsing the OMCW to determine what is being displayed. 

    This coincides with the release of the 4000 being all text based, mimicking the 3000's operation. It was easy for them to do. 

    Anyways, a lot of effort would need to be put into conforming to, and formatting the data in a particular way if i were to use this data card.

    Also, we need to build a satellite subcarrier encoder, and an SDLC framer. None of which I felt like doing. 

    The satellite no longer exists anymore either. 

    I could do what I did with the I/O card, and replace the ROM with my own since it too is also MCS51 based. Trouble is, I would still be stuck with SDLC framing with NRZI-FSK encoding. 

    At the end of the day, we really needed to bring the data delivery system into the 21st century to work with our modern standards if we really wanted to use a 1980s machine with 2019 data standards. 

    So? I voted for the nuclear option. forgo the entire data card and engineer a new one! 

  • I/O Card: Developing new firmware

    techknight4 days ago 0 comments

    Since we had a good success with writing a program and running it on the I/O card, we need to start writing the initialization routines for the various hardware bits on that card, as well as writing the protocol for communicating back and forth between it, and the driver on the 68K side. 

    The UART took a little bit of trickery. If you look back at the schematic, the baud clock for the UART IC is driven from one of the PIO chip's internal timer. 

    We decided to setup the UART to work with 1200 baud. Slow? yes... but it provides a method for getting characters in and out of the 68K system so we will stick with this. 

    Initializing the UART: 

    And, setting up the initial PIO states and setup to produce the proper baudclock:

    Presto! Now, we need to write some protocol code so I can send commands across the bus back and forth to the UART, an ex: 

    Seems pretty simple in concept, doesn't it? It does, however it took a lot of time and iterations/modifications and rewriting the ROM over and over to get this to work correctly. And when I say a lot.. I mean A LOT of trial and error. 

    Anyways... that's basically it in a nutshell.

    The mainloop handles all of the commands coming across the bus, it also has a Watchdog reset you must keep happy or the I/O card will keep resetting on you. 

    So with that mostly complete, we need to test each part of the firmware as we go along so we dont get caught up in bug hell... 

    Testing the UART <-> Bus communication:

    Success!!! We can communicate across the bus from the arduino, to my protocol, and out of the UART. 

    So we are well on our way to making functional I/O card firmware

    Next part is to make sure the protocol is working with the 68K's System bus by testing it in an actual system. 

    The trouble is, we have not written any 68K code at all yet. So quickly, I grabbed a copy of VASM and made a simple little ROM that keeps the watchdog happy on the CPU card, and then i sent a random LED set byte across the bus over to the I/O card and then loop forever. a very simple "hello world" program on the 68K side:

    perfect! We knocked two birds out with one stone here. one, we confirmed the 68K can communicate with my firmware on the I/O card, and then the 68K CPU itself can run a "Hello World" ASM program assembled with VASM and send a byte out to the I/O card. 

    I know, I jumped way ahead here but i needed to demonstrate the functionality of the IO card. 

    Next up... the data card. 

  • I/O Card: A silver lining?

    techknight4 days ago 0 comments

    Given the original ROM is a dumpster fire to me, trying to write a driver to run that from the 68K side is going to be a nightmare since we do not have the original codebase that used this card, that was downloaded from the satellite and embedded into the binary. 

    So to save what little sanity I had left, I need to look at custom ROM development for the 8051 with this particular card. 

    Prior to this point, I have never written MCS51 assembly before! We are going to need to find a toolchain, and learn the assembly language so we can do something with it. Luckily the assembly language is fairly similar to any other 8 bit microcontroller of the era, so it wasnt hard to pick up. 

    But.... before we can do anything, we need to prep the hardware so we can shove some bytes back and forth into the card to test operations. 

    I did try the brute-force method with the original ROMs with poor results. 

    Yep. This one was just as crazy as the graphics card. 

    I am going things this way to eliminate every potential variable I can, up to this point I have not touched the 68K architecture at all, nor written a hello world program to download and run on the 68K system itself. 

    Using an arduino as the "CPU" in place of the 68K makes this task much easier when I need to hack on and work with a specific piece of hardware, as I did with the graphics card. 

    The next piece of the puzzle, I need a toolchain for the MCS51. I did some searching around and the ONLY thing I could find was Keil. Keil is a paid product sadly so I had to deal with this situation. I dont know of any open-source assemblers for the MCS51 either, So I stuck with Keil. 

    We need to start the skeleton of the ROM code, such as defining port pins and where they are going to go:

    Since the INT0 and INT1 routines from the original ROM are fairly simple and timing critical, we shall borrow those for the new ROM project as I think that would be the easiest:

    Now, when writing a new firmware for a completely foreign card that you wish to do your own thing with, you have to start thinking about the protocol you are going to use in which your driver will be written to communicate with the card. 

    With that in mind, you can see my commented code/lines in the original INT handlers for bus communications where I tried to do some special things my own way for my own protocol adaptation. 

    Unfortunately, no matter how hard I tried, anything i added/took away here would break the bus timing and prevent the 68K from reading/writing legible bytes from the I/O card. 

    so this interrupt absolutely COULD NOT be messed with in any way, it was timing critical! So I left these routines bland for the most part, all they do is read/write into the external SRAM in each PIO chip. C0XXX writes the first chip, C4XXXX writes the 2nd chip. From this point, it is up to me to detect byte changes in that chip in the mainloop and handle whats going on, change those bytes, and raise an interrupt back to the 68K CPU to service what I just did. 

    But, before we get so carried away with making a protocol, writing the control logic for the RS232 UART, GPIO, LEDs, etc... how about a simple hello world ROM? 

    why, of course! the best way to test this card standalone imho to make sure the ROM I am writing is working properly is to attempt to initialize the PIO hardware, and do some blinkenLEDS: 

    Success!!!! We able to write code, and assemble it. Burn it onto a ROM, and run the board. :-)

    At this point? we need to start looking at initializing all the peripherals on the card, and then writing a protocol to communicate with all of this from the 68K side. 

    Now, time to move onto developing the ROM for this card! 

  • I/O Card: Trouble in paradise

    techknight4 days ago 0 comments

    After completing the schematic and architecture of the I/O card, It is time to take a look at the ROM. 

    With how unique the bus interface logic is back to the 68K, essentially using a PIO and a few other things to mesh with the 68K read/write cycles from the MCS51, I just had to take a look at the routines and document how they pulled this off. 

    A quick study of the bus interface logic, reveals a PAL which acts as an address decoder. This address decoder is tied to INT0 and INT1 of the CPU:

    This helps aid in finding where those routines are in ROM. Digging through ROM, I was able to locate the routines: 

    The above subroutine is INT1, which is decoded at the address space I had commented in the above code. It is a fairly clever subroutine! 

    I commented all of the ASM code to better understand what is going on with the above subroutine. It may not be 100% accurate, but its as close as I can get it. 

    The INT0 routine is a bit longer, as its doing some other stuff. 

    As we can see, the C0XXXX code is much larger, and it has additional checks in it. I am going to assume at this point, this is where the commands to actually control the card are sent. whereas the INT1 handler might just be for data only. But your guess would be about as bad as mine. 

    At this point, we have somewhere to start at least. Since INT0/INT1 are the only way the 68K can speak to this card, we can assume the command processor has to be triggered from this as well. you can see where it reads 4 bytes into 0x14-0x17 in the CPU's RAM space. So we must find the routine that handles all of this outside of the interrupt in order to learn how to control this card in the method they proposed. 

    So to help try and find this, Lets take a look at the RESET vector:

    As you can see with the above, we have basic initialization code for the registers and RAM, a call to a code block at 2047, and then a branch always loop at code_52. so it just sits there indefinitely.

    That clues us in that this device is entirely interrupt driven. Now, bear in mind this device also has to run a modem, AT keyboard, and a host of other things. 

    But the problem begins with the code block at 2047. its literally a nightmare scenario, and its super hard to follow as it goes through. 

    If we take a look at the timer interrupt, we have this: 

    And that's it, all it does. Appears to be settings/clearing flags and changing some things in RAM, however I do not know precisely what its actually doing. It might be related to the modem or AT keyboard. since they are using the same UART on the CPU for both the modem, and the AT keyboard it could be related to both. Who knows... 

    However, with all of that said I fail to see how it catches and processes data from the bus. this code is super hard to follow. For example, that code block called from RESET just goes on and on and on forever:

    So instead of spending days and days and days trying to chase this code since theres no examples on how to use the API of this ROM, I had to make a decision to abandon the ROM entirely and just write my own thing to save grace here, I could also avoid writing the routines for the modem, and AT keyboard since I did not need either of them. I only wanted the LEDs, the RS232 port, and general purpose I/O. 

    The main code block are literally ljmp ops all over the place, back and forth. they arnt using any rts or jsrs, its the epitome of spaghetti code... 

    Hence, trouble in paradise... 

  • Analysis: The I/O Card

    techknight5 days ago 0 comments

    Up to this point, we have only focused on the basic architecture of the system and went into detail on the graphics card, and CPU card, only briefly mentioning the data and I/O cards. 

    But before we can begin to get a hello world program on the system, we must take a closer look at the I/O card. 

    In a previous blog post, we mentioned the schematic of the I/O card, however to keep things simple, we will post the schematic again here: 

    With that, here is the system architecture of said card: 

    Peripheral - I/O Card
    8051 Memory Map:
    $0000 - $01FF = RAM
    $0200 	      = UART Data
    $0201         = UART Command
    $8000         = PIO1 Command/Status Register
    $8001         = PIO1 PORTA (VME Address/Data AD1-AD8)
    $8002         = PIO1 PORTB (Rear External Switches 1-8)
    $8003         = PIO1 PORTC (Various)
    $8004         = PIO1 TCNT0 Low
    $8005         = PIO1 TCNT0 High
    $8100         = PIO2 Command/Status Register
    $8101         = PIO2 PORTA (Indicator LEDs)
    $8102         = PIO2 PORTB (DTMF Dialer Codes, 1 Indicator LED)
    $8103         = PIO2 PORTC (Bus Clear, Charging Indicator)
    $8104         = PIO2 TCNT0 Low
    $8105         = PIO2 TCNT0 High
    I/O Card VME Addressing:
    $C00000 to $C001FF = 8051 INT0
    $C04000 to $C041FF = 8051 INT1
    PA0: VME Address/Data 0
    PA1: VME Address/Data 1
    PA2: VME Address/Data 2
    PA3: VME Address/Data 3
    PA4: VME Address/Data 4
    PA5: VME Address/Data 5
    PA6: VME Address/Data 6
    PA7: VME Address/Data 7
    PB0: Switch 1
    PB1: Switch 2
    PB2: Switch 3
    PB3: Switch 4
    PB4: Switch 5
    PB5: Switch 6
    PB6: Switch 7
    PB7: Switch 8
    PC0: (LED 7) Modem In Use LED
    PC1: Modem ALB
    PC2: Modem SQT
    PC3: Modem/AT Serial Select
    PC4: Cube Relay Control
    PC5: VME IRQ2
    PA0: (LED 10) System Error LED
    PA1: (LED 8) Spare LED
    PA2: (LED 2) Sat Data LED
    PA3: (LED 3) Local Video LED
    PA4: (LED 6) On Air LED
    PA5: (LED 4) Spare LED
    PA6: (LED 1) Satellite Video LED
    PA7: (LED 5) Pre-Roll LED
    PB0: DTMF Keypad Column 1 
    PB1: DTMF Keypad Column 2
    PB2: DTMF Keypad Column 3
    PB3: DTMF Keypad Row 1
    PB4: DTMF Keypad Row 2
    PB5: DTMF Keypad Row 3
    PB6: DTMF Keypad Row 4
    PB7: (LED 9) Spare LED
    PC0: VME IACK2
    PC1: Battery Charging Indicator
    PC2 - PC5 unused. 

    This is basically where everything is hooked up as far as the address map. Essentially, it is an 8051 based card, with two PIO/RAM ICs, and a UART IC. 

    The card handles the LEDs, a modem, AT keyboard, serial port, and some general purpose I/O. 

    The funny thing about this card however, is the 68K bus interface logic and timing. It is all software-based and its controlled via interrupt on the 8051, so the interrupt execution time has to be in-step with the 68K Bus Cycles. absolutely insane, and there is NO room for timing variances or error on the bus read/write interrupt handlers or errors will ensue. I tried it. 

    One thing i noticed right away on the design of this particular card, is how there is an inverter off of the Q output on the flipflop, instead of just using the /Q output. As seen here:

    This part made no sense to me. You would save a gate if you just wired the /Q output instead of using a makeshift inverter. Maybe there is a timing reason? Honestly, I do not know. 

    In the next part, we need to disassemble the ROM and take a look at what is going on, since we have no driver or any details on how to drive this card. 

  • Animation

    techknight5 days ago 0 comments

    Now up to this point we have the graphics card pretty much figured out. We have the framebuffer control on its fundamental levels where we can set palettes and viewports and their resolutions. 

    The next part of this puzzle though is how do we achieve animation? the weather icons used on the WS4000 had an animation to them where you can see the rain falling, such as: 

    How do we achieve this effect on the WS4000, and how did they do this originally? 

    Well, they use palette swapping trickery. Thing is, you can upload a palette to the RAMDAC as we explained in an earlier post, and you can rotate colors around on the index to do this. 

    BUT. the WS4000 actually offers a method to do this on its own, which is identified by this function from ROM that I had documented here: 

    code:00000776 CHECK_FOR_ANIMATE:                      ; CODE XREF: code:0000111Cp
    code:00000776                                         ; code:0000113Fp ...
    code:00000776                 mov     A, RAM_39
    code:00000778                 jz      code_782
    code:0000077A                 mov     DPTR, #0x601
    code:0000077D                 clr     A
    code:0000077E                 movx    @DPTR, A
    code:0000077F                 lcall   Animate_Palette
    code:00000782 code_782:                               ; CODE XREF: CHECK_FOR_ANIMATE+2j
    code:00000782                 lcall   code_734
    code:00000785                 ret
    code:00000785 ; End of function CHECK_FOR_ANIMATE
    code:00000786 ; =============== S U B R O U T I N E =======================================
    code:00000786 Animate_Palette:                        ; CODE XREF: CHECK_FOR_ANIMATE+9p
    code:00000786                                         ; Animate_Palettej ...
    code:00000786                 jb      RAM_20.3, Animate_Palette ; Wait for previous operations to clear
    code:00000789                 mov     R7, RAM_39      ; How many color blocks to copy
    code:0000078B                 mov     R0, #0x35 ; '5' ; Beginning location for RAMDAC Palette Addresses
    code:0000078D code_78D:                               ; CODE XREF: Animate_Palette+4Ej
    code:0000078D                 mov     DPTR, #0x601    ; Location Pointer to Palette Table
    code:00000790                 movx    A, @DPTR
    code:00000791                 mov     R1, A           ; Store Value at RAM 0x0601 into R1
    code:00000792                 mov     A, @R0
    code:00000793                 mov     P2, #0x80 ; 'Ç' ; Set Palette Write Address
    code:00000796                 movx    @R0, A          ; Store Value in I-RAM 0x35 as RAMDAC Palette Address
    code:00000797                 mov     P2, #0x88 ; 'ê' ; Set RAMDAC to Palette Memory
    code:0000079A                 mov     A, #7
    code:0000079C                 clr     C
    code:0000079D                 subb    A, R1           ; Subtract 7 from the contents in 0x0601
    code:0000079E                 mov     R2, A           ; Store Result into R2
    code:0000079F                 mov     A, @R0          ; Read Palette Address from I-RAM 0x35
    code:000007A0                 add     A, R1           ; Add Current 0x0601 Value to RAMDAC Palette Address
    code:000007A1                 mov     B, #3           ; B Register
    code:000007A4                 mul     AB              ; Multiply Added Palette Address with 3. A contains Low byte, B contains High Byte Result
    code:000007A5                 mov     DPL, A          ; Data Pointer, Low Byte
    code:000007A7                 mov     A, B            ; B Register
    code:000007A9                 add     A, #0           ; Store 16-bit Multiply Result into Data Pointer.
    code:000007AB                 mov     DPH, A          ; Data Pointer, High Byte
    code:000007AD code_7AD:                               ; CODE XREF: Animate_Palette+30j
    code:000007AD                 movx    A, @DPTR        ; Read Red Value from RAM @DPTR
    code:000007AE                 inc     DPTR            ; Advance to Next Value (Green)
    code:000007AF                 movx    @R0, A          ; Write Red Value to RAMDAC Palette
    code:000007B0                 movx    A, @DPTR        ; Read Green Value from RAM @DPTR
    code:000007B1                 inc     DPTR            ; Advance to Next Value (Blue)
    code:000007B2                 movx    @R0, A          ; Write Green into RAMDAC Palette
    code:000007B3                 movx    A, @DPTR        ; Read Blue Value from RAM @DPTR
    code:000007B4                 inc     DPTR            ; Advance to Next Value (Red)
    code:000007B5                 movx    @R0, A          ; Write Blue into RAMDAC Palette
    code:000007B6                 djnz    R2, code_7AD    ; Decrement R2, Repeat Palette Write until 0
    code:000007B8                 mov     A, R1
    code:000007B9                 jz      code_7D3
    code:000007BB                 mov     A, @R0
    code:000007BC                 mov     B, #3           ; B Register
    code:000007BF                 mul     AB
    code:000007C0                 mov     DPL, A          ; Data Pointer, Low Byte
    code:000007C2                 mov     A, B            ; B Register
    code:000007C4                 add     A, #0
    code:000007C6                 mov     DPH, A          ; Data Pointer, High Byte
    code:000007C8 code_7C8:                               ; CODE XREF: Animate_Palette+4Bj
    code:000007C8                 movx    A, @DPTR
    code:000007C9                 inc     DPTR
    code:000007CA                 movx    @R0, A
    code:000007CB movx A, @DPTR...
    Read more »

  • Deciphering Viewports

    techknight05/01/2021 at 00:59 0 comments

    As we discussed in the previous installment, we can switch resolutions and send color palettes via the CMD01 being sent to framebuffer control. 

    Now, as we discussed there must be a way to be able to change where the on-screen viewport points to in memory. 

    When discovering the command, I found this in ROM: 

    To most people, this doesnt mean much. But... This is called on startup very early in the process as that 8051 ROM boots. the location in RAM 0x710 is the same areas where CMD01 configures for its viewports. 

    With that known, the above basically gives us an initial state the framebuffer is during startup. So with my analysis of CMD01 in the previous installment, lets take a peek at ROM to figure out the code flow and see how the viewports are configured. 

    Looking at the startup code, its looped 3 times. So thats something to keep in mind for going forward.

    One thing that clued me in however, was from a conversation we had with Michael Searce. He had mentioned the WS4000 had at least 3 framebuffers that can be shown on screen at any point in time. Aha. So.... lets follow the code-flow to confirm our suspicions. 

    If we thumb through the CMD 01 handler, we can see towards the bottom here our familiar memory location 0x710 that I have labeled. So, lets take a look at that subroutine. 

    So if we follow this code, First off it reads a byte from the FIFO and stores it into Register 2. Then it sets the DPTR to point to 0x710 in RAM. It then reads 4 bytes from the FIFO, then performs a calculation whether it sets the 5th byte 0x30 or 0x60, but it is not read from the FIFO so it does not matter here. 

    Then.... you encounter the djnz instruction. This Decrements R2, and if its not Zero, it will jump off back to $0x13A5 which reads another block of 4 bytes. 

    So given this, it certainly confirms the information Mike has supplied about having multiple framebuffers. Trouble is, there are no boundary checks so technically you could copy 4 byte blocks 255 times. That.... would be bad because you will corrupt the rest of SRAM, and then run out of it! So.... don't do that. 

    So.... if we reference what we see in the above code, versus the example byte values in the Startup code, we can make a reasonable assumption of what to send it. 

    We can now formulate an example command:

    01 02 03 00 00 01 00 00 00 F0

     So lets break this down a bit: 

    01 = Command 01

    02 = Enable Graphics Output

    03 = Resolution (768x480)

    00 = Send Palette

    00 = Send Animation

    01 = Number of 4 byte viewport blocks

    00 = Viewport Byte 1

    00 = Viewport Byte 2

    00 = Viewport Byte 3

    F0 = Viewport Byte 4

    So this is what we know so far. Not much at this point, I know. But at least we have something to start with. And matter of fact, This is the command I used initially to get graphics to show up on the output at all as this was critical. 

    So at this point its a process of elimination. Try changing byte values and see what the hell happens. But before we do that, lets take a look at the value F0 at the end

    This was the value that was in in ROM to begin with as an initial setting, so there must be some significance to this. So lets take an educated guess on what that means. So the first thing we should to is convert the 0xF0 to Decimal, which is: 240. Huh interesting. My mind is trained to see patterns so knowing the framebuffer height is 480, its awful convenient that this value is 240. 

    So, I must assume 0xF0 is the Viewport height Divided by 2. so if we multiply the decimal equivalent of 0xF0, we get 480. Magic.... 

    So that just leaves 3 values that are unknown at this point. Sending this command as it is will use the default SMPTE Palette that is built into ROM, and show a 768x480 image of whatever is sitting framebuffer. 

    So the next step in this process is lets get something drawn into the framebuffer. We start with something like...

    Read more »

  • Resolutions

    techknight04/30/2021 at 01:40 0 comments

    Now that we are able to draw images into the framebuffer, we basically have the graphics card under our belt now. Except for one thing....

    Framebuffer control. We briefly touched on this in a previous blog entry, but now its time to go into some more detail. 

    Thing is, we can draw images into the framebuffer that is being displayed, but unfortunately drawing is fairly slow. so you would see the drawing process on screen. 

    So, to prevent this from happening and to provide a better experience to the viewer, we need to double buffer the drawing. The way to do this is simply drawing graphics/text into other areas of RAM that are not currently visible on the screen. 

    This is going to require more experimentation and studying of one of the most important commands. CMD01. 

    This command not only contains the Palette data thats sent, it also contains up to 3 framebuffer window settings. So, you can have up to 3 different set viewport partitions on screen at the same time. Plus there is CMD06 which is an overlay window that comes up from the bottom on a specified height. 

    from Studying the ROM, this was my initial analysis on the CMD01:

    Command $01: (Ex: 01 00 FF 00 00 01 00 00 00 00 )
    Byte 1 gets copied into a couple other registers before manipulated and written into the FPGA. 
    Byte 1.0 = Enable/Disable Local Video
    Byte 1.1 = Does a multiply by 2 on the counters. (Maybe switch active pages)
    Byte 1.2 = Disable/Enable Graphics Output. When disabled, Ends CMD01 processing entirely. (Set to Disable, MUST be last byte when Set!)
    Byte 1.3 = Does the same thing as Byte 1.1 (MAybe at different times though)
    Byte 2 (Switches Output Resolution)
    0 = 384x120
    1 = 384x240
    2 = 768x240
    3 = 768x480
    Byte 2.0 = Sets/Clears $22.1
    Byte 2.1 = Sets/Clears $20.6, and $28.3
    Byte 3 
    Condition Flag whether to do a full Palette Copy and Counters signaling copy. 
    If 0 We skip Palette load, and Read Byte 4.
    Byte 4 if Byte 3 = 0
    Condition flag whether we copy animate data or not.  
    If 0, we skip animation info and go into copy data into 0x710. Return from cMD01. 
    Byte 3
    If 1 then we load the palette1 with data. A full 0x0300 byte load is EXPECTED/REQUIRED!
    Byte 4 if Byte 3 was 1. 
    Still serves the same purpose, with an addition. 
    IF 0, We skip the animation info and go into the copy data into 0x710. BUT, Now, after that, the recently loaded Palette gets copied into RAMDAC. End CMD08
    If Byte 4 > 0 in either case, it loads the appropriate info for Palette animation. 
    Byte 4 = Number of Blocks of color palette to animate. Each Block is 7 Colors.  
    Each byte after this is each block location. Last byte is the speed at which the animation occurs. 

    Sorry if the above is confusing, but it was my quick analysis at the time.  But it will get a bit more clear the further we go on. 

    Now, if we put 768x480 images in framebuffer, we only have enough RAM for maybe 4 of those images. We already know from watching in the past that the radar is at least 6 frames of animation. These frames are obviously not drawn in real-time because it would be too slow. So its definitely pre-rendered into RAM, and the pointers move around to point where each image has been drawn in VRAM. 

    So in order to do this, the machine has to switch resolutions to a lower resolution so we can "fit more" into memory. 

    Thus enters Byte 2. This byte seems...

    Read more »

  • Degredation

    techknight04/28/2021 at 23:25 0 comments

    So as we saw in the previous blog, we have an image being drawn in the framebuffer, and the colors set. 

    The problem though, I noticed that some of the colors either not being set, or kept flipping to different colors after they were set. I started doubting my framebuffer command that I was sending, or my setup and started to get frustrated. 

    But then it suddenly dawned on me. Maybe this was the degradation that started happening to alot of these machines later in their lives. This was acting very similarly. 

    Here are some well-known cases of it going on in the field: 

    Here is a picture of one in my files that also had problems: 

    Here is another one a friend of mine owns which is running my software:

    So my mind quickly shifted from being a problem on my end with my experiments, to the potential of hardware failure. 

    Now giving my troubleshooting skills are very sharp, and I have repaired electronics for years and years prior to this point, I had a pretty good idea right where to go to. 

    the RAMDAC. So, on an indexed-color system, most graphics cards from this era have whats called a RAMDAC. the RAMDAC means Random Access Memory Digital to Analog Converter. 

    Without forcing you all to go to wikipedia, the nutshell is basically this. the RAM of RAMDAC means there is a small section of SRAM inside the chip which holds the R, G, B values for each color index starting from 0, up to 255. DAC is the analog converter. it takes the digital R,G,B values that are stored in the SRAM lookup table, and then applies those as a voltage or current. the higher the number for each R,G,B value, the higher the voltage/current level is on the output. and 0 being no output. Each value from 0 to 255 is applied from the bus through an 8-bit port. This is known as the index. this index will pick which slot to apply to the DAC output with that slot's programmed color values. There are also mask registers, etc but i don't want to get into that, and the 4000 doesn't use them anyway. 

    Pretty straight forward. 

    With the colors suddenly and spontaneously changing from their set values, or unable to be changed, points me to believe the SRAM inside the RAMDAC has failed in some rather unique way. So it is unable to retain its set RGB values. so bits will flip. when bits flip states, its going to change the color for that index value.

    So the next step at this point is to just basically replace the BT471. 

    However there is a catch. the BT471 is hard to find since it wasnt commonly used. Everyone used the BT478, as it supports full 8bit RGB. 

    So, i bought a few of the BT478s on ebay. I figured why not, plus it can upgrade the possible number of colors to its mask! or so i thought. 

    I put the new chip in, but the picture was very dark. I even sent full RGB888 values, and the picture was still very dark. 

    So i took a peek at the ROM, and sure enough this little bit here stands in the way: 

    If you look at that, you can see the anl 0x3F opcode. That basically clips off the byte value and keep it as 6 bits. So damn.... So much for full 8bit RGB. haha. Those operations are peppered everywhere in ROm so its not as simple as just patching that bit out. 

    So i left it be. I figured i was going to need to replace the BT471 with another 471. 

    Turns out, studying the datasheet there is a pin which you can connect to ground, and it forces the chip to run in backwards-compatible RGB666 mode! 

    So, lets do that! 

    Here is the new chip installed. 

    Turns out, the pin I needed to ground was right next to a ground. So i didnt even need a bodge wire. the pin was unconnected on the original design as well, so all I did was lay down a solder blob to ground the pin. 

    Time to see if it worked: 

    BOOM fixed! that's exactly what it was. Although there is still some color noise, but that...

    Read more »

View all 28 project logs

Enjoy this project?



Frank Smith wrote 03/18/2021 at 23:32 point

you may want to reach out to Doug Pincock who was involved in the development of the Weather Star: One of the links on this page is about the WS.

  Are you sure? yes | no

techknight wrote 03/19/2021 at 00:00 point

Thanks for the tip! its been so many years, I doubt he would remember a whole lot of fine detail. Plus, I doubt he works at Amirix anymore. But I sent an email just out of curiosity! 

  Are you sure? yes | no

freefuel wrote 03/15/2021 at 19:17 point

rather than implementing the system in MAME perhaps consider the MiSTer FPGA project?

  Are you sure? yes | no

techknight wrote 03/15/2021 at 19:25 point

I don't know enough about FPGA design to do such thing. Plus someone would need to finish what I started with trying to reverse engineer the XC2018 FPGA bitstream into proper logic blocks, so it could be ported to another FPGA arch. 

  Are you sure? yes | no

freefuel wrote 03/15/2021 at 19:44 point

may I suggest you post your project up there anyway, you might find some people who know more about the system.  I also suggest you reach out to anyone you can contact here, who knows you might find some of the original developers. 

  Are you sure? yes | no

Mike Szczys wrote 03/15/2021 at 15:44 point

That is just so cool! Kudos on making this one happen :-D

  Are you sure? yes | no

techknight wrote 03/15/2021 at 16:20 point

Not a problem! I still have a bunch more project log entries to post. including that of probing the graphics card afterwords. 

  Are you sure? yes | no

Darren wrote 03/15/2021 at 05:14 point

The MAME project has a utility for turning PAL dumps into logic called jedutil.  It looks like it also handles registered logic.

  Are you sure? yes | no

techknight wrote 03/15/2021 at 11:46 point

I am not 100% familiar with MAME, so I didn't know that. I did get in touch with a MAME developer long ago about writing a WS4000 driver for MAME. It is partially complete, but it isn't 100%. I abandoned that avenue and just wrote the software and debugging on the machine itself. But I will discuss this later in the series. 

  Are you sure? yes | no

Dan Maloney wrote 03/08/2021 at 19:50 point

Sounds like a fun journey. Waiting eagerly for updates.

  Are you sure? yes | no

techknight wrote 03/08/2021 at 22:33 point

Oh definitely! Lots of juicy content coming, little by little. This has occured over the last 2 years (Still occuring) and just thought to do a project blog here. so its gonna take some time. :-)

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates