;
;    Mecrisp - A native code Forth implementation for MSP430 microcontrollers
;    Copyright (C) 2011  Matthias Koch
;
;    This program is free software: you can redistribute it and/or modify
;    it under the terms of the GNU General Public License as published by
;    the Free Software Foundation, either version 3 of the License, or
;    (at your option) any later version.
;
;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.
;
;    You should have received a copy of the GNU General Public License
;    along with this program.  If not, see <http://www.gnu.org/licenses/>.
;


 ; Star-Slash the Scalar und schöne mathematische Routinen...
 ; Entstanden am 10. und 11. Dezember 2007 analog zu dem Exemplar in Forth

 ; Weitergebastelt am 12. Dezember 2007 - nach dem Finden schwerer Fehler in der Division.

 ; Als ich die Grundlage für meinen ganzzahligen, digitalen PID-Regler legen wollte...

;------------------------------------------------------------------------------
  Kapitelbirne_flash "--- Punktrechnung ---"   
;------------------------------------------------------------------------------


;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar|Flag_faltbar_2, "um*"  ; Eigentlich um* (ans), vorher u* (fig)
um_star: ; Multipliziert vorzeichenlos 16*16 = 32
        ; ( u u -- d )
;------------------------------------------------------------------------------  
  push r10 ; Ergebnis high
  push r11 ; Ergebnis low
  push r12 ; 1. Faktor
  push r13 ; 2. Faktor
  push r14 ; Zähler

  mov @r4,   r12 ;popda r12
  mov 2(r4), r13 ;popda r13
  
  ; Multiplikation von r12 * r13, Ergebnis in r10:r11
  clr r10 ; Ergebnis-high löschen
  clr r11 ; Ergebnis-low  löschen
  mov #16, r14 ; Schleifenzähler setzen
- rla r11 ; Letztes Ergebnis * 2
  rlc r10 ;   rotieren  
  rla r13 ; Nächstes Bit ins Carry-Flag schieben
  jnc +
  add r12, r11 
  adc r10 
+ dec r14 
  jnz -
        
  mov r11, 2(r4) ; pushda r11 ; Ergebnis low
  mov r10, 0(r4) ; pushda r10 ;          high
  
  pop r14
  pop r13
  pop r12
  pop r11
  pop r10
  ret

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar|Flag_faltbar_2, "u/mod" ; 16 / 16 = 16 Rest 16. Läuft nie über.
u_divmod: ; Dividiert 16/16 = 16 Rest 16.
;------------------------------------------------------------------------------  
  ; ( Dividend Divisor -- Ergebnis Rest)
  ;        2        0

  push r10 ; Dividend
  push r11 ; Hineinschiebe-Register für den Dividenden
  push r12 ; Divisor
  push r13 ; Ergebnis
  push r14 ; Zähler
  
  clr r11 ; Ausnullen des "Schieberegisters"
  mov   @r4, r12 ; Dividenden
  mov 2(r4), r10 ; Divisor

  ; Ergebnis braucht nicht gelöscht zu werden, da dorthinein geschoben wird und 16 Schritte alle Bits belegen.

  mov #16, r14
- ; Schleife
  ; Schieben.
  rla r10 ; Dividenden schrittweise
  rlc r11 ;            ins Schieberegister rotieren.

  cmp r12, r11 ; Schieberegister mit Divisor vergleichen.
  jhs +  
  ; Kleiner: Nicht abziehen.
  clrc    ; Null fürs Ergebnis
  jmp ++
+ ; Gleich oder größer, Abzugsbereit !
  sub  r12, r11 
  setc    ; Eins fürs Ergebnis
+ rlc r13 ; Ins Ergebnis rotieren.

  dec r14
  jnz -

  mov r13, 0(r4)
  mov r11, 2(r4)

  pop r14  ; Zähler
  pop r13
  pop r12
  pop r11
  pop r10
  ret

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar|Flag_faltbar_2, "/mod"
divmod:
  ; Vorzeichenrichtige, symmetrische Division mit Rest.
  ; ( Dividend Divisor -- Rest Ergebnis )
;------------------------------------------------------------------------------  
  push r10
  clr r10

  bit #8000h, 0(r4) ; Prüfe den Divisor
  jnc +
    inc r10  
    negate

+ bit #8000h, 2(r4) ; Prüfe den Dividend
  push sr           ; Merke, ob er negativ gewesen ist !
  jnc +
    inc r10  
    inv 2(r4) ; Negate an zweiter Stelle auf dem Stack.
    inc 2(r4)
+

  call #u_divmod

  pop sr ; Hole den Zustand des Dividenden zurück
  jnc +
    inv 2(r4) ; Negiere den Rest, wenn der Dividend negativ gewesen ist.
    inc 2(r4)
+
 
  ; Setze das Vorzeichen vom Ergebnis. 
  bit #1, r10
  jnc +
  negate
+ 
  pop r10
  ret

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar|Flag_faltbar_2, "mod"
  ; Vorzeichenrichtige, symmetrische Division mit Rest.
  ; ( Dividend Divisor -- Rest )
;------------------------------------------------------------------------------ 
  call #divmod
  drop
  ret

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar|Flag_faltbar_2, "/"
  ; Vorzeichenrichtige, symmetrische Division mit Rest.
  ; ( Dividend Divisor -- Ergebnis )
;------------------------------------------------------------------------------ 
  call #divmod
  nip
  ret

;------------------------------------------------------------------------------
  Wortbirne Flag_sichtbar|Flag_faltbar_2, "*" ; Behandelt - sofern kein Überlauf auftritt - auch Vorzeichen richtig.
  call #um_star
  drop
  ret

;------------------------------------------------------------------------------
  Kapitelbirne "--- Punktrechnung ---"   
;------------------------------------------------------------------------------
