- 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?
Review of Native Documentation (Part 2 - Design Issues)
Review of Native Documentation (Part 2 - Design Issues)
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:
Mike Perks
Re: Review of Native Documentation (Part 2 - Design Issues)
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:mikep wrote:It would be good if there was a ZX compiler flag to generate the C prototypes.
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)
Code: Select all
void foo(int16_t zp_ival, float *zp_fval) USED;
static int32_t bar(int16_t *zp_data) USED;
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 the alias feature be applied to regular ZBasic sub/functions or only those that are declared?
You can if you know its corresponding __vector_NN name.mikep wrote:Can an ISR be called from (or jumped to) from another ISR?
Agreed. That change was made yesterday.mikep wrote:A naked ISR might be better declared as ISR Timer1_CompB() Attribute(Naked)
An ISR must be public. The compiler will issue an error if it is Private.mikep wrote:What happens if someone declares a PRIVATE ISR?
Yes, the value 2 indicates an unhandled interrupt. This information has been added to the manual.mikep wrote:Presumably the unhandled interrupt error is retrievable using Register.FaultType.
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.mikep wrote:Is there a way for C code to call the ZBasic provided library functions such as calling zf_UpdateRTC.
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.
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.mikep wrote:An atomic block construction achieves the same as calls to DisableInt()/EnableInt() but without the nesting which could be a problem.
- Don Kinzer
Re: Review of Native Documentation (Part 2 - Design Issues)
Now I understand the build process, it isn't necessary. The --keep-files will do the job.dkinzer wrote:A special option could be created, I suppose.
I started a new thread on this one.dkinzer wrote:Currently, an alias can only be defined for an external procedure via the Declare statement.mikep wrote:Can the alias feature be applied to regular ZBasic sub/functions or only those that are declared?
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 betterdkinzer 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.
Mike Perks
Re: Review of Native Documentation (Part 2 - Design Issues)
Consider this code:mikep wrote:What I don't yet understand is the interaction between the Atomic block and the Disable/EnableInt() routines.
Code: Select all
Dim index as Integer
Sub Main()
Atomic
index = index + 1
End Atomic
End Sub
Code: Select all
static int16_t zv_index;
void zf_Main(void)
{
initHeap();
begin_atomic
zv_index++;
end_atomic
}
Code: Select all
void zf_Main(void)
{
e0: 0e 94 c0 03 call 0x780 ; 0x780 <initHeap>
initHeap();
begin_atomic
e4: 2f b7 in r18, 0x3f ; 63
e6: f8 94 cli
zv_index++;
e8: 80 91 02 01 lds r24, 0x0102
ec: 90 91 03 01 lds r25, 0x0103
f0: 01 96 adiw r24, 0x01 ; 1
f2: 90 93 03 01 sts 0x0103, r25
f6: 80 93 02 01 sts 0x0102, r24
end_atomic
fa: 2f bf out 0x3f, r18 ; 63
fc: 08 95 ret
}
Now consider this equivalent program:
Code: Select all
Dim index as Integer
Sub Main()
Dim flags as Byte
flags = DisableInt()
index = index + 1
Call EnableInt(flags)
End Sub
Code: Select all
void zf_Main(void)
{
uint8_t zv_flags;
initHeap();
zv_flags = dsblInt();
zv_index++;
enblInt(zv_flags);
}
Code: Select all
void zf_Main(void)
{
e0: 0e 94 c1 03 call 0x782 ; 0x782 <initHeap>
uint8_t zv_flags;
initHeap();
zv_flags = dsblInt();
e4: 2f b7 in r18, 0x3f ; 63
e6: f8 94 cli
zv_index++;
e8: 80 91 02 01 lds r24, 0x0102
ec: 90 91 03 01 lds r25, 0x0103
f0: 01 96 adiw r24, 0x01 ; 1
f2: 90 93 03 01 sts 0x0103, r25
f6: 80 93 02 01 sts 0x0102, r24
enblInt(zv_flags);
fa: 27 fd sbrc r18, 7
fc: 78 94 sei
fe: 08 95 ret
}
Having both constructs is useful but, unless you know exactly what you're doing, you shouldn't mix them.
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.mikep wrote:Disable/EnableCriticalSection()
- Don Kinzer
Re: Review of Native Documentation (Part 2 - Design Issues)
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.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
- Don Kinzer
Re: Review of Native Documentation (Part 2 - Design Issues)
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:Code: Select all
Dim index as Integer Sub Main() Atomic index = index + 1 End Atomic End Sub
Code: Select all
Dim index as Integer Sub Main() Dim flags as Byte flags = DisableInt() index = index + 1 Call EnableInt(flags) End Sub
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.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.
Mike Perks