-
Notifications
You must be signed in to change notification settings - Fork 1
/
calc_sup.S
745 lines (599 loc) · 18.7 KB
/
calc_sup.S
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
; ****************************************************************************
;
; Calculator support operations
;
; ****************************************************************************
#include "include.inc"
.text
; ----------------------------------------------------------------------------
; Push registers R4..R27, R30, R31
; ----------------------------------------------------------------------------
; OUTPUT: R1 = 0
; DESTROYS: R0
; STACK: push 26 bytes
; ----------------------------------------------------------------------------
.global PushAll
PushAll:
; ----- save return address
pop r1 ; (HIGH)
pop r0 ; (LOW) get return address from the stack
; ----- push registers
push r4
push r5
push r6
push r7
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
push r16
push r17
push r18
push r19
push r20
push r21
push r22
push r23
push r24
push r25
push r26
push r27
push r30
push r31
; ----- restore return address
PushAll2:
push r0 ; (LOW)
push r1 ; (HIGH)
clr r1 ; restore R1
ret
; ----------------------------------------------------------------------------
; Pop registers R4..R27, R30, R31
; ----------------------------------------------------------------------------
; OUTPUT: R1 = 0
; DESTROYS: R0
; STACK: pop 26 bytes
; ----------------------------------------------------------------------------
; Use rcall, not rjmp!
.global PopAll
PopAll:
; ----- save return address
pop r1 ; (HIGH)
pop r0 ; (LOW) get return address from the stack
; ----- pop registers
pop r31
pop r30
pop r27
pop r26
pop r25
pop r24
pop r23
pop r22
pop r21
pop r20
pop r19
pop r18
pop r17
pop r16
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop r7
pop r6
pop r5
pop r4
; ----- restore return address
rjmp PushAll2 ; return
; ----------------------------------------------------------------------------
; Load number from Z into R_M1..R_M10
; ----------------------------------------------------------------------------
; INPUT: R31:R30 (Z) = pointer to number
; OUTPUT: R_M1..R_M10 number
; DESTROYS: -
; ----------------------------------------------------------------------------
.global CalcLoadNum
CalcLoadNum:
ldd R_M1,Z+0
ldd R_M2,Z+1
ldd R_M3,Z+2
ldd R_M4,Z+3
ldd R_M5,Z+4
ldd R_M6,Z+5
ldd R_M7,Z+6
ldd R_M8,Z+7
ldd R_M9,Z+8
ldd R_M10,Z+9
ret
; ----------------------------------------------------------------------------
; Save number from R_M1..R_M10 to Z
; ----------------------------------------------------------------------------
; INPUT: R31:R30 (Z) = pointer to number
; R_M1..R_M10 number
; DESTROYS: -
; ----------------------------------------------------------------------------
.global CalcSaveNum
CalcSaveNum:
std Z+0,R_M1
std Z+1,R_M2
std Z+2,R_M3
std Z+3,R_M4
std Z+4,R_M5
std Z+5,R_M6
std Z+6,R_M7
std Z+7,R_M8
std Z+8,R_M9
std Z+9,R_M10
ret
; ----------------------------------------------------------------------------
; Round pre-correction (add little correction to mantissa of top number) (C_PRECOR)
; ----------------------------------------------------------------------------
; DESTROYS: R31, R30, R25, R24, R_M1..R_M10
; ----------------------------------------------------------------------------
.global CalcPreCor
CalcPreCor:
; ----- get pointer to top number -> Z
; OUTPUT: R31:R30 (Z) = last number on calculator stack
; DESTROYS: -
rcall CalcTop ; get last number -> Z
; ----- load number
; INPUT: R31:R30 (Z) = pointer to number
; OUTPUT: R_M1..R_M10 number
; DESTROYS: -
rcall CalcLoadNum
; ----- save sign
mov r25,R_M3 ; save sign
ori R_M3,0x80 ; restore hidden bit
; ----- add correction to round up
ldi r24,PRECOR ; rounding pre-correction
add R_M10,r24
adc R_M9,R_ZERO
adc R_M8,R_ZERO
adc R_M7,R_ZERO
adc R_M6,R_ZERO
adc R_M5,R_ZERO
adc R_M4,R_ZERO
adc R_M3,R_ZERO
brcc 2f ; no carry
; ----- carry, shift mantissa right
rcall CalcMantRor
inc R_M2
brne 2f
inc R_M1
brne 2f
; ----- overflow
; INPUT: R31:R30 = float number
; DESTROYS: R_M1,...R_M10
call CalcZOver ; set overflow
; ----- restore sign
2: andi r25,0x80 ; old sign bit
andi R_M3,0x7f ; clear hidden bit
or R_M3,r25 ; restore sign
; ----- save number
; INPUT: R31:R30 (Z) = pointer to number
; R_M1..R_M10 number
; DESTROYS: -
rjmp CalcSaveNum
; ----------------------------------------------------------------------------
; Clear number R_M1..R_M10
; ----------------------------------------------------------------------------
; OUTPUT: R_M1..R_M10 number (= 0)
; DESTROYS: -
; ----------------------------------------------------------------------------
.global CalcClearNum
CalcClearNum:
clr R_M1
.global CalcClearNum2
CalcClearNum2:
clr R_M2
clr R_M3
clr R_M4
movw R_M6,R_M4
movw R_M8,R_M4
movw R_M10,R_M4
ret
; ----------------------------------------------------------------------------
; Copy number (Z -> X)
; ----------------------------------------------------------------------------
; INPUT: R31:R30 (Z) = source address in RAM
; R27:R26 (X) = destination address in RAM
; OUTPUT: R31:R30 (Z) = next source address in RAM
; R27:R26 (X) = next destination address in RAM
; DESTROYS: R25, R24
; ----------------------------------------------------------------------------
.global CalcCopyNum
CalcCopyNum:
ldi r25,NUM_BYTES ; length of a number
1: ld r24,Z+
st X+,r24
dec r25
brne 1b
ret
; ----------------------------------------------------------------------------
; Fetch top number from calculator stack and delete it
; ----------------------------------------------------------------------------
; OUTPUT: R_M1..R_M10 = number
; DESTROYS: R30, R31
; CALCULATOR STACK: -1
; ----------------------------------------------------------------------------
.global CalcFetch
CalcFetch:
; ----- get pointer to top number -> Z
; OUTPUT: R31:R30 (Z) = last number on calculator stack
; DESTROYS: -
rcall CalcTop ; get last number -> Z
; ----- load number
CalcFetch1: ; jump here from CalcFetch2
; INPUT: R31:R30 (Z) = pointer to number
; OUTPUT: R_M1..R_M10 number
; DESTROYS: -
rcall CalcLoadNum
; ----- delete top number
; DESTROYS: R31, R30
; CALCULATOR STACK: -1
rjmp CalcDel
; ----------------------------------------------------------------------------
; Fetch 2 numbers and delete top number
; ----------------------------------------------------------------------------
; INPUT: R31:R30 (Z) = pointer to 1st number
; R27:R26 (X) = pointer to 2nd number
; OUTPUT: R_M1..R_M10 = 1st number (from Z)
; R_N1..R:N10 = 2nd number (from X)
; DESTROYS: R31, R30, R27, R26
; CALCULATOR STACK: -1
; ----------------------------------------------------------------------------
; None of the numbers need to be top number.
.global CalcFetch2
CalcFetch2:
; ----- fetch 2nd number
ld R_N1,X+
ld R_N2,X+
ld R_N3,X+
ld R_N4,X+
ld R_N5,X+
ld R_N6,X+
ld R_N7,X+
ld R_N8,X+
ld R_N9,X+
ld R_N10,X
rjmp CalcFetch1 ; fetch 1st number and delete top number
; ----------------------------------------------------------------------------
; Store unsigned integer number into calculator stack
; ----------------------------------------------------------------------------
; INPUT: (R25:)R24 = unsigned integer
; DESTROYS: R31, R30, R25, R24, R_M1..R_M10, R0
; CALCULATOR STACK: +1
; ----------------------------------------------------------------------------
.global CalcStackB ; stack unsigned byte R24
CalcStackB:
clr r25 ; clear number HIGH
.global CalcStackW ; stack unsigned word R25:R24
CalcStackW:
; ----- create new number 0 on end of calculator stack -> Z
; OUTPUT: R31:R30 (Z) = new number
; DESTROYS: -
; CALCULATOR STACK: +1
rcall CalcNew ; create number -> Z
; ----- clear number R_M1..R_M10
; OUTPUT: R_M1..R_M10 number (= 0)
; DESTROYS: -
rcall CalcClearNum ; clear number
; ----- number is zero
mov r0,r25 ; HIGH byte
or r0,r24 ; check zero
breq CalcStack9 ; zero number
; ----- preset exponent to maximum value 65535
ldi R_M1,hi8(EXP_BIAS+16)
ldi R_M2,lo8(EXP_BIAS+16)
; ----- normalize number
CalcStack2:
subi R_M2,1 ; decrement exponent
sbc R_M1,R_ZERO
lsl r24 ; rotate mantissa left
rol r25
brcc CalcStack2 ; until find highest bit set
; ----- correct mantissa (clear sign bit)
lsr r25 ; shift mantissa back
ror r24
; ----- save number
movw R_M4,r24
CalcStack9:
; INPUT: R31:R30 (Z) = pointer to number
; R_M1..R_M10 number
; DESTROYS: -
rjmp CalcSaveNum
; ----------------------------------------------------------------------------
; Store signed integer number into calculator stack
; ----------------------------------------------------------------------------
; INPUT: R25:R24 = signed integer
; DESTROYS: R31, R30, R25, R24, R_M1..R_M10, R0
; CALCULATOR STACK: +1
; ----------------------------------------------------------------------------
.global CalcStackS16 ; stack signed word R25:R24
CalcStackS16:
; INPUT: (R25:)R24 = unsigned integer
; DESTROYS: R31, R30, R25, R24, R_M1..R_M10, R0
; CALCULATOR STACK: +1
tst r25 ; is number negative?
brpl CalcStackW ; number is not negative, store it
; INPUT/OUTPUT: R25:R24 = number to negate
; DESTROYS: -
rcall NegW ; negate number R25:R24
; INPUT: (R25:)R24 = unsigned integer
; DESTROYS: R31, R30, R25, R24, R_M1..R_M10, R0
; CALCULATOR STACK: +1
rcall CalcStackW ; store negate number
; DESTROYS: R31, R30, R25, R24
rjmp CalcNeg ; negate number
; ----------------------------------------------------------------------------
; Store unsigned integer number into calculator stack
; ----------------------------------------------------------------------------
; INPUT: R27:R26:R25:R24 = unsigned integer
; DESTROYS: R31, R30, R27, R26, R25, R24, R_M1..R_M10, R0
; CALCULATOR STACK: +1
; ----------------------------------------------------------------------------
.global CalcStackD ; stack unsigned word R27:R26:R25:R24
CalcStackD:
; ----- create new number 0 on end of calculator stack -> Z
; OUTPUT: R31:R30 (Z) = new number
; DESTROYS: -
; CALCULATOR STACK: +1
rcall CalcNew ; create number -> Z
; ----- clear number R_M1..R_M10
; OUTPUT: R_M1..R_M10 number (= 0)
; DESTROYS: -
rcall CalcClearNum ; clear number
; ----- number is zero
mov r0,r27 ; HIGH byte
or r0,r26
or r0,r25
or r0,r24 ; check zero
breq CalcStackD9 ; zero number
; ----- preset exponent to maximum value
ldi R_M1,hi8(EXP_BIAS+32)
ldi R_M2,lo8(EXP_BIAS+32)
; ----- normalize number
CalcStackD2:
subi R_M2,1 ; decrement exponent
sbc R_M1,R_ZERO
lsl r24 ; rotate mantissa left
rol r25
rol r26
rol r27
brcc CalcStackD2 ; until find highest bit set
; ----- correct mantissa (clear sign bit)
lsr r27 ; shift mantissa back
ror r26
ror r25
ror r24
; ----- save number
movw R_M6,r24
movw R_M4,r26
CalcStackD9:
; INPUT: R31:R30 (Z) = pointer to number
; R_M1..R_M10 number
; DESTROYS: -
rjmp CalcSaveNum
; ----------------------------------------------------------------------------
; Get unsigned integer number word (rounded towards zero)
; ----------------------------------------------------------------------------
; OUTPUT: R25:R24 = unsigned integer
; R_M3 = negative flag (0 or B7)
; C flag is set = overflow valid range
; Z flag is set = number is positive or 0 (breq), NZ = number is negative (brne)
; DESTROYS: R31, R30, R23, R_M1..R_M10
; CALCULATOR STACK: -1
; ----------------------------------------------------------------------------
; Valid range is 0..65535.9999 (negative or positive)
.global CalcUnstackW
CalcUnstackW:
; ------ round pre-correction
; DESTROYS: R31, R30, R25, R24, R_M1..R_M10
rcall CalcPreCor
; ---- fetch number from stack and delete it
; OUTPUT: R_M1..R_M10 = number
; DESTROYS: R30, R31
; CALCULATOR STACK: -1
rcall CalcFetch
; ----- check minimal exponent: number is zero or < 1
clr r25 ; clear result HIGH
clr r24 ; clear result LOW
subi R_M2,lo8(EXP_BIAS) ; minimal exponent
sbci R_M1,hi8(EXP_BIAS) ; check value < 1 (including zero)
brcs CalcUnstack7 ; number is zero or < 1
; R_M1:R_M2 = exponent 0..32767
; ----- get mantissa high and restore hidden bit '1' -> R25:R24
movw r24,R_M4 ; get first 2 bytes of mantissa R_M3:R_M4
ori r25,B7 ; set implied hidden bit '1'
; ----- check maximal exponent
subi R_M2,lo8(16)
sbci R_M1,hi8(16) ; subtract max. exponent of 65535
brcc CalcUnstack8 ; overflow exponent (>= 65535)
; R_M1:R_M2 = exponent -16..-1
; ----- number of shifts to normalize
com R_M2 ; negate and decrement exponent -> value 0..15
breq CalcUnstack7 ; already maximal exponent 0
; ----- normalize mantissa
CalcUnstack4:
lsr r25 ; rotate mantissa HIGH right
ror r24 ; rotate mantissa LOW right
dec R_M2 ; decrement exponent
brne CalcUnstack4 ; normalize
; ----- result is OK
CalcUnstack7:
clc ; clear carry flag (=OK)
rjmp CalcUnstack9
; ----- overflow
CalcUnstack8:
ldi r24,0xff ; max. value
ldi r25,0xff
sec ; set carry flag (=overflow)
; ----- check sign
CalcUnstack9:
andi R_M3,B7 ; check negative flag (set Z = number is positive or 0); C flag stays unchanged
CalcUnstack10:
ret
; ----------------------------------------------------------------------------
; Get unsigned integer number byte (rounded towards zero)
; ----------------------------------------------------------------------------
; OUTPUT: R24 = unsigned integer
; C flag is set = overflow valid range
; Z flag is set = number is positive or 0 (breq), NZ = number is negative (brne)
; DESTROYS: R31, R30, R25, R23, R_M1..R_M10
; CALCULATOR STACK: -1
; ----------------------------------------------------------------------------
; Valid range is 0..255.9999 (negative or positive)
.global CalcUnstackB
CalcUnstackB:
; OUTPUT: R25:R24 = unsigned integer
; R_M3 = negative flag (0 or B7)
; C flag is set = overflow valid range
; Z flag is set = number is positive or 0 (breq), NZ = number is negative (brne)
; DESTROYS: R31, R30, R_M1..R_M10
; CALCULATOR STACK: -1
rcall CalcUnstackW
brcs CalcUnstack10 ; overflow
tst r25 ; check number HIGH
breq CalcUnstack7 ; number is <= 255, result is OK
rjmp CalcUnstack8 ; overflow
; ----------------------------------------------------------------------------
; Get signed integer number from calculator stack (signed word)
; ----------------------------------------------------------------------------
; OUTPUT: R25:R24 = signed integer
; DESTROYS: R31, R30, R_M1..R_M10
; CALCULATOR STACK: -1
; ----------------------------------------------------------------------------
.global CalcUnstackS16
CalcUnstackS16:
; ----- get unsigned number
; OUTPUT: R25:R24 = unsigned integer
; R_M3 = negative flag (0 or B7)
; C flag is set = overflow valid range
; Z flag is set = number is positive or 0 (breq), NZ = number is negative (brne)
; DESTROYS: R31, R30, R_M1..R_M10
; CALCULATOR STACK: -1
rcall CalcUnstackW ; get number -> R25:R24
breq 4f ; number is positive
; ----- number is negatve - limit to 0x8000 and negate
ldi R_M1,0x80
cpi r24,1
cpc r25,R_M1 ; check overflow (compare 0x8001)
brcs 2f ; number is OK
ldi r24,0
ldi r25,0x80 ; limit number to 0x8000
; INPUT/OUTPUT: R25:R24 = number to negate
; DESTROYS: -
2: rjmp NegW ; negate number R25:R24
; ----- number is positive - limit to 0x7FFF
4: ldi R_M1,0x80
cpi r24,0
cpc r25,R_M1 ; check overflow (compare 0x8000)
brcs 5f ; number is OK
ldi r24,0xff
ldi r25,0x7f ; limit number to 0x7FFF
5: ret
; ----------------------------------------------------------------------------
; Get unsigned integer number dword (rounded towards zero)
; ----------------------------------------------------------------------------
; OUTPUT: R27:R26:R25:R24 = unsigned integer
; R_M3 = negative flag (0 or B7)
; C flag is set = overflow valid range
; Z flag is set = number is positive or 0 (breq), NZ = number is negative (brne)
; DESTROYS: R31, R30, R_M1..R_M10
; CALCULATOR STACK: -1
; ----------------------------------------------------------------------------
.global CalcUnstackD
CalcUnstackD:
; ------ round pre-correction
; DESTROYS: R31, R30, R25, R24, R_M1..R_M10
rcall CalcPreCor
; ---- fetch number from stack and delete it
; OUTPUT: R_M1..R_M10 = number
; DESTROYS: R30, R31
; CALCULATOR STACK: -1
rcall CalcFetch
; ----- check minimal exponent: number is zero or < 1
clr r27 ; clear result HIGH
clr r26
clr r25
clr r24 ; clear result LOW
subi R_M2,lo8(EXP_BIAS) ; minimal exponent
sbci R_M1,hi8(EXP_BIAS) ; check value < 1 (including zero)
brcs CalcUnstackD7 ; number is zero or < 1
; R_M1:R_M2 = exponent 0..32767
; ----- get mantissa high and restore hidden bit '1' -> R27:R26:R25:R24
movw r26,R_M4
movw r24,R_M6 ; get first 4 bytes of mantissa R_M3:R_M4:R_M5:R_M6
ori r27,B7 ; set implied hidden bit '1'
; ----- check maximal exponent
subi R_M2,lo8(32)
sbci R_M1,hi8(32) ; subtract max. exponent
brcc CalcUnstackD8 ; overflow exponent
; R_M1:R_M2 = exponent -32..-1
; ----- number of shifts to normalize
com R_M2 ; negate and decrement exponent -> value 0..31
breq CalcUnstackD7 ; already maximal exponent 0
; ----- normalize mantissa
CalcUnstackD4:
lsr r27 ; rotate mantissa HIGH right
ror r26
ror r25
ror r24 ; rotate mantissa LOW right
dec R_M2 ; decrement exponent
brne CalcUnstackD4 ; normalize
; ----- result is OK
CalcUnstackD7:
clc ; clear carry flag (=OK)
rjmp CalcUnstackD9
; ----- overflow
CalcUnstackD8:
ldi r24,0xff ; max. value
ldi r25,0xff
ldi r26,0xff
ldi r27,0xff
sec ; set carry flag (=overflow)
; ----- check sign
CalcUnstackD9:
andi R_M3,B7 ; check negative flag (set Z = number is positive or 0); C flag stays unchanged
;CalcUnstackD10:
ret
; ----------------------------------------------------------------------------
; Exchange literal pointer and Z
; ----------------------------------------------------------------------------
; INPUT/OUTPUT: R31:R30, R_LITH:R_LITL
; DESTROYS: -
; ----------------------------------------------------------------------------
.global CalcLitExc
CalcLitExc:
eor r30,R_LITL ; R30 ^ LITL
eor R_LITL,r30 ; R30
eor r30,R_LITL ; LITL
eor r31,R_LITH ; R31 ^ LITH
eor R_LITH,r31 ; R31
eor r31,R_LITH ; LITH
ret
; ----------------------------------------------------------------------------
; Load next literal
; ----------------------------------------------------------------------------
; INPUT: R_LITH:R_LITL = pointer to literals
; OUTPUT: R_LITH:R_LITL = new pointer to literals
; R24 = next literal
; DESTROYS: -
; ----------------------------------------------------------------------------
.global CalcLit
CalcLit:
; INPUT/OUTPUT: R31:R30, R_LITH:R_LITL
; DESTROYS: -
rcall CalcLitExc ; exchange literal pointer and Z
lpm r24,Z+ ; R24 <- load next literal
; INPUT/OUTPUT: R31:R30, R_LITH:R_LITL
; DESTROYS: -
rcall CalcLitExc ; exchange literal pointer and Z
ret