welcome: please sign in

Exactly one page like "SamplerPitchShifter" found, redirecting to page.

Clear message
location: PitchShifterChromatic

MICrODEC: Stock Functions

Chromatic Pitch Shifter

This function implements a pitch shifter. It takes mono in on the left channel, and outputs a mono signal to both the left and the right channels. The rotary encoder (MOD2) adjusts the pitch shift amount, and the pot (MOD1) adjusts the buffer size (and resultant delay).

The easiest way to change the pitch of a signal, is to play back the samples at a faster or slower rate, although this introduces two problems. The first is that if you are sending out data at a rate different from the rate at which you are receiving data, you will eventually run out of data to send out. This is called a buffer over-run or under-run, depending upon whether you're going faster or slower, and hitting the top or bottom of the buffer (the data stored in SRAM). The second problem is that the data is sampled at discreet points in time, and if you want a playback speed that is not a multiple of this time, you will need data from somewhere between those sample periods.

There are a number of options for how to deal with buffer boundaries, but the main issue which we are trying to overcome, is the sharp transition as you go from one end of the buffer to the other, and the data is no longer consistent. This creates an audible click in the sample playback. In this case, we are using a fading method. This involves having two samples playing back simultaneously, each from a different point in the buffer (spaced a half-buffer's distance from each other). As one sample gets closer to the boundary, its volume is faded down, and the other is faded up. This continues as each sample moves forward in the buffer, with the volume of the sample being determined by its distance from the buffer boundary. This gives very smooth transitions across the buffer boundary, but also has a slight reverb effect, as multiple delayed signals are being mixed together.

The most common method of dealing with the second problem (fractional sample rates) is interpolation. Interpolation is a method of guessing what a value might have been if we actually had sampled at that point in time. For this pitch-shifter function, we use a linear interpolation. This means we draw a straight line between the two adjacent samples from where we want data, and assume our value is on that line. So if we're closer in time to one sample versus the other, than our output value is closer in value to that sample (the output is a sum of the two values, weighted by their distance to our sample point).

The pot (MOD1) varies the buffer size used for sample playback, from 12ms to 1.5s. Smaller buffer sizes give a more accurate pitch-shifting effect, as you are playing through only very small samples at a time, and do not hear much of a delay. If the buffer is too small, you begin to hear the rate at which you are moving through the buffer, almost like a slight tremolo. For very large buffer sizes, it becomes a pitch shifted delay, which can be interesting when used with feedback, as each time the signal gets fed back in, its pitch is shifted again, causing an ever rising tone.

The rotary encoder (MOD2) varies the pitch shift amount, from -1 octave to +1 octave, in 12 chromatic steps each direction. In this way, the output can always be made to be "in tune" with the original signal. Rotations to the right increase the pitch, and vice versa. To maintain the correct pitch shift amount, a look-up table in program memory is used to store the precalculated values for each step.

pitchshifter_chromatic.asm


   1 ; program: pitch_shifter-16b-fading.asm
   2 ; UID = 000039 - this is a unique id so variables dont conflict
   3 ; 16b address space (1.5s sample time)
   4 ; mono data in on left channel, mono out on both left and right
   5 ; rotary encoder (MOD2) controlled playback speed
   6 ; pot (MOD1) controlled buffer size
   7 
   8 ; program overview
   9 ;
  10 ; data is sent out and taken in from the codec.  data is taken in on the
  11 ; left channel, and played out on both left and right.  a buffer of the
  12 ; past n seconds is kept and the output is the result of sampling this
  13 ; buffer at varying playback speeds. the speed at which it plays through
  14 ; the memory is controlled by the rotary encoder (MOD2).  turning the
  15 ; encoder to the right speeds playback up, and turning it the left slows
  16 ; playback down.  this playback speed is limited to chromatic steps by
  17 ; using a lookup table in program memory to determine playback speed.  the
  18 ; audio is kept clean over fractional sample periods by interpolating
  19 ; between the two closest samples.  the output is a mix of the current
  20 ; sample, and a sample from the opposite side of the buffer, with the
  21 ; relative mix being determined by the distance to the buffer boundary.
  22 ; in this way, the audio is faded down as it crosses the buffer boundary.
  23 ; the pot (MOD1) controls the buffer size.  the adc samples the pot 256
  24 ; times and deadbands the signal to remove glitches.
  25 
  26 ; constant definitions
  27 ;
  28 .equ buffer_min_000039 = $0200 ; minimum buffer size
  29 .equ delay_mem_000039 = $0200 ; memory position for desired delay time
  30 ;
  31 ;.equ step-12_000039 = $0080 ; these are the playback speeds used
  32 ;.equ step-11_000039 = $0088 ; they are stored in program memory
  33 ;.equ step-10_000039 = $0090 ; and not used here
  34 ;.equ step-9_000039 = $0098
  35 ;.equ step-8_000039 = $00A1
  36 ;.equ step-7_000039 = $00AB
  37 ;.equ step-6_000039 = $00B5
  38 ;.equ step-5_000039 = $00C0
  39 ;.equ step-4_000039 = $00CB
  40 ;.equ step-3_000039 = $00D7
  41 ;.equ step-2_000039 = $00E4
  42 ;.equ step-1_000039 = $00F2
  43 ;.equ step00_000039 = $0100
  44 ;.equ step01_000039 = $010F
  45 ;.equ step02_000039 = $011F
  46 ;.equ step03_000039 = $0130
  47 ;.equ step04_000039 = $0143
  48 ;.equ step05_000039 = $0156
  49 ;.equ step06_000039 = $016A
  50 ;.equ step07_000039 = $0180
  51 ;.equ step08_000039 = $0196
  52 ;.equ step09_000039 = $01AF
  53 ;.equ step10_000039 = $01C8
  54 ;.equ step11_000039 = $01E3
  55 ;.equ step12_000039 = $0200
  56 
  57 ; register usage - may be redefined in other sections
  58 ;
  59 ; r0  multiply result lsb
  60 ; r1  multiply result msb
  61 ; r2  sample 3/4 lsb
  62 ; r3  sample 3/4 msb
  63 ; r4  left/right lsb out
  64 ; r5  left/right msb out
  65 ; r6  left lsb in/temporary swap register
  66 ; r7  left msb in/temporary swap register
  67 ; r8  rotary encoder position counter
  68 ; r9  adc msb accumulator
  69 ; r10 adc fractional byte accumulator
  70 ; r11 adc lsb accumulator
  71 ; r12 playback speed increment lsb value ($0100 is normal speed)
  72 ; r13 playback speed increment msb value
  73 ; r14 rotary encoder counter
  74 ; r15 switch\adc counter
  75 ; r16 temporary swap register
  76 ; r17 temporary swap register
  77 ; r18 signed multiply register
  78 ; r19 signed multiply register
  79 ; r20 unsigned multiply register
  80 ; r21 unsigned multiply register
  81 ; r22 write address third byte/null register
  82 ; r23 read address fractional byte
  83 ; r24 write address lsb
  84 ; r25 write address msb
  85 ; r26 buffer length lsb
  86 ; r27 buffer length msb
  87 ; r28 read address lsb
  88 ; r29 read address msb
  89 ; r30 jump location for interrupt lsb
  90 ; r31 jump location for interrupt msb
  91 ; t   rotary encoder edge indicator
  92 
  93 ;program starts here first time
  94 ; intialize registers
  95 ldi r30,$29 ; set jump location to program start
  96 clr r24 ; clear write register
  97 clr r25
  98 ldi r22,$00 ; setup write address high byte
  99 clr r18 ; setup r18 as null register for carry addition and ddr setting
 100 ldi r17,$ff ; setup r17 for ddr setting
 101 
 102 clear_000039: ; clear delay buffer
 103 ; eliminates static when first switching to the delay setting
 104 
 105 adiw r25:r24,$01 ; increment write register
 106 adc r22,r18 ; increment write third byte
 107 cpi r22,$01 ; check if 16b memory space has been cleared
 108 breq cleardone_000039 ; continue until end of buffer reached
 109 out portd,r24 ; set address
 110 sts porth,r25
 111 out portg,r22 ; pull ce low,we low,and set high bits of address
 112 out ddra,r17 ; set porta as output for data write
 113 out ddrc,r17 ; set portc as output for data write
 114 out porta,r18 ; set data
 115 out portc,r18 ; r18 is cleared above
 116 sbi portg,portg2 ; pull we high to write
 117 out ddra,r18 ; set porta as input for data lines
 118 out ddrc,r18 ; set portc as input for data lines
 119 rjmp clear_000039 ; continue clearing
 120 
 121 cleardone_000039: ; reset registers
 122 
 123 ldi r24,$00 ; initialize write register
 124 ldi r25,$00
 125 clr r22 ; setup null register
 126 ldi r28,$00 ; set read address to minimum delay
 127 ldi r29,$fd
 128 clr r4 ; initialize data output registers
 129 clr r5
 130 ldi r26,$00 ; initialize buffer size
 131 ldi r27,$06
 132 sts delay_mem_000039,r26 ; store desired buffer size
 133 sts (delay_mem_000039 + 1),r27 ; i ran out of registers
 134 clr r12 ; initialize playback speed
 135 ldi r16,$01
 136 mov r13,r16
 137 ldi r16,$0c ; initialize playback speed pointer
 138 mov r8,r16
 139 reti ; return and wait for next interrupt
 140 
 141 ;program begins here every time but first
 142 ; initiate data transfer to codec
 143 sbi portb,portb0 ; toggle slave select pin
 144 out spdr,r5 ; send out left channel msb
 145 cbi portb,portb0
 146 
 147 ;increment write address
 148 adiw r25:r24,$01 ; increment write address
 149 cp r24,r26 ; check if at end of buffer
 150 cpc r25,r27
 151 brlo wait1_000039 ; do nothing if not at end of buffer
 152 clr r24 ; reset buffer to bottom
 153 clr r25
 154 
 155 wait1_000039: ; check if byte has been sent
 156 
 157 in r17,spsr
 158 sbrs r17,spif
 159 rjmp wait1_000039
 160 in r7,spdr ; recieve in left channel msb
 161 out spdr,r4 ; send out left channel lsb
 162 
 163 ;increment read address
 164 add r23,r12 ; increment read register
 165 adc r28,r13
 166 adc r29,r22 ; r22 is cleared above
 167 cp r28,r26 ; check if at end of buffer
 168 cpc r29,r27
 169 brlo wait2_000039 ; do nothing if not at end of buffer
 170 clr r28 ; reset buffer to bottom
 171 clr r29
 172 
 173 wait2_000039: ; check if byte has been sent
 174 
 175 in r17,spsr
 176 sbrs r17,spif
 177 rjmp wait2_000039
 178 in r6,spdr ; recieve in left channel lsb
 179 out spdr,r5 ; send out right channel msb
 180 
 181 ;write left channel data to sram
 182 out portd,r24 ; set address
 183 sts porth,r25
 184 out portg,r22 ; pull ce low,we low,and set high bits of address
 185 ldi r17,$ff
 186 out ddra,r17 ; set porta as output for data write
 187 out ddrc,r17 ; set portc as output for data write
 188 out porta,r6 ; set data
 189 out portc,r7
 190 sbi portg,portg2 ; pull we high to write
 191 out ddra,r22 ; set porta as input for data lines
 192 out ddrc,r22 ; set portc as input for data lines
 193 
 194 wait3_000039: ; check if byte has been sent
 195 
 196 in r17,spsr
 197 sbrs r17,spif
 198 rjmp wait3_000039
 199 in r17,spdr ; recieve in right channel msb
 200 out spdr,r4 ; send out right channel lsb
 201 
 202 ;get left channel sample 1 data from sram
 203 movw r17:r16,r29:r28 ; move read address to temporary register
 204 out portd,r16 ; set address
 205 sts porth,r17
 206 ldi r21,$01 ; increment read address
 207 add r16,r21 ; placed here to use 2 cycle wait
 208 adc r17,r22 ; r22 is cleared above
 209 in r6,pina ; get data
 210 in r18,pinc ; get data
 211 cp r16,r26 ; check if at end of buffer
 212 cpc r17,r27
 213 brlo wait4_000039 ; do nothing if not at end of buffer
 214 clr r16 ; reset buffer to bottom
 215 clr r17
 216 
 217 wait4_000039: ; check if byte has been sent
 218 
 219 in r19,spsr
 220 sbrs r19,spif
 221 rjmp wait4_000039
 222 in r19,spdr ; recieve in right channel lsb
 223 
 224 ;get left channel sample 2 data from sram
 225 out portd,r16 ; set address
 226 sts porth,r17
 227 nop ; wait 2 cycle setup time
 228 nop
 229 in r7,pina ; get data
 230 in r19,pinc ; get data
 231 
 232 ;multiply sample 1 by distance
 233 mov r20,r23 ; get distance from sample 1
 234 com r20
 235 mulsu r18,r20 ; (signed)Ah * (unsigned)B
 236 movw r5:r4,r1:r0
 237 mul     r6,r20  ; (unsigned)Al * (unsigned)B
 238 add     r4,r1
 239 adc     r5,r22 ; r22 is cleared above
 240 mov r17,r0
 241 
 242 ;multiply and accumulate sample 2 by distance
 243 mulsu r19,r23 ; (signed)Ah * (unsigned)B
 244 add r4,r0 ; accumulate result
 245 adc r5,r1
 246 mul     r7,r23  ; (unsigned)Al * (unsigned)B
 247 add r17,r0 ; accumulate result
 248 adc     r4,r1
 249 adc     r5,r22 ; r22 is cleared above
 250 
 251 ;get sample from other side of buffer
 252 movw r17:r16,r29:r28 ; move current position to temporary register
 253 movw r7:r6,r27:r26 ; move buffer size to temporary register
 254 lsr r7 ; divide buffer size by 2
 255 ror r6
 256 cp r16,r6 ; check if in lower or upper half of buffer
 257 cpc r17,r7
 258 brsh buffer_flip_000039 ; subtract half buffer if in upper half
 259 add r16,r6 ; add half buffer size if in lower half
 260 adc r17,r7
 261 rjmp getsample3_000039 ; continue
 262 
 263 buffer_flip_000039: ; adjust to opposite side of memory
 264 
 265 sub r16,r6 ; subtract half buffer size if in upper half
 266 sbc r17,r7
 267 
 268 getsample3_000039: ;get left channel sample 3 data from sram
 269 
 270 out portd,r16 ; set address
 271 sts porth,r17
 272 add r16,r21 ; increment read address - r21 set to $01 above
 273 adc r17,r22 ; r22 is cleared above
 274 in r6,pina ; get data
 275 in r18,pinc ; get data
 276 cp r16,r26 ; check if at end of buffer
 277 cpc r17,r27
 278 brlo getsample4_000039 ; do nothing if not at end of buffer
 279 clr r16 ; reset buffer to bottom
 280 clr r17
 281 
 282 getsample4_000039: ;get left channel sample 4 data from sram
 283 
 284 out portd,r16 ; set address
 285 sts porth,r17
 286 nop ; wait 2 cycle setup time
 287 nop
 288 in r7,pina ; get data
 289 in r19,pinc ; get data
 290 
 291 ;multiply sample 3 by distance
 292 mulsu r18,r20 ; (signed)ah * b
 293 movw r3:r2,r1:r0
 294 mul     r6,r20 ; al * b
 295 add     r2,r1
 296 adc     r3,r22 ; r22 is cleared above
 297 mov r17,r0
 298 
 299 ;multiply sample 4 by distance
 300 mulsu r19,r23 ; (signed)ah * b
 301 add r2,r0 ; accumulate result
 302 adc r3,r1
 303 mul     r7,r23  ; al * b
 304 add r17,r0 ; accumulate result
 305 adc     r2,r1
 306 adc     r3,r22 ; r22 is cleared above
 307 
 308 ;get distance to boundary
 309 movw r17:r16,r29:r28 ; move read address to temporary register
 310 mov r18,r23
 311 sub r16,r24 ; find distance to loop boundary
 312 sbc r17,r25
 313 brcc half_000039 ; check if result is negative
 314 com r16 ; invert distance if negative
 315 com r17
 316 com r18
 317 add r18,r21 ; r21 set to $01 above
 318 adc r16,r22 ; r22 cleared above
 319 adc r17,r22
 320 
 321 half_000039: ; check if result is greater than half the buffer size
 322 
 323 movw r7:r6,r27:r26 ; move buffer size to temporary register
 324 lsr r7 ; divide buffer size by 2
 325 ror r6
 326 cp r16,r6 ; check if result is greater than half the buffer size
 327 cpc r17,r7
 328 brlo scale_000039 ; skip flip if not
 329 sub r16,r26 ; flip result around boundary
 330 sbc r17,r27
 331 com r16
 332 com r17
 333 com r18
 334 add r18,r21 ; r21 set to $01 above
 335 adc r16,r22
 336 adc r17,r22
 337 
 338 scale_000039: ; scale distance to match buffer size - 50% accurate
 339 
 340 movw r7:r6,r27:r26 ; move buffer size to temporary register
 341 sbrc r7,$07 ; check if msb of buffer size is set
 342 rjmp attenuate_000039 ; attenuate signal if 16b value
 343 
 344 shift_000039: ; shift buffer size till it occupies full 16b
 345 
 346 lsl r6 ; multiply buffer size by 2
 347 rol r7
 348 lsl r18 ; multiply distance by 2
 349 rol r16
 350 rol r17
 351 sbrs r7,$07 ; check if msb of buffer size is set
 352 rjmp shift_000039 ; keep checking if not set
 353 
 354 attenuate_000039: ; multiply sample 1/2 by distance
 355 
 356 lsl r18 ; multiply distance by 2 since max value is 1/2 buffer size
 357 rol r16
 358 rol r17
 359 sub r6,r16 ; find complementary distance of sample 3/4
 360 sbc r7,r17 ; only 1 bit error for not subtracting r18 as well
 361 movw r21:r20,r7:r6 ; move distance to signed multiply register
 362 movw r19:r18,r5:r4 ; move value to signed multiply register
 363 mulsu r19,r17 ; (signed)ah * bh
 364 movw r5:r4,r1:r0
 365 mul     r18,r16 ; al * bl
 366 movw r7:r6,r1:r0
 367 mulsu r19,r16 ; (signed)ah * bl
 368 sbc     r5,r22 ; r22 is cleared above
 369 add     r7,r0
 370 adc     r4,r1
 371 adc     r5,r22
 372 mul r17,r18 ; bh * al
 373 add     r7,r0
 374 adc     r4,r1
 375 adc     r5,r22
 376 
 377 ;multiply and accumulate sample 3/4 with result from above
 378 movw r19:r18,r3:r2 ; move value to signed multiply register
 379 mulsu r19,r21 ; (signed)ah * bh
 380 add     r4,r0 ; accumulate result
 381 adc     r5,r1
 382 mul     r18,r20 ; al * bl
 383 add     r6,r0 ; accumulate result
 384 adc     r7,r1
 385 adc     r4,r22 ; r22 is cleared above
 386 adc     r5,r22
 387 mulsu r19,r20 ; (signed)ah * bl
 388 sbc     r5,r22 ; accumulate result
 389 add     r7,r0
 390 adc     r4,r1
 391 adc     r5,r22
 392 mul r21,r18 ; bh * al
 393 add     r7,r0
 394 adc     r4,r1
 395 adc     r5,r22
 396 
 397 rotary_000039: ; check rotary encoder and adjust playback rate
 398 ; rotary encoder is externally debounced, so that is not done here.
 399 ; pin1 is sampled on a transition from high to low on pin0.  if pin1 is
 400 ; high, a left turn occured, if pin1 is low, a right turn occured.
 401 dec r14 ; reduce the sampling rate to help with debounce
 402 brne check_000039 ; continue if not ready yet
 403 ldi r17,$40 ; adjust sample frequency to catch all rising edges (1.5ms)
 404 mov r14,r17
 405 lds r17,pinj ; get switch data
 406 sbrs r17,$00 ; check if pin0 is low
 407 rjmp edge_000039 ; check if pin0 was low on previous sample
 408 clt ;  clear state register if back high
 409 rjmp check_000039 ; finish off
 410 
 411 edge_000039: ; check for falling edge
 412 
 413 brts check_000039 ; do nothing if the edge was already detected
 414 set ; set state register to indicate a falling edge occured
 415 sbrs r17,$01 ; check if pin1 is high
 416 rjmp increment_000039 ; increment playback if right rotation
 417 ldi r16,$01 ; check if pitch at min
 418 cp r8,r16
 419 brlo check_000039 ; do nothing it at bottom
 420 dec r8 ; decrement rotary encoder position counter
 421 movw r17:r16,z ; store z register
 422 ldi zh,$4c ; setup z pointer to fetch tone from lookup table
 423 mov zl,r8
 424 lsl zl
 425 lpm r12,z+ ; move tone to pitch register
 426 lpm r13,z
 427 movw z,r17:r16 ; restore z register
 428 rjmp check_000039 ; finish off
 429 
 430 increment_000039: ; increment playback speed
 431 
 432 ldi r16,$18 ; check if pitch at max
 433 cp r8,r16
 434 brsh reset1_000039 ; do nothing if at max already
 435 inc r8 ; increment rotary encoder position counter
 436 movw r17:r16,z ; store z register
 437 ldi zh,$4c ; setup z pointer to fetch tone from lookup table
 438 mov zl,r8
 439 lsl zl
 440 lpm r12,z+ ; move tone to pitch register
 441 lpm r13,z
 442 movw z,r17:r16 ; restore z register
 443 rjmp check_000039 ; finish off
 444 
 445 reset1_000039: ; reset tone register in case it goes too high
 446 
 447 mov r8,r16 ; set tone register to max
 448 
 449 check_000039: ; check if buffer size is correct
 450 
 451 lds r16,delay_mem_000039 ; fetch desired buffer size
 452 lds r17,(delay_mem_000039 + 1) ; i ran out of registers
 453 cp r26,r16 ; compare current delay to desired delay
 454 cpc r27,r17
 455 brlo upcount_000039 ; increment if smaller than
 456 breq adcsample_000039 ; do nothing if they are same size
 457 sbiw r27:r26,$02 ; decrement buffer size
 458 rjmp adcsample_000039 ; finish off
 459 
 460 upcount_000039: ; increment buffer size register
 461 
 462 adiw r27:r26,$02 ; increment buffer size
 463 
 464 adcsample_000039: ; get loop setting
 465 
 466 lds r17,adcsra ; get adc control register
 467 sbrs r17,adif ; check if adc conversion is complete
 468 rjmp done_000039 ; skip adc sampling
 469 lds r16,adcl ; get low byte adc value
 470 lds r17,adch ; get high byte adc value
 471 add r10,r16 ; accumulate adc samples
 472 adc r11,r17
 473 adc r9,r22 ; r22 is cleared above
 474 ldi r17,$f7
 475 sts adcsra,r17 ; clear interrupt flag
 476 dec r15 ; countdown adc sample clock
 477 brne done_000039 ; move adc value to loop setting after 256 samples
 478 lsr r9 ; divide accumulated value by 4 to make a 16b value
 479 ror r11
 480 ror r10
 481 lsr r9
 482 ror r11
 483 ror r10
 484 ldi r16,low(buffer_min_000039) ; fetch min buffer size
 485 ldi r17,high(buffer_min_000039)
 486 cp r10,r16 ; compare adc value to min buffer size
 487 cpc r11,r17
 488 brsh compare_000039 ; skip if above minimum buffer size
 489 movw r11:r10,r17:r16 ; else set to minimum buffer size
 490 
 491 compare_000039: ; compare to previous value
 492 
 493 lds r16,delay_mem_000039 ; fetch desired delay time
 494 lds r17,(delay_mem_000039 + 1) ; i ran out of registers
 495 sub r16,r10 ; find difference between adc value and desired buffer size
 496 sbc r17,r11
 497 brcc deadband_000039 ; check for magnitude of change if positive
 498 neg r16 ; else invert difference if negative
 499 adc r17,r22 ; r22 is cleared above
 500 neg r17
 501 
 502 deadband_000039: ; see if pot has moved or if its just noise
 503 
 504 cpi r16,$40 ; see if difference is greater than 1 lsb
 505 cpc r17,r22 ; r22 is cleared above
 506 brlo nochange_000039 ; dont update loop time if difference is not large enough
 507 ldi r16,$fe ; make sure buffer size is even
 508 and r10,r16
 509 sts delay_mem_000039,r10 ; store new desired buffer size
 510 sts (delay_mem_000039 + 1),r11 ; i ran out of registers
 511 
 512 nochange_000039: ; clear accumulation registers
 513 
 514 clr r10 ; empty accumulation registers
 515 clr r11
 516 clr r9
 517 
 518 switchsample_000039: ; check rotary switch state
 519 
 520 lds r16,pinj ; get switch data
 521 andi r16,$78 ; mask off rotary switch
 522 lsr r16 ; adjust switch position to program memory location
 523 lsr r16
 524 ldi r17,$02
 525 add r16,r17
 526 cpse r16,r31 ; check if location has changed
 527 clr r30 ; reset jump register to intial state
 528 mov r31,r16
 529 
 530 done_000039:
 531 
 532 reti ; return to waiting
 533