| Zeile 248: | Zeile 248: | ||
</pre> | </pre> | ||
Die Links-Rechts Routinen sind hier sparsam gehalten. Normalerweise wird man hier wohl Schritte zählen. | Die Links-Rechts Routinen sind hier sparsam gehalten. Normalerweise wird man hier wohl Schritte zählen. | ||
| + | ==Dividieren 32Bit (LONG)== | ||
| + | Das folgende Beispiel ist eigentlich eine Bascom-Source mit inline Assembler. Es zeigt, wie Bascom zwei signed LONG dividiert, und durch den Assembler kann man sich das auch im Simulator bitweise anschauen, wie das läuft. | ||
| + | |||
| + | Anmerkung: Nach der Division befindet sich der Divisionsrest in r16:r19. Wenn den jemand braucht, könnte er ihn nach einer long-Division dort abholen. | ||
| + | |||
| + | *Aufruf | ||
| + | <pre> | ||
| + | $regfile = "m32def.dat" ' specify the used micro | ||
| + | $crystal = 8000000 | ||
| + | |||
| + | Dim Vala As Long | ||
| + | Dim Valb As Long | ||
| + | Dim Valc As Long | ||
| + | |||
| + | Vala = 14 | ||
| + | Valb = 1 | ||
| + | ' das Bascom Äquivalent wäre: | ||
| + | ' Valc = Vala / Valb | ||
| + | $asm | ||
| + | Loadadr Vala , X ' Laden r16:r19 mit Vala | ||
| + | LD r16,X+ | ||
| + | LD r17,X+ | ||
| + | LD r18,X+ | ||
| + | LD r19,X+ | ||
| + | Loadadr Valb , X ' Laden r20:r23 mit Valb | ||
| + | LD r20,X+ | ||
| + | LD r21,X+ | ||
| + | LD r22,X+ | ||
| + | LD r23,X+ | ||
| + | $end Asm | ||
| + | |||
| + | Gosub L_0x0112 ' Dividieren r16:r19 / r20:r23 --> r20:r23 | ||
| + | |||
| + | $asm | ||
| + | Loadadr Valc , X ' speichern Ergebnis in Valc | ||
| + | ST X+,r20 | ||
| + | ST X+,r21 | ||
| + | ST X+,r22 | ||
| + | ST X+,r23 | ||
| + | $end Asm | ||
| + | |||
| + | End | ||
| + | </pre> | ||
| + | *Funktions-Code | ||
| + | <pre> | ||
| + | '----------------------------------------- | ||
| + | ' | ||
| + | '----------------------------------------- | ||
| + | L_0x0112: | ||
| + | $asm | ||
| + | RCALL L_0x0174 'Operanden Vorzeichen prüfung und ggf. umdrehen auf positiv | ||
| + | 'register r0.0 zeigt, ob das Ergebnis negativ ist | ||
| + | RCALL L_0x0120 'Prüfen Divisor auf NULL. Wenn nicht, dann dividieren | ||
| + | BLD r0,1 't-bit -> r0.1 (Error) | ||
| + | RJMP L_0x010C 'finish | ||
| + | |||
| + | L_0x010C: | ||
| + | SBRC r0,0 'Ergebnis negativ ? | ||
| + | RCALL L_0x011A 'ja, Ergebnis negativ machen | ||
| + | RET 'so oder so, Funktionsende | ||
| + | |||
| + | L_0x011A: | ||
| + | RCALL L_0x018A 'Ergebnis auf negativ (r20:r23) | ||
| + | RCALL L_0x019C 'Rest auf negativ (r16:r19) | ||
| + | RET | ||
| + | |||
| + | |||
| + | '----------------------------------------- | ||
| + | ' Prüfen divisor auf NULL | ||
| + | '----------------------------------------- | ||
| + | L_0x0120: | ||
| + | MOV r24,r20 'Kopieren r20:r23 -> r24:r27 | ||
| + | MOV r25,r21 | ||
| + | MOV r26,r22 | ||
| + | MOV r27,r23 | ||
| + | CLT ' clear T-bit | ||
| + | !OR r20,r21 ' prüfen auf NULL | ||
| + | !OR r20,r22 | ||
| + | !OR r20,r23 | ||
| + | BRNE L_0x0136 ' <> NULL --> dividieren | ||
| + | !SET ' ==NULL set t-Bit | ||
| + | RET | ||
| + | '----------------------------------------- | ||
| + | ' dividieren r16:r19 / r20:r23 --> r20:r23 | ||
| + | '----------------------------------------- | ||
| + | L_0x0136: | ||
| + | MOV r20,r16 'r16:r19->r20:r23 | ||
| + | MOV r21,r17 | ||
| + | MOV r22,r18 | ||
| + | MOV r23,r19 | ||
| + | CLR r16 'clear r16:r19 | ||
| + | CLR r17 | ||
| + | CLR r18 | ||
| + | CLR r19 | ||
| + | |||
| + | LDI ZH,32 'Zähler (32 Bit) | ||
| + | '----------------------------------------- | ||
| + | ' divisions-Schleife | ||
| + | '----------------------------------------- | ||
| + | L_0x0148: | ||
| + | LSL r20 'shift left r20:r23 | ||
| + | ROL r21 | ||
| + | ROL r22 | ||
| + | ROL r23 '(das oberste Bit wandert nach r16:r19) | ||
| + | |||
| + | ROL r16 'shift left r16:r19 | ||
| + | ROL r17 | ||
| + | ROL r18 | ||
| + | ROL r19 | ||
| + | |||
| + | !SUB r16,r24 'subtrahieren | ||
| + | SBC r17,r25 | ||
| + | SBC r18,r26 | ||
| + | SBC r19,r27 | ||
| + | |||
| + | ORI r20,1 'Ergebnis auf 1 | ||
| + | BRCC L_0x016E 'kein Überlauf, bleibt so | ||
| + | |||
| + | ADD r16,r24 'Überlauf, wieder addieren | ||
| + | ADC r17,r25 | ||
| + | ADC r18,r26 | ||
| + | ADC r19,r27 | ||
| + | ANDI r20,254 'und den Ergebnis 1-er wieder löschen | ||
| + | L_0x016E: | ||
| + | DEC ZH 'Zähler-- | ||
| + | BRNE L_0x0148 '23-Bit Schleife | ||
| + | RET 'fertig | ||
| + | |||
| + | '----------------------------------------- | ||
| + | ' Prüfen der Operanden | ||
| + | '----------------------------------------- | ||
| + | L_0x0174: | ||
| + | CLR r0 'Kontroll-Register säubern | ||
| + | CLT 't-bit löschen | ||
| + | SBRS r23,7 ' r20:r23 Negativ ? | ||
| + | RJMP L_0x0180 'nö, ist positiv | ||
| + | RCALL L_0x018A 'ja, auf positiv umdrehen | ||
| + | !SET 'und t-bit setzen | ||
| + | L_0x0180: | ||
| + | BLD r0,0 't-bit ins Kontroll-register r0.0 | ||
| + | RCALL L_0x0196 'Prüfen r16:r19 | ||
| + | BLD r1,0 't-bit -> r1.0 | ||
| + | EOR r0,r1 'Exklusiv Or der beiden +- Prüfungen | ||
| + | 'Vorzeichen verschieden ---> Ergebnis muss negativ sein | ||
| + | RET 'e bien | ||
| + | '----------------------------------------- | ||
| + | ' r20:r23 auf positiv | ||
| + | '----------------------------------------- | ||
| + | L_0x018A: | ||
| + | RCALL L_0x01B0 'invertieren | ||
| + | SUBI r20,0xFF ' +1 | ||
| + | SBCI r21,0xFF | ||
| + | SBCI r22,0xFF | ||
| + | SBCI r23,0xFF | ||
| + | RET | ||
| + | '----------------------------------------- | ||
| + | ' Prüfen r16:r19 | ||
| + | '----------------------------------------- | ||
| + | L_0x0196: | ||
| + | CLT 'clear t-bit | ||
| + | SBRS r19,7 'sign bit set ? | ||
| + | RET 'no | ||
| + | '----------------------------------------- | ||
| + | ' r16:r19 auf positiv | ||
| + | '----------------------------------------- | ||
| + | L_0x019C: | ||
| + | COM r16 'invertieren | ||
| + | COM r17 | ||
| + | COM r18 | ||
| + | COM r19 | ||
| + | SUBI r16,0xFF '+1 | ||
| + | SBCI r17,0xFF | ||
| + | SBCI r18,0xFF | ||
| + | SBCI r19,0xFF | ||
| + | !SET 't-bit setzen | ||
| + | RET | ||
| + | '----------------------------------------- | ||
| + | ' invertieren r20:r23 | ||
| + | '----------------------------------------- | ||
| + | L_0x01B0: | ||
| + | COM r20 | ||
| + | COM r21 | ||
| + | COM r22 | ||
| + | COM r23 | ||
| + | RET | ||
| + | |||
| + | $end Asm | ||
| + | |||
| + | Return | ||
| + | </pre> | ||
==Autor== | ==Autor== | ||
Version vom 9. Juni 2006, 10:12 Uhr
Für den allgemein Interessierten und für Power-User, die ihr Bascom-Programm mit etwas Assembler-Code aufpeppen möchten, stelle ich recht zwanglos einige Assembler-Codeschnipsel zusammen mit den zugehörigen Bascom-Statements zur Verfügung.
Die meisten Beispiele könnte man mit Inline-Assemler direkt ersetzen, wenn man mal probieren wollte, man muß natürlich auf Daten- und Labeladressen aufpassen
Die meisten Bascom-Funktionen ergeben im Code dann folgende Teile:
- Der Aufruf
- die Vorbereitung, also das Laden von Registern mit den konkreten Argumenten,
- einen Call auf den eigentliche Funktions-code
- das Abliefern des Ergebnisses
- Die Funktion selbst
Inhaltsverzeichnis
ADC
Config ADC
CONFIG Adc = Single , Prescaler = Auto
LDI r24,0x06 OUT ADCSR,r24
Start ADC
START ADC
SBI ADCSR,ADEN
Getadc()
- Aufruf
DIM X AS WORD laut "prog.RPT" an der Adresse 0x0063 X = GETADC(0)
ergibt folgenden Code:
LDI r24,0x00 ADC-Kanal-Nummer nach Register 24 OUT ADMUX,r24 in den ADC-Multiplexer CALL L_0x00F6 Aufruf der getadc-funktion LDI XL,0x63 laden der Ergebnisadresse (DIM X AS WORD) LDI XH,0x00 ST X+,r24 Speichern ergebnis (R24:r25) in "X" ST X,r25
- Funktion
L_0x00F6: SBI ADCSR,ADSC Starten der 1. Konversion L_0x00F8: SBIC ADCSR,ADSC Fertig ? RJMP L_0x00F8 nein, Loop1 SBI ADCSR,ADSC Starten der 2. Konversion L_0x00FE: SBIC ADCSR,ADSC Fertig ? RJMP L_0x00FE nein, Loop2 IN r24,ADCL Ergebnis auslesen r24:r25 IN r25,ADCH RET fertig
BITWAIT
Bitvariable
- Aufruf
Dim A As Bit BITWAIT A , Set 'wait until bit a is set
- Code
L_0x007A: LDS r24,0x0060 SBRS r24,7 RJMP L_0x007A
IO-Register
- Aufruf
BITWAIT Portb.7 , Reset 'wait until bit 7 of Port B is 0.
- Code
L_0x008C: SBIC PORTB,PB7 RJMP L_0x008C
PULSEIN
DIM Result AS WORD PULSEIN Result , Pind , 2 , 1
- Aufruf
LDI ZL,0x30 // Adresse von SFR PIND LDI r24,0x02 // PinNr 2 LDI r16,0xFF // State 1 CALL PULSEIN LDI XL,0x60 laden der Ergebnisadresse LDI XH,0x00 ST X+,r24 // store result ST X,r25
Hier kommen mehrere Funktionen zum Einsatz, die auch ggf. für andere Zwecke aufgerufen werden.
- Idle Loop
L_0x009C: SBIW ZL,0x0001 BRNE L_0x009C RET
- Set_ErrBit
SET BLD r6,2 RET
- Clear_ErrBit
CLT BLD r6,2 RET
- MakeMask
LDI r25,0x01 AND r24,r24 BREQ L_0x00BC CLC L_0x00B6: ROL r25 DEC r24 BRNE L_0x00B6 L_0x00BC: MOV r24,r25 COM r25 RET
- Pulsein, die eigentliche Funktion
PULSEIN: CALL Clear_ErrBit CLR ZH CLR XL // clear Timout Lo CLR XH // clear Timout Hi CALL MakeMask // R24 Mask, R25 neg Mask AND r16,r24 LDD r0,Z + 1 // DDRD AND r0,r25 // Make Input STD Z + 1,r0 // DDRD L_0x00D8: // ------------- Loop LDD r0,Z + 0 // PIND AND r0,r24 // PIND & Mask EOR r0,r16 // (PIND & Mask) ^ State BRNE L_0x00E6 // Ok PIN != State ADIW XL,0x0001 // Timeout counter++ BREQ L_0x0118 // elapsed->ERR-Exit RJMP L_0x00D8 // cont'd Loop L_0x00E6: CLR XL // clear Timout Lo CLR XH // clear Timout Hi L_0x00EA: // ------------- Loop LDD r0,Z + 0 // PIND AND r0,r24 // PIND & Mask EOR r0,r16 // (PIND & Mask) ^ State BREQ L_0x00F8 // Ok PIN == State ADIW XL,0x0001 // Timeout counter++ BREQ L_0x0118 // elapsed->ERR-Exit RJMP L_0x00EA // cont'd Loop L_0x00F8: CLR XL // clear Timout Lo CLR XH // clear Timout Hi L_0x00FC: PUSH ZL // Save PUSH ZH LDI ZL,0x20 // calc from $XTAL LDI ZH,0x00 CALL L_0x009C // 10 µS Idle POP ZH // Restore POP ZL LDD r0,Z + 0 // PIND AND r0,r24 // PIND & Mask EOR r0,r16 // (PIND & Mask) ^ State BRNE L_0x011C // OK, Pulsein done ********* ADIW XL,0x0001 // PulseCounter++ BRNE L_0x00FC // cont'd Loop L_0x0118: CALL Set_ErrBit L_0x011C: MOV r24,XL // result --> R24:r25 MOV r25,XH RET // that's it
ENCODER
- Aufruf
DIM Result AS BYTE Result = ENCODER(pind.1 , Pind.3 , Leftlabel , Rightlabel , 0) ... ... Leftlabel: Return Rightlabel: Return
- Code
LDI XL,0x60 ; result LDI XH,0x00 CLT ; clear t-bit (no wait) L_0x008C: LD r20,X ; Lesen Result (vorheriger Pin-Wert) IN r16,PIND CLR r24 ; clear SBRC r16,1 ; pind.1 ORI r24,0x01 ; A = 1 SBRC r16,3 ; pind.3 ORI r24,0x02 ; B = 2 CP r24,r20 ; <> previous BRNE L_0x00A2 ; difference BRTS L_0x008C ; t-bit ? ( wait) RJMP L_0x00C2 ; no diff, no wait -> xit L_0x00A2: ST X,r24 ; store new as old SWAP r20 ; OLD ADD r24,r20 ; OLD + NEW CPI r24,0x02 BREQ Leftlabel ; left CPI r24,0x10 BREQ Leftlabel ; left CPI r24,0x23 BREQ Leftlabel ; left CPI r24,0x31 BREQ L_0x00BE ; left CALL Rightlabel ; call Right RJMP L_0x00C2 ; xit L_0x00BE: CALL Leftlabel ; call Left L_0x00C2:
Anmerkung: hier ist kein Return, da der Code direkt eingefügt wird
Leftlabel: RET Rightlabel: RET
Die Links-Rechts Routinen sind hier sparsam gehalten. Normalerweise wird man hier wohl Schritte zählen.
Dividieren 32Bit (LONG)
Das folgende Beispiel ist eigentlich eine Bascom-Source mit inline Assembler. Es zeigt, wie Bascom zwei signed LONG dividiert, und durch den Assembler kann man sich das auch im Simulator bitweise anschauen, wie das läuft.
Anmerkung: Nach der Division befindet sich der Divisionsrest in r16:r19. Wenn den jemand braucht, könnte er ihn nach einer long-Division dort abholen.
- Aufruf
$regfile = "m32def.dat" ' specify the used micro $crystal = 8000000 Dim Vala As Long Dim Valb As Long Dim Valc As Long Vala = 14 Valb = 1 ' das Bascom Äquivalent wäre: ' Valc = Vala / Valb $asm Loadadr Vala , X ' Laden r16:r19 mit Vala LD r16,X+ LD r17,X+ LD r18,X+ LD r19,X+ Loadadr Valb , X ' Laden r20:r23 mit Valb LD r20,X+ LD r21,X+ LD r22,X+ LD r23,X+ $end Asm Gosub L_0x0112 ' Dividieren r16:r19 / r20:r23 --> r20:r23 $asm Loadadr Valc , X ' speichern Ergebnis in Valc ST X+,r20 ST X+,r21 ST X+,r22 ST X+,r23 $end Asm End
- Funktions-Code
'-----------------------------------------
'
'-----------------------------------------
L_0x0112:
$asm
RCALL L_0x0174 'Operanden Vorzeichen prüfung und ggf. umdrehen auf positiv
'register r0.0 zeigt, ob das Ergebnis negativ ist
RCALL L_0x0120 'Prüfen Divisor auf NULL. Wenn nicht, dann dividieren
BLD r0,1 't-bit -> r0.1 (Error)
RJMP L_0x010C 'finish
L_0x010C:
SBRC r0,0 'Ergebnis negativ ?
RCALL L_0x011A 'ja, Ergebnis negativ machen
RET 'so oder so, Funktionsende
L_0x011A:
RCALL L_0x018A 'Ergebnis auf negativ (r20:r23)
RCALL L_0x019C 'Rest auf negativ (r16:r19)
RET
'-----------------------------------------
' Prüfen divisor auf NULL
'-----------------------------------------
L_0x0120:
MOV r24,r20 'Kopieren r20:r23 -> r24:r27
MOV r25,r21
MOV r26,r22
MOV r27,r23
CLT ' clear T-bit
!OR r20,r21 ' prüfen auf NULL
!OR r20,r22
!OR r20,r23
BRNE L_0x0136 ' <> NULL --> dividieren
!SET ' ==NULL set t-Bit
RET
'-----------------------------------------
' dividieren r16:r19 / r20:r23 --> r20:r23
'-----------------------------------------
L_0x0136:
MOV r20,r16 'r16:r19->r20:r23
MOV r21,r17
MOV r22,r18
MOV r23,r19
CLR r16 'clear r16:r19
CLR r17
CLR r18
CLR r19
LDI ZH,32 'Zähler (32 Bit)
'-----------------------------------------
' divisions-Schleife
'-----------------------------------------
L_0x0148:
LSL r20 'shift left r20:r23
ROL r21
ROL r22
ROL r23 '(das oberste Bit wandert nach r16:r19)
ROL r16 'shift left r16:r19
ROL r17
ROL r18
ROL r19
!SUB r16,r24 'subtrahieren
SBC r17,r25
SBC r18,r26
SBC r19,r27
ORI r20,1 'Ergebnis auf 1
BRCC L_0x016E 'kein Überlauf, bleibt so
ADD r16,r24 'Überlauf, wieder addieren
ADC r17,r25
ADC r18,r26
ADC r19,r27
ANDI r20,254 'und den Ergebnis 1-er wieder löschen
L_0x016E:
DEC ZH 'Zähler--
BRNE L_0x0148 '23-Bit Schleife
RET 'fertig
'-----------------------------------------
' Prüfen der Operanden
'-----------------------------------------
L_0x0174:
CLR r0 'Kontroll-Register säubern
CLT 't-bit löschen
SBRS r23,7 ' r20:r23 Negativ ?
RJMP L_0x0180 'nö, ist positiv
RCALL L_0x018A 'ja, auf positiv umdrehen
!SET 'und t-bit setzen
L_0x0180:
BLD r0,0 't-bit ins Kontroll-register r0.0
RCALL L_0x0196 'Prüfen r16:r19
BLD r1,0 't-bit -> r1.0
EOR r0,r1 'Exklusiv Or der beiden +- Prüfungen
'Vorzeichen verschieden ---> Ergebnis muss negativ sein
RET 'e bien
'-----------------------------------------
' r20:r23 auf positiv
'-----------------------------------------
L_0x018A:
RCALL L_0x01B0 'invertieren
SUBI r20,0xFF ' +1
SBCI r21,0xFF
SBCI r22,0xFF
SBCI r23,0xFF
RET
'-----------------------------------------
' Prüfen r16:r19
'-----------------------------------------
L_0x0196:
CLT 'clear t-bit
SBRS r19,7 'sign bit set ?
RET 'no
'-----------------------------------------
' r16:r19 auf positiv
'-----------------------------------------
L_0x019C:
COM r16 'invertieren
COM r17
COM r18
COM r19
SUBI r16,0xFF '+1
SBCI r17,0xFF
SBCI r18,0xFF
SBCI r19,0xFF
!SET 't-bit setzen
RET
'-----------------------------------------
' invertieren r20:r23
'-----------------------------------------
L_0x01B0:
COM r20
COM r21
COM r22
COM r23
RET
$end Asm
Return
Autor
Siehe auch