Recent

Author Topic: UnoLib - library in Pascal for Arduino Uno and ATMega328p  (Read 9391 times)

ackarwow

  • Full Member
  • ***
  • Posts: 139
    • Andrzej Karwowski's Homepage
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #15 on: March 01, 2025, 02:16:57 pm »
We prepared new translations of PulseIn routines. I placed them in dedicated pulse.pas unit (attached). This unit includes:
- pulseIn (based partially on assembler routine; returns time in microseconds)
- pulseInLong (returns time in microseconds)
- pulseInMilli (written by @Dzandaa, returns time in milliseconds)
Also test program using HC-SR04 sensor is attached.

I have some doubts on the assembler routine pulseInSimplAsm. It is simply function pulseInSimpl compiled and disassembled via avr-objdump to find number of clock cycles in main iteration (20 as I estimated). My doubts are about the registers, I am not sure if these particular registers can be used without problems and without negatively affecting the rest of the program...

ccrause

  • Hero Member
  • *****
  • Posts: 1007
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #16 on: March 01, 2025, 04:34:33 pm »
We prepared new translations of PulseIn routines. I placed them in dedicated pulse.pas unit (attached). This unit includes:
- pulseIn (based partially on assembler routine; returns time in microseconds)
- pulseInLong (returns time in microseconds)
- pulseInMilli (written by @Dzandaa, returns time in milliseconds)
Also test program using HC-SR04 sensor is attached.
After a quick look I think that the local variable width in function pulseInSimpl is unnecessary and can be replaced by Result.  This wouldn't make the body of the function much smaller, but would reduce stack use, since width is stored on the stack.

Quote
I have some doubts on the assembler routine pulseInSimplAsm. It is simply function pulseInSimpl compiled and disassembled via avr-objdump to find number of clock cycles in main iteration (20 as I estimated). My doubts are about the registers, I am not sure if these particular registers can be used without problems and without negatively affecting the rest of the program...
The obvious consideration is that the compiler generated the code for the Pascal function, so it should be safe to call in the context of the current program.  FPC follows the avr-gcc ABI and the usage of registers (which registers can be left modified and which registers should be restored when a routine returns) is described here.

ackarwow

  • Full Member
  • ***
  • Posts: 139
    • Andrzej Karwowski's Homepage
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #17 on: March 01, 2025, 05:23:26 pm »
@ccrause, thanks for your comments :)

After a quick look I think that the local variable width in function pulseInSimpl is unnecessary and can be replaced by Result.  This wouldn't make the body of the function much smaller, but would reduce stack use, since width is stored on the stack.

Of course you are right. But this variable appears in original C code, so I used it.

The obvious consideration is that the compiler generated the code for the Pascal function, so it should be safe to call in the context of the current program.  FPC follows the avr-gcc ABI and the usage of registers (which registers can be left modified and which registers should be restored when a routine returns) is described here.

Thanks for the link. So according to ABI rules the function should work even after changing the compiler optimization level, do I understand correctly?

I don't know if the arduino compiler uses ABI rules (but uses code generated by avr-gcc). If yes - perhaps it's possible to use the original assembler code from the arduino sources (please correct me if I'm wrong):

Code: Text  [Select][+][-]
  1.  
  2. /*
  3.  * The following routine was generated by avr-gcc 4.8.3 with the following parameters
  4.  * -gstabs -Wa,-ahlmsd=output.lst -dp -fverbose-asm -O2
  5.  * on the original C function
  6.  *
  7.  * unsigned long pulseInSimpl(volatile uint8_t *port, uint8_t bit, uint8_t stateMask, unsigned long maxloops)
  8.  * {
  9.  *     unsigned long width = 0;
  10.  *     // wait for any previous pulse to end
  11.  *     while ((*port & bit) == stateMask)
  12.  *         if (--maxloops == 0)
  13.  *             return 0;
  14.  *
  15.  *     // wait for the pulse to start
  16.  *     while ((*port & bit) != stateMask)
  17.  *         if (--maxloops == 0)
  18.  *             return 0;
  19.  *
  20.  *     // wait for the pulse to stop
  21.  *     while ((*port & bit) == stateMask) {
  22.  *         if (++width == maxloops)
  23.  *             return 0;
  24.  *     }
  25.  *     return width;
  26.  * }
  27.  *
  28.  * some compiler outputs were removed but the rest of the code is untouched
  29.  */
  30.  
  31. #include <avr/io.h>
  32.  
  33. .section .text
  34.  
  35. .global countPulseASM
  36.  
  37. countPulseASM:
  38.  
  39. .LM0:
  40. .LFBB1:
  41.     push r12   ;   ;  130 pushqi1/1 [length = 1]
  42.     push r13   ;   ;  131 pushqi1/1 [length = 1]
  43.     push r14   ;   ;  132 pushqi1/1 [length = 1]
  44.     push r15   ;   ;  133 pushqi1/1 [length = 1]
  45.     push r16   ;   ;  134 pushqi1/1 [length = 1]
  46.     push r17   ;   ;  135 pushqi1/1 [length = 1]
  47. /* prologue: function */
  48. /* frame size = 0 */
  49. /* stack size = 6 */
  50. .L__stack_usage = 6
  51.     mov r30,r24  ;  port, port   ;  2 *movhi/1  [length = 2]
  52.     mov r31,r25  ;  port, port
  53. /*     unsigned long width = 0;
  54. ***     // wait for any previous pulse to end
  55. ***     while ((*port & bit) == stateMask)
  56. */
  57. .LM1:
  58.     rjmp .L2   ;   ;  181 jump  [length = 1]
  59. .L4:
  60. /*         if (--maxloops == 0) */
  61. .LM2:
  62.     subi r16,1   ;  maxloops,  ;  17  addsi3/2  [length = 4]
  63.     sbc r17, r1   ;  maxloops
  64.     sbc r18, r1   ;  maxloops
  65.     sbc r19, r1   ;  maxloops
  66.     breq .L13  ; ,   ;  19  branch  [length = 1]
  67. .L2:
  68. /*         if (--maxloops == 0) */
  69. .LM3:
  70.     ld r25,Z   ;  D.1554, *port_7(D)   ;  22  movqi_insn/4  [length = 1]
  71.     and r25,r22  ;  D.1554, bit  ;  24  andqi3/1  [length = 1]
  72.     cp r25,r20   ;  D.1554, stateMask  ;  25  *cmpqi/2  [length = 1]
  73.     breq .L4   ; ,   ;  26  branch  [length = 1]
  74.     rjmp .L6   ;   ;  184 jump  [length = 1]
  75. .L7:
  76. /*             return 0;
  77. ***
  78. ***     // wait for the pulse to start
  79. ***     while ((*port & bit) != stateMask)
  80. ***         if (--maxloops == 0)
  81. */
  82. .LM4:
  83.     subi r16,1   ;  maxloops,  ;  31  addsi3/2  [length = 4]
  84.     sbc r17, r1   ;  maxloops
  85.     sbc r18, r1   ;  maxloops
  86.     sbc r19, r1   ;  maxloops
  87.     breq .L13  ; ,   ;  33  branch  [length = 1]
  88. .L6:
  89. /*         if (--maxloops == 0) */
  90. .LM5:
  91.     ld r25,Z   ;  D.1554, *port_7(D)   ;  41  movqi_insn/4  [length = 1]
  92.     and r25,r22  ;  D.1554, bit  ;  43  andqi3/1  [length = 1]
  93.     cpse r25,r20   ;  D.1554, stateMask  ;  44  enable_interrupt-3  [length = 1]
  94.     rjmp .L7   ;
  95.     mov r12, r1   ;  width  ;  7 *movsi/2  [length = 4]
  96.     mov r13, r1   ;  width
  97.     mov r14, r1   ;  width
  98.     mov r15, r1   ;  width
  99.     rjmp .L9   ;   ;  186 jump  [length = 1]
  100. .L10:
  101. /*             return 0;
  102. ***
  103. ***     // wait for the pulse to stop
  104. ***     while ((*port & bit) == stateMask) {
  105. ***         if (++width == maxloops)
  106. */
  107. .LM6:
  108.     ldi r24,-1   ; ,   ;  50  addsi3/3  [length = 5]
  109.     sub r12,r24  ;  width,
  110.     sbc r13,r24  ;  width,
  111.     sbc r14,r24  ;  width,
  112.     sbc r15,r24  ;  width,
  113.     cp r16,r12   ;  maxloops, width  ;  51  *cmpsi/2  [length = 4]
  114.     cpc r17,r13  ;  maxloops, width
  115.     cpc r18,r14  ;  maxloops, width
  116.     cpc r19,r15  ;  maxloops, width
  117.     breq .L13  ; ,   ;  52  branch  [length = 1]
  118. .L9:
  119. /*         if (++width == maxloops) */
  120. .LM7:
  121.     ld r24,Z   ;  D.1554, *port_7(D)   ;  60  movqi_insn/4  [length = 1]
  122.     and r24,r22  ;  D.1554, bit  ;  62  andqi3/1  [length = 1]
  123.     cp r24,r20   ;  D.1554, stateMask  ;  63  *cmpqi/2  [length = 1]
  124.     breq .L10  ; ,   ;  64  branch  [length = 1]
  125. /*             return 0;
  126. ***     }
  127. ***     return width;
  128. */
  129. .LM8:
  130.     mov r22,r12  ;  D.1553, width  ;  108 movqi_insn/1  [length = 1]
  131.     mov r23,r13  ;  D.1553, width  ;  109 movqi_insn/1  [length = 1]
  132.     mov r24,r14  ;  D.1553, width  ;  110 movqi_insn/1  [length = 1]
  133.     mov r25,r15  ;  D.1553, width  ;  111 movqi_insn/1  [length = 1]
  134. /* epilogue start */
  135. .LM9:
  136.     pop r17  ;   ;  171 popqi [length = 1]
  137.     pop r16  ;   ;  172 popqi [length = 1]
  138.     pop r15  ;   ;  173 popqi [length = 1]
  139.     pop r14  ;   ;  174 popqi [length = 1]
  140.     pop r13  ;   ;  175 popqi [length = 1]
  141.     pop r12  ;   ;  176 popqi [length = 1]
  142.     ret  ;  177 return_from_epilogue  [length = 1]
  143. .L13:
  144. .LM10:
  145.     ldi r22,0  ;  D.1553   ;  120 movqi_insn/1  [length = 1]
  146.     ldi r23,0  ;  D.1553   ;  121 movqi_insn/1  [length = 1]
  147.     ldi r24,0  ;  D.1553   ;  122 movqi_insn/1  [length = 1]
  148.     ldi r25,0  ;  D.1553   ;  123 movqi_insn/1  [length = 1]
  149. /* epilogue start */
  150. .LM11:
  151.     pop r17  ;   ;  138 popqi [length = 1]
  152.     pop r16  ;   ;  139 popqi [length = 1]
  153.     pop r15  ;   ;  140 popqi [length = 1]
  154.     pop r14  ;   ;  141 popqi [length = 1]
  155.     pop r13  ;   ;  142 popqi [length = 1]
  156.     pop r12  ;   ;  143 popqi [length = 1]
  157.     ret  ;  144 return_from_epilogue  [length = 1]
  158.  
  159.  

Of course it is not necessary, but for fun it can be done :)

Edit

I tried to compile my adaptation of assembler code above without success. I have two errors "conditional branch destination is out of range". Interestingly they appear when branching to L13.

Code: Pascal  [Select][+][-]
  1.  
  2. function pulseInSimplAsmOrg(PortPtr: PUint8; bit, stateMask: Uint8; maxloops: UInt32): UInt32; assembler; nostackframe;
  3. label
  4. //  LFBB1,
  5.   L2,
  6.   L4,
  7.   L13,
  8.   L6,
  9.   L7,
  10.   //LM0,
  11.   //LM1,
  12.   //LM2,
  13.   //LM3,
  14.   //LM4,
  15.   //LM5,
  16.   L9,
  17.   L10;
  18.   //LM6,
  19.   //LM7,
  20.   //LM8,
  21.   //LM9,
  22.   //LM10,
  23.   //LM11;
  24. asm
  25. //LM0:
  26. //LFBB1:
  27.     push r12   //   //  130 pushqi1/1 [length = 1]
  28.     push r13   //   //  131 pushqi1/1 [length = 1]
  29.     push r14   //   //  132 pushqi1/1 [length = 1]
  30.     push r15   //   //  133 pushqi1/1 [length = 1]
  31.     push r16   //   //  134 pushqi1/1 [length = 1]
  32.     push r17   //   //  135 pushqi1/1 [length = 1]
  33. // prologue: function
  34. // frame size = 0
  35. // stack size = 6
  36. //.L__stack_usage = 6
  37.     mov r30,r24  //  port, port   //  2 *movhi/1  [length = 2]
  38.     mov r31,r25  //  port, port
  39. //     unsigned long width = 0//
  40. //     // wait for any previous pulse to end
  41. //     while ((*port & bit) == stateMask)
  42.  
  43. //LM1:
  44.     rjmp L2   //   //  181 jump  [length = 1]
  45. L4:
  46. //         if (--maxloops == 0)
  47. //LM2:
  48.     subi r16,1   //  maxloops,  //  17  addsi3/2  [length = 4]
  49.     sbc r17, r1   //  maxloops
  50.     sbc r18, r1   //  maxloops
  51.     sbc r19, r1   //  maxloops
  52.     breq L13  // ,   //  19  branch  [length = 1]
  53. L2:
  54. //         if (--maxloops == 0)
  55. //LM3:
  56.     ld r25,Z   //  D.1554, *port_7(D)   //  22  movqi_insn/4  [length = 1]
  57.     and r25,r22  //  D.1554, bit  //  24  andqi3/1  [length = 1]
  58.     cp r25,r20   //  D.1554, stateMask  //  25  *cmpqi/2  [length = 1]
  59.     breq L4   // ,   //  26  branch  [length = 1]
  60.     rjmp L6   //   //  184 jump  [length = 1]
  61. L7:
  62. //             return 0
  63. //
  64. //    // wait for the pulse to start
  65. //     while ((*port & bit) != stateMask)
  66. //         if (--maxloops == 0)
  67.  
  68. //LM4:
  69.     subi r16,1   //  maxloops,  //  31  addsi3/2  [length = 4]
  70.     sbc r17, r1   //  maxloops
  71.     sbc r18, r1   //  maxloops
  72.     sbc r19, r1   //  maxloops
  73.     breq L13  // ,   //  33  branch  [length = 1]  <-- @ackarwow here error "conditional branch destination is out of range"
  74. L6:
  75. //         if (--maxloops == 0)
  76. //LM5:
  77.     ld r25,Z   //  D.1554, *port_7(D)   //  41  movqi_insn/4  [length = 1]
  78.     and r25,r22  //  D.1554, bit  //  43  andqi3/1  [length = 1]
  79.     cpse r25,r20   //  D.1554, stateMask  //  44  enable_interrupt-3  [length = 1]
  80.     rjmp L7   //
  81.     mov r12, r1   //  width  //  7 *movsi/2  [length = 4]
  82.     mov r13, r1   //  width
  83.     mov r14, r1   //  width
  84.     mov r15, r1   //  width
  85.     rjmp L9   //   //  186 jump  [length = 1]
  86. L10:
  87. //             return 0//
  88. //
  89. //     // wait for the pulse to stop
  90. //     while ((*port & bit) == stateMask) {
  91. //         if (++width == maxloops)
  92. //
  93. //LM6:
  94.     ldi r24,-1   // ,   //  50  addsi3/3  [length = 5]
  95.     sub r12,r24  //  width,
  96.     sbc r13,r24  //  width,
  97.     sbc r14,r24  //  width,
  98.     sbc r15,r24  //  width,
  99.     cp r16,r12   //  maxloops, width  //  51  *cmpsi/2  [length = 4]
  100.     cpc r17,r13  //  maxloops, width
  101.     cpc r18,r14  //  maxloops, width
  102.     cpc r19,r15  //  maxloops, width
  103.     breq L13  // ,   //  52  branch  [length = 1]  <-- @ackarwow here error "conditional branch destination is out of range"
  104. L9:
  105. //         if (++width == maxloops)
  106. //LM7:
  107.     ld r24,Z   //  D.1554, *port_7(D)   //  60  movqi_insn/4  [length = 1]
  108.     and r24,r22  //  D.1554, bit  //  62  andqi3/1  [length = 1]
  109.     cp r24,r20   //  D.1554, stateMask  //  63  *cmpqi/2  [length = 1]
  110.     breq L10  // ,   //  64  branch  [length = 1]
  111. //             return 0//
  112. //
  113. //     return width//
  114. //
  115. //LM8:
  116.     mov r22,r12  //  D.1553, width  //  108 movqi_insn/1  [length = 1]
  117.     mov r23,r13  //  D.1553, width  //  109 movqi_insn/1  [length = 1]
  118.     mov r24,r14  //  D.1553, width  //  110 movqi_insn/1  [length = 1]
  119.     mov r25,r15  //  D.1553, width  //  111 movqi_insn/1  [length = 1]
  120. // epilogue start
  121. //LM9:
  122.     pop r17  //   //  171 popqi [length = 1]
  123.     pop r16  //   //  172 popqi [length = 1]
  124.     pop r15  //   //  173 popqi [length = 1]
  125.     pop r14  //   //  174 popqi [length = 1]
  126.     pop r13  //   //  175 popqi [length = 1]
  127.     pop r12  //   //  176 popqi [length = 1]
  128. //    ret  //  177 return_from_epilogue  [length = 1]
  129. L13:
  130. //LM10:
  131.     ldi r22,0  //  D.1553   //  120 movqi_insn/1  [length = 1]
  132.     ldi r23,0  //  D.1553   //  121 movqi_insn/1  [length = 1]
  133.     ldi r24,0  //  D.1553   //  122 movqi_insn/1  [length = 1]
  134.     ldi r25,0  //  D.1553   //  123 movqi_insn/1  [length = 1]
  135. // epilogue start
  136. //LM11:
  137.     pop r17  //   //  138 popqi [length = 1]
  138.     pop r16  //   //  139 popqi [length = 1]
  139.     pop r15  //   //  140 popqi [length = 1]
  140.     pop r14  //   //  141 popqi [length = 1]
  141.     pop r13  //   //  142 popqi [length = 1]
  142.     pop r12  //   //  143 popqi [length = 1]
  143. //    ret  //  144 return_from_epilogue  [length = 1]
  144. end;
  145.  
  146.  
« Last Edit: March 01, 2025, 09:04:50 pm by ackarwow »

ccrause

  • Hero Member
  • *****
  • Posts: 1007
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #18 on: March 02, 2025, 06:12:24 am »
The obvious consideration is that the compiler generated the code for the Pascal function, so it should be safe to call in the context of the current program.  FPC follows the avr-gcc ABI and the usage of registers (which registers can be left modified and which registers should be restored when a routine returns) is described here.

Thanks for the link. So according to ABI rules the function should work even after changing the compiler optimization level, do I understand correctly?
Yes

Quote
I don't know if the arduino compiler uses ABI rules (but uses code generated by avr-gcc). If yes - perhaps it's possible to use the original assembler code from the arduino sources (please correct me if I'm wrong):
There is no Arduino compiler, it uses existing tool chains.  For AVR controllers it uses avr-gcc as compiler, so yes in principle the assembler code should conform to the same ABI and can be re-used in Pascal.

ccrause

  • Hero Member
  • *****
  • Posts: 1007
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #19 on: March 02, 2025, 07:56:37 am »
Edit

I tried to compile my adaptation of assembler code above without success. I have two errors "conditional branch destination is out of range". Interestingly they appear when branching to L13.

I suspect this is due to the compiler calculating offsets based on code size in bytes, but branch offsets should be word based offsets.  Basically a compiler bug, I am working on a fix.

ccrause

  • Hero Member
  • *****
  • Posts: 1007
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #20 on: March 02, 2025, 02:04:25 pm »
Edit

I tried to compile my adaptation of assembler code above without success. I have two errors "conditional branch destination is out of range". Interestingly they appear when branching to L13.

I suspect this is due to the compiler calculating offsets based on code size in bytes, but branch offsets should be word based offsets.  Basically a compiler bug, I am working on a fix.

Refer to #41174 for details.

ackarwow

  • Full Member
  • ***
  • Posts: 139
    • Andrzej Karwowski's Homepage
Re: UnoLib - library in Pascal for Arduino Uno and ATMega328p
« Reply #21 on: March 02, 2025, 04:29:30 pm »
Edit

I tried to compile my adaptation of assembler code above without success. I have two errors "conditional branch destination is out of range". Interestingly they appear when branching to L13.

I suspect this is due to the compiler calculating offsets based on code size in bytes, but branch offsets should be word based offsets.  Basically a compiler bug, I am working on a fix.

Refer to #41174 for details.

@ccrause, thank you :) I updated my local FPC sources (aasmcpu.pas with your changes) and now the function compiles without errors!

 

TinyPortal © 2005-2018