b54941677c4a8f7e150c4200298a603f41f6f3ba
[goodfet] / client / GoodFETARM.py
1 #!/usr/bin/env python
2 # GoodFET ARM Client Library
3
4 #
5 # Good luck with alpha / beta code.
6 # Contributions and bug reports welcome.
7 #
8
9 import sys, binascii, struct, time
10 import atlasutils.smartprint as asp
11 from GoodFET import GoodFET
12 from intelhex import IntelHex
13
14 platforms = {
15     "at91sam7": {0:(0x100000, "Flash before remap, SRAM after remap"),
16                  0x100000: (0x100000, "Internal Flash"),
17                  0x200000: (0x100000, "Internal SRAM"),
18                  },
19     }
20                 
21
22 #Global Commands
23 READ  = 0x00
24 WRITE = 0x01
25 PEEK  = 0x02
26 POKE  = 0x03
27 SETUP = 0x10
28 START = 0x20
29 STOP  = 0x21
30 CALL  = 0x30
31 EXEC  = 0x31
32 NOK   = 0x7E
33 OK    = 0x7F
34
35 # ARM7TDMI JTAG commands
36 GET_DEBUG_CTRL      = 0x80
37 SET_DEBUG_CTRL      = 0x81
38 GET_PC              = 0x82
39 SET_PC              = 0x83
40 GET_CHIP_ID         = 0x84
41 GET_DEBUG_STATE     = 0x85
42 GET_WATCHPOINT      = 0x86
43 SET_WATCHPOINT      = 0x87
44 GET_REGISTER        = 0x88
45 SET_REGISTER        = 0x89
46 GET_REGISTERS       = 0x8a
47 SET_REGISTERS       = 0x8b
48 HALTCPU             = 0x8c
49 RESUMECPU           = 0x8d
50 DEBUG_INSTR         = 0x8e      #
51 STEP_INSTR          = 0x8f      #
52 STEP_REPLACE        = 0x90      #
53 PROGRAM_FLASH       = 0x95
54 LOCKCHIP            = 0x96      # ??
55 CHIP_ERASE          = 0x97      # can do?
56 # Really ARM specific stuff
57 GET_CPSR            = 0x98
58 SET_CPSR            = 0x99
59 GET_SPSR            = 0x9a
60 SET_SPSR            = 0x9b
61 SET_MODE_THUMB      = 0x9c
62 SET_MODE_ARM        = 0x9d
63 SET_IR              = 0x9e
64 WAIT_DBG            = 0x9f
65 SHIFT_DR            = 0xa0
66 SETWATCH0           = 0xa1
67 SETWATCH1           = 0xa2
68 CHAIN0              = 0xa3
69
70
71 MSB         = 0
72 LSB         = 1
73 NOEND       = 2
74 NORETIDLE   = 4
75
76 PM_usr = 0b10000
77 PM_fiq = 0b10001
78 PM_irq = 0b10010
79 PM_svc = 0b10011
80 PM_abt = 0b10111
81 PM_und = 0b11011
82 PM_sys = 0b11111
83 proc_modes = {
84     PM_usr: ("User Processor Mode", "usr", "Normal program execution mode"),
85     PM_fiq: ("FIQ Processor Mode", "fiq", "Supports a high-speed data transfer or channel process"),
86     PM_irq: ("IRQ Processor Mode", "irq", "Used for general-purpose interrupt handling"),
87     PM_svc: ("Supervisor Processor Mode", "svc", "A protected mode for the operating system"),
88     PM_irq: ("Abort Processor Mode", "irq", "Implements virtual memory and/or memory protection"),
89     PM_und: ("Undefined Processor Mode", "und", "Supports software emulation of hardware coprocessor"),
90     PM_sys: ("System Processor Mode", "sys", "Runs privileged operating system tasks (ARMv4 and above)"),
91 }
92
93 PSR_bits = [ 
94     None,
95     None,
96     None,
97     None,
98     None,
99     "Thumb",
100     "nFIQ_int",
101     "nIRQ_int",
102     "nImprDataAbort_int",
103     "BIGendian",
104     None,
105     None,
106     None,
107     None,
108     None,
109     None,
110     "GE_0",
111     "GE_1",
112     "GE_2",
113     "GE_3",
114     None,
115     None,
116     None,
117     None,
118     "Jazelle",
119     None,
120     None,
121     "Q (DSP-overflow)",
122     "oVerflow",
123     "Carry",
124     "Zero",
125     "Neg",
126     ]
127
128
129
130 ARM_INSTR_NOP =             0xe1a00000L
131 ARM_INSTR_BX_R0 =           0xe12fff10L
132 ARM_INSTR_STR_Rx_r14 =      0xe58f0000L # from atmel docs
133 ARM_READ_REG =              ARM_INSTR_STR_Rx_r14
134 ARM_INSTR_LDR_Rx_r14 =      0xe59f0000L # from atmel docs
135 ARM_WRITE_REG =             ARM_INSTR_LDR_Rx_r14
136 ARM_INSTR_LDR_R1_r0_4 =     0xe4901004L
137 ARM_READ_MEM =              ARM_INSTR_LDR_R1_r0_4
138 ARM_INSTR_STR_R1_r0_4 =     0xe4801004L
139 ARM_WRITE_MEM =             ARM_INSTR_STR_R1_r0_4
140 ARM_INSTR_MRS_R0_CPSR =     0xe10f0000L
141 ARM_INSTR_MSR_cpsr_cxsf_R0 =0xe12ff000L
142 ARM_INSTR_STMIA_R14_r0_rx = 0xE88E0000L      # add up to 65k to indicate which registers...
143 ARM_STORE_MULTIPLE =        ARM_INSTR_STMIA_R14_r0_rx
144 ARM_INSTR_SKANKREGS =       0xE88F7fffL
145 ARM_INSTR_CLOBBEREGS =      0xE89F7fffL
146
147 ARM_INSTR_B_PC =            0xea000000L
148 ARM_INSTR_BX_PC =           0xe1200010L      # need to set r0 to the desired address
149 THUMB_INSTR_STR_R0_r0 =     0x60006000L
150 THUMB_INSTR_MOV_R0_PC =     0x46b846b8L
151 THUMB_INSTR_BX_PC =         0x47784778L
152 THUMB_INSTR_NOP =           0x1c001c00L
153 ARM_REG_PC =                15
154
155 ARM7TDMI_IR_EXTEST =            0x0
156 ARM7TDMI_IR_SCAN_N =            0x2
157 ARM7TDMI_IR_SAMPLE =            0x3
158 ARM7TDMI_IR_RESTART =           0x4
159 ARM7TDMI_IR_CLAMP =             0x5
160 ARM7TDMI_IR_HIGHZ =             0x7
161 ARM7TDMI_IR_CLAMPZ =            0x9
162 ARM7TDMI_IR_INTEST =            0xC
163 ARM7TDMI_IR_IDCODE =            0xE
164 ARM7TDMI_IR_BYPASS =            0xF
165
166
167 def PSRdecode(psrval):
168     output = [ "(%s mode)"%proc_modes[psrval&0x1f][1] ]
169     for x in xrange(5,32):
170         if psrval & (1<<x):
171             output.append(PSR_bits[x])
172     return " ".join(output)
173    
174 fmt = [None, "B", "<H", None, "<L", None, None, None, "<Q"]
175 def chop(val,byts):
176     s = struct.pack(fmt[byts], val)
177     return [ord(b) for b in s ]
178         
179 class GoodFETARM(GoodFET):
180     """A GoodFET variant for use with ARM7TDMI microprocessor."""
181     def ARMhaltcpu(self):
182         """Halt the CPU."""
183         self.writecmd(0x13,HALTCPU,0,self.data)
184         print "CPSR: (%s) %s"%(self.ARMget_regCPSRstr())
185     halt=ARMhaltcpu
186     def ARMreleasecpu(self):
187         """Resume the CPU."""
188         self.writecmd(0x13,RESUMECPU,0,self.data)
189     def ARMsetModeArm(self, restart=0):
190         self.writecmd(0x13,SET_MODE_ARM,0,[restart])
191     def ARMsetModeThumb(self, restart=0):
192         self.writecmd(0x13,SET_MODE_THUMB,0,[restart])
193     def ARMtest(self):
194         #self.ARMreleasecpu()
195         #self.ARMhaltcpu()
196         print "Status: %s" % self.ARMstatusstr()
197         
198         #Grab ident three times, should be equal.
199         ident1=self.ARMident()
200         ident2=self.ARMident()
201         ident3=self.ARMident()
202         if(ident1!=ident2 or ident2!=ident3):
203             print "Error, repeated ident attempts unequal."
204             print "%04x, %04x, %04x" % (ident1, ident2, ident3)
205         
206         #Set and Check Registers
207         regs = [1024+x for x in range(0,15)]
208         regr = []
209         for x in range(len(regs)):
210             self.ARMset_register(x, regs[x])
211
212         for x in range(len(regs)):
213             regr.append(self.ARMget_register(x))
214         
215         for x in range(len(regs)):
216             if regs[x] != regr[x]:
217                 print "Error, R%d fail: %x != %x"%(x,regs[x],regr[x])
218
219         return
220
221
222
223
224         #Single step, printing PC.
225         print "Tracing execution at startup."
226         for i in range(15):
227             pc=self.ARMgetPC()
228             byte=self.ARMpeekcodebyte(i)
229             #print "PC=%04x, %02x" % (pc, byte)
230             self.ARMstep_instr()
231         
232         print "Verifying that debugging a NOP doesn't affect the PC."
233         for i in range(1,15):
234             pc=self.ARMgetPC()
235             self.ARMdebuginstr([NOP])
236             if(pc!=self.ARMgetPC()):
237                 print "ERROR: PC changed during ARMdebuginstr([NOP])!"
238         
239         print "Checking pokes to XRAM."
240         for i in range(0xf000,0xf020):
241             self.ARMpokedatabyte(i,0xde)
242             if(self.ARMpeekdatabyte(i)!=0xde):
243                 print "Error in DATA at 0x%04x" % i
244         
245         #print "Status: %s." % self.ARMstatusstr()
246         #Exit debugger
247         self.stop()
248         print "Done."
249
250     def setup(self):
251         """Move the FET into the JTAG ARM application."""
252         #print "Initializing ARM."
253         self.writecmd(0x13,SETUP,0,self.data)
254     def ARMget_dbgstate(self):
255         """Read the config register of an ARM."""
256         self.writecmd(0x13,GET_DEBUG_STATE,0,self.data)
257         retval = struct.unpack("<L", self.data[:4])[0]
258         return retval
259     def ARMget_dbgctrl(self):
260         """Read the config register of an ARM."""
261         self.writecmd(0x13,GET_DEBUG_CTRL,0,self.data)
262         retval = struct.unpack("B", self.data)[0]
263         return retval
264     def ARMset_dbgctrl(self,config):
265         """Write the config register of an ARM."""
266         self.writecmd(0x13,SET_DEBUG_CTRL,1,[config&7])
267     def ARMlockchip(self):
268         """Set the flash lock bit in info mem.
269         Chip-Specific.  Not implemented"""
270         #self.writecmd(0x13, LOCKCHIP, 0, [])
271         raise Exception("Unimplemented: lockchip.  This is chip specific and must be implemented for each chip.")
272     
273
274     def ARMidentstr(self):
275         ident=self.ARMident()
276         ver     = ident >> 28
277         partno  = (ident >> 12) & 0x10
278         mfgid   = ident & 0xfff
279         return "mfg: %x\npartno: %x\nver: %x\n(%x)" % (ver, partno, mfgid, ident); 
280     def ARMident(self):
281         """Get an ARM's ID."""
282         self.writecmd(0x13,GET_CHIP_ID,0,[])
283         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
284         return retval
285     def ARMsetPC(self, val):
286         """Set an ARM's PC.  Note: real PC gets all wonky in debug mode, this changes the "saved" PC which is used when exiting debug mode"""
287         self.writecmd(0x13,SET_PC,0,chop(val,4))
288     def ARMgetPC(self):
289         """Get an ARM's PC. Note: real PC gets all wonky in debug mode, this is the "saved" PC"""
290         self.writecmd(0x13,GET_PC,0,[])
291         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
292         return retval
293     def ARMget_register(self, reg):
294         """Get an ARM's Register"""
295         self.writecmd(0x13,GET_REGISTER,1,[reg&0xff])
296         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
297         return retval
298     def ARMset_register(self, reg, val):
299         """Get an ARM's Register"""
300         self.writecmd(0x13,SET_REGISTER,8,[val&0xff, (val>>8)&0xff, (val>>16)&0xff, val>>24, reg,0,0,0])
301         #self.writecmd(0x13,SET_REGISTER,8,[reg,0,0,0, (val>>16)&0xff, val>>24, val&0xff, (val>>8)&0xff])
302         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
303         return retval
304     def ARMget_registers(self):
305         """Get ARM Registers"""
306         regs = [ self.ARMget_register(x) for x in range(15) ]
307         regs.append(self.ARMgetPC())            # make sure we snag the "static" version of PC
308         return regs
309     def ARMset_registers(self, regs, mask):
310         """Set ARM Registers"""
311         for x in xrange(15):
312           if (1<<x) & mask:
313             self.ARMset_register(x,regs.pop())
314         if (1<<15) & mask:                      # make sure we set the "static" version of PC or changes will be lost
315           self.ARMsetPC(regs.pop())
316     def ARMget_regCPSRstr(self):
317         psr = self.ARMget_regCPSR()
318         return hex(psr), PSRdecode(psr)
319     def ARMget_regCPSR(self):
320         """Get an ARM's Register"""
321         self.writecmd(0x13,GET_CPSR,0,[])
322         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
323         return retval
324     def ARMset_regCPSR(self, val):
325         """Get an ARM's Register"""
326         self.writecmd(0x13,SET_CPSR,4,[val&0xff, (val>>8)&0xff, (val>>16)&0xff, val>>24])
327     def ARMcmd(self,phrase):
328         self.writecmd(0x13,READ,len(phrase),phrase)
329         val=ord(self.data[0])
330         print "Got %02x" % val
331         return val
332     def ARMdebuginstr(self,instr,bkpt):
333         if type (instr) == int or type(instr) == long:
334             instr = struct.pack("<L", instr)
335         instr = [int("0x%x"%ord(x),16) for x in instr]
336         instr.extend([bkpt])
337         self.writecmd(0x13,DEBUG_INSTR,len(instr),instr)
338         return (self.data)
339     def ARM_nop(self, bkpt):
340         return self.ARMdebuginstr(ARM_INSTR_NOP, bkpt)
341     def ARMset_IR(self, IR, noretidle=0):
342         self.writecmd(0x13,SET_IR,2, [IR, LSB|noretidle])
343         return self.data
344     def ARMshiftDR(self, data, bits, flags):
345         self.writecmd(0x13,SHIFT_DR,8,[bits&0xff, flags&0xff, 0, 0, data&0xff,(data>>8)&0xff,(data>>16)&0xff,(data>>24)&0xff])
346         return self.data
347     def ARMwaitDBG(self, timeout=0xff):
348         self.writecmd(0x13,WAIT_DBG,2,[timeout&0xf,timeout>>8])
349         return self.data
350     def ARMrestart(self):
351         #self.ARMset_IR(ARM7TDMI_IR_BYPASS)
352         self.ARMset_IR(ARM7TDMI_IR_RESTART)
353     def ARMset_watchpoint0(self, addr, addrmask, data, datamask, ctrl, ctrlmask):
354         self.data = []
355         self.data.extend(chop(addr,4))
356         self.data.extend(chop(addrmask,4))
357         self.data.extend(chop(data,4))
358         self.data.extend(chop(datamask,4))
359         self.data.extend(chop(ctrl,4))
360         self.data.extend(chop(ctrlmask,4))
361         self.writecmd(0x13,SETWATCH0,24,self.data)
362         return self.data
363     def ARMset_watchpoint1(self, addr, addrmask, data, datamask, ctrl, ctrlmask):
364         self.data = []
365         self.data.extend(chop(addr,4))
366         self.data.extend(chop(addrmask,4))
367         self.data.extend(chop(data,4))
368         self.data.extend(chop(datamask,4))
369         self.data.extend(chop(ctrl,4))
370         self.data.extend(chop(ctrlmask,4))
371         self.writecmd(0x13,SETWATCH1,24,self.data)
372         return self.data
373     def ARMreadMem(self, adr, wrdcount):
374         retval = [] 
375         r0 = self.ARMget_register(0);        # store R0 and R1
376         r1 = self.ARMget_register(1);
377         #print >>sys.stderr,("CPSR:\t%x"%self.ARMget_regCPSR())
378         for word in range(adr, adr+(wrdcount*4), 4):
379             sys.stdin.readline()
380             self.ARMset_register(0, word);        # write address into R0
381             #time.sleep(1)
382             self.ARMset_register(1, 0xdeadbeef)
383             #time.sleep(1)
384             self.ARM_nop(0)
385             #time.sleep(1)
386             self.ARM_nop(1)
387             #time.sleep(1)
388             self.ARMdebuginstr(ARM_READ_MEM, 0); # push LDR R1, [R0], #4 into instruction pipeline  (autoincrements for consecutive reads)
389             #time.sleep(1)
390             self.ARM_nop(0)
391             #time.sleep(1)
392             self.ARMrestart()
393             #time.sleep(1)
394             self.ARMwaitDBG()
395             #time.sleep(1)
396             print hex(self.ARMget_register(1))
397
398
399             # FIXME: this may end up changing te current debug-state.  should we compare to current_dbgstate?
400             #print repr(self.data[4])
401             if (len(self.data)>4 and self.data[4] == '\x00'):
402               print >>sys.stderr,("FAILED TO READ MEMORY/RE-ENTER DEBUG MODE")
403               raise Exception("FAILED TO READ MEMORY/RE-ENTER DEBUG MODE")
404               return (-1);
405             else:
406               retval.append( self.ARMget_register(1) )  # read memory value from R1 register
407               #print >>sys.stderr,("CPSR: %x\t\tR0: %x\t\tR1: %x"%(self.ARMget_regCPSR(),self.ARMget_register(0),self.ARMget_register(1)))
408         self.ARMset_register(1, r1);       # restore R0 and R1 
409         self.ARMset_register(0, r0);
410         return retval
411
412     def ARMwriteMem(self, adr, wordarray):
413         r0 = self.ARMget_register(0);        # store R0 and R1
414         r1 = self.ARMget_register(1);
415         #print >>sys.stderr,("CPSR:\t%x"%self.ARMget_regCPSR())
416         for word in xrange(adr, adr+len(string), 4):
417             self.ARMset_register(0, word);        # write address into R0
418             self.ARM_nop(0)
419             self.ARM_nop(1)
420             self.ARMdebuginstr(ARM_WRITE_MEM, 0); # push STR R1, [R0], #4 into instruction pipeline  (autoincrements for consecutive writes)
421             self.ARM_nop(0)
422             self.ARMrestart()
423             self.ARMwaitDBG()
424             print hex(self.ARMget_register(1))
425
426
427             # FIXME: this may end up changing te current debug-state.  should we compare to current_dbgstate?
428             #print repr(self.data[4])
429             if (len(self.data)>4 and self.data[4] == '\x00'):
430               print >>sys.stderr,("FAILED TO READ MEMORY/RE-ENTER DEBUG MODE")
431               raise Exception("FAILED TO READ MEMORY/RE-ENTER DEBUG MODE")
432               return (-1);
433             else:
434               retval.append( self.ARMget_register(1) )  # read memory value from R1 register
435               #print >>sys.stderr,("CPSR: %x\t\tR0: %x\t\tR1: %x"%(self.ARMget_regCPSR(),self.ARMget_register(0),self.ARMget_register(1)))
436         self.ARMset_register(1, r1);       # restore R0 and R1 
437         self.ARMset_register(0, r0);
438         return retval
439
440     def ARMpeekcodewords(self,adr,words):
441         """Read the contents of code memory at an address."""
442         self.data=[adr&0xff, (adr>>8)&0xff, (adr>>16)&0xff, (adr>>24)&0xff, words&0xff, (words>>8)&0xff, (words>>16)&0xff, (words>>24)&0xff ]
443         self.writecmd(0x13,READ_CODE_MEMORY,8,self.data)
444         retval = []
445         retval.append(self.serialport.read(words*4))
446         #retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
447         return "".join(retval)
448     def ARMpeekdatabyte(self,adr):
449         """Read the contents of data memory at an address."""
450         self.data=[ adr&0xff, (adr>>8)&0xff, (adr>>16)&0xff, (adr>>24)&0xff ]
451         self.writecmd(0x13, PEEK, 4, self.data)
452         #retval.append(self.serialport.read(words*4))
453         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
454         return retval
455     def ARMpokedatabyte(self,adr,val):
456         """Write a byte to data memory."""
457         self.data=[adr&0xff, (adr>>8)&0xff, (adr>>16)&0xff, (adr>>24)&0xff, val&0xff, (val>>8)&0xff, (val>>16)&0xff, (val>>24)&0xff ]
458         self.writecmd(0x13, POKE, 8, self.data)
459         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
460         return retval
461     #def ARMchiperase(self):
462     #    """Erase all of the target's memory."""
463     #    self.writecmd(0x13,CHIP_ERASE,0,[])
464     def ARMstatus(self):
465         """Check the status."""
466         self.writecmd(0x13,GET_DEBUG_STATE,0,[])
467         return ord(self.data[0])
468     ARMstatusbits={
469                   0x10 : "TBIT",
470                   0x08 : "cgenL",
471                   0x04 : "Interrupts Enabled (or not?)",
472                   0x02 : "DBGRQ",
473                   0x01 : "DGBACK"
474                   }
475     ARMctrlbits={
476                   0x04 : "disable interrupts",
477                   0x02 : "force dbgrq",
478                   0x01 : "force dbgack"
479                   }
480                   
481     def ARMstatusstr(self):
482         """Check the status as a string."""
483         status=self.ARMstatus()
484         str=""
485         i=1
486         while i<0x100:
487             if(status&i):
488                 str="%s %s" %(self.ARMstatusbits[i],str)
489             i*=2
490         return str
491     def ARMchain0(self, address, bits, data):
492         bulk = chop(address,4)
493         bulk.extend(chop(bits,8))
494         bulk.extend(chop(data,4))
495         print (repr(bulk))
496         self.writecmd(0x13,CHAIN0,16,bulk)
497         d1,b1,a1 = struct.unpack("<LQL",self.data)
498         return (a1,b1,d1)
499     def start(self):
500         """Start debugging."""
501         self.writecmd(0x13,START,0,self.data)
502         ident=self.ARMidentstr()
503         print "Target identifies as %s." % ident
504         print "Debug Status: %s." % self.ARMstatusstr()
505         #print "System State: %x." % self.ARMget_regCPSRstr()
506         #self.ARMreleasecpu()
507         #self.ARMhaltcpu()
508         
509     def stop(self):
510         """Stop debugging."""
511         self.writecmd(0x13,STOP,0,self.data)
512     #def ARMstep_instr(self):
513     #    """Step one instruction."""
514     #    self.writecmd(0x13,STEP_INSTR,0,self.data)
515     #def ARMflashpage(self,adr):
516     #    """Flash 2kB a page of flash from 0xF000 in XDATA"""
517     #    data=[adr&0xFF,
518     #          (adr>>8)&0xFF,
519     #          (adr>>16)&0xFF,
520     #          (adr>>24)&0xFF]
521     #    print "Flashing buffer to 0x%06x" % adr
522     #    self.writecmd(0x13,MASS_FLASH_PAGE,4,data)
523
524     def writecmd(self, app, verb, count=0, data=[]):
525         """Write a command and some data to the GoodFET."""
526         self.serialport.write(chr(app))
527         self.serialport.write(chr(verb))
528         count = len(data)
529         #if data!=None:
530         #    count=len(data); #Initial count ignored.
531
532         #print "TX %02x %02x %04x" % (app,verb,count)
533
534         #little endian 16-bit length
535         self.serialport.write(chr(count&0xFF))
536         self.serialport.write(chr(count>>8))
537
538         #print "count=%02x, len(data)=%04x" % (count,len(data))
539
540         if count!=0:
541             if(isinstance(data,list)):
542                 for i in range(0,count):
543                     #print "Converting %02x at %i" % (data[i],i)
544                     data[i]=chr(data[i])
545             #print type(data)
546             outstr=''.join(data)
547             self.serialport.write(outstr)
548         if not self.besilent:
549             self.readcmd()
550
551
552