Introduction.

Hey there,

In this blog-post, I will be sharing the source code of the needle sweep patch made for cluster VMMJ08MH 9.00, including a brief explanation on how this works.

This is useful for new developers or if you want to learn how to make a patch code for these clusters – once you manage to control it, you can create infinite amounts of codes and/or fixes for the cluster.

I won’t go into full detail here, these clusters have a W65C816 MCU that controls the entire cluster, and you write code in W65C816 assembler opcodes to give instructions on what to do – this is basically programming the MCU.

The MCU model that this cluster uses is a Micronas CDVV 1304.

 The code.

DISCLAIMER: this code is licensed under the GNU LGPL v3.0 or later, see the LICENSE tab for more details.

; Needle and led sweep for cluster VMMJ08MH 9.00
; Made by Feche
; https://feche.ar/

    .org    $0E8F
    
MaxSweepProgress = $55
MaxDelayProgress = $60

sub_5587:
    ; Check if cluster in on
    JSR     $6027
    BCC     ResetSweep
    
    ; Check if needles are homed
    JSR     IsHomed
    BCS     ResetSweep
    
    ; If sweep is done, branch
    JSR     Progress
    BCS     Continue
    
    JSR     NeedleSweep
    JSR     LightSweep
    
Continue:
    JSR     CheckLeds
    
    LDA     $81
    JMP     $558A
    
ResetSweep:
    STZ     DelayProgress
    STZ     SweepProgress
    STZ     EnableLeds
    
    BCC     Continue
    ; Disable delay if not homed
    LDA     #MaxDelayProgress
    STA     DelayProgress
    BRA     Continue

; NeedleSweep ------------------------------------------------------------------
NeedleSweep:
    LDA     SweepProgress
    CMP     #(MaxSweepProgress/2)
    BCS     ZeroOutNeedles
    
    ; Do needle sweep!
    LDA     #$5      
    STA     $26D                
    STA     $273
    
    LDA     #$80
    STA     $272
    STA     $26C
    
    LDA     #$0F
    STA     $26F                
    STA     $271
    
    LDA     #$B0
    STA     $270
    STA     $26E

    RTS
    
ZeroOutNeedles:
    ; Set needles to zero
    LDX     #$0
Loop:
    STZ     $26C, X
    INX
    CPX     #8
    BNE     Loop
    
    RTS
    
; LightSweep ------------------------------------------------------------------
LightSweep:
    PHX
    PHY
    
    STZ     $ACD
    STZ     $ACE
    
    ; Load speedometer
    LDX     #$E7
    ; Get needle position
    LDY     7, X
    LDA     LedSweepData, Y
    
    LDY     SweepProgress
    CPY     #(MaxSweepProgress/2)
    BCS     NeedleDown

    TSB     LedData
    BRA     LightContinue
    
NeedleDown:
    TRB     LedData

LightContinue:
    LDA     LedData
    STA     $ACE
    
    LDA     #$CD
    STA     0
    LDA     #$A
    STA     1
    
    ; this function turns on/off leds
    JSR     $65D9
    
    PLY
    PLX
    RTS
    
; Progress ---------------------------------------------------------------------
Progress:
    ; Check if we need to apply delay
    LDA     DelayProgress
    CMP     #MaxDelayProgress
    ; Delay done
    BEQ     DelayDone
    
    ; Increment delay counter
    SEC
    INC     DelayProgress
    JSR     ZeroOutNeedles
    RTS
    
DelayDone:
    ; Increment progress counter
    SEC
    LDA     SweepProgress
    CMP     #MaxSweepProgress
    ; Progress done!
    BEQ     ProgressDone
    
    CLC
    INC     SweepProgress
    RTS
    
ProgressDone:
    ; Enable leds after we finish the sweep
    LDA     #1
    STA     EnableLeds
    ;STZ     SweepProgress ; Loop!
    RTS

; CheckLeds --------------------------------------------------------------------
CheckLeds:
    LDA     EnableLeds
    CMP     #0
    BEQ     DisableLed
    RTS
    
DisableLed:
    LDA     #1
    TSB     $A56
    RTS
    
; IsHomed ----------------------------------------------------------------------
IsHomed:
    LDA     $B41
    CMP     #$1E
    BEQ     HomedTrue
    CMP     #$10
    BEQ     HomedTrue
HomedFalse:
    SEC
    RTS
HomedTrue:
    CLC
    RTS
;                     0    1    2    3    4    5    6    7    8    9
LedSweepData:   .byte $00, $80, $00, $01, $00, $40, $00, $04, $00, $02
                .byte $00, $00, $20, $00, $08, $00, $00, $00, $00, $00
                .byte $00, $00, $00, $00, $00, $00, $00
                
DelayProgress:  .byte $00
SweepProgress:  .byte $00
EnableLeds:     .byte $00
LedData:        .byte $00

Explanation.

I’ve included comments in the code to understand it better, but in a quick glance:

  • We call routine $6027 to check if cluster is on/off. (ignition on/off)
  • We call routine IsHomed() to check if the needles are in the homed position. (needles ready)
  • If one of the both are false, the needle sweep resets back to zero and this process repeats until both checks are true.
  • Now that we are ready, we call Progress() routine to increment the progress variable used as reference for start – end – stop cycle of the sweep.
  • Inside Progress() routine, we check for delay – a delay is needed when the needle is already homed, caused by a rapid on/off of the ignition switch. If the needle is not homed, the cluster constantly checks the needles until it is ready – this process produces a delay, and this is the delay we simulate to keep both sweeps (homed/not homed) having the same duration.
  • Once the delay is done, the DelayProgress variable starts to increment until MaxSweepProgress is reached, once reached the sweep is done.
  • If the sweep is not done yet, we move the needles/leds upwards until MaxSweepProgress/2, beyond that the needles need to go down, the ‘zero-out’ position.

The code can then be compiled in any W65C816 compiler, there is an online compiler version above in ‘useful links’.

This is programming in low-level language, and in order to fully understand it you will need previous knowledge on bitwise operations, memory addressing, stack pointer, etc.

Useful links.

Happy coding 🙂

UPDATES:

09/12/24
– Fixed an issue with the temperature needle not being zeroed correctly.
– Reduced initial sweep delay.

Leave a Reply

Your email address will not be published. Required fields are marked *