Review of Native Documentation (Part 2 - Design Issues)

A private (members-only) forum for discussing all issues related to the Beta test of Native mode devices.
Locked
mikep
Posts: 796
Joined: 24 September 2005, 15:54 PM

Review of Native Documentation (Part 2 - Design Issues)

Post by mikep »

I have reviewed the modified documentation for the native mode devices. I concentrated on chapter 4 and the Resource Usage section. I have the following design/implementation questions:
  • Defining external routines for use by ZBasic code is good. However mistakes can be made. It would be good if there was a ZX compiler flag to generate the C prototypes. Then these could be used to either verify correctness of the C code or as a starting point to write the implementation. If the signatures get changed then the programmer is on their own to apply the newly generated signatures to existing C code.
  • Can the alias feature be applied to regular ZBasic sub/functions or only those that are declared? There would be no harm in allowing this synonym capability. If so then the description of the alias feature in section 3.21 needs to be extended.
  • Can an ISR be called from (or jumped to) from another ISR? This then leads to the idea of declaring an ISR as having an external alias.
  • A naked ISR might be better declared as ISR Timer1_CompB() Attribute(Naked), thereby using the same ATTRIBUTE keyword with a value rather than adding yet another specialized keyword (Naked).
  • Presumably an ISR is always Public by default and could be declared as PUBLIC ISR Timer1_CompB(). What happens if someone declares a PRIVATE ISR? This is a compiler error? Or could PRIVATE be used as a scoping check to someone tried to Call or Jump into an ISR, this wouldn't be allowed by the ZBasic compiler.
  • Presumably the unhandled interrupt error is retrievable using Register.FaultType. The text on the top of page 90 should clarify this fact.
  • The new functions DisableInt()/EnableInt() describe what the function implements rather than what it does. If this was renamed to something like Enter/ExitCriticalSection then these same routines could also be used in regular ZBasic code.
  • If an ISR is written in C, how can it update the RTC if needed? Is there a way for C code to call the ZBasic provided library functions such as calling zf_UpdateRTC. I couldn't find a description of this anywhere.
  • An atomic block construction achieves the same as calls to DisableInt()/EnableInt() but without the nesting which could be a problem. There should really only be one solution to this problem. DisableInt()/EnableInt() at its simplest could result in a function that is inlined or is that what Atomic does?
I put everything together in one append. Feel free to start new threads for items that need further discussion/exploration.
Mike Perks
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: Review of Native Documentation (Part 2 - Design Issues)

Post by dkinzer »

mikep wrote:It would be good if there was a ZX compiler flag to generate the C prototypes.
A special option could be created, I suppose. However, prototypes are generated for all routines as part of the normal build process. Consider the case where these two line are in myprog.bas:

Code: Select all

Public Declare Sub foo(ByVal ival as Integer, ByRef fval as Single) Attribute(Used)
Private Declare Function bar(ByRef data() as Integer) as Long Attribute(Used)
The Attribute(Used) was added to ensure that the prototypes get generated even if the routines are not referenced. The prototype for foo() will be in myproj.h (since it is Public) while the prototype for bar() will be in myproj.c (since it is Private). The prototypes will look like this:

Code: Select all

void foo(int16_t zp_ival, float *zp_fval) USED;
static int32_t bar(int16_t *zp_data) USED;
You'll have to use the --keep-files option to force retention of the files and the gcc compiler will issue a warning that the static bar() was declared but never defined.
mikep wrote:Can the alias feature be applied to regular ZBasic sub/functions or only those that are declared?
Currently, an alias can only be defined for an external procedure via the Declare statement. I'm not sure what value there would be in allowing an alias for a normal ZBasic procedure. An alias is necessary for an external has a name that is not a legal ZBasic identifier. For example, if an external has a name that begins with an underscore it can't be directly referenced.
mikep wrote:Can an ISR be called from (or jumped to) from another ISR?
You can if you know its corresponding __vector_NN name.
mikep wrote:A naked ISR might be better declared as ISR Timer1_CompB() Attribute(Naked)
Agreed. That change was made yesterday.
mikep wrote:What happens if someone declares a PRIVATE ISR?
An ISR must be public. The compiler will issue an error if it is Private.
mikep wrote:Presumably the unhandled interrupt error is retrievable using Register.FaultType.
Yes, the value 2 indicates an unhandled interrupt. This information has been added to the manual.
mikep wrote:Is there a way for C code to call the ZBasic provided library functions such as calling zf_UpdateRTC.
None of the several hundred ZX Library routines are documented and, except for special cases, they are not likely to be. If you examine the generated code you can fairly easily deduce the semantics of the parameters to the ZX Library routines but I think it unwise to publish documentation on them at this point. (Incidentally, none of the ZX library routines have a prefix like zf_ since they might then collide with routines defined in user code.

Another option is to define a procedure in ZBasic and call it either from inline C code or from an external C module. Here again, the prototypes generated by the ZBasic compiler will tell you how to invoke them if it's not otherwise obvious.
mikep wrote:An atomic block construction achieves the same as calls to DisableInt()/EnableInt() but without the nesting which could be a problem.
Atomic blocks do nest properly. The primary advantage to using an Atomic Block is that there is no danger of forgetting to re-enable interrupts and that it correctly handles the exception cases like Exit Do and Exit Sub. Using an Atomic block is the recommended method but there are conceivably cases where lower level control via DisableInt() and EnableInt() would be necessary or desirable.
- Don Kinzer
mikep
Posts: 796
Joined: 24 September 2005, 15:54 PM

Re: Review of Native Documentation (Part 2 - Design Issues)

Post by mikep »

dkinzer wrote:A special option could be created, I suppose.
Now I understand the build process, it isn't necessary. The --keep-files will do the job.
dkinzer wrote:
mikep wrote:Can the alias feature be applied to regular ZBasic sub/functions or only those that are declared?
Currently, an alias can only be defined for an external procedure via the Declare statement.
I started a new thread on this one.
dkinzer wrote:Atomic blocks do nest properly. The primary advantage to using an Atomic Block is that there is no danger of forgetting to re-enable interrupts and that it correctly handles the exception cases like Exit Do and Exit Sub. Using an Atomic block is the recommended method but there are conceivably cases where lower level control via DisableInt() and EnableInt() would be necessary or desirable.
I think the block constructor Atomic...End Atomic works providing that they are paired at the correct level of nesting within other block structures such as a While Loop. It does make it easy on the ZBasic programmer. What I don't yet understand is the interaction between the Atomic block and the Disable/EnableInt() routines. Using both could be a problem and more of a problem than just having one construct or the other. The advantage of the library routines is that they don't introduce any special extra language constructs. It's a bit like using Semaphore. If you don't use it properly you can get into trouble but then again you shouldn't be using it unless you understand what you are doing. Same for Disable/EnableCriticalSection() - I still like this name better :)
Mike Perks
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: Review of Native Documentation (Part 2 - Design Issues)

Post by dkinzer »

mikep wrote:What I don't yet understand is the interaction between the Atomic block and the Disable/EnableInt() routines.
Consider this code:

Code: Select all

Dim index as Integer
Sub Main()
   Atomic
    index = index + 1
  End Atomic
End Sub
The resulting C code is:

Code: Select all

static int16_t zv_index;
void zf_Main(void)
{
  initHeap();
  begin_atomic
    zv_index++;
  end_atomic
}
The initHeap() call is added to the Main() routine only; as the name suggests, it initializes the heap. The macros begin_atomic and end_atomic are defined in zxlib.h, included in every source file. The resulting assembly code looks like this:

Code: Select all

void zf_Main(void)
{
  e0&#58;	0e 94 c0 03 	call	0x780	; 0x780 <initHeap>
	initHeap&#40;&#41;;
	begin_atomic
  e4&#58;	2f b7       	in	r18, 0x3f	; 63
  e6&#58;	f8 94       	cli
		zv_index++;
  e8&#58;	80 91 02 01 	lds	r24, 0x0102
  ec&#58;	90 91 03 01 	lds	r25, 0x0103
  f0&#58;	01 96       	adiw	r24, 0x01	; 1
  f2&#58;	90 93 03 01 	sts	0x0103, r25
  f6&#58;	80 93 02 01 	sts	0x0102, r24
	end_atomic
  fa&#58;	2f bf       	out	0x3f, r18	; 63
  fc&#58;	08 95       	ret
&#125;
(Note: I/O port 0x3f is the processor status register.)

Now consider this equivalent program:

Code: Select all

Dim index as Integer
Sub Main&#40;&#41;
  Dim flags as Byte
  flags = DisableInt&#40;&#41;
    index = index + 1
  Call EnableInt&#40;flags&#41;
End Sub
This yields the C code:

Code: Select all

void zf_Main&#40;void&#41;
&#123;
  uint8_t zv_flags;
  initHeap&#40;&#41;;
  zv_flags = dsblInt&#40;&#41;;
  zv_index++;
  enblInt&#40;zv_flags&#41;;
&#125;
Here, again, dsblInt() and enblInt() are macros. The resulting assembly code is:

Code: Select all

void zf_Main&#40;void&#41;
&#123;
  e0&#58;	0e 94 c1 03 	call	0x782	; 0x782 <initHeap>
	uint8_t zv_flags;
	initHeap&#40;&#41;;
	zv_flags = dsblInt&#40;&#41;;
  e4&#58;	2f b7       	in	r18, 0x3f	; 63
  e6&#58;	f8 94       	cli
	zv_index++;
  e8&#58;	80 91 02 01 	lds	r24, 0x0102
  ec&#58;	90 91 03 01 	lds	r25, 0x0103
  f0&#58;	01 96       	adiw	r24, 0x01	; 1
  f2&#58;	90 93 03 01 	sts	0x0103, r25
  f6&#58;	80 93 02 01 	sts	0x0102, r24
	enblInt&#40;zv_flags&#41;;
  fa&#58;	27 fd       	sbrc	r18, 7
  fc&#58;	78 94       	sei
  fe&#58;	08 95       	ret
&#125;
You'll observe that this implementation is one instruction (two bytes) longer because it executes the SEI instruction conditionally instead of just writing the saved status register value back to the status register. The enblInt() macro is written that way so that it can be used in situations where preserving the other status flags is important. Admittedly, that's not often needed in C code; more so in assembly language code, however, from which it is derived.

Having both constructs is useful but, unless you know exactly what you're doing, you shouldn't mix them.
mikep wrote:Disable/EnableCriticalSection()
I considered EnterCriticalSection/LeaveCriticalSection and BeginCriticalSection/EndCriticalSection but opted for Atomic/End Atomic both due to brevity and the similarity to other block structures in ZBasic (e.g. Sub/End Sub, Select/End Select, If/End If, etc.). I suppose that CriticalSection/End CriticalSection would work, too.
- Don Kinzer
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: Review of Native Documentation (Part 2 - Design Issues)

Post by dkinzer »

mikep wrote:I think the block constructor Atomic...End Atomic works providing that they are paired at the correct level of nesting within other block structures such as a While Loop
I forgot to mention that the compiler ensures that the beginning and ending of an Atomic block are properly paired and properly nested just as it does with all other types of blocks. It will generate an error if you forget to close an inner block.
- Don Kinzer
mikep
Posts: 796
Joined: 24 September 2005, 15:54 PM

Re: Review of Native Documentation (Part 2 - Design Issues)

Post by mikep »

dkinzer wrote:

Code: Select all

Dim index as Integer
Sub Main&#40;&#41;
   Atomic
    index = index + 1
  End Atomic
End Sub

Code: Select all

Dim index as Integer
Sub Main&#40;&#41;
  Dim flags as Byte
  flags = DisableInt&#40;&#41;
    index = index + 1
  Call EnableInt&#40;flags&#41;
End Sub
Thank you for explaining this detail on the generated code. Now I'm even more convinced that the second form is very unlikely to ever be needed. Perhaps you should exclude it until someone has the requirement.
dkinzer wrote:I considered EnterCriticalSection/LeaveCriticalSection and BeginCriticalSection/EndCriticalSection but opted for Atomic/End Atomic both due to brevity and the similarity to other block structures in ZBasic (e.g. Sub/End Sub, Select/End Select, If/End If, etc.). I suppose that CriticalSection/End CriticalSection would work, too.
Atomic/End Atomic is fine. I was suggesting that Disable/EnableInt are renamed to something like Enter/ExitCriticalSection but that doesn't matter if you decide not to include these APIs per my point above.
Mike Perks
Locked