summaryrefslogtreecommitdiff
path: root/rtl/avr/divide.inc
blob: 3a03f3ca2f32022bd0b2d2b30067240b01236dea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// Based on restoring division algorithm
// Algorithm source document: Lecture notes by S. Galal and D. Pham, Division algorithms and hardware implementations.
// Link to documentation http://www.seas.ucla.edu/~ingrid/ee213a/lectures/division_presentV2.pdf
// Also refer to description on Wikipedia: https://en.wikipedia.org/wiki/Division_algorithm#Restoring_division

// Note that the algorithm automatically yields the following results for special cases:
// z div 0 = MAX(type)
// 0 div 0 = MAX(type)
// 0 div n = 0
// Checks for z = 0; n = [0,1]; n = z and n > z could shortcut the algorithm for speed-ups
// but would add extra code
// Perhaps add the checks depending on optimization settings?

// z in Ra, n in Rb, 0 in Rp
function fpc_divmod_byte(n, z: byte): byte; assembler; nostackframe;
label
  div1, div2, div3, finish;
asm
// Symbol  Name        Register(s)
// z (A)   dividend    R22
// n (B)   divisor     R24
// p (P)   remainder   R20
// i       counter     R18
  clr R20         // clear remainder
  ldi R18, 8      // iterate over 8 bits

div1:
  lsl R22         // shift left A
  rol R20         // shift left P with carry from A shift
  sub R20, R24    // Subtract B from P, P <= P - B
  brlo div2
  ori R22, 1      // Set A[0] = 1
  rjmp div3
div2:             // negative branch, A[0] = 0 (default after shift), restore P
  add R20, R24    // restore old value of P

div3:
  dec R18
  brne div1

finish:
  mov R24, R22    // Move result from R22 to R24
end;

{$if defined(CPUAVR_16_REGS) or not(defined(CPUAVR_HAS_MOVW))}
function fpc_divmod_word(n, z: word): word; assembler; nostackframe;
label
  div1, div2, div3, finish;
asm
// Symbol  Name        Register(s)
// z (A)   dividend    R23, R22
// n (B)   divisor     R25, R24
// p (P)   remainder   R21, R20
// i       counter     R18

  clr R20         // clear remainder low
  clr R21         // clear remainder hi
  ldi R18, 16     // iterate over 16 bits

div1:
  lsl R22         // shift left A_L
  rol R23
  rol R20         // shift left P with carry from A shift
  rol R21
  sub R20, R24    // Subtract B from P, P <= P - B
  sbc R21, R25
  brlo div2
  ori R22, 1      // Set A[0] = 1
  rjmp div3
div2:             // negative branch, A[0] = 0 (default after shift), restore P
  add R20, R24    // restore old value of P
  adc R21, R25

div3:
  dec R18
  brne div1

finish:
  mov  R24, R22    // Move result from R22:R23 to R24:R25
  mov  R25, R23    // Move result from R22:R23 to R24:R25
end;

function fpc_divmod_dword(n, z: dword): dword; assembler; nostackframe;
label
  div1, div2, div3, finish;
asm
end;
{$else defined(CPUAVR_16_REGS) or not(defined(CPUAVR_HAS_MOVW))}
// z in Ra, n in Rb, 0 in Rp
function fpc_divmod_word(n, z: word): word; assembler; nostackframe;
label
  div1, div2, div3, finish;
asm
// Symbol  Name        Register(s)
// z (A)   dividend    R23, R22
// n (B)   divisor     R25, R24
// p (P)   remainder   R21, R20
// i       counter     R18

  clr R20         // clear remainder low
  clr R21         // clear remainder hi
  ldi R18, 16     // iterate over 16 bits

div1:
  lsl R22         // shift left A_L
  rol R23
  rol R20         // shift left P with carry from A shift
  rol R21
  sub R20, R24    // Subtract B from P, P <= P - B
  sbc R21, R25
  brlo div2
  ori R22, 1      // Set A[0] = 1
  rjmp div3
div2:             // negative branch, A[0] = 0 (default after shift), restore P
  add R20, R24    // restore old value of P
  adc R21, R25

div3:
  dec R18
  brne div1

finish:
  movw R24, R22    // Move result from R22:R23 to R24:R25
end;

// z in Ra, n in Rb, 0 in Rp
function fpc_divmod_dword(n, z: dword): dword; assembler; nostackframe;
label
  div1, div2, div3, finish;
asm
// Symbol  Name        Register(s)
// z (A)   dividend    R21, R20, R19, R18
// n (B)   divisor     R25, R24, R23, R22
// p (P)   remainder   R17, R16, R15, R14 -> Returned in R25, R24, R23, R22
// i       counter     R26
  push R17
  push R16
  push R15
  push R14

  clr R14         // clear remainder
  clr R15         // clear remainder
  clr R16
  clr R17
  ldi R26, 32     // iterate over 32 bits

div1:
  lsl R18         // shift left A_L
  rol R19
  rol R20
  rol R21
  rol R14         // shift left P with carry from A shift
  rol R15
  rol R16
  rol R17
  sub R14, R22    // Subtract B from P, P <= P - B
  sbc R15, R23
  sbc R16, R24
  sbc R17, R25
  brlo div2
  ori R18, 1      // Set A[0] = 1
  rjmp div3
div2:             // negative branch, A[0] = 0 (default after shift), restore P
  add R14, R22    // restore old value of P
  adc R15, R23
  adc R16, R24
  adc R17, R25

div3:
  dec R26
  brne div1

finish:
  movw R22, R14    // Move remainder into reg that is not volatile
  movw R24, R16

  pop R14
  pop R15
  pop R16
  pop R17
end;
{$endif defined(CPUAVR_16_REGS) or not(defined(CPUAVR_HAS_MOVW))}