Das Problem
Microchip hatte 2020 entschieden, den (alten) MPASM nicht mehr zu unterstützen bzw. auch nicht weiterzuentwickeln.
The MPASM™ assembler toolchain is a 32-bit application and is no longer installed with MPLAB® X IDE starting with v5.40. This means no toolchain will be available by default when MPLAB X IDE is installed for the first time.
Aktuell habe ich die Version „MPLAB X IDE v6.25“ installiert. Diese IDE bringt einen XC8 Compiler mit, der mit dem im XC8 enthaltenden Assembler (pic-as) auch Assembler-Code übersetzt. Diese bzw. die zukünftigen Versionen mögen neuere Controller unterstützen, jedoch haben viele Hobby-Elektroniker zumeist kleinere Programme für noch ältere Controller geschrieben, die sich nun nicht mehr kompilieren lassen. Man hat nun zwei Möglichkeiten:
- Installation von MPLABx 5.35, wenn man dort den (alten) MPASM nutzen möchte.
- Den (alten) Source-Code anpassen, so dass dieser vom XC8 in MPLAB X IDE v6.25 übersetzt wird.
Für beide Wege gibt es in Foren Lösungsansätze. Auch sind Tools vorgeschlagen, die den alten in neuen Assemblercode portieren, mit unterschiedlichen Erfolg. Geht man jedoch den ersten Weg und nutzt weiterhin die alten Tools, verpasst man vielleicht die Chance sich in neuere Tools einzuarbeiten und die Option neuere Controllern einsetzen zu können. Ich halte den Aufwand für die Portierung des alten MPASM Codes insbesondere für Hobby-Elektroniker für vertretbar und habe mich auch entschieden, den alten Code für den neuen Assembler XC8 (pic-as) zu portieren. Sämtlichen Code, den ich auf diesen Seiten veröffentliche, ist auch mit der Entwicklungsumgebung „MPLAB X IDE v6.25“ übersetzbar.
Portierung des alten MPASM Assemblercodes für XC8 (pic-as)
Hier ein paar wesentliche Beispiele von notwendigen Anpassungen des alten Assemblercodes. Dazu das folgende Listing, welches angepasst werden sollte. Das abgebildete Programm erfüllt keinen (technischen) Zweck und dient nur der Veranschaulichung, was man im alten Listing verändern muss.
;****************************************************************************** LIST P=PIC16F1826 ;directive to define processor #include <p16f1826.inc> ;processor specific variable definitions ;****************************************************************************** ; configuration bits __CONFIG _CONFIG1, _FOSC_INTOSC & _CLKOUTEN_OFF & _WDTE_OFF & _MCLRE_ON ;****************************************************************************** #define THRESHOLD_VALUE D'292' Timer0_500_ms equ (.255-.243) Time_02Minute equ .30 AD_CONVERSION_DONE equ 0 ;****************************************************************************** RAM_Bank0 UDATA 0x20 ; 80 Bytes RAM in Bank0 GN_GSR0_L RES 1 GN_GSR0_H RES 1 ;****************************************************************************** RAM_Bank1 UDATA 0xA0 ; 80 Bytes RAM in Bank1 CONTROL_xx RES 1 ;****************************************************************************** RAM_Bank2 UDATA 0x120 ; 80 Bytes RAM in Bank2 CONTROL_xxx RES 1 ;****************************************************************************** SharedBank UDATA_SHR ; 16 Bytes RAM that are shared across all RAM banks CONTROL_FLAGS RES 1 FOTOWID_H RES 1 FOTOWID_L RES 1 TMR0_500MS RES 1 TMR1_4SEC RES 1 ;****************************************************************************** ;EEPROM data ; Data to be programmed into the Data EEPROM is defined here DATA_EEPROM CODE 0xf00000 DE "Test Data", 0, 1, 2, 3, 4, 5 ;****************************************************************************** ;Reset vector ; This code will start executing when a reset occurs. RESET_VECTOR CODE 0x0000 goto Start nop ;****************************************************************************** ; interrupt vector ; This code will start executing when a interrupt occurs INT_VECTOR CODE 0x0004 CheckIntTMR0 btfss INTCON, TMR0IF bra CheckIntTMR1 ; jump to the next routine ; interrupt for timer0 occurred bcf INTCON, TMR0IF incf TMR0_500MS, F movlw Timer0_500_ms banksel TMR0 ; bank0 movwf TMR0 bra EnableINTandReturn CheckIntTMR1 banksel PIR1 ; bank0 btfss PIR1, TMR1IF bra EnableINTandReturn ; interrupt for Timer1 occurred bcf PIR1, TMR1IF ; .... EnableINTandReturn bsf INTCON, GIE retfie ;****************************************************************************** ;Start of main program Start ; Configure oscillator movlw b'00111010' movwf OSCCON banksel OPTION_REG ; bank1 movlw B'00000111' movwf OPTION_REG ; .... SwitchValve clrf TMR1_4SEC WaitLongTime btfsc CONTROL_FLAGS, AD_CONVERSION_DONE call SubRoutine movf TMR1_4SEC, W sublw Time_02Minute btfsc STATUS, C bra WaitLongTime bra SwitchValve ;****************************************************************************** SubRoutine bcf CONTROL_FLAGS, AD_CONVERSION_DONE movlw low(THRESHOLD_VALUE) subwf FOTOWID_L,W movlw high(THRESHOLD_VALUE) subwfb FOTOWID_H, W return
- LIST P=PIC16F1826
Der Controller wird in den Projekteinstellungen der MPLAB X IDE angegeben, daher kann diese Directive hier entfallen. - #include <p16f1826.inc>
In der MPLAB X IDE gibt es keine solche Datei. Die Controller-spezifischen Definitionen befinden sich (unter Linux) im Verzeichnis /opt/Microchip/v6.25/packs/Microchip/PIC12-16F1xxx_DFP/1.7.242/xc8/pic/include/proc/.. In diesem Verzeichnis nach der richtigen Include-Datei (pic16f1826.inc) suchen und entsprechend im Listing ändern. Diese include-Anweisung nach der Include-Anweisung für xc.inc einfügen. Siehe dazu auch den nächsten Punkt! - __CONFIG _CONFIG1, _FOSC_INTOSC & _CLKOUTEN_OFF & _WDTE_OFF & _MCLRE_ON
In der MPLAB X IDE werden die Configurations-Bits mit Hilfe eines Tools generiert. Dazu im Menü unter Windows->Target Memory Views -> Configuration Bits dieses Tool aufrufen, die Configuration definieren und danach die Schaltfläche „Generate Source Code to Output“ drücken. Den so genierten Code kopieren und in das Listing einfügen (mit der include-Anweisung xc.inc). - (Dezimale) Zahlenangaben sind falsch wie .30 oder D'292'
Standardmäßig interpretiert MPASM numerische Zahlen als hexadezimale Zahlen. XC8 dagegen als dezimale Zahlen. Daher diese Definition ohne Zusatz definieren also 30 oder 292 - (Binäre) Zahlenangaben sind falsch wie b'00111010'
Die Notation lautet jetzt 0b00111010 - RAM_Bank0 UDATA 0x20 erzeugt einen Syntax-Fehler
Im MPLAB X IDE kann man Code-Segmente definieren. Anstelle obiger Anweisung ist hier psect udata_bank0, class=BANK0, space=SPACE_DATA, noexec, size=80 zu schreiben. XC8 definiert einige Code-Segmente, die am Ende der Controller-spezifischen Datei pic16f1826.inc auch definiert worden sind. Man kann in dieser Datei nachschauen und sich die richtige Anweisung von dort herauskopieren. Zum Beispiel finden sich in der Include-Datei pic16f1826.inc für den PIC16F1826 folgende Zeilen:
psect udata_shr,class=COMMON,space=SPACE_DATA,noexec
psect udata,class=RAM,space=SPACE_DATA,noexec
psect udata_bank0,class=BANK0,space=SPACE_DATA,noexec
psect udata_bank1,class=BANK1,space=SPACE_DATA,noexec
psect udata_bank2,class=BANK2,space=SPACE_DATA,noexec
psect code,class=CODE,space=SPACE_CODE,delta=2
psect data,class=STRCODE,space=SPACE_CODE,delta=2,noexec
psect edata,class=EEDATA,space=SPACE_EEPROM,delta=2,noexec
Zusätzlich gebe ich mit size=80 die maximale Größe (wenn sinnvoll) für dieses Segment an. Der Compiler warnt mich, sollte ich diese Größe überschreiten. - Alle Labels wie Start oder SubRoutine müssen jetzt am Ende einen Doppelpunkt haben, also Start: oder SubRoutine:
- ORG-Anweisungen beziehen ich beim XC8 immer auf den Anfang eines Code-Segments und nicht auf die Adresse 000h. Daher sind, wenn diese notwendig sein sollten, entsprechend zu korrigieren. Oft können diese durch die Definition der Segmente entfallen. Hat ein Code-Segment einen Namen bekommen, so muss dem Linker mitgeteilt werden, wo dieses Segement platziert werden soll. Zum Beispiel werden zwei Segment folgendermaßen definiert:
PSECT resetVec, class=CODE, delta=2, size=4h
PSECT interruptVector, class=CODE, space=SPACE_CODE, delta=2
Das Segment mit dem Namen resetVec hat eine Größe von 4 (Programmspeicherplätze), bevor das nächste Segment interruptVector auf der Adresse 04h beginnen muss (siehe Spezifikation PIC16F1824). Dem Linker werden mit folgender Option diese Adressen mitgeteilt:
-presetVec=0h
-pinterruptVector=04h
Dies erfolgt in der Entwicklungsumgebung unter Project-Properties -> Conf:[default] -> pic-as Global Option -> pic-as Linker -> Custom linker options
- Zeilen in der Form GN_GSR0_L RES 1 haben zwei Fehler
Zum einen fehlt der Doppelpunkt am Ende des Labels und zum zweiten muss RES mit DS ersetzt werden. Die Zeile lautet also richtig: GN_GSR0_L: DS 1 - Zeichenketten "Test Data" lassen sich im XC8 nicht definieren. Die Zeichen müssen einzeln definiert werden 'T','e','s','t',' ','D','a','t','a'.
- Entsprechend der Definitionen in pic16f1826.inc ist in der Zeile btfss INTCON, TMR0IF mit TMR0IF nicht mehr als das 2. Bit (vom Register INTCON) definiert.
In pic16f1826.inc sind folgende (relevante) Definition vorhanden:
INTCON_TMR0IF_POSN equ 0002h
INTCON_TMR0IF_POSITION equ 0002h
#define TMR0IF BANKMASK(INTCON), 2
Entsprechend muss die Zeile nun lauten: Entweder btfss TMR0IF oder btfss INTCON, INTCON_TMR0IF_POSITION
Hier versteckt sich eine gewisser Aufwand, da an vielen Stellen die Definitionen in der pic16f1826.inc nicht mehr zum alten MPASM passen. Mit Suchen und Ersetzen ist der Aufwand aber wieder vertretbar. - Eigentlich behauptet Microchip im MPASMTM to MPLAB® XC8 PIC® Assembler Migration Guide dass Assembler-Source-Dateien die Endung .s oder .S haben sollten. Es scheint aber auch mit der Erweiterung .asm zu funktionieren. Der Dateiname bzw. die Erweiterung muss also nicht umbenannt werden.
Wenn man all diese Stellen im alten Source-Code ersetzt, dann ergibt sich für das oben genannte Listing folgender Code (der vom XC8 (pic-as) auch übersetzt wird):
;******************************************************************************
; configuration bits
; CONFIG1
CONFIG FOSC = INTOSC ; Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
CONFIG WDTE = OFF ; Watchdog Timer Enable (WDT disabled)
CONFIG PWRTE = OFF ; Power-up Timer Enable (PWRT disabled)
CONFIG MCLRE = ON ; MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
CONFIG CP = OFF ; Flash Program Memory Code Protection (Program memory code protection is disabled)
CONFIG CPD = OFF ; Data Memory Code Protection (Data memory code protection is disabled)
CONFIG BOREN = OFF ; Brown-out Reset Enable (Brown-out Reset disabled)
CONFIG CLKOUTEN = OFF ; Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
CONFIG IESO = OFF ; Internal/External Switchover (Internal/External Switchover mode is disabled)
CONFIG FCMEN = OFF ; Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)
; CONFIG2
CONFIG WRT = OFF ; Flash Memory Self-Write Protection (Write protection off)
CONFIG PLLEN = ON ; PLL Enable (4x PLL enabled)
CONFIG STVREN = ON ; Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
CONFIG BORV = LO ; Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
CONFIG DEBUG = OFF ; In-Circuit Debugger Mode (In-Circuit Debugger disabled, ICSPCLK and ICSPDAT are general purpose I/O pins)
CONFIG LVP = OFF ; Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)
// config statements should precede project file includes.
#include <xc.inc>
#include <p16f1826.inc>
;******************************************************************************
#define THRESHOLD_VALUE 292
Timer0_500_ms equ 255-243
Time_02Minute equ 30
AD_CONVERSION_DONE equ 0
;******************************************************************************
psect udata_bank0, class=BANK0, space=SPACE_DATA, noexec, size=80
GN_GSR0_L: DS 1
GN_GSR0_H: DS 1
;******************************************************************************
psect udata_bank1, class=BANK1, space=SPACE_DATA, noexec, size=80
CONTROL_xx: DS 1
;******************************************************************************
psect udata_bank2, class=BANK2, space=SPACE_DATA, noexec, size=80
CONTROL_xxx: DS 1
;******************************************************************************
psect udata_shr, class=COMMON, space=SPACE_DATA, noexec, size=16
CONTROL_FLAGS: DS 1
FOTOWID_H: DS 1
FOTOWID_L: DS 1
TMR0_500MS: DS 1
TMR1_4SEC: DS 1
;******************************************************************************
;EEPROM data
; Data to be programmed into the Data EEPROM is defined here
PSECT eEPROM, class=EEDATA, delta=2, space=3, noexec, size=256
DB 'T','e','s','t',' ','D','a','t','a', 0, 1, 2, 3, 4, 5
;******************************************************************************
;Reset vector
; This code will start executing when a reset occurs.
PSECT resetVec, class=CODE, delta=2, size=4h
goto Start
nop
;******************************************************************************
; interrupt vector
; This code will start executing when a interrupt occurs
PSECT interruptVector, class=CODE, space=SPACE_CODE, delta=2
CheckIntTMR0:
btfss TMR0IF
bra CheckIntTMR1 ; jump to the next routine
; interrupt for timer0 occurred
bcf TMR0IF
incf TMR0_500MS, F
movlw Timer0_500_ms
banksel TMR0 ; bank0
movwf TMR0
bra EnableINTandReturn
CheckIntTMR1:
banksel PIR1 ; bank0
btfss TMR1IF
bra EnableINTandReturn
; interrupt for Timer1 occurred
bcf TMR1IF
; ....
EnableINTandReturn:
bsf GIE
retfie
;******************************************************************************
;Start of main program
Start:
; Configure oscillator
movlw 0b00111010
movwf OSCCON
banksel OPTION_REG ; bank1
movlw 0b00000111
movwf OPTION_REG
; ....
SwitchValve:
clrf TMR1_4SEC
WaitLongTime:
btfsc CONTROL_FLAGS, AD_CONVERSION_DONE
call SubRoutine
movf TMR1_4SEC, W
sublw Time_02Minute
btfsc CARRY
bra WaitLongTime
bra SwitchValve
;******************************************************************************
SubRoutine:
bcf CONTROL_FLAGS, AD_CONVERSION_DONE
movlw low(THRESHOLD_VALUE)
subwf FOTOWID_L,W
movlw high(THRESHOLD_VALUE)
subwfb FOTOWID_H, W
return
