Unfortunately I had to abandon my previous attempts and mark them as legacy. I'm now doing everything from scratch once again.I’ve hidden the old versions (readable_mxt + engineered_mxt) so they don’t mix with the new code. Working with AI models turned out to be too optimistic, so I started over in src2/ – I’m reading the original ASM from MXT.TXT, mapping function by function, verifying behavior, and rewriting it in C. Claude is only helping me as support, but all the logic is manually checked by me.At the moment I have almost everything mapped and working except LCD, EEPROM and a few unclear parts.
Announcement
Collapse
No announcement yet.
Announcement
Collapse
No announcement yet.
Let's make a closely MXT like detector!
Collapse
X
-
Originally posted by KRinAZ View PostA quick update -
I'm going to take a shot at assembling code to program a PIC16F76 and test the functionality. Seems like that would be a good step towards creating code for the STM32...
The original code for the PIC16F76 is very heavily optimized for a slow 8-bit microcontroller. It uses things like virtually no stack, heavy bank switching, and lots of obscure low-level instruction tricks. From these it’s often very hard to extract 100% semantically correct code - meaning it’s difficult to perfectly reconstruct exactly what the original engineer had in mind.I’ve changed my mind on this. Porting the code 1:1 to an STM32 would be a nightmare and a massive waste of time.In my opinion, the much better approach is reverse > understanding > clean rewrite. Take the knowledge and key algorithms from the disassembly, but write everything from scratch, directly for the target 32-bit platform STM32. That way we can properly use modern solutions and implement the important parts (like the DSP core, ground tracking, etc.) cleanly in the new project.
Comment
-
Don't forget that the ROM of the PIC16C76 is only 8k, which, in my opinion, means that the written code is highly optimized (almost certainly in assembler), so the decompiled data of HEX file are as close as possible to the original, but quite different from the list-assembly file, if this program was written in "C". This means that in order to understand what the logic of the processes in the metal detector is, you must first restore the assembler program, in the original form understandable to you (variables - designated only as cells in the decompiled file, subprograms designated only as addresses, etc.). Here AI could not help you, because this concerns not only code, but also expected possible actions controlled by the code.
Moreover, in order to fit into this small ROM volume, the programmer, writing in assembler, used repeatedly block subprograms for calculations and control, which he used from different places in the main program, and when referring/calling to them, he loaded them with different input values, which makes things even more complicated.
My opinion is that the extracted HEX file is a great achievement and is ready for the construction of a metal detector, the circuit and hardware of which are known, but it is an extremely difficult task to generate an understandable source file by compiling.
I would like to repeat again that in this case AI is unlikely to be useful.
Comment
-
top down reverse engineering ( approx 5 minutes with AI assist ). The inputs are the hex dump and the schematic.
decoded the bank switching.
See attached PDF ...
and on this "originality" subject maybe you should be using the 16F876a chip ( flash version and still in production )
Here is what the AI said....
Short answer: yes — and it's about as clean a port as you'll ever get, because the 16F876 is essentially the flash successor to the 16C76. Same 14-bit midrange core, same 28-pin pinout, same 8K×14 program memory and 368 bytes RAM, same Timer0/1/2 + dual CCP + SSP + ADC peripheral set. The disassembled code words transfer essentially unchanged; this is exactly the C-part→F-part migration Microchip designed the F876 for, and the big payoff is that it's flash, so you can reflash during the rebuild instead of burning OTP parts.
The things that actually need attention are few, and only one is a real trap:
The config word has to be regenerated. You can't literally burn the C76 image — the config word at 0x2007 is encoded differently and the F876 has options the C76 doesn't (BOR, PWRT, and the LVP bit). Set HS oscillator for the 8 MHz crystal, WDT to match the original (the firmware does clrwdt, so it expects WDT enabled), and code-protect to taste.
The trap is LVP / RB3. On the 16F876, RB3 doubles as the PGM pin for low-voltage programming, and an erased config defaults LVP enabled, which steals RB3 from you as I/O. The MXT uses RB3 as an input (it's in your TRISB=0x8B, feeding the mode/trac switch network). So you must explicitly program LVP = 0 or that switch input dies. The C76 has no LVP bit, which is why this never came up on the original.
The ADC is 10-bit instead of 8-bit, but it barely matters here. The internal ADC only reads the control pots (the real signal is the external ADS8320), and as long as you leave the result left-justified (ADFM = 0, which is what ADCON1 = 0x00 already gives you), the top 8 bits land in ADRESH exactly where the existing 8-bit reads expect them. So the pot reads work unchanged; you just gain a spare ADRESL you can ignore. If anything you could later widen those reads to 10-bit for finer pot resolution, but it's optional.
One thing to verify rather than assume: the F876's MSSP is a superset of the C76's basic SSP, and you're only using SPI master, so it'll work — but the SPI clock polarity/phase is set by CKP (SSPCON) plus CKE/SMP (SSPSTAT), and the original writes SSPCON=0x20 / SSPSTAT=0x40. Those should produce the same SPI mode on the MSSP, but since the ADS8320, LTC1446 and the LCD pod all care about edge timing, it's worth a scope check on first bring-up rather than trusting it blind.
Everything else — the 13.89 kHz CCP1 PWM, the ~244 Hz Timer0 ISR, the 24-bit math, the whole analog front end — is untouched. The analog board doesn't change at all; this is purely an MCU swap plus a regenerated config word.
Two practical notes. First, you'll almost certainly be using the 16F876A in practice, not the original 16F876 — the non-A part is long obsolete and had errata the A cleaned up. It's pin- and code-compatible; just a couple of flash/EEPROM write-timing and config specifics differ, none of which affect this design. Second, rather than binary-patching the OTP image, the better move is to reassemble the disassembly against the p16f876a include file — you fix the handful of register-name differences (ADRESL now exists, MSSP naming) once and end up with a maintainable, reflashable source project, which is the whole point of moving to a flash part.
If you want, I can mark up the init routine (0x88 / 0xA9) with the specific F876A register/config changes side-by-side against the C76 originals, so you've got a concrete porting diff to work from.
FORENSIC CODE ANALYSIS :
Strong evidence it's hand-written assembler, not C. Several independent tells line up, and a couple are close to decisive.
The clearest one is the reset layout. The first four words are doing real work crammed into the gap before the interrupt vector:
0x0000 call 0x71 ; init
0x0001 clrf TMR0
0x0002 bsf INTCON,GIE ; enable interrupts
0x0003 goto 0x3 ; main loop = spin here forever
0x0004 ... ; interrupt vector
A C compiler puts a single goto __startup at 0x0000 and leaves 0x0001–0x0003 as dead space, because its runtime (RAM clear, data init, then main) lives in a relocatable block elsewhere. Hand-packing init-call + TMR0 clear + GIE + the spin loop into exactly those four otherwise-wasted vector-slot words is a classic assembly-programmer trick. The "main loop" is literally goto $ — everything runs in the Timer0 ISR.
Then the indirect-addressing count: only 1.4% of instructions touch INDF/FSR. That's the big one. C on a PIC16 leans heavily on FSR for stack-relative locals, struct passing and pointer derefs — you'd expect that figure several times higher. 1.4% means the code is almost entirely fixed, direct-addressed global variables, which is how a human writes PIC16 assembler, not how a compiler manages C scope.
The ISR timing is hand-counted. The 20-NOP run at 0x000A is a deliberate phase pad so the sample lands at a fixed point in the TX cycle, and the movlw 5; addwf TMR0 reload is someone trimming the interrupt period by counted instruction cycles. A compiler never emits twenty bare NOPs for timing — that's a person who knows their Tcy budget.
The math seals it. The routines at 0x5EA / 0x678 work on hand-chosen scratch registers (0x45–0x4E, 0x20–0x22) with explicit carry/borrow propagation, and they have application logic woven into the arithmetic — e.g. branching on bit 5 of variable 0x36 mid-multiply. That's a bespoke fixed-point routine, not a generic compiler library call. And the arithmetic is 24-bit (3-byte): no mainstream PIC16 C compiler of the 2002 era had a native 24-bit type (HI-TECH and CCS both gave you 8/16/32), so a compiler wouldn't naturally land on three bytes — a human picks 24 bits to fit the dynamic range exactly.
The one thing that pointed the other way, and why I checked rather than assumed: some byte-copy routines flip the bank select per byte (set RP0, read, clear RP0, store, repeat), which is the kind of redundant banking compilers emit. But at 9.9% overall bank-switch density and only ~47 of those tight flip pairs, it's better explained as a hand-written "copy a banked byte" macro reused a lot than as compiler output — especially against everything else.
So: hand assembler, almost certainly MPASM, written by someone who counted cycles and laid out RAM by hand. (The "Generated by MPLAB" header on the dump is just the disassembler talking, not the original toolchain.) The only caveat I'd put on it is that I can't fully exclude C-with-heavy-inline-asm — but the near-absence of FSR traffic argues there isn't much compiled C hiding in there at all. If you want, I can confirm by scanning for a compiler runtime signature (a HI-TECH/CCS startup clears RAM and inits globals in a very stereotyped way) — if it's hand asm, that fingerprint won't be there.
THE BIT THAT DOES THE PHASE ( DISC )
there's no series-expansion arctan and no CORDIC, but there is a phase routine, and it does arctan the cheap way that small MCUs always did — a divide to form the I/Q ratio, then a 32-entry arctan lookup table. The routine at 0x07A5 lays it out end to end:
07A5 clrf 0x6D ; clear quotient
07A6 movf 0x6C,W
07A7 iorwf 0x6B,W
07A8 btfsc STATUS,Z
07A9 return ; guard: denominator ~0 (the I->0 / 90-deg case)
07AA movlw 0xC0 ... movlw 0x0C -> counter = 12
07AE movlw 0x10 ; <-- 12-iteration shift-subtract DIVIDE
07AF subwf 0x6D,F ; forms the ratio in 0x6B:0x6C
07B0 rlf 0x6C,F
07B1 rlf 0x6B,F
... decfsz counter, loop ...
07B8 rrf 0x6B (x4); bcf top 3 bits ; reduce ratio to a 5-bit index (0..31)
07BF call L07C3
07C3 movlw 0x7; movwf PCLATH; movf 0x6B,W; addwf PCL,F ; computed jump into table
07C7 <32-entry table> ; retlw the angle
That table at 0x07C7 reads 0, 5, 19, 28, 34, 39, 43, 46, 49, 52, 54, 56, 58, 60, 62, 64, 65, … 80 — monotonic, rising fast then flattening toward a ceiling. That's the arctan curve: angle vs. ratio, saturating as the ratio grows. So the chain is divide → 5-bit index → arctan LUT → angle, with an explicit divide-by-zero guard up front handling the vertical (I→0, 90°) case. CORDIC is ruled out — none of the tables contain the tell-tale atan(2⁻ⁱ) angle sequence (45, 26.6, 14, 7.1, 3.6…) a CORDIC needs.
A couple of things fell out of dumping the other tables that also correct an earlier guess of mine. The 100-entry table at 0x1F81 that I'd tentatively called a "VDI/scaling curve" is actually a binary→packed-BCD table (value = BCD of the index: 0→0x00 … 99→0x99) — pure display formatting. And 0x1F08 is a hex→ASCII table (0x30–0x39, 0x41–0x46). Both are for the LCD, not the signal path. The arctan LUT is specifically the 32-entry one at 0x07C7. The remaining odd tables (0x07EE, the descending 0x1F2D) are likely the complementary-octant angle range and/or audio/sensitivity curves — the single 32-entry table only covers one octant, so there's almost certainly quadrant/octant folding (swap I↔Q when the ratio exceeds 1, then reflect) wrapped around it, plus sign handling for the full target-ID range.
moodz
Attached Files
Comment
-
Hi moodz,
I still think that AI wouldn't do that much useful work. What AI wrote to you is true and it generates that, based on statistical data, close enough to the code given to as a model, and not because AI understands the code and processes. My opinion remains the same - if you want to have a correct source file, suitable for porting to another microcontroller, you should open the Datasheet of the PIC16C76 and start changing things line by line. For example, the initial "call 0x71" becomes "call init", and address 0x71 gets the label "init". In the initial initialization procedure "000071 clrf 0xA" becomes "000071 clrf PCLAH" , "000072 bcf 0x6,0x2" becomes "000072 bcf PORTB,2" etc. It's really slow work and requires a lot of time, but if you really need to understandably connect the source code with real processes - I don't see another way.. Moreover, whoever wrote the code, structured it very well and it is easy to read (at least those subprograms that I looked at had only one input and only one output).
Only when the full source code is restored will it be possible to make a correct guess as to which source module refers to which hardware block and process and what exactly it does.
This is of course just my opinion and there may be an easier way to accomplish the task, so if you can actually do it entirely with AL you will be my hero. Really, I mean it completely seriously.
Comment
-
you are right ... but it also comes down to understanding how the AI works and how we can use that tool ( the AI ) to do alot ( all ? ) of the heavy lifting for us.Originally posted by boilcoil View PostHi moodz,
I still think that AI wouldn't do that much useful work. What AI wrote to you is true and it generates that, based on statistical data, close enough to the code given to as a model, and not because AI understands the code and processes. My opinion remains the same - if you want to have a correct source file, suitable for porting to another microcontroller, you should open the Datasheet of the PIC16C76 and start changing things line by line. For example, the initial "call 0x71" becomes "call init", and address 0x71 gets the label "init". In the initial initialization procedure "000071 clrf 0xA" becomes "000071 clrf PCLAH" , "000072 bcf 0x6,0x2" becomes "000072 bcf PORTB,2" etc. It's really slow work and requires a lot of time, but if you really need to understandably connect the source code with real processes - I don't see another way.. Moreover, whoever wrote the code, structured it very well and it is easy to read (at least those subprograms that I looked at had only one input and only one output).
Only when the full source code is restored will it be possible to make a correct guess as to which source module refers to which hardware block and process and what exactly it does.
This is of course just my opinion and there may be an easier way to accomplish the task, so if you can actually do it entirely with AL you will be my hero. Really, I mean it completely seriously.
Broadly speaking there is a new technique in programming using AI that I will call "CONTEXT ENGINEERING" ... this has evolved from so called "vibe programming". In CE you give the AI the correct context so it can apply its vast programming "knowledge".
AI s have no desires or motives, they are just vast probability machines that will give you the ( most probable ) right solution .... if you ask the right question(s) ... this is the context.
In this case the AI looked at the hexdump and regenerated all the raw machine code.
It then was told that the code is a metal detector and shown the schematic.
So it matched code functionality to the pin signals ( LCD, ADC, digital io etc etc )
Then it was shown the user manual for the MXT ... so the AI had information regarding the use ( ie controls and displays ).
The AI I use already has a very good knowledge of how VLF detectors work ... so now I told it to extract the obvious stuff like VDI, ground balance.
Then I told the AI to simulate the code ... with stubs attached to present the correct inputs to the detector loop.
All of this is context ... its how you present it ( the order of questions and context 0 that will determine your success or not.
This may be all moot ... I do have a fully commented assembly file here that the AI generated. Its identified all the algorithms, math routines, ground tracking etc and It compiles, runs and all the maths, ground trac, VDI etc works.
The AI even ported it to the 876 chip ( flash version ) because its pin for pin the same as the MXT orgininal.
HOWEVER
If Garrett do own the rights to the MXT firmware then it is a copyrighted work under the law.
If you publish, distribute, or sell your modified firmware, you are likely creating an unauthorized "derivative work". This frequently violates both the original manufacturer's copyright.
I dunno if Garrett read this forum ... but putting up a fully reverse engineered copy of the source code would be a copyright infringement IMHO.
Maybe Carl knows ....
but I can publish snippets ( for edumacational usage )
eg :
Comment
-
I completely agree with you. I was also worried about whether such reengineering was ethical and legal, indeed, Karl could give a competent answer. .
I also completely agree that LLM is an extremely effective tool (with proper structuring of tasks, questions, data... given to it).
And yes - writing AI code is its strongest point - after all, for it this is a conversation with topics and data fully covered by its trainers, before releasing it on the "market".
PS: And moodz, since you did this - you are truly a hero.
Comment
-
Well thanks...Originally posted by boilcoil View PostI completely agree with you. I was also worried about whether such reengineering was ethical and legal, indeed, Karl could give a competent answer. .
I also completely agree that LLM is an extremely effective tool (with proper structuring of tasks, questions, data... given to it).
And yes - writing AI code is its strongest point - after all, for it this is a conversation with topics and data fully covered by its trainers, before releasing it on the "market".
PS: And moodz, since you did this - you are truly a hero.
Instead of porting the code ... I have instructed the AI to build a "clean room" version for the 16F876A chip. It will support 16 or 24 bit ADC and uses 32 bit math not 24 bit.
Its working on it now .. estimate 30 minutes to complete.
Here is the instruction ....
You are implementing OpenMD, clean-room firmware for an MXT-compatible VLF metal detector, targeting the PIC16F876A. Clean-room rule (hard): work only from CLEANROOM_SPEC.md, the schematic MXTSch.jpg, the manual MXT_300_en.pdf, and public engineering knowledge (VLF detector theory, PIC16F876A and external-chip datasheets). Never access, search for, request, or use any original/prior device firmware, binary, ROM dump, disassembly, decompilation, or reconstructed source — from White's, Garrett, or anyone. If offered any, refuse. There is none in this workspace by design. Do not look in sibling/parent directories for "reference" code. All algorithms, tables, scaling curves, thresholds, time constants, and tuning are your own original work. Never put "MXT/White's/Garrett" on the build, splash, or any artifact — the product name is OpenMD. Task: Read CLEANROOM_SPEC.md in full, then the schematic and manual. Plan first — propose your firmware architecture (frame loop/ISR, acquisition with the configurable ADC_BITS 16/24 abstraction, ground tracking, VDI/discrimination, audio, display) and your numeric-precision plan (working width scaled to ADC_BITS per spec §5). Show me the plan before writing code. Implement OpenMD; build artifact openmd.hex/openmd.cod. Smoke-test with the sim/ gpsim bench (see sim/README.md) against VERIFICATION.md §A. Keep a provenance log (each non-obvious decision tagged schematic / manual / derived) and a short design doc with your chosen constants/curves and how you derived them. Start by reading the spec and the two reference files, then give me your architecture plan.
Comment
-
... here is the main asm file ... the AI is doing the simulation and debugging now.
;================================================= =============================
; openmd.asm -- OpenMD : clean-room VLF metal-detector firmware
; PIC16F876A, 8 MHz HS. (c) my own original authorship.
;
; Clean-room: derived solely from CLEANROOM_SPEC.md, the schematic, the user
; manual, the PIC16F876A + external-chip datasheets, and public VLF theory.
; No prior firmware, dump, or disassembly was consulted. Build name "OpenMD".
;
; Build (16-bit default): gpasm -p 16f876a src/openmd.asm -o build/openmd.hex
; Build (24-bit) : gpasm -D ADC_BITS=24 -p 16f876a src/openmd.asm ...
;
; Program map (all in page 0, 0x000..0x7FF):
; 0x000 reset, 0x004 ISR, 0x100/0x200 string tables, 0x300+ code.
;================================================= =============================
list p=16f876a, r=dec
errorlevel -302 ; silence "register not in bank 0" (banksel used)
errorlevel -306 ; silence "crossing page boundary" info on tables we page-align
errorlevel -1302 ; silence "RAM bank undefined" info (banksel is explicit)
#include <p16f876a.inc>
#include "config.inc"
#include "hw.inc"
__CONFIG _HS_OSC & _WDT_OFF & _PWRTE_ON & _BOREN_ON & _LVP_OFF & _CP_OFF
#include "vars.inc"
#include "fixed.inc" ; macros only (no code emitted)
;================================================= =============================
; Reset + interrupt vectors
;================================================= =============================
org 0x000
goto start
org 0x004
;------------------------------------------------------------------------------
; ISR : conventional context save/restore; Timer1 -> set frame flag, reload.
;------------------------------------------------------------------------------
isr:
movwf w_temp
swapf STATUS, W
movwf status_temp
movf PCLATH, W
movwf pclath_temp
clrf PCLATH ; ISR body lives in page 0: force page bits 0
banksel PIR1
btfss PIR1, TMR1IF
goto isr_exit
bcf PIR1, TMR1IF
banksel TMR1H
movlw T1_PRE_H
movwf TMR1H
movlw T1_PRE_L
movwf TMR1L
bsf flags, F_FRAME ; flags is common RAM (no bank issue)
isr_exit:
movf pclath_temp, W
movwf PCLATH
swapf status_temp, W
movwf STATUS
swapf w_temp, F
swapf w_temp, W
retfie
;================================================= =============================
; Code (flows right after the ISR; all in page 0, < 0x0800)
;================================================= =============================
start:
; --- bank1 SFRs: TRIS, ADC justify, pull-ups, PWM period, interrupts ---
banksel TRISA
movlw TRISA_INIT
movwf TRISA
movlw TRISB_INIT
movwf TRISB
movlw TRISC_INIT
movwf TRISC
movlw ADCON1_INIT
movwf ADCON1
movlw 0x00 ; OPTION_REG: /RBPU=0 (PORTB pull-ups on)
movwf OPTION_REG
movlw TX_PR2
movwf PR2
movlw (1<<TMR1IE) ; PIE1: Timer1 interrupt
movwf PIE1
; --- bank0 SFRs ---
banksel PORTA
clrf PORTA
clrf PORTB
clrf PORTC
movlw ADCON0_BASE
movwf ADCON0
bcf STATUS, IRP ; indirect addressing targets banks 0/1
call clear_ram
; --- defaults (bank0) ---
banksel mode
clrf mode ; Coin & Jewelry
movlw d'2'
movwf trac ; Ground-On
call spi_init
call pwm_init
call timer1_init
; --- splash for ~1 s (244 frames) ---
call acquire_frame ; get a battery reading for the splash
call build_splash
call lcd_flush
banksel splash
movlw d'244'
movwf splash
bsf INTCON, PEIE
bsf INTCON, GIE
;------------------------------------------------------------------------------
; main loop : one pass per 4 ms frame tick
;------------------------------------------------------------------------------
main_loop:
btfss flags, F_FRAME
goto main_loop
bcf flags, F_FRAME
call acquire_frame
call check_battery
banksel splash
movf splash, F
btfsc STATUS, Z
goto ml_search ; splash done -> search
decf splash, F
goto ml_end ; still showing splash
ml_search:
call read_switches
call dsp_filter
call dsp_ground
call dsp_metrics
call dsp_vdi
call dsp_strength_depth
call dsp_discriminate
call audio_update
; repaint the LCD every 8 frames (~32 ms) to keep SPI traffic light
banksel framecnt
incf framecnt, F
btfsc STATUS, Z
incf framecnt+1, F
movf framecnt, W
andlw 0x07
btfss STATUS, Z
goto ml_end
call build_search
call lcd_flush
ml_end:
call spi_resync ; keep the shared bus on a 48-bit boundary
goto main_loop
;------------------------------------------------------------------------------
; clear_ram : zero GPR 0x20..0x7F and 0xA0..0xEF (IRP=0 indirect).
;------------------------------------------------------------------------------
clear_ram:
bcf STATUS, IRP
movlw 0x20
movwf FSR
cr1:
clrf INDF
incf FSR, F
movlw 0x80
subwf FSR, W
btfss STATUS, C
goto cr1
movlw 0xA0
movwf FSR
cr2:
clrf INDF
incf FSR, F
movlw 0xF0
subwf FSR, W
btfss STATUS, C
goto cr2
return
;------------------------------------------------------------------------------
; pwm_init : CCP1 PWM on RC2 ~13.9 kHz, 50% (Timer2 time-base).
;------------------------------------------------------------------------------
pwm_init:
banksel CCPR1L
movlw TX_DUTY_H
movwf CCPR1L
movlw b'00001100' ; CCP1CON: PWM mode, DC1B=00
movwf CCP1CON
movlw b'00000100' ; T2CON: TMR2ON=1, prescale 1:1
movwf T2CON
return
;------------------------------------------------------------------------------
; timer1_init : 4 ms frame tick, internal clock, 1:1, preloaded.
;------------------------------------------------------------------------------
timer1_init:
banksel T1CON
movlw b'00000001' ; TMR1ON=1, 1:1, internal
movwf T1CON
movlw T1_PRE_H
movwf TMR1H
movlw T1_PRE_L
movwf TMR1L
banksel PIR1
bcf PIR1, TMR1IF
return
;------------------------------------------------------------------------------
; check_battery : set/clear F_LOWBATT from vbatt vs LOWBATT_CNT (10-bit).
;------------------------------------------------------------------------------
check_battery:
banksel vbatt
movf vbatt+1, W
sublw high(LOWBATT_CNT) ; (hi_thr) - vbatt_hi ; C=1 if vbatt_hi <= hi_thr
btfss STATUS, C
goto cb_ok ; vbatt_hi > threshold hi -> ok
movf vbatt+1, W
btfsc STATUS, Z
goto cb_low ; vbatt_hi == 0 (< 256) -> low
movlw low(LOWBATT_CNT)
subwf vbatt, W ; vbatt_lo - thr_lo ; C=1 if >= -> ok
btfsc STATUS, C
goto cb_ok
cb_low:
bsf flags, F_LOWBATT
return
cb_ok:
bcf flags, F_LOWBATT
return
;------------------------------------------------------------------------------
; read_switches : 2-phase diode-matrix scan -> mode (phase1) + trac (phase2).
; Each 3-position toggle grounds one of RB2/RB3/RB4 in its scan phase.
; No return active -> keep previous value (debounce-by-hold).
;------------------------------------------------------------------------------
read_switches:
banksel PORTB
; ---- phase 1: MODE (scan = RB1 low, RB5 high) ----
bsf PORTB, SW_SCAN2
bcf PORTB, SW_SCAN
nop
nop
nop
comf PORTB, W ; active-low returns -> 1 = pressed
andlw SW_RET_MASK
movwf t0
btfsc t0, SW_RET0
clrf mode ; RB2 -> Coin & Jewelry
btfsc t0, SW_RET1
goto rs_mode1
btfsc t0, SW_RET2
goto rs_mode2
goto rs_trac
rs_mode1:
movlw d'1'
movwf mode
goto rs_trac
rs_mode2:
movlw d'2'
movwf mode
rs_trac:
; ---- phase 2: TRAC (scan = RB5 low, RB1 high) ----
banksel PORTB
bsf PORTB, SW_SCAN
bcf PORTB, SW_SCAN2
nop
nop
nop
comf PORTB, W
andlw SW_RET_MASK
movwf t0
btfsc t0, SW_RET0
clrf trac ; RB2 -> Salt-Beach
btfsc t0, SW_RET1
goto rs_trac1
btfsc t0, SW_RET2
goto rs_trac2
goto rs_done
rs_trac1:
movlw d'1'
movwf trac ; Hold-Lock
goto rs_done
rs_trac2:
movlw d'2'
movwf trac ; Ground-On
rs_done:
banksel PORTB
bsf PORTB, SW_SCAN ; idle both scans high
bsf PORTB, SW_SCAN2
return
;================================================= =============================
; Subsystem implementations
;================================================= =============================
#include "cordic.inc"
#include "spi.inc"
#include "acq.inc"
#include "dsp.inc"
#include "audio.inc"
#include "lcd.inc"
;================================================= =============================
; String tables (program page 1, org 0x800 -- see tables.inc)
;================================================= =============================
#include "tables.inc"
end
Comment
-
it lives ! ( on the simulator )
this means the code compiles and runs ( on a 3rd party simulator not mine ).
=== adc_sample=40000 : printable runs (>=6 chars) in SPI stream ===
.......OpenMD 1.0 BATT 133 .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. .................................................. ...........VDI-95 ........ DP255 BT133 GND .................................................. ............................................VDI-95 ........ DP255 BT133 GND .................................................. ............................................VDI-95 ........ DP255 BT133 GND .................................................. ............................................VDI-95 ........ DP255 BT133 GND .................................................. ............................................VDI-95 ........ DP255 BT133 GND .................................................. ............................................VDI-95 ........ DP255 BT133 GND .................................................. ............................
=== adc_sample=8000 : printable runs (>=6 chars) in SPI stream ===
.......OpenMD 1.0 BATT 133 ...........................................
Comment
-
Done .. complete clean room build in pin for pin chip ( 16F876A ) for the MXT schematic. They still make the 16F876 in a DIP chip so its good for all you oldtimers.
Math is 32bit and CORDIC. 100% assembly code.
24 bit ADC support ( would need to be an SDI chip .. and some small additional code to support ) but easily fits in the timing cycle. ( 4 ms ).
The AI took maybe an hour to do this... I estimate a couple of months for an experienced engineer ( from scratch + debugging ).
The chip utilisation shows one super power of assembly code ....
Comment


Comment